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