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