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