1bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor/*
21d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Copyright (C) 2009 The Guava Authors
3bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor *
4bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Licensed under the Apache License, Version 2.0 (the "License");
5bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * you may not use this file except in compliance with the License.
6bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * You may obtain a copy of the License at
7bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor *
8bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * http://www.apache.org/licenses/LICENSE-2.0
9bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor *
10bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Unless required by applicable law or agreed to in writing, software
11bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * distributed under the License is distributed on an "AS IS" BASIS,
12bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * See the License for the specific language governing permissions and
14bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * limitations under the License.
15bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */
16bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
17bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorpackage com.google.common.util.concurrent;
18bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
197dd252788645e940eada959bdde927426e2531c9Paul Duffinimport static com.google.common.base.Preconditions.checkArgument;
20bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport static com.google.common.base.Preconditions.checkNotNull;
217dd252788645e940eada959bdde927426e2531c9Paul Duffinimport static com.google.common.base.Preconditions.checkState;
220888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport static com.google.common.util.concurrent.Service.State.FAILED;
230888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport static com.google.common.util.concurrent.Service.State.NEW;
240888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport static com.google.common.util.concurrent.Service.State.RUNNING;
250888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport static com.google.common.util.concurrent.Service.State.STARTING;
260888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport static com.google.common.util.concurrent.Service.State.STOPPING;
270888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport static com.google.common.util.concurrent.Service.State.TERMINATED;
28bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.annotations.Beta;
300888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport com.google.common.util.concurrent.ListenerCallQueue.Callback;
310888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport com.google.common.util.concurrent.Monitor.Guard;
320888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport com.google.common.util.concurrent.Service.State; // javadoc needs this
331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
340888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport java.util.ArrayList;
350888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport java.util.Collections;
367dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.List;
377dd252788645e940eada959bdde927426e2531c9Paul Duffinimport java.util.concurrent.Executor;
38bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport java.util.concurrent.TimeUnit;
39bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport java.util.concurrent.TimeoutException;
407dd252788645e940eada959bdde927426e2531c9Paul Duffin
417dd252788645e940eada959bdde927426e2531c9Paul Duffinimport javax.annotation.Nullable;
427dd252788645e940eada959bdde927426e2531c9Paul Duffinimport javax.annotation.concurrent.GuardedBy;
437dd252788645e940eada959bdde927426e2531c9Paul Duffinimport javax.annotation.concurrent.Immutable;
44bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
45bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor/**
467dd252788645e940eada959bdde927426e2531c9Paul Duffin * Base class for implementing services that can handle {@link #doStart} and {@link #doStop}
477dd252788645e940eada959bdde927426e2531c9Paul Duffin * requests, responding to them with {@link #notifyStarted()} and {@link #notifyStopped()}
487dd252788645e940eada959bdde927426e2531c9Paul Duffin * callbacks. Its subclasses must manage threads manually; consider
497dd252788645e940eada959bdde927426e2531c9Paul Duffin * {@link AbstractExecutionThreadService} if you need only a single execution thread.
50bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor *
51bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * @author Jesse Wilson
527dd252788645e940eada959bdde927426e2531c9Paul Duffin * @author Luke Sandberg
531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @since 1.0
54bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */
551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert@Beta
56bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorpublic abstract class AbstractService implements Service {
570888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static final Callback<Listener> STARTING_CALLBACK =
580888a09821a98ac0680fad765217302858e70fa4Paul Duffin      new Callback<Listener>("starting()") {
590888a09821a98ac0680fad765217302858e70fa4Paul Duffin        @Override void call(Listener listener) {
600888a09821a98ac0680fad765217302858e70fa4Paul Duffin          listener.starting();
610888a09821a98ac0680fad765217302858e70fa4Paul Duffin        }
620888a09821a98ac0680fad765217302858e70fa4Paul Duffin      };
630888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static final Callback<Listener> RUNNING_CALLBACK =
640888a09821a98ac0680fad765217302858e70fa4Paul Duffin      new Callback<Listener>("running()") {
650888a09821a98ac0680fad765217302858e70fa4Paul Duffin        @Override void call(Listener listener) {
660888a09821a98ac0680fad765217302858e70fa4Paul Duffin          listener.running();
670888a09821a98ac0680fad765217302858e70fa4Paul Duffin        }
680888a09821a98ac0680fad765217302858e70fa4Paul Duffin      };
690888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static final Callback<Listener> STOPPING_FROM_STARTING_CALLBACK =
700888a09821a98ac0680fad765217302858e70fa4Paul Duffin      stoppingCallback(STARTING);
710888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static final Callback<Listener> STOPPING_FROM_RUNNING_CALLBACK =
720888a09821a98ac0680fad765217302858e70fa4Paul Duffin      stoppingCallback(RUNNING);
730888a09821a98ac0680fad765217302858e70fa4Paul Duffin
740888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static final Callback<Listener> TERMINATED_FROM_NEW_CALLBACK =
750888a09821a98ac0680fad765217302858e70fa4Paul Duffin      terminatedCallback(NEW);
760888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static final Callback<Listener> TERMINATED_FROM_RUNNING_CALLBACK =
770888a09821a98ac0680fad765217302858e70fa4Paul Duffin      terminatedCallback(RUNNING);
780888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static final Callback<Listener> TERMINATED_FROM_STOPPING_CALLBACK =
790888a09821a98ac0680fad765217302858e70fa4Paul Duffin      terminatedCallback(STOPPING);
800888a09821a98ac0680fad765217302858e70fa4Paul Duffin
810888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static Callback<Listener> terminatedCallback(final State from) {
820888a09821a98ac0680fad765217302858e70fa4Paul Duffin    return new Callback<Listener>("terminated({from = " + from + "})") {
830888a09821a98ac0680fad765217302858e70fa4Paul Duffin      @Override void call(Listener listener) {
840888a09821a98ac0680fad765217302858e70fa4Paul Duffin        listener.terminated(from);
850888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
860888a09821a98ac0680fad765217302858e70fa4Paul Duffin    };
870888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
880888a09821a98ac0680fad765217302858e70fa4Paul Duffin
890888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private static Callback<Listener> stoppingCallback(final State from) {
900888a09821a98ac0680fad765217302858e70fa4Paul Duffin    return new Callback<Listener>("stopping({from = " + from + "})") {
910888a09821a98ac0680fad765217302858e70fa4Paul Duffin      @Override void call(Listener listener) {
920888a09821a98ac0680fad765217302858e70fa4Paul Duffin        listener.stopping(from);
930888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
940888a09821a98ac0680fad765217302858e70fa4Paul Duffin    };
950888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
960888a09821a98ac0680fad765217302858e70fa4Paul Duffin
970888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private final Monitor monitor = new Monitor();
98bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
990888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private final Guard isStartable = new Guard(monitor) {
1000888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override public boolean isSatisfied() {
1010888a09821a98ac0680fad765217302858e70fa4Paul Duffin      return state() == NEW;
1020888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
1030888a09821a98ac0680fad765217302858e70fa4Paul Duffin  };
1040888a09821a98ac0680fad765217302858e70fa4Paul Duffin
1050888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private final Guard isStoppable = new Guard(monitor) {
1060888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override public boolean isSatisfied() {
1070888a09821a98ac0680fad765217302858e70fa4Paul Duffin      return state().compareTo(RUNNING) <= 0;
1080888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
1090888a09821a98ac0680fad765217302858e70fa4Paul Duffin  };
1100888a09821a98ac0680fad765217302858e70fa4Paul Duffin
1110888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private final Guard hasReachedRunning = new Guard(monitor) {
1120888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override public boolean isSatisfied() {
1130888a09821a98ac0680fad765217302858e70fa4Paul Duffin      return state().compareTo(RUNNING) >= 0;
1140888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
1150888a09821a98ac0680fad765217302858e70fa4Paul Duffin  };
1160888a09821a98ac0680fad765217302858e70fa4Paul Duffin
1170888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private final Guard isStopped = new Guard(monitor) {
1180888a09821a98ac0680fad765217302858e70fa4Paul Duffin    @Override public boolean isSatisfied() {
1190888a09821a98ac0680fad765217302858e70fa4Paul Duffin      return state().isTerminal();
1200888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
1210888a09821a98ac0680fad765217302858e70fa4Paul Duffin  };
122dbd967a6e5c96cc1a97c5521f88dc1564ba2f81bPaul Duffin
123bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /**
1240888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * The listeners to notify during a state transition.
125bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   */
1260888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @GuardedBy("monitor")
1270888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private final List<ListenerCallQueue<Listener>> listeners =
1280888a09821a98ac0680fad765217302858e70fa4Paul Duffin      Collections.synchronizedList(new ArrayList<ListenerCallQueue<Listener>>());
129bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
130bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /**
1317dd252788645e940eada959bdde927426e2531c9Paul Duffin   * The current state of the service.  This should be written with the lock held but can be read
1327dd252788645e940eada959bdde927426e2531c9Paul Duffin   * without it because it is an immutable object in a volatile field.  This is desirable so that
1337dd252788645e940eada959bdde927426e2531c9Paul Duffin   * methods like {@link #state}, {@link #failureCause} and notably {@link #toString} can be run
1340888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * without grabbing the lock.
1350888a09821a98ac0680fad765217302858e70fa4Paul Duffin   *
1360888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * <p>To update this field correctly the lock must be held to guarantee that the state is
1377dd252788645e940eada959bdde927426e2531c9Paul Duffin   * consistent.
1387dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
1390888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @GuardedBy("monitor")
1400888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private volatile StateSnapshot snapshot = new StateSnapshot(NEW);
1417dd252788645e940eada959bdde927426e2531c9Paul Duffin
1427dd252788645e940eada959bdde927426e2531c9Paul Duffin  /** Constructor for use by subclasses. */
1435cab40b862c21656c2ace19596874eb3ffe1b649Paul Duffin  protected AbstractService() {}
1447dd252788645e940eada959bdde927426e2531c9Paul Duffin
1457dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
1460888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * This method is called by {@link #startAsync} to initiate service startup. The invocation of
1470888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * this method should cause a call to {@link #notifyStarted()}, either during this method's run,
1480888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * or after it has returned. If startup fails, the invocation should cause a call to
1497dd252788645e940eada959bdde927426e2531c9Paul Duffin   * {@link #notifyFailed(Throwable)} instead.
150bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   *
1517dd252788645e940eada959bdde927426e2531c9Paul Duffin   * <p>This method should return promptly; prefer to do work on a different thread where it is
1520888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * convenient. It is invoked exactly once on service startup, even when {@link #startAsync} is
1530888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * called multiple times.
154bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   */
155bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  protected abstract void doStart();
156bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
157bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /**
1587dd252788645e940eada959bdde927426e2531c9Paul Duffin   * This method should be used to initiate service shutdown. The invocation of this method should
1597dd252788645e940eada959bdde927426e2531c9Paul Duffin   * cause a call to {@link #notifyStopped()}, either during this method's run, or after it has
1607dd252788645e940eada959bdde927426e2531c9Paul Duffin   * returned. If shutdown fails, the invocation should cause a call to
1617dd252788645e940eada959bdde927426e2531c9Paul Duffin   * {@link #notifyFailed(Throwable)} instead.
162bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   *
1637dd252788645e940eada959bdde927426e2531c9Paul Duffin   * <p> This method should return promptly; prefer to do work on a different thread where it is
1640888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * convenient. It is invoked exactly once on service shutdown, even when {@link #stopAsync} is
1650888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * called multiple times.
166bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   */
167bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  protected abstract void doStop();
168bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
1690888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @Override public final Service startAsync() {
1700888a09821a98ac0680fad765217302858e70fa4Paul Duffin    if (monitor.enterIf(isStartable)) {
1710888a09821a98ac0680fad765217302858e70fa4Paul Duffin      try {
1720888a09821a98ac0680fad765217302858e70fa4Paul Duffin        snapshot = new StateSnapshot(STARTING);
1737dd252788645e940eada959bdde927426e2531c9Paul Duffin        starting();
174bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor        doStart();
1750888a09821a98ac0680fad765217302858e70fa4Paul Duffin       // TODO(user): justify why we are catching Throwable and not RuntimeException
1760888a09821a98ac0680fad765217302858e70fa4Paul Duffin      } catch (Throwable startupFailure) {
1770888a09821a98ac0680fad765217302858e70fa4Paul Duffin        notifyFailed(startupFailure);
1780888a09821a98ac0680fad765217302858e70fa4Paul Duffin      } finally {
1790888a09821a98ac0680fad765217302858e70fa4Paul Duffin        monitor.leave();
1800888a09821a98ac0680fad765217302858e70fa4Paul Duffin        executeListeners();
181bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      }
1820888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } else {
1830888a09821a98ac0680fad765217302858e70fa4Paul Duffin      throw new IllegalStateException("Service " + this + " has already been started");
184bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    }
1850888a09821a98ac0680fad765217302858e70fa4Paul Duffin    return this;
1860888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
187bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
1880888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @Override public final Service stopAsync() {
1890888a09821a98ac0680fad765217302858e70fa4Paul Duffin    if (monitor.enterIf(isStoppable)) {
1900888a09821a98ac0680fad765217302858e70fa4Paul Duffin      try {
1910888a09821a98ac0680fad765217302858e70fa4Paul Duffin        State previous = state();
1920888a09821a98ac0680fad765217302858e70fa4Paul Duffin        switch (previous) {
1930888a09821a98ac0680fad765217302858e70fa4Paul Duffin          case NEW:
1940888a09821a98ac0680fad765217302858e70fa4Paul Duffin            snapshot = new StateSnapshot(TERMINATED);
1950888a09821a98ac0680fad765217302858e70fa4Paul Duffin            terminated(NEW);
1960888a09821a98ac0680fad765217302858e70fa4Paul Duffin            break;
1970888a09821a98ac0680fad765217302858e70fa4Paul Duffin          case STARTING:
1980888a09821a98ac0680fad765217302858e70fa4Paul Duffin            snapshot = new StateSnapshot(STARTING, true, null);
1990888a09821a98ac0680fad765217302858e70fa4Paul Duffin            stopping(STARTING);
2000888a09821a98ac0680fad765217302858e70fa4Paul Duffin            break;
2010888a09821a98ac0680fad765217302858e70fa4Paul Duffin          case RUNNING:
2020888a09821a98ac0680fad765217302858e70fa4Paul Duffin            snapshot = new StateSnapshot(STOPPING);
2030888a09821a98ac0680fad765217302858e70fa4Paul Duffin            stopping(RUNNING);
2040888a09821a98ac0680fad765217302858e70fa4Paul Duffin            doStop();
2050888a09821a98ac0680fad765217302858e70fa4Paul Duffin            break;
2060888a09821a98ac0680fad765217302858e70fa4Paul Duffin          case STOPPING:
2070888a09821a98ac0680fad765217302858e70fa4Paul Duffin          case TERMINATED:
2080888a09821a98ac0680fad765217302858e70fa4Paul Duffin          case FAILED:
2090888a09821a98ac0680fad765217302858e70fa4Paul Duffin            // These cases are impossible due to the if statement above.
2100888a09821a98ac0680fad765217302858e70fa4Paul Duffin            throw new AssertionError("isStoppable is incorrectly implemented, saw: " + previous);
2110888a09821a98ac0680fad765217302858e70fa4Paul Duffin          default:
2120888a09821a98ac0680fad765217302858e70fa4Paul Duffin            throw new AssertionError("Unexpected state: " + previous);
2130888a09821a98ac0680fad765217302858e70fa4Paul Duffin        }
2140888a09821a98ac0680fad765217302858e70fa4Paul Duffin        // TODO(user): justify why we are catching Throwable and not RuntimeException.  Also, we
2150888a09821a98ac0680fad765217302858e70fa4Paul Duffin        // may inadvertently catch our AssertionErrors.
2160888a09821a98ac0680fad765217302858e70fa4Paul Duffin      } catch (Throwable shutdownFailure) {
2170888a09821a98ac0680fad765217302858e70fa4Paul Duffin        notifyFailed(shutdownFailure);
2180888a09821a98ac0680fad765217302858e70fa4Paul Duffin      } finally {
2190888a09821a98ac0680fad765217302858e70fa4Paul Duffin        monitor.leave();
2200888a09821a98ac0680fad765217302858e70fa4Paul Duffin        executeListeners();
221bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      }
222bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    }
2230888a09821a98ac0680fad765217302858e70fa4Paul Duffin    return this;
2240888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
225bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
2260888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @Override public final void awaitRunning() {
2270888a09821a98ac0680fad765217302858e70fa4Paul Duffin    monitor.enterWhenUninterruptibly(hasReachedRunning);
2280888a09821a98ac0680fad765217302858e70fa4Paul Duffin    try {
2290888a09821a98ac0680fad765217302858e70fa4Paul Duffin      checkCurrentState(RUNNING);
2300888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } finally {
2310888a09821a98ac0680fad765217302858e70fa4Paul Duffin      monitor.leave();
2320888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
2330888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
2340888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2350888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @Override public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException {
2360888a09821a98ac0680fad765217302858e70fa4Paul Duffin    if (monitor.enterWhenUninterruptibly(hasReachedRunning, timeout, unit)) {
2370888a09821a98ac0680fad765217302858e70fa4Paul Duffin      try {
2380888a09821a98ac0680fad765217302858e70fa4Paul Duffin        checkCurrentState(RUNNING);
2390888a09821a98ac0680fad765217302858e70fa4Paul Duffin      } finally {
2400888a09821a98ac0680fad765217302858e70fa4Paul Duffin        monitor.leave();
2410888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
2420888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } else {
2430888a09821a98ac0680fad765217302858e70fa4Paul Duffin      // It is possible due to races the we are currently in the expected state even though we
2440888a09821a98ac0680fad765217302858e70fa4Paul Duffin      // timed out. e.g. if we weren't event able to grab the lock within the timeout we would never
2450888a09821a98ac0680fad765217302858e70fa4Paul Duffin      // even check the guard.  I don't think we care too much about this use case but it could lead
2460888a09821a98ac0680fad765217302858e70fa4Paul Duffin      // to a confusing error message.
2470888a09821a98ac0680fad765217302858e70fa4Paul Duffin      throw new TimeoutException("Timed out waiting for " + this + " to reach the RUNNING state. "
2480888a09821a98ac0680fad765217302858e70fa4Paul Duffin          + "Current state: " + state());
2490888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
2500888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
2510888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2520888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @Override public final void awaitTerminated() {
2530888a09821a98ac0680fad765217302858e70fa4Paul Duffin    monitor.enterWhenUninterruptibly(isStopped);
2540888a09821a98ac0680fad765217302858e70fa4Paul Duffin    try {
2550888a09821a98ac0680fad765217302858e70fa4Paul Duffin      checkCurrentState(TERMINATED);
2560888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } finally {
2570888a09821a98ac0680fad765217302858e70fa4Paul Duffin      monitor.leave();
2580888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
2590888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
2600888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2610888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @Override public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException {
2620888a09821a98ac0680fad765217302858e70fa4Paul Duffin    if (monitor.enterWhenUninterruptibly(isStopped, timeout, unit)) {
2630888a09821a98ac0680fad765217302858e70fa4Paul Duffin      try {
2640888a09821a98ac0680fad765217302858e70fa4Paul Duffin        checkCurrentState(TERMINATED);
2650888a09821a98ac0680fad765217302858e70fa4Paul Duffin      } finally {
2660888a09821a98ac0680fad765217302858e70fa4Paul Duffin        monitor.leave();
2670888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
2680888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } else {
2690888a09821a98ac0680fad765217302858e70fa4Paul Duffin      // It is possible due to races the we are currently in the expected state even though we
2700888a09821a98ac0680fad765217302858e70fa4Paul Duffin      // timed out. e.g. if we weren't event able to grab the lock within the timeout we would never
2710888a09821a98ac0680fad765217302858e70fa4Paul Duffin      // even check the guard.  I don't think we care too much about this use case but it could lead
2720888a09821a98ac0680fad765217302858e70fa4Paul Duffin      // to a confusing error message.
2730888a09821a98ac0680fad765217302858e70fa4Paul Duffin      throw new TimeoutException("Timed out waiting for " + this + " to reach a terminal state. "
2740888a09821a98ac0680fad765217302858e70fa4Paul Duffin          + "Current state: " + state());
2750888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
2760888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
2770888a09821a98ac0680fad765217302858e70fa4Paul Duffin
2780888a09821a98ac0680fad765217302858e70fa4Paul Duffin  /** Checks that the current state is equal to the expected state. */
2790888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @GuardedBy("monitor")
2800888a09821a98ac0680fad765217302858e70fa4Paul Duffin  private void checkCurrentState(State expected) {
2810888a09821a98ac0680fad765217302858e70fa4Paul Duffin    State actual = state();
2820888a09821a98ac0680fad765217302858e70fa4Paul Duffin    if (actual != expected) {
2830888a09821a98ac0680fad765217302858e70fa4Paul Duffin      if (actual == FAILED) {
2840888a09821a98ac0680fad765217302858e70fa4Paul Duffin        // Handle this specially so that we can include the failureCause, if there is one.
2850888a09821a98ac0680fad765217302858e70fa4Paul Duffin        throw new IllegalStateException("Expected the service to be " + expected
2860888a09821a98ac0680fad765217302858e70fa4Paul Duffin            + ", but the service has FAILED", failureCause());
2870888a09821a98ac0680fad765217302858e70fa4Paul Duffin      }
2880888a09821a98ac0680fad765217302858e70fa4Paul Duffin      throw new IllegalStateException("Expected the service to be " + expected + ", but was "
2890888a09821a98ac0680fad765217302858e70fa4Paul Duffin          + actual);
2900888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }
2910888a09821a98ac0680fad765217302858e70fa4Paul Duffin  }
2920888a09821a98ac0680fad765217302858e70fa4Paul Duffin
293bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /**
2947dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Implementing classes should invoke this method once their service has started. It will cause
2957dd252788645e940eada959bdde927426e2531c9Paul Duffin   * the service to transition from {@link State#STARTING} to {@link State#RUNNING}.
296bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   *
2977dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @throws IllegalStateException if the service is not {@link State#STARTING}.
298bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   */
299bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  protected final void notifyStarted() {
3000888a09821a98ac0680fad765217302858e70fa4Paul Duffin    monitor.enter();
301bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    try {
3020888a09821a98ac0680fad765217302858e70fa4Paul Duffin      // We have to examine the internal state of the snapshot here to properly handle the stop
3030888a09821a98ac0680fad765217302858e70fa4Paul Duffin      // while starting case.
3040888a09821a98ac0680fad765217302858e70fa4Paul Duffin      if (snapshot.state != STARTING) {
305bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor        IllegalStateException failure = new IllegalStateException(
3067dd252788645e940eada959bdde927426e2531c9Paul Duffin            "Cannot notifyStarted() when the service is " + snapshot.state);
307bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor        notifyFailed(failure);
308bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor        throw failure;
309bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      }
310bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
3117dd252788645e940eada959bdde927426e2531c9Paul Duffin      if (snapshot.shutdownWhenStartupFinishes) {
3120888a09821a98ac0680fad765217302858e70fa4Paul Duffin        snapshot = new StateSnapshot(STOPPING);
3130888a09821a98ac0680fad765217302858e70fa4Paul Duffin        // We don't call listeners here because we already did that when we set the
3147dd252788645e940eada959bdde927426e2531c9Paul Duffin        // shutdownWhenStartupFinishes flag.
3157dd252788645e940eada959bdde927426e2531c9Paul Duffin        doStop();
316bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      } else {
3170888a09821a98ac0680fad765217302858e70fa4Paul Duffin        snapshot = new StateSnapshot(RUNNING);
3187dd252788645e940eada959bdde927426e2531c9Paul Duffin        running();
319bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      }
320bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    } finally {
3210888a09821a98ac0680fad765217302858e70fa4Paul Duffin      monitor.leave();
3227dd252788645e940eada959bdde927426e2531c9Paul Duffin      executeListeners();
323bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    }
324bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  }
325bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
326bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /**
3277dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Implementing classes should invoke this method once their service has stopped. It will cause
3287dd252788645e940eada959bdde927426e2531c9Paul Duffin   * the service to transition from {@link State#STOPPING} to {@link State#TERMINATED}.
329bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   *
3307dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @throws IllegalStateException if the service is neither {@link State#STOPPING} nor
3317dd252788645e940eada959bdde927426e2531c9Paul Duffin   *         {@link State#RUNNING}.
332bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   */
333bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  protected final void notifyStopped() {
3340888a09821a98ac0680fad765217302858e70fa4Paul Duffin    monitor.enter();
335bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    try {
3360888a09821a98ac0680fad765217302858e70fa4Paul Duffin      // We check the internal state of the snapshot instead of state() directly so we don't allow
3370888a09821a98ac0680fad765217302858e70fa4Paul Duffin      // notifyStopped() to be called while STARTING, even if stop() has already been called.
3380888a09821a98ac0680fad765217302858e70fa4Paul Duffin      State previous = snapshot.state;
3390888a09821a98ac0680fad765217302858e70fa4Paul Duffin      if (previous != STOPPING && previous != RUNNING) {
340bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor        IllegalStateException failure = new IllegalStateException(
3410888a09821a98ac0680fad765217302858e70fa4Paul Duffin            "Cannot notifyStopped() when the service is " + previous);
342bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor        notifyFailed(failure);
343bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor        throw failure;
344bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      }
3450888a09821a98ac0680fad765217302858e70fa4Paul Duffin      snapshot = new StateSnapshot(TERMINATED);
3467dd252788645e940eada959bdde927426e2531c9Paul Duffin      terminated(previous);
347bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    } finally {
3480888a09821a98ac0680fad765217302858e70fa4Paul Duffin      monitor.leave();
3497dd252788645e940eada959bdde927426e2531c9Paul Duffin      executeListeners();
350bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    }
351bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  }
352bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
353bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /**
3547dd252788645e940eada959bdde927426e2531c9Paul Duffin   * Invoke this method to transition the service to the {@link State#FAILED}. The service will
3557dd252788645e940eada959bdde927426e2531c9Paul Duffin   * <b>not be stopped</b> if it is running. Invoke this method when a service has failed critically
3567dd252788645e940eada959bdde927426e2531c9Paul Duffin   * or otherwise cannot be started nor stopped.
357bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   */
358bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  protected final void notifyFailed(Throwable cause) {
359bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    checkNotNull(cause);
360bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
3610888a09821a98ac0680fad765217302858e70fa4Paul Duffin    monitor.enter();
362bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    try {
3630888a09821a98ac0680fad765217302858e70fa4Paul Duffin      State previous = state();
3640888a09821a98ac0680fad765217302858e70fa4Paul Duffin      switch (previous) {
3657dd252788645e940eada959bdde927426e2531c9Paul Duffin        case NEW:
3667dd252788645e940eada959bdde927426e2531c9Paul Duffin        case TERMINATED:
3670888a09821a98ac0680fad765217302858e70fa4Paul Duffin          throw new IllegalStateException("Failed while in state:" + previous, cause);
3687dd252788645e940eada959bdde927426e2531c9Paul Duffin        case RUNNING:
3697dd252788645e940eada959bdde927426e2531c9Paul Duffin        case STARTING:
3707dd252788645e940eada959bdde927426e2531c9Paul Duffin        case STOPPING:
3710888a09821a98ac0680fad765217302858e70fa4Paul Duffin          snapshot = new StateSnapshot(FAILED, false, cause);
3727dd252788645e940eada959bdde927426e2531c9Paul Duffin          failed(previous, cause);
3737dd252788645e940eada959bdde927426e2531c9Paul Duffin          break;
3747dd252788645e940eada959bdde927426e2531c9Paul Duffin        case FAILED:
3757dd252788645e940eada959bdde927426e2531c9Paul Duffin          // Do nothing
3767dd252788645e940eada959bdde927426e2531c9Paul Duffin          break;
3777dd252788645e940eada959bdde927426e2531c9Paul Duffin        default:
3780888a09821a98ac0680fad765217302858e70fa4Paul Duffin          throw new AssertionError("Unexpected state: " + previous);
379bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      }
380bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    } finally {
3810888a09821a98ac0680fad765217302858e70fa4Paul Duffin      monitor.leave();
3827dd252788645e940eada959bdde927426e2531c9Paul Duffin      executeListeners();
383bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    }
384bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  }
385bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
3860888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @Override
387bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  public final boolean isRunning() {
3880888a09821a98ac0680fad765217302858e70fa4Paul Duffin    return state() == RUNNING;
389bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  }
390bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
3910888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @Override
392bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  public final State state() {
3937dd252788645e940eada959bdde927426e2531c9Paul Duffin    return snapshot.externalState();
3947dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
3957dd252788645e940eada959bdde927426e2531c9Paul Duffin
3967dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
3977dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @since 14.0
3987dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
3990888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @Override
4007dd252788645e940eada959bdde927426e2531c9Paul Duffin  public final Throwable failureCause() {
4017dd252788645e940eada959bdde927426e2531c9Paul Duffin    return snapshot.failureCause();
4027dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
4037dd252788645e940eada959bdde927426e2531c9Paul Duffin
4047dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
4057dd252788645e940eada959bdde927426e2531c9Paul Duffin   * @since 13.0
4067dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
4070888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @Override
4087dd252788645e940eada959bdde927426e2531c9Paul Duffin  public final void addListener(Listener listener, Executor executor) {
4097dd252788645e940eada959bdde927426e2531c9Paul Duffin    checkNotNull(listener, "listener");
4107dd252788645e940eada959bdde927426e2531c9Paul Duffin    checkNotNull(executor, "executor");
4110888a09821a98ac0680fad765217302858e70fa4Paul Duffin    monitor.enter();
412bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    try {
4130888a09821a98ac0680fad765217302858e70fa4Paul Duffin      if (!state().isTerminal()) {
4140888a09821a98ac0680fad765217302858e70fa4Paul Duffin        listeners.add(new ListenerCallQueue<Listener>(listener, executor));
415bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      }
416bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    } finally {
4170888a09821a98ac0680fad765217302858e70fa4Paul Duffin      monitor.leave();
418bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    }
419bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  }
420bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
4210888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @Override public String toString() {
4221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    return getClass().getSimpleName() + " [" + state() + "]";
4231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4250888a09821a98ac0680fad765217302858e70fa4Paul Duffin  /**
4260888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * Attempts to execute all the listeners in {@link #listeners} while not holding the
4270888a09821a98ac0680fad765217302858e70fa4Paul Duffin   * {@link #monitor}.
4287dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
4297dd252788645e940eada959bdde927426e2531c9Paul Duffin  private void executeListeners() {
4300888a09821a98ac0680fad765217302858e70fa4Paul Duffin    if (!monitor.isOccupiedByCurrentThread()) {
4310888a09821a98ac0680fad765217302858e70fa4Paul Duffin      // iterate by index to avoid concurrent modification exceptions
4320888a09821a98ac0680fad765217302858e70fa4Paul Duffin      for (int i = 0; i < listeners.size(); i++) {
4330888a09821a98ac0680fad765217302858e70fa4Paul Duffin        listeners.get(i).execute();
4347dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
4357dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
4367dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
4377dd252788645e940eada959bdde927426e2531c9Paul Duffin
4380888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @GuardedBy("monitor")
4397dd252788645e940eada959bdde927426e2531c9Paul Duffin  private void starting() {
4400888a09821a98ac0680fad765217302858e70fa4Paul Duffin    STARTING_CALLBACK.enqueueOn(listeners);
4417dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
4427dd252788645e940eada959bdde927426e2531c9Paul Duffin
4430888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @GuardedBy("monitor")
4447dd252788645e940eada959bdde927426e2531c9Paul Duffin  private void running() {
4450888a09821a98ac0680fad765217302858e70fa4Paul Duffin    RUNNING_CALLBACK.enqueueOn(listeners);
4467dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
4477dd252788645e940eada959bdde927426e2531c9Paul Duffin
4480888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @GuardedBy("monitor")
4497dd252788645e940eada959bdde927426e2531c9Paul Duffin  private void stopping(final State from) {
4500888a09821a98ac0680fad765217302858e70fa4Paul Duffin    if (from == State.STARTING) {
4510888a09821a98ac0680fad765217302858e70fa4Paul Duffin      STOPPING_FROM_STARTING_CALLBACK.enqueueOn(listeners);
4520888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } else if (from == State.RUNNING) {
4530888a09821a98ac0680fad765217302858e70fa4Paul Duffin      STOPPING_FROM_RUNNING_CALLBACK.enqueueOn(listeners);
4540888a09821a98ac0680fad765217302858e70fa4Paul Duffin    } else {
4550888a09821a98ac0680fad765217302858e70fa4Paul Duffin      throw new AssertionError();
4567dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
4577dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
4587dd252788645e940eada959bdde927426e2531c9Paul Duffin
4590888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @GuardedBy("monitor")
4607dd252788645e940eada959bdde927426e2531c9Paul Duffin  private void terminated(final State from) {
4610888a09821a98ac0680fad765217302858e70fa4Paul Duffin    switch(from) {
4620888a09821a98ac0680fad765217302858e70fa4Paul Duffin      case NEW:
4630888a09821a98ac0680fad765217302858e70fa4Paul Duffin        TERMINATED_FROM_NEW_CALLBACK.enqueueOn(listeners);
4640888a09821a98ac0680fad765217302858e70fa4Paul Duffin        break;
4650888a09821a98ac0680fad765217302858e70fa4Paul Duffin      case RUNNING:
4660888a09821a98ac0680fad765217302858e70fa4Paul Duffin        TERMINATED_FROM_RUNNING_CALLBACK.enqueueOn(listeners);
4670888a09821a98ac0680fad765217302858e70fa4Paul Duffin        break;
4680888a09821a98ac0680fad765217302858e70fa4Paul Duffin      case STOPPING:
4690888a09821a98ac0680fad765217302858e70fa4Paul Duffin        TERMINATED_FROM_STOPPING_CALLBACK.enqueueOn(listeners);
4700888a09821a98ac0680fad765217302858e70fa4Paul Duffin        break;
4710888a09821a98ac0680fad765217302858e70fa4Paul Duffin      case STARTING:
4720888a09821a98ac0680fad765217302858e70fa4Paul Duffin      case TERMINATED:
4730888a09821a98ac0680fad765217302858e70fa4Paul Duffin      case FAILED:
4740888a09821a98ac0680fad765217302858e70fa4Paul Duffin      default:
4750888a09821a98ac0680fad765217302858e70fa4Paul Duffin        throw new AssertionError();
4767dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
4777dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
4787dd252788645e940eada959bdde927426e2531c9Paul Duffin
4790888a09821a98ac0680fad765217302858e70fa4Paul Duffin  @GuardedBy("monitor")
4807dd252788645e940eada959bdde927426e2531c9Paul Duffin  private void failed(final State from, final Throwable cause) {
4810888a09821a98ac0680fad765217302858e70fa4Paul Duffin    // can't memoize this one due to the exception
4820888a09821a98ac0680fad765217302858e70fa4Paul Duffin    new Callback<Listener>("failed({from = " + from + ", cause = " + cause + "})") {
4830888a09821a98ac0680fad765217302858e70fa4Paul Duffin      @Override void call(Listener listener) {
4840888a09821a98ac0680fad765217302858e70fa4Paul Duffin        listener.failed(from, cause);
4857dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
4860888a09821a98ac0680fad765217302858e70fa4Paul Duffin    }.enqueueOn(listeners);
4877dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
4887dd252788645e940eada959bdde927426e2531c9Paul Duffin
4897dd252788645e940eada959bdde927426e2531c9Paul Duffin  /**
4907dd252788645e940eada959bdde927426e2531c9Paul Duffin   * An immutable snapshot of the current state of the service. This class represents a consistent
4917dd252788645e940eada959bdde927426e2531c9Paul Duffin   * snapshot of the state and therefore it can be used to answer simple queries without needing to
4927dd252788645e940eada959bdde927426e2531c9Paul Duffin   * grab a lock.
4937dd252788645e940eada959bdde927426e2531c9Paul Duffin   */
4947dd252788645e940eada959bdde927426e2531c9Paul Duffin  @Immutable
4957dd252788645e940eada959bdde927426e2531c9Paul Duffin  private static final class StateSnapshot {
4967dd252788645e940eada959bdde927426e2531c9Paul Duffin    /**
4977dd252788645e940eada959bdde927426e2531c9Paul Duffin     * The internal state, which equals external state unless
4987dd252788645e940eada959bdde927426e2531c9Paul Duffin     * shutdownWhenStartupFinishes is true.
4997dd252788645e940eada959bdde927426e2531c9Paul Duffin     */
5007dd252788645e940eada959bdde927426e2531c9Paul Duffin    final State state;
5017dd252788645e940eada959bdde927426e2531c9Paul Duffin
5027dd252788645e940eada959bdde927426e2531c9Paul Duffin    /**
5037dd252788645e940eada959bdde927426e2531c9Paul Duffin     * If true, the user requested a shutdown while the service was still starting
5047dd252788645e940eada959bdde927426e2531c9Paul Duffin     * up.
5057dd252788645e940eada959bdde927426e2531c9Paul Duffin     */
5067dd252788645e940eada959bdde927426e2531c9Paul Duffin    final boolean shutdownWhenStartupFinishes;
5077dd252788645e940eada959bdde927426e2531c9Paul Duffin
5087dd252788645e940eada959bdde927426e2531c9Paul Duffin    /**
5097dd252788645e940eada959bdde927426e2531c9Paul Duffin     * The exception that caused this service to fail.  This will be {@code null}
5107dd252788645e940eada959bdde927426e2531c9Paul Duffin     * unless the service has failed.
5117dd252788645e940eada959bdde927426e2531c9Paul Duffin     */
5127dd252788645e940eada959bdde927426e2531c9Paul Duffin    @Nullable
5137dd252788645e940eada959bdde927426e2531c9Paul Duffin    final Throwable failure;
5147dd252788645e940eada959bdde927426e2531c9Paul Duffin
5157dd252788645e940eada959bdde927426e2531c9Paul Duffin    StateSnapshot(State internalState) {
5167dd252788645e940eada959bdde927426e2531c9Paul Duffin      this(internalState, false, null);
5177dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
5187dd252788645e940eada959bdde927426e2531c9Paul Duffin
5190888a09821a98ac0680fad765217302858e70fa4Paul Duffin    StateSnapshot(
5200888a09821a98ac0680fad765217302858e70fa4Paul Duffin        State internalState, boolean shutdownWhenStartupFinishes, @Nullable Throwable failure) {
5210888a09821a98ac0680fad765217302858e70fa4Paul Duffin      checkArgument(!shutdownWhenStartupFinishes || internalState == STARTING,
5227dd252788645e940eada959bdde927426e2531c9Paul Duffin          "shudownWhenStartupFinishes can only be set if state is STARTING. Got %s instead.",
5237dd252788645e940eada959bdde927426e2531c9Paul Duffin          internalState);
5240888a09821a98ac0680fad765217302858e70fa4Paul Duffin      checkArgument(!(failure != null ^ internalState == FAILED),
5257dd252788645e940eada959bdde927426e2531c9Paul Duffin          "A failure cause should be set if and only if the state is failed.  Got %s and %s "
5260888a09821a98ac0680fad765217302858e70fa4Paul Duffin          + "instead.", internalState, failure);
5277dd252788645e940eada959bdde927426e2531c9Paul Duffin      this.state = internalState;
5287dd252788645e940eada959bdde927426e2531c9Paul Duffin      this.shutdownWhenStartupFinishes = shutdownWhenStartupFinishes;
5297dd252788645e940eada959bdde927426e2531c9Paul Duffin      this.failure = failure;
5307dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
5317dd252788645e940eada959bdde927426e2531c9Paul Duffin
5327dd252788645e940eada959bdde927426e2531c9Paul Duffin    /** @see Service#state() */
5337dd252788645e940eada959bdde927426e2531c9Paul Duffin    State externalState() {
5340888a09821a98ac0680fad765217302858e70fa4Paul Duffin      if (shutdownWhenStartupFinishes && state == STARTING) {
5350888a09821a98ac0680fad765217302858e70fa4Paul Duffin        return STOPPING;
5367dd252788645e940eada959bdde927426e2531c9Paul Duffin      } else {
5377dd252788645e940eada959bdde927426e2531c9Paul Duffin        return state;
5387dd252788645e940eada959bdde927426e2531c9Paul Duffin      }
5397dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
5407dd252788645e940eada959bdde927426e2531c9Paul Duffin
5417dd252788645e940eada959bdde927426e2531c9Paul Duffin    /** @see Service#failureCause() */
5427dd252788645e940eada959bdde927426e2531c9Paul Duffin    Throwable failureCause() {
5430888a09821a98ac0680fad765217302858e70fa4Paul Duffin      checkState(state == FAILED,
5447dd252788645e940eada959bdde927426e2531c9Paul Duffin          "failureCause() is only valid if the service has failed, service is %s", state);
5457dd252788645e940eada959bdde927426e2531c9Paul Duffin      return failure;
5467dd252788645e940eada959bdde927426e2531c9Paul Duffin    }
5477dd252788645e940eada959bdde927426e2531c9Paul Duffin  }
548bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor}
549