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