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