001/*
002 * Copyright (C) 2009 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014
015package com.google.common.util.concurrent;
016
017import static com.google.common.util.concurrent.Platform.restoreInterruptIfIsInterruptedException;
018
019import com.google.common.annotations.GwtIncompatible;
020import com.google.common.annotations.J2ktIncompatible;
021import com.google.errorprone.annotations.CanIgnoreReturnValue;
022import java.time.Duration;
023import java.util.concurrent.Executor;
024import java.util.concurrent.TimeUnit;
025import java.util.concurrent.TimeoutException;
026
027/**
028 * Base class for services that can implement {@link #startUp}, {@link #run} and {@link #shutDown}
029 * methods. This class uses a single thread to execute the service; consider {@link AbstractService}
030 * if you would like to manage any threading manually.
031 *
032 * @author Jesse Wilson
033 * @since 1.0
034 */
035@GwtIncompatible
036@J2ktIncompatible
037public abstract class AbstractExecutionThreadService implements Service {
038  /* use AbstractService for state management */
039  private final Service delegate =
040      new AbstractService() {
041        @Override
042        protected final void doStart() {
043          Executor executor = MoreExecutors.renamingDecorator(executor(), () -> serviceName());
044          executor.execute(
045              () -> {
046                try {
047                  startUp();
048                  notifyStarted();
049                  // If stopAsync() is called while starting we may be in the STOPPING state in
050                  // which case we should skip right down to shutdown.
051                  if (isRunning()) {
052                    try {
053                      AbstractExecutionThreadService.this.run();
054                    } catch (Throwable t) {
055                      restoreInterruptIfIsInterruptedException(t);
056                      try {
057                        shutDown();
058                      } catch (Exception ignored) {
059                        restoreInterruptIfIsInterruptedException(ignored);
060                        t.addSuppressed(ignored);
061                      }
062                      notifyFailed(t);
063                      return;
064                    }
065                  }
066
067                  shutDown();
068                  notifyStopped();
069                } catch (Throwable t) {
070                  restoreInterruptIfIsInterruptedException(t);
071                  notifyFailed(t);
072                }
073              });
074        }
075
076        @Override
077        protected void doStop() {
078          triggerShutdown();
079        }
080
081        @Override
082        public String toString() {
083          return AbstractExecutionThreadService.this.toString();
084        }
085      };
086
087  /** Constructor for use by subclasses. */
088  protected AbstractExecutionThreadService() {}
089
090  /**
091   * Start the service. This method is invoked on the execution thread.
092   *
093   * <p>By default this method does nothing.
094   */
095  protected void startUp() throws Exception {}
096
097  /**
098   * Run the service. This method is invoked on the execution thread. Implementations must respond
099   * to stop requests. You could poll for lifecycle changes in a work loop:
100   *
101   * <pre>
102   *   public void run() {
103   *     while ({@link #isRunning()}) {
104   *       // perform a unit of work
105   *     }
106   *   }
107   * </pre>
108   *
109   * <p>...or you could respond to stop requests by implementing {@link #triggerShutdown()}, which
110   * should cause {@link #run()} to return.
111   */
112  protected abstract void run() throws Exception;
113
114  /**
115   * Stop the service. This method is invoked on the execution thread.
116   *
117   * <p>By default this method does nothing.
118   */
119  // TODO: consider supporting a TearDownTestCase-like API
120  protected void shutDown() throws Exception {}
121
122  /**
123   * Invoked to request the service to stop.
124   *
125   * <p>By default this method does nothing.
126   *
127   * <p>Currently, this method is invoked while holding a lock. If an implementation of this method
128   * blocks, it can prevent this service from changing state. If you need to performing a blocking
129   * operation in order to trigger shutdown, consider instead registering a listener and
130   * implementing {@code stopping}. Note, however, that {@code stopping} does not run at exactly the
131   * same times as {@code triggerShutdown}.
132   */
133  protected void triggerShutdown() {}
134
135  /**
136   * Returns the {@link Executor} that will be used to run this service. Subclasses may override
137   * this method to use a custom {@link Executor}, which may configure its worker thread with a
138   * specific name, thread group or priority. The returned executor's {@link
139   * Executor#execute(Runnable) execute()} method is called when this service is started, and should
140   * return promptly.
141   *
142   * <p>The default implementation returns a new {@link Executor} that sets the name of its threads
143   * to the string returned by {@link #serviceName}
144   */
145  protected Executor executor() {
146    return command -> MoreExecutors.newThread(serviceName(), command).start();
147  }
148
149  @Override
150  public String toString() {
151    return serviceName() + " [" + state() + "]";
152  }
153
154  @Override
155  public final boolean isRunning() {
156    return delegate.isRunning();
157  }
158
159  @Override
160  public final State state() {
161    return delegate.state();
162  }
163
164  /**
165   * @since 13.0
166   */
167  @Override
168  public final void addListener(Listener listener, Executor executor) {
169    delegate.addListener(listener, executor);
170  }
171
172  /**
173   * @since 14.0
174   */
175  @Override
176  public final Throwable failureCause() {
177    return delegate.failureCause();
178  }
179
180  /**
181   * @since 15.0
182   */
183  @CanIgnoreReturnValue
184  @Override
185  public final Service startAsync() {
186    delegate.startAsync();
187    return this;
188  }
189
190  /**
191   * @since 15.0
192   */
193  @CanIgnoreReturnValue
194  @Override
195  public final Service stopAsync() {
196    delegate.stopAsync();
197    return this;
198  }
199
200  /**
201   * @since 15.0
202   */
203  @Override
204  public final void awaitRunning() {
205    delegate.awaitRunning();
206  }
207
208  /**
209   * @since 28.0
210   */
211  @Override
212  public final void awaitRunning(Duration timeout) throws TimeoutException {
213    Service.super.awaitRunning(timeout);
214  }
215
216  /**
217   * @since 15.0
218   */
219  @Override
220  public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException {
221    delegate.awaitRunning(timeout, unit);
222  }
223
224  /**
225   * @since 15.0
226   */
227  @Override
228  public final void awaitTerminated() {
229    delegate.awaitTerminated();
230  }
231
232  /**
233   * @since 28.0
234   */
235  @Override
236  public final void awaitTerminated(Duration timeout) throws TimeoutException {
237    Service.super.awaitTerminated(timeout);
238  }
239
240  /**
241   * @since 15.0
242   */
243  @Override
244  public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException {
245    delegate.awaitTerminated(timeout, unit);
246  }
247
248  /**
249   * Returns the name of this service. {@link AbstractExecutionThreadService} may include the name
250   * in debugging output.
251   *
252   * <p>Subclasses may override this method.
253   *
254   * @since 14.0 (present in 10.0 as getServiceName)
255   */
256  protected String serviceName() {
257    return getClass().getSimpleName();
258  }
259}