@Override public void stopping(State from) { ServiceManagerState state = this.state.get(); if (state != null) { state.transitionService(service, from, STOPPING); } }
@Override public void running() { ServiceManagerState state = this.state.get(); if (state != null) { state.transitionService(service, STARTING, RUNNING); } }
@Override public void starting() { ServiceManagerState state = this.state.get(); if (state != null) { state.transitionService(service, NEW, STARTING); if (!(service instanceof NoOpService)) { logger.log(Level.FINE, "Starting {0}.", service); } } }
@Override public void terminated(State from) { ServiceManagerState state = this.state.get(); if (state != null) { if (!(service instanceof NoOpService)) { logger.log( Level.FINE, "Service {0} has terminated. Previous state was: {1}", new Object[] {service, from}); } state.transitionService(service, from, TERMINATED); } }
@Override public void failed(State from, Throwable failure) { ServiceManagerState state = this.state.get(); if (state != null) { // Log before the transition, so that if the process exits in response to server failure, // there is a higher likelihood that the cause will be in the logs. if (!(service instanceof NoOpService)) { logger.log( Level.SEVERE, "Service " + service + " has failed in the " + from + " state.", failure); } state.transitionService(service, from, FAILED); } }
/** * Initiates service {@linkplain Service#startAsync startup} on all the services being managed. It * is only valid to call this method if all of the services are {@linkplain State#NEW new}. * * @return this * @throws IllegalStateException if any of the Services are not {@link State#NEW new} when the * method is called. */ public ServiceManager startAsync() { for (Service service : services) { State state = service.state(); checkState(state == NEW, "Service %s is %s, cannot start it.", service, state); } for (Service service : services) { try { state.tryStartTiming(service); service.startAsync(); } catch (IllegalStateException e) { // This can happen if the service has already been started or stopped (e.g. by another // service or listener). Our contract says it is safe to call this method if // all services were NEW when it was called, and this has already been verified above, so we // don't propagate the exception. logger.log(Level.WARNING, "Unable to start Service " + service, e); } } return this; }
/** * Returns the service load times. This value will only return startup times for services that * have finished starting. * * @return Map of services and their corresponding startup time in millis, the map entries will be * ordered by startup time. */ public ImmutableMap<Service, Long> startupTimes() { return state.startupTimes(); }
/** * Provides a snapshot of the current state of all the services under management. * * <p>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. */ public ImmutableMultimap<State, Service> servicesByState() { return state.servicesByState(); }
/** * Waits for the all the services to reach a terminal state for no more than the given time. After * this method returns all services will either be {@linkplain Service.State#TERMINATED * terminated} or {@linkplain Service.State#FAILED failed}. * * @param timeout the maximum time to wait * @param unit the time unit of the timeout argument * @throws TimeoutException if not all of the services have stopped within the deadline */ public void awaitStopped(long timeout, TimeUnit unit) throws TimeoutException { state.awaitStopped(timeout, unit); }
/** * Waits for the all the services to reach a terminal state. After this method returns all * services will either be {@linkplain Service.State#TERMINATED terminated} or {@linkplain * Service.State#FAILED failed}. */ public void awaitStopped() { state.awaitStopped(); }
/** * Waits for the {@link ServiceManager} to become {@linkplain #isHealthy() healthy} for no more * than the given time. The manager will become healthy after all the component services have * reached the {@linkplain State#RUNNING running} state. * * @param timeout the maximum time to wait * @param unit the time unit of the timeout argument * @throws TimeoutException if not all of the services have finished starting within the deadline * @throws IllegalStateException if the service manager reaches a state from which it cannot * become {@linkplain #isHealthy() healthy}. */ public void awaitHealthy(long timeout, TimeUnit unit) throws TimeoutException { state.awaitHealthy(timeout, unit); }
/** * Waits for the {@link ServiceManager} to become {@linkplain #isHealthy() healthy}. The manager * will become healthy after all the component services have reached the {@linkplain State#RUNNING * running} state. * * @throws IllegalStateException if the service manager reaches a state from which it cannot * become {@linkplain #isHealthy() healthy}. */ public void awaitHealthy() { state.awaitHealthy(); }
/** * Registers a {@link Listener} to be run when this {@link 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 {@linkplain Service#startAsync started}. * * <p>{@code 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. * * <p>RuntimeExceptions thrown by a listener will be caught and logged. * * @param listener the listener to run when the manager changes state */ public void addListener(Listener listener) { state.addListener(listener, directExecutor()); }
/** * Registers a {@link Listener} to be {@linkplain Executor#execute 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 {@linkplain Service#startAsync * started}. * * <p>{@code 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. * * <p>RuntimeExceptions thrown by a listener will be caught and logged. Any exception thrown * during {@code Executor.execute} (e.g., a {@code RejectedExecutionException}) will be caught and * logged. * * <p>For fast, lightweight listeners that would be safe to execute in any thread, consider * calling {@link #addListener(Listener)}. * * @param listener the listener to run when the manager changes state * @param executor the executor in which the listeners callback methods will be run. */ public void addListener(Listener listener, Executor executor) { state.addListener(listener, executor); }