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