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@Beta 036@GwtIncompatible 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 /** 105 * Constructor for use by subclasses. 106 */ 107 protected AbstractExecutionThreadService() {} 108 109 /** 110 * Start the service. This method is invoked on the execution thread. 111 * 112 * <p>By default this method does nothing. 113 */ 114 protected void startUp() throws Exception {} 115 116 /** 117 * Run the service. This method is invoked on the execution thread. Implementations must respond 118 * to stop requests. You could poll for lifecycle changes in a work loop: 119 * 120 * <pre> 121 * public void run() { 122 * while ({@link #isRunning()}) { 123 * // perform a unit of work 124 * } 125 * } 126 * </pre> 127 * 128 * <p>...or you could respond to stop requests by implementing {@link #triggerShutdown()}, which 129 * should cause {@link #run()} to return. 130 */ 131 protected abstract void run() throws Exception; 132 133 /** 134 * Stop the service. This method is invoked on the execution thread. 135 * 136 * <p>By default this method does nothing. 137 */ 138 // TODO: consider supporting a TearDownTestCase-like API 139 protected void shutDown() throws Exception {} 140 141 /** 142 * Invoked to request the service to stop. 143 * 144 * <p>By default this method does nothing. 145 */ 146 protected void triggerShutdown() {} 147 148 /** 149 * Returns the {@link Executor} that will be used to run this service. Subclasses may override 150 * this method to use a custom {@link Executor}, which may configure its worker thread with a 151 * specific name, thread group or priority. The returned executor's {@link 152 * Executor#execute(Runnable) execute()} method is called when this service is started, and should 153 * return promptly. 154 * 155 * <p>The default implementation returns a new {@link Executor} that sets the name of its threads 156 * to the string returned by {@link #serviceName} 157 */ 158 protected Executor executor() { 159 return new Executor() { 160 @Override 161 public void execute(Runnable command) { 162 MoreExecutors.newThread(serviceName(), command).start(); 163 } 164 }; 165 } 166 167 @Override 168 public String toString() { 169 return serviceName() + " [" + state() + "]"; 170 } 171 172 @Override 173 public final boolean isRunning() { 174 return delegate.isRunning(); 175 } 176 177 @Override 178 public final State state() { 179 return delegate.state(); 180 } 181 182 /** 183 * @since 13.0 184 */ 185 @Override 186 public final void addListener(Listener listener, Executor executor) { 187 delegate.addListener(listener, executor); 188 } 189 190 /** 191 * @since 14.0 192 */ 193 @Override 194 public final Throwable failureCause() { 195 return delegate.failureCause(); 196 } 197 198 /** 199 * @since 15.0 200 */ 201 @CanIgnoreReturnValue 202 @Override 203 public final Service startAsync() { 204 delegate.startAsync(); 205 return this; 206 } 207 208 /** 209 * @since 15.0 210 */ 211 @CanIgnoreReturnValue 212 @Override 213 public final Service stopAsync() { 214 delegate.stopAsync(); 215 return this; 216 } 217 218 /** 219 * @since 15.0 220 */ 221 @Override 222 public final void awaitRunning() { 223 delegate.awaitRunning(); 224 } 225 226 /** 227 * @since 15.0 228 */ 229 @Override 230 public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { 231 delegate.awaitRunning(timeout, unit); 232 } 233 234 /** 235 * @since 15.0 236 */ 237 @Override 238 public final void awaitTerminated() { 239 delegate.awaitTerminated(); 240 } 241 242 /** 243 * @since 15.0 244 */ 245 @Override 246 public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { 247 delegate.awaitTerminated(timeout, unit); 248 } 249 250 /** 251 * Returns the name of this service. {@link AbstractExecutionThreadService} may include the name 252 * in debugging output. 253 * 254 * <p>Subclasses may override this method. 255 * 256 * @since 14.0 (present in 10.0 as getServiceName) 257 */ 258 protected String serviceName() { 259 return getClass().getSimpleName(); 260 } 261}