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  @Override public final boolean isRunning() {
156    return delegate.isRunning();
157  }
158
159  @Override public final State state() {
160    return delegate.state();
161  }
162
163  /**
164   * @since 13.0
165   */
166  @Override public final void addListener(Listener listener, Executor executor) {
167    delegate.addListener(listener, executor);
168  }
169  
170  /**
171   * @since 14.0
172   */
173  @Override public final Throwable failureCause() {
174    return delegate.failureCause();
175  }
176  
177  /**
178   * @since 15.0
179   */
180  @Override public final Service startAsync() {
181    delegate.startAsync();
182    return this;
183  }
184  
185  /**
186   * @since 15.0
187   */
188  @Override public final Service stopAsync() {
189    delegate.stopAsync();
190    return this;
191  }
192  
193  /**
194   * @since 15.0
195   */
196  @Override public final void awaitRunning() {
197    delegate.awaitRunning();
198  }
199  
200  /**
201   * @since 15.0
202   */
203  @Override public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException {
204    delegate.awaitRunning(timeout, unit);
205  }
206  
207  /**
208   * @since 15.0
209   */
210  @Override public final void awaitTerminated() {
211    delegate.awaitTerminated();
212  }
213  
214  /**
215   * @since 15.0
216   */
217  @Override public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException {
218    delegate.awaitTerminated(timeout, unit);
219  }
220  
221  /**
222   * Returns the name of this service. {@link AbstractExecutionThreadService}
223   * may include the name in debugging output.
224   *
225   * <p>Subclasses may override this method.
226   *
227   * @since 14.0 (present in 10.0 as getServiceName)
228   */
229  protected String serviceName() {
230    return getClass().getSimpleName();
231  }
232}