001/* 002 * Copyright (C) 2009 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.util.concurrent; 018 019import com.google.common.annotations.Beta; 020import com.google.common.base.Supplier; 021 022import java.util.concurrent.Executor; 023import java.util.concurrent.TimeUnit; 024import java.util.concurrent.TimeoutException; 025import java.util.logging.Level; 026import java.util.logging.Logger; 027 028/** 029 * Base class for services that can implement {@link #startUp}, {@link #run} and 030 * {@link #shutDown} methods. This class uses a single thread to execute the 031 * service; consider {@link AbstractService} if you would like to manage any 032 * threading manually. 033 * 034 * @author Jesse Wilson 035 * @since 1.0 036 */ 037@Beta 038public abstract class AbstractExecutionThreadService implements Service { 039 private static final Logger logger = Logger.getLogger( 040 AbstractExecutionThreadService.class.getName()); 041 042 /* use AbstractService for state management */ 043 private final Service delegate = new AbstractService() { 044 @Override protected final void doStart() { 045 Executor executor = MoreExecutors.renamingDecorator(executor(), new Supplier<String>() { 046 @Override public String get() { 047 return serviceName(); 048 } 049 }); 050 executor.execute(new Runnable() { 051 @Override 052 public void run() { 053 try { 054 startUp(); 055 notifyStarted(); 056 // If stopAsync() is called while starting we may be in the STOPPING state in which 057 // case we should skip right down to shutdown. 058 if (isRunning()) { 059 try { 060 AbstractExecutionThreadService.this.run(); 061 } catch (Throwable t) { 062 try { 063 shutDown(); 064 } catch (Exception ignored) { 065 // TODO(lukes): if guava ever moves to java7, this would be a good candidate for 066 // a suppressed exception, or maybe we could generalize Closer.Suppressor 067 logger.log(Level.WARNING, 068 "Error while attempting to shut down the service" 069 + " after failure.", ignored); 070 } 071 notifyFailed(t); 072 return; 073 } 074 } 075 076 shutDown(); 077 notifyStopped(); 078 } catch (Throwable t) { 079 notifyFailed(t); 080 } 081 } 082 }); 083 } 084 085 @Override protected void doStop() { 086 triggerShutdown(); 087 } 088 089 @Override public String toString() { 090 return AbstractExecutionThreadService.this.toString(); 091 } 092 }; 093 094 /** 095 * Constructor for use by subclasses. 096 */ 097 protected AbstractExecutionThreadService() {} 098 099 /** 100 * Start the service. This method is invoked on the execution thread. 101 * 102 * <p>By default this method does nothing. 103 */ 104 protected void startUp() throws Exception {} 105 106 /** 107 * Run the service. This method is invoked on the execution thread. 108 * Implementations must respond to stop requests. You could poll for lifecycle 109 * changes in a work loop: 110 * <pre> 111 * public void run() { 112 * while ({@link #isRunning()}) { 113 * // perform a unit of work 114 * } 115 * } 116 * </pre> 117 * ...or you could respond to stop requests by implementing {@link 118 * #triggerShutdown()}, which should cause {@link #run()} to return. 119 */ 120 protected abstract void run() throws Exception; 121 122 /** 123 * Stop the service. This method is invoked on the execution thread. 124 * 125 * <p>By default this method does nothing. 126 */ 127 // TODO: consider supporting a TearDownTestCase-like API 128 protected void shutDown() throws Exception {} 129 130 /** 131 * Invoked to request the service to stop. 132 * 133 * <p>By default this method does nothing. 134 */ 135 protected void triggerShutdown() {} 136 137 /** 138 * Returns the {@link Executor} that will be used to run this service. 139 * Subclasses may override this method to use a custom {@link Executor}, which 140 * may configure its worker thread with a specific name, thread group or 141 * priority. The returned executor's {@link Executor#execute(Runnable) 142 * execute()} method is called when this service is started, and should return 143 * promptly. 144 * 145 * <p>The default implementation returns a new {@link Executor} that sets the 146 * name of its threads to the string returned by {@link #serviceName} 147 */ 148 protected Executor executor() { 149 return new Executor() { 150 @Override 151 public void execute(Runnable command) { 152 MoreExecutors.newThread(serviceName(), command).start(); 153 } 154 }; 155 } 156 157 @Override public String toString() { 158 return serviceName() + " [" + state() + "]"; 159 } 160 161 @Override public final boolean isRunning() { 162 return delegate.isRunning(); 163 } 164 165 @Override public final State state() { 166 return delegate.state(); 167 } 168 169 /** 170 * @since 13.0 171 */ 172 @Override public final void addListener(Listener listener, Executor executor) { 173 delegate.addListener(listener, executor); 174 } 175 176 /** 177 * @since 14.0 178 */ 179 @Override public final Throwable failureCause() { 180 return delegate.failureCause(); 181 } 182 183 /** 184 * @since 15.0 185 */ 186 @Override public final Service startAsync() { 187 delegate.startAsync(); 188 return this; 189 } 190 191 /** 192 * @since 15.0 193 */ 194 @Override public final Service stopAsync() { 195 delegate.stopAsync(); 196 return this; 197 } 198 199 /** 200 * @since 15.0 201 */ 202 @Override public final void awaitRunning() { 203 delegate.awaitRunning(); 204 } 205 206 /** 207 * @since 15.0 208 */ 209 @Override public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { 210 delegate.awaitRunning(timeout, unit); 211 } 212 213 /** 214 * @since 15.0 215 */ 216 @Override public final void awaitTerminated() { 217 delegate.awaitTerminated(); 218 } 219 220 /** 221 * @since 15.0 222 */ 223 @Override public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { 224 delegate.awaitTerminated(timeout, unit); 225 } 226 227 /** 228 * Returns the name of this service. {@link AbstractExecutionThreadService} 229 * may include the name in debugging output. 230 * 231 * <p>Subclasses may override this method. 232 * 233 * @since 14.0 (present in 10.0 as getServiceName) 234 */ 235 protected String serviceName() { 236 return getClass().getSimpleName(); 237 } 238}