1 /* 2 * Copyright (C) 2009 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.util.concurrent; 18 19 import com.google.common.annotations.Beta; 20 import com.google.common.base.Supplier; 21 import com.google.common.base.Throwables; 22 23 import java.util.concurrent.Executor; 24 import java.util.concurrent.TimeUnit; 25 import java.util.concurrent.TimeoutException; 26 import java.util.logging.Level; 27 import java.util.logging.Logger; 28 29 /** 30 * Base class for services that can implement {@link #startUp}, {@link #run} and 31 * {@link #shutDown} methods. This class uses a single thread to execute the 32 * service; consider {@link AbstractService} if you would like to manage any 33 * threading manually. 34 * 35 * @author Jesse Wilson 36 * @since 1.0 37 */ 38 @Beta 39 public abstract class AbstractExecutionThreadService implements Service { 40 private static final Logger logger = Logger.getLogger( 41 AbstractExecutionThreadService.class.getName()); 42 43 /* use AbstractService for state management */ 44 private final Service delegate = new AbstractService() { 45 @Override protected final void doStart() { 46 Executor executor = MoreExecutors.renamingDecorator(executor(), new Supplier<String>() { 47 @Override public String get() { 48 return serviceName(); 49 } 50 }); 51 executor.execute(new Runnable() { 52 @Override 53 public void run() { 54 try { 55 startUp(); 56 notifyStarted(); 57 58 if (isRunning()) { 59 try { 60 AbstractExecutionThreadService.this.run(); 61 } catch (Throwable t) { 62 try { 63 shutDown(); 64 } catch (Exception ignored) { 65 logger.log(Level.WARNING, 66 "Error while attempting to shut down the service" 67 + " after failure.", ignored); 68 } 69 throw t; 70 } 71 } 72 73 shutDown(); 74 notifyStopped(); 75 } catch (Throwable t) { 76 notifyFailed(t); 77 throw Throwables.propagate(t); 78 } 79 } 80 }); 81 } 82 83 @Override protected void doStop() { 84 triggerShutdown(); 85 } 86 }; 87 88 /** 89 * Constructor for use by subclasses. 90 */ AbstractExecutionThreadService()91 protected AbstractExecutionThreadService() {} 92 93 /** 94 * Start the service. This method is invoked on the execution thread. 95 * 96 * <p>By default this method does nothing. 97 */ startUp()98 protected void startUp() throws Exception {} 99 100 /** 101 * Run the service. This method is invoked on the execution thread. 102 * Implementations must respond to stop requests. You could poll for lifecycle 103 * changes in a work loop: 104 * <pre> 105 * public void run() { 106 * while ({@link #isRunning()}) { 107 * // perform a unit of work 108 * } 109 * } 110 * </pre> 111 * ...or you could respond to stop requests by implementing {@link 112 * #triggerShutdown()}, which should cause {@link #run()} to return. 113 */ run()114 protected abstract void run() throws Exception; 115 116 /** 117 * Stop the service. This method is invoked on the execution thread. 118 * 119 * <p>By default this method does nothing. 120 */ 121 // TODO: consider supporting a TearDownTestCase-like API shutDown()122 protected void shutDown() throws Exception {} 123 124 /** 125 * Invoked to request the service to stop. 126 * 127 * <p>By default this method does nothing. 128 */ triggerShutdown()129 protected void triggerShutdown() {} 130 131 /** 132 * Returns the {@link Executor} that will be used to run this service. 133 * Subclasses may override this method to use a custom {@link Executor}, which 134 * may configure its worker thread with a specific name, thread group or 135 * priority. The returned executor's {@link Executor#execute(Runnable) 136 * execute()} method is called when this service is started, and should return 137 * promptly. 138 * 139 * <p>The default implementation returns a new {@link Executor} that sets the 140 * name of its threads to the string returned by {@link #serviceName} 141 */ executor()142 protected Executor executor() { 143 return new Executor() { 144 @Override 145 public void execute(Runnable command) { 146 MoreExecutors.newThread(serviceName(), command).start(); 147 } 148 }; 149 } 150 151 @Override public String toString() { 152 return serviceName() + " [" + state() + "]"; 153 } 154 155 @Override public final boolean isRunning() { 156 return delegate.isRunning(); 157 } 158 159 @Override public final State state() { 160 return delegate.state(); 161 } 162 163 /** 164 * @since 13.0 165 */ 166 @Override public final void addListener(Listener listener, Executor executor) { 167 delegate.addListener(listener, executor); 168 } 169 170 /** 171 * @since 14.0 172 */ 173 @Override public final Throwable failureCause() { 174 return delegate.failureCause(); 175 } 176 177 /** 178 * @since 15.0 179 */ 180 @Override public final Service startAsync() { 181 delegate.startAsync(); 182 return this; 183 } 184 185 /** 186 * @since 15.0 187 */ 188 @Override public final Service stopAsync() { 189 delegate.stopAsync(); 190 return this; 191 } 192 193 /** 194 * @since 15.0 195 */ 196 @Override public final void awaitRunning() { 197 delegate.awaitRunning(); 198 } 199 200 /** 201 * @since 15.0 202 */ 203 @Override public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { 204 delegate.awaitRunning(timeout, unit); 205 } 206 207 /** 208 * @since 15.0 209 */ 210 @Override public final void awaitTerminated() { 211 delegate.awaitTerminated(); 212 } 213 214 /** 215 * @since 15.0 216 */ 217 @Override public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { 218 delegate.awaitTerminated(timeout, unit); 219 } 220 221 /** 222 * Returns the name of this service. {@link AbstractExecutionThreadService} 223 * may include the name in debugging output. 224 * 225 * <p>Subclasses may override this method. 226 * 227 * @since 14.0 (present in 10.0 as getServiceName) 228 */ 229 protected String serviceName() { 230 return getClass().getSimpleName(); 231 } 232 } 233