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:
Viktor Kolomeyko
2020-02-03 09:47:12 +00:00
committed by GitHub
parent 0c9b41591f
commit 90df56c173
10 changed files with 167 additions and 36 deletions

View File

@ -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()
}
}

View File

@ -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()
}

View File

@ -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
}