11d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/*
21d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Copyright (C) 2009 The Guava Authors
31d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
41d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License");
51d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * you may not use this file except in compliance with the License.
61d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * You may obtain a copy of the License at
71d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
81d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * http://www.apache.org/licenses/LICENSE-2.0
91d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Unless required by applicable law or agreed to in writing, software
111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS,
121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * See the License for the specific language governing permissions and
141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * limitations under the License.
151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */
161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpackage com.google.common.util.concurrent;
181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport static java.lang.Thread.currentThread;
201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport static java.util.concurrent.TimeUnit.SECONDS;
211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport junit.framework.Assert;
231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport junit.framework.TestCase;
241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.lang.Thread.UncaughtExceptionHandler;
261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.concurrent.CountDownLatch;
271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.concurrent.Future;
281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/**
301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Unit test for {@link AbstractService}.
311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert *
321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @author Jesse Wilson
331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */
341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpublic class AbstractServiceTest extends TestCase {
351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private Thread executionThread;
371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private Throwable thrownByExecutionThread;
381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testNoOpServiceStartStop() {
401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    Assert.assertEquals(Service.State.NEW, service.state());
421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.running);
441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start();
461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.RUNNING, service.state());
471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.isRunning());
481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.running);
491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.stop();
511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.TERMINATED, service.state());
521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.running);
541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testNoOpServiceStartAndWaitStopAndWait() throws Exception {
571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start().get();
601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.RUNNING, service.state());
611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.stop().get();
631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.TERMINATED, service.state());
641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testNoOpServiceStartStopIdempotence() throws Exception {
671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start();
701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start();
711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.RUNNING, service.state());
721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.stop();
741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.stop();
751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.TERMINATED, service.state());
761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testNoOpServiceStartStopIdempotenceAfterWait() throws Exception {
791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start().get();
821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start();
831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.RUNNING, service.state());
841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.stop().get();
861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.stop();
871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.TERMINATED, service.state());
881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testNoOpServiceStartStopIdempotenceDoubleWait() throws Exception {
911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start().get();
941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start().get();
951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.RUNNING, service.state());
961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.stop().get();
981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.stop().get();
991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.TERMINATED, service.state());
1001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testNoOpServiceStartStopAndWaitUninterruptible()
1031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      throws Exception {
1041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
1051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    currentThread().interrupt();
1071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    try {
1081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      service.startAndWait();
1091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(Service.State.RUNNING, service.state());
1101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      service.stopAndWait();
1121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(Service.State.TERMINATED, service.state());
1131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertTrue(currentThread().isInterrupted());
1151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    } finally {
1161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      Thread.interrupted(); // clear interrupt for future tests
1171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static class NoOpService extends AbstractService {
1211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    boolean running = false;
1221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStart() {
1241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse(running);
1251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      running = true;
1261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      notifyStarted();
1271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStop() {
1301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertTrue(running);
1311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      running = false;
1321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      notifyStopped();
1331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
1341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testManualServiceStartStop() {
1371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ManualSwitchedService service = new ManualSwitchedService();
1381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start();
1401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.STARTING, service.state());
1411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
1421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.doStartCalled);
1431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStarted(); // usually this would be invoked by another thread
1451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.RUNNING, service.state());
1461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.isRunning());
1471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.stop();
1491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.STOPPING, service.state());
1501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
1511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.doStopCalled);
1521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStopped(); // usually this would be invoked by another thread
1541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.TERMINATED, service.state());
1551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
1561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testManualServiceStopWhileStarting() {
1591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ManualSwitchedService service = new ManualSwitchedService();
1601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start();
1621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.STARTING, service.state());
1631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
1641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.doStartCalled);
1651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.stop();
1671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.STOPPING, service.state());
1681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
1691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.doStopCalled);
1701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStarted();
1721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.STOPPING, service.state());
1731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
1741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.doStopCalled);
1751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStopped();
1771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.TERMINATED, service.state());
1781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
1791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testManualServiceUnrequestedStop() {
1821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ManualSwitchedService service = new ManualSwitchedService();
1831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start();
1851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStarted();
1871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.RUNNING, service.state());
1881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertTrue(service.isRunning());
1891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.doStopCalled);
1901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.notifyStopped();
1921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.TERMINATED, service.state());
1931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.isRunning());
1941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertFalse(service.doStopCalled);
1951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
1961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
1981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * The user of this service should call {@link #notifyStarted} and {@link
1991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * #notifyStopped} after calling {@link #start} and {@link #stop}.
2001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
2011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static class ManualSwitchedService extends AbstractService {
2021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    boolean doStartCalled = false;
2031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    boolean doStopCalled = false;
2041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStart() {
2061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse(doStartCalled);
2071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      doStartCalled = true;
2081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
2091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStop() {
2111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertFalse(doStopCalled);
2121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      doStopCalled = true;
2131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
2141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
2151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testThreadedServiceStartAndWaitStopAndWait() throws Throwable {
2171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ThreadedService service = new ThreadedService();
2181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start().get();
2201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.RUNNING, service.state());
2211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.awaitRunChecks();
2231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.stop().get();
2251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.TERMINATED, service.state());
2261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    throwIfSet(thrownByExecutionThread);
2281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
2291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testThreadedServiceStartStopIdempotence() throws Throwable {
2311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ThreadedService service = new ThreadedService();
2321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start();
2341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start().get();
2351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.RUNNING, service.state());
2361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.awaitRunChecks();
2381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.stop();
2401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.stop().get();
2411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.TERMINATED, service.state());
2421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    throwIfSet(thrownByExecutionThread);
2441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
2451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testThreadedServiceStartStopIdempotenceAfterWait()
2471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      throws Throwable {
2481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ThreadedService service = new ThreadedService();
2491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start().get();
2511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start();
2521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.RUNNING, service.state());
2531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.awaitRunChecks();
2551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.stop().get();
2571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.stop();
2581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.TERMINATED, service.state());
2591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    executionThread.join();
2611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    throwIfSet(thrownByExecutionThread);
2631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
2641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testThreadedServiceStartStopIdempotenceDoubleWait()
2661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      throws Throwable {
2671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    ThreadedService service = new ThreadedService();
2681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start().get();
2701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.start().get();
2711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.RUNNING, service.state());
2721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.awaitRunChecks();
2741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.stop().get();
2761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.stop().get();
2771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.TERMINATED, service.state());
2781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    throwIfSet(thrownByExecutionThread);
2801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
2811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private class ThreadedService extends AbstractService {
2831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    final CountDownLatch hasConfirmedIsRunning = new CountDownLatch(1);
2841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    /*
2861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * The main test thread tries to stop() the service shortly after
2871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * confirming that it is running. Meanwhile, the service itself is trying
2881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * to confirm that it is running. If the main thread's stop() call happens
2891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * before it has the chance, the test will fail. To avoid this, the main
2901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * thread calls this method, which waits until the service has performed
2911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     * its own "running" check.
2921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert     */
2931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    void awaitRunChecks() throws InterruptedException {
2941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertTrue("Service thread hasn't finished its checks. "
2951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          + "Exception status (possibly stale): " + thrownByExecutionThread,
2961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          hasConfirmedIsRunning.await(10, SECONDS));
2971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
2981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
2991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStart() {
3001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(State.STARTING, state());
3011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      invokeOnExecutionThreadForTest(new Runnable() {
3021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        @Override public void run() {
3031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          assertEquals(State.STARTING, state());
3041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          notifyStarted();
3051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          assertEquals(State.RUNNING, state());
3061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          hasConfirmedIsRunning.countDown();
3071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        }
3081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      });
3091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStop() {
3121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(State.STOPPING, state());
3131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      invokeOnExecutionThreadForTest(new Runnable() {
3141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        @Override public void run() {
3151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          assertEquals(State.STOPPING, state());
3161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          notifyStopped();
3171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert          assertEquals(State.TERMINATED, state());
3181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        }
3191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      });
3201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private void invokeOnExecutionThreadForTest(Runnable runnable) {
3241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    executionThread = new Thread(runnable);
3251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    executionThread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
3261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      @Override
3271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      public void uncaughtException(Thread thread, Throwable e) {
3281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        thrownByExecutionThread = e;
3291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      }
3301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    });
3311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    executionThread.start();
3321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static void throwIfSet(Throwable t) throws Throwable {
3351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    if (t != null) {
3361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      throw t;
3371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testStopUnstartedService() throws Exception {
3411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    NoOpService service = new NoOpService();
3421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    Future<Service.State> stopResult = service.stop();
3431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.TERMINATED, service.state());
3441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.TERMINATED, stopResult.get());
3451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    Future<Service.State> startResult = service.start();
3471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.TERMINATED, service.state());
3481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    assertEquals(Service.State.TERMINATED, startResult.get());
3491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testThrowingServiceStartAndWait() throws Exception {
3521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    StartThrowingService service = new StartThrowingService();
3531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    try {
3551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      service.startAndWait();
3561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      fail();
3571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    } catch (UncheckedExecutionException e) {
3581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(EXCEPTION, e.getCause());
3591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testThrowingServiceStopAndWait_stopThrowing() throws Exception {
3631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    StopThrowingService service = new StopThrowingService();
3641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.startAndWait();
3661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    try {
3671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      service.stopAndWait();
3681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      fail();
3691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    } catch (UncheckedExecutionException e) {
3701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(EXCEPTION, e.getCause());
3711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  public void testThrowingServiceStopAndWait_runThrowing() throws Exception {
3751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    RunThrowingService service = new RunThrowingService();
3761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    service.startAndWait();
3781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    try {
3791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      service.stopAndWait();
3801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      fail();
3811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    } catch (UncheckedExecutionException e) {
3821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      assertEquals(EXCEPTION, e.getCause().getCause());
3831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static class StartThrowingService extends AbstractService {
3871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStart() {
3881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      notifyFailed(EXCEPTION);
3891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStop() {
3921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      fail();
3931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
3941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
3951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
3961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static class RunThrowingService extends AbstractService {
3971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStart() {
3981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      notifyStarted();
3991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      notifyFailed(EXCEPTION);
4001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
4011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStop() {
4031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      fail();
4041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
4051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static class StopThrowingService extends AbstractService {
4081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStart() {
4091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      notifyStarted();
4101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
4111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    @Override protected void doStop() {
4131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      notifyFailed(EXCEPTION);
4141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    }
4151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
4161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
4171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static final Exception EXCEPTION = new Exception();
4181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert}
419