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