@GwtIncompatible public final class ServiceManager extends Object
While it is recommended that service lifecycles be managed via this class, state transitions
initiated via other mechanisms do not impact the correctness of its methods. For example, if the
services are started by some mechanism besides startAsync()
, the listeners will be invoked
when appropriate and awaitHealthy()
will still work as expected.
Here is a simple example of how to use a ServiceManager
to start a server.
class Server {
public static void main(String[] args) {
Set<Service> services = ...;
ServiceManager manager = new ServiceManager(services);
manager.addListener(new Listener() {
public void stopped() {}
public void healthy() {
// Services have been initialized and are healthy, start accepting requests...
}
public void failure(Service service) {
// Something failed, at this point we could log it, notify a load balancer, or take
// some other action. For now we will just exit.
System.exit(1);
}
},
MoreExecutors.directExecutor());
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
// Give the services 5 seconds to stop to ensure that we are responsive to shutdown
// requests.
try {
manager.stopAsync().awaitStopped(5, TimeUnit.SECONDS);
} catch (TimeoutException timeout) {
// stopping timed out
}
}
});
manager.startAsync(); // start all the services asynchronously
}
}
This class uses the ServiceManager's methods to start all of its services, to respond to service failure and to ensure that when the JVM is shutting down all the services are stopped.
Modifier and Type | Class and Description |
---|---|
static class |
ServiceManager.Listener
A listener for the aggregate state changes of the services that are under management.
|
Constructor and Description |
---|
ServiceManager(Iterable<? extends Service> services)
Constructs a new instance for managing the given services.
|
Modifier and Type | Method and Description |
---|---|
void |
addListener(ServiceManager.Listener listener)
Deprecated.
Use the overload that accepts an
executor. For equivalent behavior, pass
MoreExecutors.directExecutor() . However,
consider whether another executor would be more appropriate, as discussed in the docs for
ListenableFuture.addListener . This method is scheduled
for deletion in October 2020. |
void |
addListener(ServiceManager.Listener listener,
Executor executor)
Registers a
ServiceManager.Listener to be executed on the given
executor. |
void |
awaitHealthy()
Waits for the
ServiceManager to become healthy. |
void |
awaitHealthy(long timeout,
TimeUnit unit)
Waits for the
ServiceManager to become healthy for no more
than the given time. |
void |
awaitStopped()
Waits for the all the services to reach a terminal state.
|
void |
awaitStopped(long timeout,
TimeUnit unit)
Waits for the all the services to reach a terminal state for no more than the given time.
|
boolean |
isHealthy()
Returns true if all services are currently in the running state.
|
ImmutableSetMultimap<Service.State,Service> |
servicesByState()
Provides a snapshot of the current state of all the services under management.
|
ServiceManager |
startAsync()
Initiates service startup on all the services being managed.
|
ImmutableMap<Service,Long> |
startupTimes()
Returns the service load times.
|
ServiceManager |
stopAsync()
Initiates service shutdown if necessary on all the services
being managed.
|
String |
toString()
Returns a string representation of the object.
|
public ServiceManager(Iterable<? extends Service> services)
services
- The services to manageIllegalArgumentException
- if not all services are new or if there
are any duplicate services.public void addListener(ServiceManager.Listener listener, Executor executor)
ServiceManager.Listener
to be executed on the given
executor. The listener will not have previous state changes replayed, so it is suggested that
listeners are added before any of the managed services are started.
addListener
guarantees execution ordering across calls to a given listener but not
across calls to multiple listeners. Specifically, a given listener will have its callbacks
invoked in the same order as the underlying service enters those states. Additionally, at most
one of the listener's callbacks will execute at once. However, multiple listeners' callbacks
may execute concurrently, and listeners may execute in an order different from the one in which
they were registered.
RuntimeExceptions thrown by a listener will be caught and logged. Any exception thrown
during Executor.execute
(e.g., a RejectedExecutionException
) will be caught and
logged.
When selecting an executor, note that directExecutor
is dangerous in some cases. See
the discussion in the ListenableFuture.addListener
documentation.
listener
- the listener to run when the manager changes stateexecutor
- the executor in which the listeners callback methods will be run.@Beta @Deprecated public void addListener(ServiceManager.Listener listener)
MoreExecutors.directExecutor()
. However,
consider whether another executor would be more appropriate, as discussed in the docs for
ListenableFuture.addListener
. This method is scheduled
for deletion in October 2020.ServiceManager.Listener
to be run when this ServiceManager
changes state. The
listener will not have previous state changes replayed, so it is suggested that listeners are
added before any of the managed services are started.
addListener
guarantees execution ordering across calls to a given listener but not
across calls to multiple listeners. Specifically, a given listener will have its callbacks
invoked in the same order as the underlying service enters those states. Additionally, at most
one of the listener's callbacks will execute at once. However, multiple listeners' callbacks
may execute concurrently, and listeners may execute in an order different from the one in which
they were registered.
RuntimeExceptions thrown by a listener will be caught and logged.
listener
- the listener to run when the manager changes state@CanIgnoreReturnValue public ServiceManager startAsync()
IllegalStateException
- if any of the Services are not new
when the
method is called.public void awaitHealthy()
ServiceManager
to become healthy. The manager
will become healthy after all the component services have reached the running state.IllegalStateException
- if the service manager reaches a state from which it cannot
become healthy.public void awaitHealthy(long timeout, TimeUnit unit) throws TimeoutException
ServiceManager
to become healthy for no more
than the given time. The manager will become healthy after all the component services have
reached the running state.timeout
- the maximum time to waitunit
- the time unit of the timeout argumentTimeoutException
- if not all of the services have finished starting within the deadlineIllegalStateException
- if the service manager reaches a state from which it cannot
become healthy.@CanIgnoreReturnValue public ServiceManager stopAsync()
public void awaitStopped()
public void awaitStopped(long timeout, TimeUnit unit) throws TimeoutException
timeout
- the maximum time to waitunit
- the time unit of the timeout argumentTimeoutException
- if not all of the services have stopped within the deadlinepublic boolean isHealthy()
Users who want more detailed information should use the servicesByState()
method to
get detailed information about which services are not running.
public ImmutableSetMultimap<Service.State,Service> servicesByState()
N.B. This snapshot is guaranteed to be consistent, i.e. the set of states returned will correspond to a point in time view of the services.
ImmutableMultimap
since 14.0)public ImmutableMap<Service,Long> startupTimes()
public String toString()
java.lang.Object
toString
method returns a string that
"textually represents" this object. The result should
be a concise but informative representation that is easy for a
person to read.
It is recommended that all subclasses override this method.
The toString
method for class Object
returns a string consisting of the name of the class of which the
object is an instance, the at-sign character `@
', and
the unsigned hexadecimal representation of the hash code of the
object. In other words, this method returns a string equal to the
value of:
getClass().getName() + '@' + Integer.toHexString(hashCode())
Copyright © 2010–2020. All rights reserved.