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