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