mirror of
https://github.com/corda/corda.git
synced 2024-12-28 16:58:55 +00:00
0978500a9a
* CORDA-2942: Port minimal set of changes to make lifecycle events work ... and make codebase compile. * CORDA-2942: Undo some changes which are not strictly speaking necessary * CORDA-2942: Make `NodeServicesContext` leaner and delete `extensions-api` module * CORDA-2942: Reduce even more number of files affected * CORDA-2942: Integration test fix * CORDA-2942: Make events `AfterStart` and `BeforeStop` generic w.r.t. `NodeServicesContext` * CORDA-2942: `NodeLifecycleObserverService` and a set of integration tests. Public API violations are expected as well as integration tests failing. * CORDA-2942: Re-work to introduce `ServiceLifecycleObserver` * CORDA-2942: Explicitly mention a type of exception that may be thrown for some events. * CORDA-2942: Register `ServiceLifecycleObserver` through `AppServiceHub` * CORDA-2942: Fix integration test + KDocs update * CORDA-2942: Detekt and `api-current` update * CORDA-2942: Improvement to `CordaServiceLifecycleFatalTests` ... or else it has side effects on other tests. * CORDA-2942: Add an integration test for new API use in Java Driver test is written in Kotlin, but services definition is written in Java. Also KDocs improvements. * CORDA-2942: Documentation and release notes update * CORDA-2942: First set of changes following review by @mnesbit * CORDA-2942: Second set of changes following review by @mnesbit * CORDA-2942: Added multi-threaded test * CORDA-2942: Fixes * CORDA-2942: Undo changes to `api-current.txt` * CORDA-2942: Bare mimimum change to `api-current.txt` for CI gate to pass. * CORDA-2942: Address review feedback from @rick-r3 * CORDA-2942: Detekt update * CORDA-2942: Delete `ServiceLifecycleObserverPriority` and replace it with `Int` after discussion with @mnesbit * CORDA-2942: Introduce more `NodeLifecycleEvent` and switch services to listen for those events * CORDA-2942: Few more changes after input from @rick-r3 * First stub on integration test Unfinished - hang on issue and pay * CORDA-2942: Switch to use out-of-process nodes for the inetgration test Currently Alice and Notary stuck waiting to hear from each other. * CORDA-2942: Extra log lines during event distribution * CORDA-2942: Asynchronously distribute lifecycle events * CORDA-2942: Await for complete P2P client start-up Next step: Add vault query to integration test * CORDA-2942: Asynchronously distribute lifecycle events Next step: Improve integration test * CORDA-2942: Fix test broken by recent changes and improve logging * CORDA-2942: Improvement of the test to be able to monitor actions performed by @CordaService in the remote process * CORDA-2942: Add node re-start step to the integration test * CORDA-2942: Remove `CORDAPP_STOPPED` event for now * CORDA-2942: s/CORDAPP_STARTED/STATE_MACHINE_STARTED/ * CORDA-2942: Inverse the meaning of `priority` as requested by @rick-r3 * CORDA-2942: Register `AppServiceHubImpl` for lifecycle events and put a warning when SMM is not ready.
130 lines
5.2 KiB
ReStructuredText
130 lines
5.2 KiB
ReStructuredText
.. highlight:: kotlin
|
|
.. raw:: html
|
|
|
|
<script type="text/javascript" src="_static/jquery.js"></script>
|
|
<script type="text/javascript" src="_static/codesets.js"></script>
|
|
|
|
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:
|
|
|
|
.. container:: codeset
|
|
|
|
.. sourcecode:: kotlin
|
|
|
|
@CordaService
|
|
class MyCordaService(private val serviceHub: AppServiceHub) : SingletonSerializeAsToken() {
|
|
|
|
init {
|
|
// Custom code ran at service creation
|
|
|
|
// Optional: Express interest in receiving lifecycle events
|
|
services.register { processEvent(it) }
|
|
}
|
|
|
|
private fun processEvent(event: ServiceLifecycleEvent) {
|
|
// Lifecycle event handling code including full use of serviceHub
|
|
when (event) {
|
|
STATE_MACHINE_STARTED -> {
|
|
services.vaultService.queryBy(...)
|
|
services.startFlow(...)
|
|
}
|
|
else -> {
|
|
// Process other types of events
|
|
}
|
|
}
|
|
}
|
|
|
|
// public api of service
|
|
}
|
|
|
|
.. sourcecode:: java
|
|
|
|
@CordaService
|
|
public class MyCordaService extends SingletonSerializeAsToken {
|
|
|
|
private final AppServiceHub serviceHub;
|
|
|
|
public MyCordaService(AppServiceHub serviceHub) {
|
|
this.serviceHub = serviceHub;
|
|
// Custom code ran at service creation
|
|
|
|
// Optional: Express interest in receiving lifecycle events
|
|
serviceHub.register(SERVICE_PRIORITY_NORMAL, this::processEvent);
|
|
}
|
|
|
|
private void processEvent(ServiceLifecycleEvent event) {
|
|
switch (event) {
|
|
case STATE_MACHINE_STARTED:
|
|
serviceHub.getVaultService().queryBy(...)
|
|
serviceHub.startFlow(...)
|
|
break;
|
|
default:
|
|
// Process other types of events
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 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 :ref:`Starting Flows from a Service <starting_flows_from_a_service>`.
|
|
|
|
The ``AppServiceHub`` also provides access to ``database`` which will enable the Service class to perform DB transactions from the threads
|
|
managed by the Service.
|
|
|
|
Also the ``AppServiceHub`` provides ability for ``CordaService`` to subscribe for lifecycle events of the node, such that it will get notified
|
|
about node finishing initialisation and when the node is shutting down such that ``CordaService`` will be able to perform clean-up of some
|
|
critical resources. For more details please have refer to KDocs for ``ServiceLifecycleObserver``.
|
|
|
|
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:
|
|
|
|
.. container:: codeset
|
|
|
|
.. sourcecode:: kotlin
|
|
|
|
val service: MyCordaService = serviceHub.cordaService(MyCordaService::class.java)
|
|
|
|
.. sourcecode:: java
|
|
|
|
MyCordaService service = serviceHub.cordaService(MyCordaService.class);
|
|
|
|
.. warning:: ``ServiceHub.cordaService`` should not be called during initialisation of a flow and should instead be called in line where
|
|
needed or set after the flow's ``call`` function has been triggered.
|
|
|
|
.. _starting_flows_from_a_service:
|
|
|
|
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.
|
|
|
|
.. note:: It is possible to avoid deadlock without following these rules depending on the number of flows running within the node. But, if the
|
|
number of flows violating these rules reaches the flow worker queue size, then the node will deadlock. It is best practice to
|
|
abide by these rules to remove this possibility. |