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
191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.annotations.Beta;
20bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport com.google.common.base.Throwables;
21bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
22bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorimport java.util.concurrent.Executor;
231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.logging.Level;
241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.logging.Logger;
25bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
26bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor/**
27bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * Base class for services that can implement {@link #startUp}, {@link #run} and
28bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * {@link #shutDown} methods. This class uses a single thread to execute the
29bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * service; consider {@link AbstractService} if you would like to manage any
30bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * threading manually.
31bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor *
32bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor * @author Jesse Wilson
331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @since 1.0
34bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor */
351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert@Beta
36bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnorpublic abstract class AbstractExecutionThreadService implements Service {
371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  private static final Logger logger = Logger.getLogger(
381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      AbstractExecutionThreadService.class.getName());
391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
40bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /* use AbstractService for state management */
41bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  private final Service delegate = new AbstractService() {
42bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    @Override protected final void doStart() {
43bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      executor().execute(new Runnable() {
441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        @Override
45bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor        public void run() {
46bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor          try {
47bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor            startUp();
48bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor            notifyStarted();
49bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
50bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor            if (isRunning()) {
51bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor              try {
52bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor                AbstractExecutionThreadService.this.run();
53bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor              } catch (Throwable t) {
54bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor                try {
55bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor                  shutDown();
561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert                } catch (Exception ignored) {
571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert                  logger.log(Level.WARNING,
581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert                      "Error while attempting to shut down the service after failure.", ignored);
591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert                }
60bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor                throw t;
61bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor              }
62bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor            }
63bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
64bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor            shutDown();
65bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor            notifyStopped();
66bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor          } catch (Throwable t) {
67bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor            notifyFailed(t);
68bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor            throw Throwables.propagate(t);
69bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor          }
70bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor        }
71bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      });
72bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    }
73bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
74bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    @Override protected void doStop() {
75bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      triggerShutdown();
76bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    }
77bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  };
78bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
79bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /**
80bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * Start the service. This method is invoked on the execution thread.
81bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   */
82bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  protected void startUp() throws Exception {}
83bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
84bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /**
85bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * Run the service. This method is invoked on the execution thread.
86bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * Implementations must respond to stop requests. You could poll for lifecycle
87bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * changes in a work loop:
88bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * <pre>
89bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   *   public void run() {
90bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   *     while ({@link #isRunning()}) {
91bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   *       // perform a unit of work
92bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   *     }
93bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   *   }
94bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * </pre>
95bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * ...or you could respond to stop requests by implementing {@link
96bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * #triggerShutdown()}, which should cause {@link #run()} to return.
97bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   */
98bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  protected abstract void run() throws Exception;
99bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
100bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /**
101bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * Stop the service. This method is invoked on the execution thread.
102bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   */
103bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  // TODO: consider supporting a TearDownTestCase-like API
104bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  protected void shutDown() throws Exception {}
105bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
106bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /**
107bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * Invoked to request the service to stop.
108bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   */
109bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  protected void triggerShutdown() {}
110bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
111bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  /**
112bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * Returns the {@link Executor} that will be used to run this service.
113bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * Subclasses may override this method to use a custom {@link Executor}, which
114bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * may configure its worker thread with a specific name, thread group or
115bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * priority. The returned executor's {@link Executor#execute(Runnable)
116bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * execute()} method is called when this service is started, and should return
117bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   * promptly.
1181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   *
1191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * <p>The default implementation returns a new {@link Executor} that sets the
1201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * name of its threads to the string returned by {@link #getServiceName}
121bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor   */
122bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  protected Executor executor() {
123bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    return new Executor() {
1241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert      @Override
125bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      public void execute(Runnable command) {
1261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert        new Thread(command, getServiceName()).start();
127bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor      }
128bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    };
129bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  }
130bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
131bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  @Override public String toString() {
1321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    return getServiceName() + " [" + state() + "]";
133bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  }
134bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
135bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  // We override instead of using ForwardingService so that these can be final.
136bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
1371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  @Override public final ListenableFuture<State> start() {
138bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    return delegate.start();
139bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  }
140bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
1411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  @Override public final State startAndWait() {
142bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    return delegate.startAndWait();
143bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  }
144bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
1451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  @Override public final boolean isRunning() {
146bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    return delegate.isRunning();
147bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  }
148bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
1491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  @Override public final State state() {
150bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    return delegate.state();
151bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  }
152bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
1531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  @Override public final ListenableFuture<State> stop() {
154bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    return delegate.stop();
155bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  }
156bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor
1571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  @Override public final State stopAndWait() {
158bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor    return delegate.stopAndWait();
159bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor  }
1601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert
1611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  /**
1621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * Returns the name of this service. {@link AbstractExecutionThreadService} may include the name
1631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * in debugging output.
1641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   *
1651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * <p>Subclasses may override this method.
1661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   *
1671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   * @since 10.0
1681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert   */
1691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  protected String getServiceName() {
1701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert    return getClass().getSimpleName();
1711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert  }
172bfe2dd089341dcb4c1fb65a5b6b077ad0ebbf6dcDan Egnor}
173