mirror of
https://github.com/corda/corda.git
synced 2025-06-13 04:38:19 +00:00
CORDA-3565: Port ServiceStateSupport
from ENT to OS (#5916)
* CORDA-3565: `ServiceStateSupport` and supporting classes * CORDA-3565:Plug `ServiceLifecycleSupport` into `MessagingService` * CORDA-3565: Detekt baseline update * CORDA-3565: React to MessagingServer going up and addition logging for up/down Co-authored-by: Matthew Nesbit <matthew.nesbit@r3.com>
This commit is contained in:
@ -0,0 +1,30 @@
|
||||
package net.corda.nodeapi.internal.lifecycle
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.json.Json
|
||||
|
||||
object LifecycleStatusHelper {
|
||||
private val serviceStatusMap = ConcurrentHashMap<String, Boolean>()
|
||||
|
||||
fun setServiceStatus(serviceName: String, active: Boolean) {
|
||||
serviceStatusMap[serviceName] = active
|
||||
}
|
||||
|
||||
fun getServiceStatus(serviceName: String) = serviceStatusMap.getOrDefault(serviceName, false)
|
||||
|
||||
/**
|
||||
* Return a string copy of a JSON object containing the status of each service,
|
||||
* and whether this bridge is the master.
|
||||
*/
|
||||
fun getServicesStatusReport(isMaster: Boolean): String {
|
||||
return Json.createObjectBuilder().apply {
|
||||
val statusList = Json.createArrayBuilder().apply {
|
||||
serviceStatusMap.forEach { name: String, status: Boolean ->
|
||||
add(Json.createObjectBuilder().add(name, status).build())
|
||||
}
|
||||
}
|
||||
add("master", isMaster)
|
||||
add("services", statusList)
|
||||
}.build().toString()
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package net.corda.nodeapi.internal.lifecycle
|
||||
|
||||
import rx.Observable
|
||||
|
||||
/**
|
||||
* Basic interface to represent the dynamic life cycles of services that may be running, but may have to await external dependencies.
|
||||
* Implementations of this should be implemented in a thread safe fashion.
|
||||
*/
|
||||
interface ServiceStateSupport {
|
||||
/**
|
||||
* Reads the current dynamic status of the service, which should only become true after the service has been started,
|
||||
* any dynamic resources have been started/registered and any network connections have been completed.
|
||||
* Failure to acquire a resource, or manual stop of the service, should return this to false.
|
||||
*/
|
||||
val active: Boolean
|
||||
|
||||
/**
|
||||
* This Observer signals changes in the [active] variable, it should not be triggered for events that don't flip the [active] state.
|
||||
*/
|
||||
val activeChange: Observable<Boolean>
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple interface for generic start/stop service lifecycle and the [active] flag indicating runtime ready state.
|
||||
*/
|
||||
interface ServiceLifecycleSupport : ServiceStateSupport, AutoCloseable {
|
||||
/**
|
||||
* Manual call to allow the service to start the process towards becoming active.
|
||||
* Note wiring up service dependencies should happen in the constructor phase, unless this is to avoid a circular reference.
|
||||
* Also, resources allocated as a result of start should be cleaned up as much as possible by stop.
|
||||
* The [start] method should allow multiple reuse, assuming a [stop] call was made to clear the state.
|
||||
*/
|
||||
fun start()
|
||||
|
||||
/**
|
||||
* Release the resources created by [start] and drops the [active] state to false.
|
||||
*/
|
||||
fun stop()
|
||||
|
||||
override fun close() = stop()
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package net.corda.nodeapi.internal.lifecycle
|
||||
|
||||
import org.slf4j.Logger
|
||||
import rx.Observable
|
||||
import rx.subjects.BehaviorSubject
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
/**
|
||||
* Simple implementation of [ServiceStateSupport] service domino logic using RxObservables.
|
||||
*/
|
||||
class ServiceStateHelper(private val log: Logger, private val serviceName: String = log.name.split(".").last()) : ServiceStateSupport {
|
||||
private val lock = ReentrantLock()
|
||||
|
||||
// Volatile to prevent deadlocks when locking on read.
|
||||
@Volatile
|
||||
private var _active: Boolean = false
|
||||
override var active: Boolean
|
||||
get() = _active
|
||||
set(value) {
|
||||
lock.withLock {
|
||||
if (value != _active) {
|
||||
_active = value
|
||||
log.info("Status change to $value")
|
||||
LifecycleStatusHelper.setServiceStatus(serviceName, value)
|
||||
_activeChange.onNext(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val _activeChange: BehaviorSubject<Boolean> = BehaviorSubject.create<Boolean>(false)
|
||||
private val _threadSafeObservable: Observable<Boolean> = _activeChange.serialize().distinctUntilChanged()
|
||||
override val activeChange: Observable<Boolean>
|
||||
get() = _threadSafeObservable
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple implementation of [ServiceStateSupport] where it only reports [active] true when a set of dependencies are all [active] true.
|
||||
*/
|
||||
class ServiceStateCombiner(val services: List<ServiceStateSupport>) : ServiceStateSupport {
|
||||
override val active: Boolean
|
||||
get() = services.all { it.active }
|
||||
|
||||
private val _activeChange = Observable.combineLatest(services.map { it.activeChange }, { x -> x.all { y -> y as Boolean } }).serialize().distinctUntilChanged()
|
||||
override val activeChange: Observable<Boolean>
|
||||
get() = _activeChange
|
||||
}
|
Reference in New Issue
Block a user