View Javadoc
1   /*
2    * Copyright (C) 2009 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  
15  package com.google.common.util.concurrent;
16  
17  import com.google.common.annotations.Beta;
18  import com.google.common.annotations.GwtIncompatible;
19  import com.google.common.base.Supplier;
20  import com.google.errorprone.annotations.CanIgnoreReturnValue;
21  import com.google.j2objc.annotations.WeakOuter;
22  import java.util.concurrent.Executor;
23  import java.util.concurrent.TimeUnit;
24  import java.util.concurrent.TimeoutException;
25  
26  /**
27   * Base class for services that do not need a thread while "running" but may need one during startup
28   * and shutdown. Subclasses can implement {@link #startUp} and {@link #shutDown} methods, each which
29   * run in a executor which by default uses a separate thread for each method.
30   *
31   * @author Chris Nokleberg
32   * @since 1.0
33   */
34  @Beta
35  @GwtIncompatible
36  public abstract class AbstractIdleService implements Service {
37  
38    /* Thread names will look like {@code "MyService STARTING"}. */
39    private final Supplier<String> threadNameSupplier = new ThreadNameSupplier();
40  
41    @WeakOuter
42    private final class ThreadNameSupplier implements Supplier<String> {
43      @Override
44      public String get() {
45        return serviceName() + " " + state();
46      }
47    }
48  
49    /* use AbstractService for state management */
50    private final Service delegate = new DelegateService();
51  
52    @WeakOuter
53    private final class DelegateService extends AbstractService {
54      @Override
55      protected final void doStart() {
56        MoreExecutors.renamingDecorator(executor(), threadNameSupplier)
57            .execute(
58                new Runnable() {
59                  @Override
60                  public void run() {
61                    try {
62                      startUp();
63                      notifyStarted();
64                    } catch (Throwable t) {
65                      notifyFailed(t);
66                    }
67                  }
68                });
69      }
70  
71      @Override
72      protected final void doStop() {
73        MoreExecutors.renamingDecorator(executor(), threadNameSupplier)
74            .execute(
75                new Runnable() {
76                  @Override
77                  public void run() {
78                    try {
79                      shutDown();
80                      notifyStopped();
81                    } catch (Throwable t) {
82                      notifyFailed(t);
83                    }
84                  }
85                });
86      }
87  
88      @Override
89      public String toString() {
90        return AbstractIdleService.this.toString();
91      }
92    }
93  
94    /** Constructor for use by subclasses. */
95    protected AbstractIdleService() {}
96  
97    /** Start the service. */
98    protected abstract void startUp() throws Exception;
99  
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 }