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; 021import com.google.common.base.Throwables; 022 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 031 * {@link #shutDown} methods. This class uses a single thread to execute the 032 * service; consider {@link AbstractService} if you would like to manage any 033 * threading manually. 034 * 035 * @author Jesse Wilson 036 * @since 1.0 037 */ 038@Beta 039public abstract class AbstractExecutionThreadService implements Service { 040 private static final Logger logger = Logger.getLogger( 041 AbstractExecutionThreadService.class.getName()); 042 043 /* use AbstractService for state management */ 044 private final Service delegate = new AbstractService() { 045 @Override protected final void doStart() { 046 Executor executor = MoreExecutors.renamingDecorator(executor(), new Supplier<String>() { 047 @Override public String get() { 048 return serviceName(); 049 } 050 }); 051 executor.execute(new Runnable() { 052 @Override 053 public void run() { 054 try { 055 startUp(); 056 notifyStarted(); 057 058 if (isRunning()) { 059 try { 060 AbstractExecutionThreadService.this.run(); 061 } catch (Throwable t) { 062 try { 063 shutDown(); 064 } catch (Exception ignored) { 065 logger.log(Level.WARNING, 066 "Error while attempting to shut down the service" 067 + " after failure.", ignored); 068 } 069 throw t; 070 } 071 } 072 073 shutDown(); 074 notifyStopped(); 075 } catch (Throwable t) { 076 notifyFailed(t); 077 throw Throwables.propagate(t); 078 } 079 } 080 }); 081 } 082 083 @Override protected void doStop() { 084 triggerShutdown(); 085 } 086 }; 087 088 /** 089 * Constructor for use by subclasses. 090 */ 091 protected AbstractExecutionThreadService() {} 092 093 /** 094 * Start the service. This method is invoked on the execution thread. 095 * 096 * <p>By default this method does nothing. 097 */ 098 protected void startUp() throws Exception {} 099 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 */ 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 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 */ 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 */ 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}