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