API: Service Classes

Service classes are long-lived instances that can trigger or be triggered by flows from within a node. A Service class is limited to a single instance per node. During startup, the node handles the creation of the service.

Services allow related, reusable, functions to be separated into their own class where their functionality is grouped together. These functions can then be called from other services or flows.

Creating a Service

To define a Service class:

  • Add the CordaService annotation
  • Add a constructor with a single parameter of AppServiceHub
  • Extend SingletonSerializeAsToken

Below is an empty implementation of a Service class:

@CordaService
class MyCordaService(private val serviceHub: AppServiceHub) : SingletonSerializeAsToken() {

    init {
        // code ran at service creation / node startup
    }

    // public api of service
}
@CordaService
public class MyCordaService extends SingletonSerializeAsToken {

    private AppServiceHub serviceHub;

    public MyCordaService(AppServiceHub serviceHub) {
        this.serviceHub = serviceHub;
        // code ran at service creation / node startup
    }

    // public api of service
}

The AppServiceHub provides the ServiceHub functionality to the Service class, with the extra ability to start flows. Starting flows from AppServiceHub is explained further in Starting Flows from a Service .

Code can be run during node startup when the class is being initialised. To do so, place the code into the init block or constructor. This is useful when a service needs to establish a connection to an external database or setup observables via ServiceHub.trackBy during its startup. These can then persist during the service’s lifetime.

Retrieving a Service

A Service class can be retrieved by calling ServiceHub.cordaService which returns the single instance of the class passed into the function:

val service: MyCordaService = serviceHub.cordaService(MyCordaService::class.java)
MyCordaService service = serviceHub.cordaService(MyCordaService.class);

Starting Flows from a Service

Starting flows via a service can lead to deadlock within the node’s flow worker queue, which will prevent new flows from starting. To avoid this, the rules bellow should be followed:

  • When called from a running flow, the service must invoke the new flow from another thread. The existing flow cannot await the execution of the new flow.
  • When ServiceHub.trackBy is placed inside the service, flows started inside the observable must be placed onto another thread.
  • Flows started by other means, do not require any special treatment.

Was this page helpful?

Thanks for your feedback!

Chat with us

Chat with us on our #docs channel on slack. You can also join a lot of other slack channels there and have access to 1-on-1 communication with members of the R3 team and the online community.

Propose documentation improvements directly

Help us to improve the docs by contributing directly. It's simple - just fork this repository and raise a PR of your own - R3's Technical Writers will review it and apply the relevant suggestions.

We're sorry this page wasn't helpful. Let us know how we can make it better!

Chat with us

Chat with us on our #docs channel on slack. You can also join a lot of other slack channels there and have access to 1-on-1 communication with members of the R3 team and the online community.

Create an issue

Create a new GitHub issue in this repository - submit technical feedback, draw attention to a potential documentation bug, or share ideas for improvement and general feedback.

Propose documentation improvements directly

Help us to improve the docs by contributing directly. It's simple - just fork this repository and raise a PR of your own - R3's Technical Writers will review it and apply the relevant suggestions.