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