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