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 after failure.", ignored);
059 }
060 throw t;
061 }
062 }
063
064 shutDown();
065 notifyStopped();
066 } catch (Throwable t) {
067 notifyFailed(t);
068 throw Throwables.propagate(t);
069 }
070 }
071 });
072 }
073
074 @Override protected void doStop() {
075 triggerShutdown();
076 }
077 };
078
079 /**
080 * Start the service. This method is invoked on the execution thread.
081 *
082 * <p>By default this method does nothing.
083 */
084 protected void startUp() throws Exception {}
085
086 /**
087 * Run the service. This method is invoked on the execution thread.
088 * Implementations must respond to stop requests. You could poll for lifecycle
089 * changes in a work loop:
090 * <pre>
091 * public void run() {
092 * while ({@link #isRunning()}) {
093 * // perform a unit of work
094 * }
095 * }
096 * </pre>
097 * ...or you could respond to stop requests by implementing {@link
098 * #triggerShutdown()}, which should cause {@link #run()} to return.
099 */
100 protected abstract void run() throws Exception;
101
102 /**
103 * Stop the service. This method is invoked on the execution thread.
104 *
105 * <p>By default this method does nothing.
106 */
107 // TODO: consider supporting a TearDownTestCase-like API
108 protected void shutDown() throws Exception {}
109
110 /**
111 * Invoked to request the service to stop.
112 *
113 * <p>By default this method does nothing.
114 */
115 protected void triggerShutdown() {}
116
117 /**
118 * Returns the {@link Executor} that will be used to run this service.
119 * Subclasses may override this method to use a custom {@link Executor}, which
120 * may configure its worker thread with a specific name, thread group or
121 * priority. The returned executor's {@link Executor#execute(Runnable)
122 * execute()} method is called when this service is started, and should return
123 * promptly.
124 *
125 * <p>The default implementation returns a new {@link Executor} that sets the
126 * name of its threads to the string returned by {@link #getServiceName}
127 */
128 protected Executor executor() {
129 return new Executor() {
130 @Override
131 public void execute(Runnable command) {
132 new Thread(command, getServiceName()).start();
133 }
134 };
135 }
136
137 @Override public String toString() {
138 return getServiceName() + " [" + state() + "]";
139 }
140
141 // We override instead of using ForwardingService so that these can be final.
142
143 @Override public final ListenableFuture<State> start() {
144 return delegate.start();
145 }
146
147 @Override public final State startAndWait() {
148 return delegate.startAndWait();
149 }
150
151 @Override public final boolean isRunning() {
152 return delegate.isRunning();
153 }
154
155 @Override public final State state() {
156 return delegate.state();
157 }
158
159 @Override public final ListenableFuture<State> stop() {
160 return delegate.stop();
161 }
162
163 @Override public final State stopAndWait() {
164 return delegate.stopAndWait();
165 }
166
167 /**
168 * Returns the name of this service. {@link AbstractExecutionThreadService} may include the name
169 * in debugging output.
170 *
171 * <p>Subclasses may override this method.
172 *
173 * @since 10.0
174 */
175 protected String getServiceName() {
176 return getClass().getSimpleName();
177 }
178 }