12. How to write Service Invocation Plugins

12.1: Example service invocation plugin

To get started, you can download from the JOpera website http://www.jopera.ethz.ch/download/demos a sample Hello World demo adapter.

12.2: Setting up a new service invocation plugin

This section describes how to create a new Eclipse plugin which extends the extension-points needed to insert a service invocation plugin into JOpera. A new Eclipse plugin project should be created with a dependency to the ch.ethz.jopera.kernel and ch.ethz.jopera.core plugins.


Figure 12.1: Dependencies of a Subsystem plugin

Once the dependency are set, the extensions can be declared by clicking on the corresponding tab.
  • The first extension plugs into the JOpera kernel a new service invocation adapter class.
    1. Add a ch.ethz.jopera.kernel.SubSystem extension
    2. Right click it and add a new SubSystem element
    3. Enter the Subsystem ID ('SID'), which uniquely identifies the type of service that are going to be invoked through the SubSystem. As an example, in this tutorial we use TEST
    4. Enter the name of the Java class which will implement the ISubsystem interface
  • The second extension defines the system parameters that describe how the service type should be invoked. This information is described in an OML file that should be packaged with the plugin.
    1. Add a ch.ethz.jopera.core.model extension
    2. Right click it and add a new model element
    3. Browse for the OML file which defines the system parameters that will be passed to the subsystem when invoking services of the corresponding type


    Figure 12.2: Extension provided by a Subsystem plugin

12.3: The ISubSystem Interface

This interface must be implemented by all service invocation plugins. It contains only two methods.

  • public void Execute(IJob Job);
    
    The Execute method is called by JOpera to invoke a service. The information controlling the invocation is stored in the IJob parameter.
  • public State Signal(int Signal);
    
    The Signal method is used by JOpera to interact with an active invocation, e.g., in order to abort it. As a first approximation, it is not necessary to implement this method, as it may not always be required to provide such advanced functionality.

12.4: The IJob Interface

The information describing the service invocation to be performed by the plugin is packaged into an IJob object, which is passed as a parameter to the execute method. This object is used to store all input/output parameters of the service invocation, and also to inform JOpera of the final outcome of the invocation, i.e., whether it was successful or it failed. The most important methods of IJob are:

  • getInput()
  • getActiveCommand()
  • getOutput()
  • getSystemOutput()
  • setState()
  • notifyFinished()
Their usage will be explained in the following sections. First we discuss how to transfer control, then how to report failures in the invocation and finally how to transfer data.

12.5: Control flow mapping

The execute method of the ISubSystem interface is called by JOpera in a dedicated thread to perform the transfer of control from a running task to the corresponding service provider. This can happen synchronously (a thread waits) or asynchronously (initiated by a thread and completed by another).

12.5.1: Synchronous Service Invocation

In the simplest case, this happens synchronously. This means that the service invocation is completed by the time the execute method returns. This is straightforward to implement as all the code for initiating the invocation (e.g., sending a request to the service provider) and completing it (e.g., reading and decoding the response) is contained in the execute method, which can be structured in the following three steps

  1. Call the service provider
  2. Wait for an answer
  3. Retrieve the response

12.5.2: Asynchronous Service Invocation

Depending on whether the service is invoked locally or remotely and depending on how long the service invocation typically lasts, it may not be efficient to keep waiting for an answer as this keeps the thread running the execute method busy. To address this problem, as an alternative, it is also possible to use the execute method to only perform step 1., i.e., initiate the call, and handle the other steps asynchronously so that the thread invoking the service does not block and can be used by JOpera to run other tasks. In this case, it is the responsibility of the service invocation plugin to notify JOpera whenever it detects that the service has completed its execution. Since the execute method has already returned, the plugin must use its own thread to run the following code:

job.setState(State.FINISHED);
job.notifyFinished();
This code will inform JOpera of the asynchronous completion of the job representing the service invocation.

12.6: Failure detection

It is the responsability of the plugin to provide the necessary logic to detect whether a service invocation was successful. To do so, a successful invocation should use the following code:

job.setState(State.FINISHED);
If a failure occurred, e.g., a timeout, or any kind of exceptional condition has been detected, JOpera can be notified with the following:
job.setState(State.FAILED);
Additional information about the failure, e.g., describing its cause with some error messages, can be stored in the plugin-specific system output parameters.

12.7: Data flow mapping

In addition to transferring control, the service invocation adapter is responsible for transferring data between JOpera and the service provider for the specific kind of service. The subsystem is responsible for implementing the required encoding/decoding of the data. JOpera structures the data parameters exchanged with the subsystem as follows. First of all, a distinction is made between input and output data parameters. From the point of view of the service, input data is sent as part of the request, whereas output data is retrieved as part of the response. JOpera also distinguishes between application-level data from system-level metadata. The IJob interface provides access to all of these parameters, identified by their name, through the following Maps

  • 'public Map getInput();' //get input data parameters
  • 'public Map getActiveCommand();' //get input metadata parameters (read-only)
  • 'public Map getOutput();' //set output data parameters
  • 'public Map getSystemOutput();' //set output metadata parameters

Note: The values for the parameters stored in these maps can be set to any Java Serializable data type.

12.8: Threading model

JOpera instantiates a new object of the given service invocation plugin class for each service invocation to be performed. Furthermore, JOpera calls the execute method of the newly created object from within a dedicated thread. Therefore, since JOpera already handles the multithreaded issues for the concurrent invocation of multiple services, the plugin -- under normal circumnstances -- should not have to fork off additional threads to perform the invocation.

12.9: Example Code for Synchronous invocation

void Execute(IJob job)
{

	//take the system input parameters
	Map c = job.getActiveCommand();

	//do something with it!
	
	//set system output parameters
	job.getSystemOutput().put("sys_output",...);

	//set output parameters	
	job.getOutput().put("output",...);	

	//detect failures (somehow) and set the outcome of the job
	if (ok)
		job.setState(State.FINISHED);
	else
		job.setState(State.FAILED);
			
}

12.10: Example Code for Asynchronous invocation

void Execute(IJob job)
{

	//take the system input parameters
	Map c = job.getActiveCommand();
	
	//start running something with it!
	
	//do not change the state of the job
	
}


//it is the responsability of the plugin to call this method
//from its own thread whenever the service invocation has completed
//asynchronously
void Complete(IJob job)
{

	//set system output parameters
	job.getSystemOutput().put("sys_output",...);
	
	job.getOutput().put("output",...);	

	if (ok)
		job.setState(State.FINISHED);
	else
		job.setState(State.FAILED);
			
	//notify JOpera about it
	job.notifyFinished();			
}

12.11: Example Code for the Signal Method

This is the simplest implementation of the signal method. No matter what signal is given to the subsystem, the state of its job is left unmodified, i.e., it remains Running.

	public State Signal(int Signal)
	{
		//by default indicate that the signal
		//did not affect the running job		
		return State.RUNNING;
	}