View Javadoc
1   /*
2    * Copyright (C) 2009 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  
15  package com.google.common.util.concurrent;
16  
17  import com.google.common.annotations.Beta;
18  import com.google.common.annotations.GwtIncompatible;
19  import com.google.common.base.Supplier;
20  import com.google.errorprone.annotations.CanIgnoreReturnValue;
21  import java.util.concurrent.Executor;
22  import java.util.concurrent.TimeUnit;
23  import java.util.concurrent.TimeoutException;
24  import java.util.logging.Level;
25  import java.util.logging.Logger;
26  
27  /**
28   * Base class for services that can implement {@link #startUp}, {@link #run} and {@link #shutDown}
29   * methods. This class uses a single thread to execute the service; consider {@link AbstractService}
30   * if you would like to manage any threading manually.
31   *
32   * @author Jesse Wilson
33   * @since 1.0
34   */
35  @Beta
36  @GwtIncompatible
37  public abstract class AbstractExecutionThreadService implements Service {
38    private static final Logger logger =
39        Logger.getLogger(AbstractExecutionThreadService.class.getName());
40  
41    /* use AbstractService for state management */
42    private final Service delegate =
43        new AbstractService() {
44          @Override
45          protected final void doStart() {
46            Executor executor =
47                MoreExecutors.renamingDecorator(
48                    executor(),
49                    new Supplier<String>() {
50                      @Override
51                      public String get() {
52                        return serviceName();
53                      }
54                    });
55            executor.execute(
56                new Runnable() {
57                  @Override
58                  public void run() {
59                    try {
60                      startUp();
61                      notifyStarted();
62                      // If stopAsync() is called while starting we may be in the STOPPING state in
63                      // which case we should skip right down to shutdown.
64                      if (isRunning()) {
65                        try {
66                          AbstractExecutionThreadService.this.run();
67                        } catch (Throwable t) {
68                          try {
69                            shutDown();
70                          } catch (Exception ignored) {
71                            // TODO(lukes): if guava ever moves to java7, this would be a good
72                            // candidate for a suppressed exception, or maybe we could generalize
73                            // Closer.Suppressor
74                            logger.log(
75                                Level.WARNING,
76                                "Error while attempting to shut down the service after failure.",
77                                ignored);
78                          }
79                          notifyFailed(t);
80                          return;
81                        }
82                      }
83  
84                      shutDown();
85                      notifyStopped();
86                    } catch (Throwable t) {
87                      notifyFailed(t);
88                    }
89                  }
90                });
91          }
92  
93          @Override
94          protected void doStop() {
95            triggerShutdown();
96          }
97  
98          @Override
99          public String toString() {
100           return AbstractExecutionThreadService.this.toString();
101         }
102       };
103 
104   /**
105    * Constructor for use by subclasses.
106    */
107   protected AbstractExecutionThreadService() {}
108 
109   /**
110    * Start the service. This method is invoked on the execution thread.
111    *
112    * <p>By default this method does nothing.
113    */
114   protected void startUp() throws Exception {}
115 
116   /**
117    * Run the service. This method is invoked on the execution thread. Implementations must respond
118    * to stop requests. You could poll for lifecycle changes in a work loop:
119    *
120    * <pre>
121    *   public void run() {
122    *     while ({@link #isRunning()}) {
123    *       // perform a unit of work
124    *     }
125    *   }
126    * </pre>
127    *
128    * <p>...or you could respond to stop requests by implementing {@link #triggerShutdown()}, which
129    * should cause {@link #run()} to return.
130    */
131   protected abstract void run() throws Exception;
132 
133   /**
134    * Stop the service. This method is invoked on the execution thread.
135    *
136    * <p>By default this method does nothing.
137    */
138   // TODO: consider supporting a TearDownTestCase-like API
139   protected void shutDown() throws Exception {}
140 
141   /**
142    * Invoked to request the service to stop.
143    *
144    * <p>By default this method does nothing.
145    */
146   protected void triggerShutdown() {}
147 
148   /**
149    * Returns the {@link Executor} that will be used to run this service. Subclasses may override
150    * this method to use a custom {@link Executor}, which may configure its worker thread with a
151    * specific name, thread group or priority. The returned executor's {@link
152    * Executor#execute(Runnable) execute()} method is called when this service is started, and should
153    * return promptly.
154    *
155    * <p>The default implementation returns a new {@link Executor} that sets the name of its threads
156    * to the string returned by {@link #serviceName}
157    */
158   protected Executor executor() {
159     return new Executor() {
160       @Override
161       public void execute(Runnable command) {
162         MoreExecutors.newThread(serviceName(), command).start();
163       }
164     };
165   }
166 
167   @Override
168   public String toString() {
169     return serviceName() + " [" + state() + "]";
170   }
171 
172   @Override
173   public final boolean isRunning() {
174     return delegate.isRunning();
175   }
176 
177   @Override
178   public final State state() {
179     return delegate.state();
180   }
181 
182   /**
183    * @since 13.0
184    */
185   @Override
186   public final void addListener(Listener listener, Executor executor) {
187     delegate.addListener(listener, executor);
188   }
189 
190   /**
191    * @since 14.0
192    */
193   @Override
194   public final Throwable failureCause() {
195     return delegate.failureCause();
196   }
197 
198   /**
199    * @since 15.0
200    */
201   @CanIgnoreReturnValue
202   @Override
203   public final Service startAsync() {
204     delegate.startAsync();
205     return this;
206   }
207 
208   /**
209    * @since 15.0
210    */
211   @CanIgnoreReturnValue
212   @Override
213   public final Service stopAsync() {
214     delegate.stopAsync();
215     return this;
216   }
217 
218   /**
219    * @since 15.0
220    */
221   @Override
222   public final void awaitRunning() {
223     delegate.awaitRunning();
224   }
225 
226   /**
227    * @since 15.0
228    */
229   @Override
230   public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException {
231     delegate.awaitRunning(timeout, unit);
232   }
233 
234   /**
235    * @since 15.0
236    */
237   @Override
238   public final void awaitTerminated() {
239     delegate.awaitTerminated();
240   }
241 
242   /**
243    * @since 15.0
244    */
245   @Override
246   public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException {
247     delegate.awaitTerminated(timeout, unit);
248   }
249 
250   /**
251    * Returns the name of this service. {@link AbstractExecutionThreadService} may include the name
252    * in debugging output.
253    *
254    * <p>Subclasses may override this method.
255    *
256    * @since 14.0 (present in 10.0 as getServiceName)
257    */
258   protected String serviceName() {
259     return getClass().getSimpleName();
260   }
261 }