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.errorprone.annotations.CanIgnoreReturnValue; 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 can implement {@link #startUp}, {@link #run} and {@link #shutDown} 031 * methods. This class uses a single thread to execute the service; consider {@link AbstractService} 032 * if you would like to manage any threading manually. 033 * 034 * @author Jesse Wilson 035 * @since 1.0 036 */ 037@GwtIncompatible 038@J2ktIncompatible 039public abstract class AbstractExecutionThreadService implements Service { 040 /* use AbstractService for state management */ 041 private final Service delegate = 042 new AbstractService() { 043 @Override 044 protected void doStart() { 045 Executor executor = renamingDecorator(executor(), () -> serviceName()); 046 executor.execute( 047 () -> { 048 try { 049 startUp(); 050 notifyStarted(); 051 // If stopAsync() is called while starting we may be in the STOPPING state in 052 // which case we should skip right down to shutdown. 053 if (isRunning()) { 054 try { 055 AbstractExecutionThreadService.this.run(); 056 } catch (Throwable t) { 057 restoreInterruptIfIsInterruptedException(t); 058 try { 059 shutDown(); 060 } catch (Exception ignored) { 061 restoreInterruptIfIsInterruptedException(ignored); 062 t.addSuppressed(ignored); 063 } 064 notifyFailed(t); 065 return; 066 } 067 } 068 069 shutDown(); 070 notifyStopped(); 071 } catch (Throwable t) { 072 restoreInterruptIfIsInterruptedException(t); 073 notifyFailed(t); 074 } 075 }); 076 } 077 078 @Override 079 protected void doStop() { 080 triggerShutdown(); 081 } 082 083 @Override 084 public String toString() { 085 return AbstractExecutionThreadService.this.toString(); 086 } 087 }; 088 089 /** Constructor for use by subclasses. */ 090 protected AbstractExecutionThreadService() {} 091 092 /** 093 * Start the service. This method is invoked on the execution thread. 094 * 095 * <p>By default this method does nothing. 096 */ 097 protected void startUp() throws Exception {} 098 099 /** 100 * Run the service. This method is invoked on the execution thread. Implementations must respond 101 * to stop requests. You could poll for lifecycle changes in a work loop: 102 * 103 * <pre> 104 * public void run() { 105 * while ({@link #isRunning()}) { 106 * // perform a unit of work 107 * } 108 * } 109 * </pre> 110 * 111 * <p>...or you could respond to stop requests by implementing {@link #triggerShutdown()}, which 112 * should cause {@link #run()} to return. 113 */ 114 protected abstract void run() throws Exception; 115 116 /** 117 * Stop the service. This method is invoked on the execution thread. 118 * 119 * <p>By default this method does nothing. 120 */ 121 // TODO: consider supporting a TearDownTestCase-like API 122 protected void shutDown() throws Exception {} 123 124 /** 125 * Invoked to request the service to stop. 126 * 127 * <p>By default this method does nothing. 128 * 129 * <p>Currently, this method is invoked while holding a lock. If an implementation of this method 130 * blocks, it can prevent this service from changing state. If you need to perform a blocking 131 * operation in order to trigger shutdown, consider instead registering a listener and 132 * implementing {@code stopping}. Note, however, that {@code stopping} does not run at exactly the 133 * same times as {@code triggerShutdown}. 134 */ 135 protected void triggerShutdown() {} 136 137 /** 138 * Returns the {@link Executor} that will be used to run this service. Subclasses may override 139 * this method to use a custom {@link Executor}, which may configure its worker thread with a 140 * specific name, thread group or priority. The returned executor's {@link 141 * Executor#execute(Runnable) execute()} method is called when this service is started, and should 142 * return promptly. 143 * 144 * <p>The default implementation returns a new {@link Executor} that sets the name of its threads 145 * to the string returned by {@link #serviceName} 146 */ 147 protected Executor executor() { 148 return command -> newThread(serviceName(), command).start(); 149 } 150 151 @Override 152 public String toString() { 153 return serviceName() + " [" + state() + "]"; 154 } 155 156 @Override 157 public final boolean isRunning() { 158 return delegate.isRunning(); 159 } 160 161 @Override 162 public final State state() { 163 return delegate.state(); 164 } 165 166 /** 167 * @since 13.0 168 */ 169 @Override 170 public final void addListener(Listener listener, Executor executor) { 171 delegate.addListener(listener, executor); 172 } 173 174 /** 175 * @since 14.0 176 */ 177 @Override 178 public final Throwable failureCause() { 179 return delegate.failureCause(); 180 } 181 182 /** 183 * @since 15.0 184 */ 185 @CanIgnoreReturnValue 186 @Override 187 public final Service startAsync() { 188 delegate.startAsync(); 189 return this; 190 } 191 192 /** 193 * @since 15.0 194 */ 195 @CanIgnoreReturnValue 196 @Override 197 public final Service stopAsync() { 198 delegate.stopAsync(); 199 return this; 200 } 201 202 /** 203 * @since 15.0 204 */ 205 @Override 206 public final void awaitRunning() { 207 delegate.awaitRunning(); 208 } 209 210 /** 211 * @since 28.0 212 */ 213 @Override 214 public final void awaitRunning(Duration timeout) throws TimeoutException { 215 Service.super.awaitRunning(timeout); 216 } 217 218 /** 219 * @since 15.0 220 */ 221 @Override 222 public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { 223 delegate.awaitRunning(timeout, unit); 224 } 225 226 /** 227 * @since 15.0 228 */ 229 @Override 230 public final void awaitTerminated() { 231 delegate.awaitTerminated(); 232 } 233 234 /** 235 * @since 28.0 236 */ 237 @Override 238 public final void awaitTerminated(Duration timeout) throws TimeoutException { 239 Service.super.awaitTerminated(timeout); 240 } 241 242 /** 243 * @since 15.0 244 */ 245 @Override 246 public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { 247 delegate.awaitTerminated(timeout, unit); 248 } 249 250 /** 251 * Returns the name of this service. {@link AbstractExecutionThreadService} may include the name 252 * in debugging output. 253 * 254 * <p>Subclasses may override this method. 255 * 256 * @since 14.0 (present in 10.0 as getServiceName) 257 */ 258 protected String serviceName() { 259 return getClass().getSimpleName(); 260 } 261}