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 com.google.common.annotations.Beta;
018import com.google.common.annotations.GwtIncompatible;
019import com.google.common.base.Supplier;
020import com.google.errorprone.annotations.CanIgnoreReturnValue;
021import com.google.j2objc.annotations.WeakOuter;
022import java.util.concurrent.Executor;
023import java.util.concurrent.TimeUnit;
024import java.util.concurrent.TimeoutException;
025
026/**
027 * Base class for services that do not need a thread while "running" but may need one during startup
028 * and shutdown. Subclasses can implement {@link #startUp} and {@link #shutDown} methods, each which
029 * run in a executor which by default uses a separate thread for each method.
030 *
031 * @author Chris Nokleberg
032 * @since 1.0
033 */
034@Beta
035@GwtIncompatible
036public abstract class AbstractIdleService implements Service {
037
038  /* Thread names will look like {@code "MyService STARTING"}. */
039  private final Supplier<String> threadNameSupplier = new ThreadNameSupplier();
040
041  @WeakOuter
042  private final class ThreadNameSupplier implements Supplier<String> {
043    @Override
044    public String get() {
045      return serviceName() + " " + state();
046    }
047  }
048
049  /* use AbstractService for state management */
050  private final Service delegate = new DelegateService();
051
052  @WeakOuter
053  private final class DelegateService extends AbstractService {
054    @Override
055    protected final void doStart() {
056      MoreExecutors.renamingDecorator(executor(), threadNameSupplier)
057          .execute(
058              new Runnable() {
059                @Override
060                public void run() {
061                  try {
062                    startUp();
063                    notifyStarted();
064                  } catch (Throwable t) {
065                    notifyFailed(t);
066                  }
067                }
068              });
069    }
070
071    @Override
072    protected final void doStop() {
073      MoreExecutors.renamingDecorator(executor(), threadNameSupplier)
074          .execute(
075              new Runnable() {
076                @Override
077                public void run() {
078                  try {
079                    shutDown();
080                    notifyStopped();
081                  } catch (Throwable t) {
082                    notifyFailed(t);
083                  }
084                }
085              });
086    }
087
088    @Override
089    public String toString() {
090      return AbstractIdleService.this.toString();
091    }
092  }
093
094  /** Constructor for use by subclasses. */
095  protected AbstractIdleService() {}
096
097  /** Start the service. */
098  protected abstract void startUp() throws Exception;
099
100  /** Stop the service. */
101  protected abstract void shutDown() throws Exception;
102
103  /**
104   * Returns the {@link Executor} that will be used to run this service. Subclasses may override
105   * this method to use a custom {@link Executor}, which may configure its worker thread with a
106   * specific name, thread group or priority. The returned executor's {@link
107   * Executor#execute(Runnable) execute()} method is called when this service is started and
108   * stopped, and should return promptly.
109   */
110  protected Executor executor() {
111    return new Executor() {
112      @Override
113      public void execute(Runnable command) {
114        MoreExecutors.newThread(threadNameSupplier.get(), command).start();
115      }
116    };
117  }
118
119  @Override
120  public String toString() {
121    return serviceName() + " [" + state() + "]";
122  }
123
124  @Override
125  public final boolean isRunning() {
126    return delegate.isRunning();
127  }
128
129  @Override
130  public final State state() {
131    return delegate.state();
132  }
133
134  /**
135   * @since 13.0
136   */
137  @Override
138  public final void addListener(Listener listener, Executor executor) {
139    delegate.addListener(listener, executor);
140  }
141
142  /**
143   * @since 14.0
144   */
145  @Override
146  public final Throwable failureCause() {
147    return delegate.failureCause();
148  }
149
150  /**
151   * @since 15.0
152   */
153  @CanIgnoreReturnValue
154  @Override
155  public final Service startAsync() {
156    delegate.startAsync();
157    return this;
158  }
159
160  /**
161   * @since 15.0
162   */
163  @CanIgnoreReturnValue
164  @Override
165  public final Service stopAsync() {
166    delegate.stopAsync();
167    return this;
168  }
169
170  /**
171   * @since 15.0
172   */
173  @Override
174  public final void awaitRunning() {
175    delegate.awaitRunning();
176  }
177
178  /**
179   * @since 15.0
180   */
181  @Override
182  public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException {
183    delegate.awaitRunning(timeout, unit);
184  }
185
186  /**
187   * @since 15.0
188   */
189  @Override
190  public final void awaitTerminated() {
191    delegate.awaitTerminated();
192  }
193
194  /**
195   * @since 15.0
196   */
197  @Override
198  public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException {
199    delegate.awaitTerminated(timeout, unit);
200  }
201
202  /**
203   * Returns the name of this service. {@link AbstractIdleService} may include the name in debugging
204   * output.
205   *
206   * @since 14.0
207   */
208  protected String serviceName() {
209    return getClass().getSimpleName();
210  }
211}