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  /** @since 13.0 */
135  @Override
136  public final void addListener(Listener listener, Executor executor) {
137    delegate.addListener(listener, executor);
138  }
139
140  /** @since 14.0 */
141  @Override
142  public final Throwable failureCause() {
143    return delegate.failureCause();
144  }
145
146  /** @since 15.0 */
147  @CanIgnoreReturnValue
148  @Override
149  public final Service startAsync() {
150    delegate.startAsync();
151    return this;
152  }
153
154  /** @since 15.0 */
155  @CanIgnoreReturnValue
156  @Override
157  public final Service stopAsync() {
158    delegate.stopAsync();
159    return this;
160  }
161
162  /** @since 15.0 */
163  @Override
164  public final void awaitRunning() {
165    delegate.awaitRunning();
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 15.0 */
181  @Override
182  public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException {
183    delegate.awaitTerminated(timeout, unit);
184  }
185
186  /**
187   * Returns the name of this service. {@link AbstractIdleService} may include the name in debugging
188   * output.
189   *
190   * @since 14.0
191   */
192  protected String serviceName() {
193    return getClass().getSimpleName();
194  }
195}