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
017 package com.google.common.util.concurrent;
018
019 import com.google.common.annotations.Beta;
020 import com.google.common.base.Throwables;
021
022 import java.util.concurrent.Executor;
023 import java.util.logging.Level;
024 import java.util.logging.Logger;
025
026 /**
027 * Base class for services that can implement {@link #startUp}, {@link #run} and
028 * {@link #shutDown} methods. This class uses a single thread to execute the
029 * service; consider {@link AbstractService} if you would like to manage any
030 * threading manually.
031 *
032 * @author Jesse Wilson
033 * @since 1.0
034 */
035 @Beta
036 public abstract class AbstractExecutionThreadService implements Service {
037 private static final Logger logger = Logger.getLogger(
038 AbstractExecutionThreadService.class.getName());
039
040 /* use AbstractService for state management */
041 private final Service delegate = new AbstractService() {
042 @Override protected final void doStart() {
043 executor().execute(new Runnable() {
044 @Override
045 public void run() {
046 try {
047 startUp();
048 notifyStarted();
049
050 if (isRunning()) {
051 try {
052 AbstractExecutionThreadService.this.run();
053 } catch (Throwable t) {
054 try {
055 shutDown();
056 } catch (Exception ignored) {
057 logger.log(Level.WARNING,
058 "Error while attempting to shut down the service"
059 + " after failure.", ignored);
060 }
061 throw t;
062 }
063 }
064
065 shutDown();
066 notifyStopped();
067 } catch (Throwable t) {
068 notifyFailed(t);
069 throw Throwables.propagate(t);
070 }
071 }
072 });
073 }
074
075 @Override protected void doStop() {
076 triggerShutdown();
077 }
078 };
079
080 /**
081 * Constructor for use by subclasses.
082 */
083 protected AbstractExecutionThreadService() {}
084
085 /**
086 * Start the service. This method is invoked on the execution thread.
087 *
088 * <p>By default this method does nothing.
089 */
090 protected void startUp() throws Exception {}
091
092 /**
093 * Run the service. This method is invoked on the execution thread.
094 * Implementations must respond to stop requests. You could poll for lifecycle
095 * changes in a work loop:
096 * <pre>
097 * public void run() {
098 * while ({@link #isRunning()}) {
099 * // perform a unit of work
100 * }
101 * }
102 * </pre>
103 * ...or you could respond to stop requests by implementing {@link
104 * #triggerShutdown()}, which should cause {@link #run()} to return.
105 */
106 protected abstract void run() throws Exception;
107
108 /**
109 * Stop the service. This method is invoked on the execution thread.
110 *
111 * <p>By default this method does nothing.
112 */
113 // TODO: consider supporting a TearDownTestCase-like API
114 protected void shutDown() throws Exception {}
115
116 /**
117 * Invoked to request the service to stop.
118 *
119 * <p>By default this method does nothing.
120 */
121 protected void triggerShutdown() {}
122
123 /**
124 * Returns the {@link Executor} that will be used to run this service.
125 * Subclasses may override this method to use a custom {@link Executor}, which
126 * may configure its worker thread with a specific name, thread group or
127 * priority. The returned executor's {@link Executor#execute(Runnable)
128 * execute()} method is called when this service is started, and should return
129 * promptly.
130 *
131 * <p>The default implementation returns a new {@link Executor} that sets the
132 * name of its threads to the string returned by {@link #getServiceName}
133 */
134 protected Executor executor() {
135 return new Executor() {
136 @Override
137 public void execute(Runnable command) {
138 new Thread(command, getServiceName()).start();
139 }
140 };
141 }
142
143 @Override public String toString() {
144 return getServiceName() + " [" + state() + "]";
145 }
146
147 // We override instead of using ForwardingService so that these can be final.
148
149 @Override public final ListenableFuture<State> start() {
150 return delegate.start();
151 }
152
153 @Override public final State startAndWait() {
154 return delegate.startAndWait();
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 @Override public final ListenableFuture<State> stop() {
166 return delegate.stop();
167 }
168
169 @Override public final State stopAndWait() {
170 return delegate.stopAndWait();
171 }
172
173 @Override public final void addListener(Listener listener, Executor executor) {
174 delegate.addListener(listener, executor);
175 }
176
177 /**
178 * Returns the name of this service. {@link AbstractExecutionThreadService}
179 * may include the name in debugging output.
180 *
181 * <p>Subclasses may override this method.
182 *
183 * @since 10.0
184 */
185 protected String getServiceName() {
186 return getClass().getSimpleName();
187 }
188 }