001 /**
002 The contents of this file are subject to the Mozilla Public License Version 1.1
003 (the "License"); you may not use this file except in compliance with the License.
004 You may obtain a copy of the License at http://www.mozilla.org/MPL/
005 Software distributed under the License is distributed on an "AS IS" basis,
006 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
007 specific language governing rights and limitations under the License.
008
009 The Original Code is "ManagedRunnable.java". Description:
010 "Base class for a unified management of threads with a defined lifecycle."
011
012 The Initial Developer of the Original Code is University Health Network. Copyright (C)
013 2001. All Rights Reserved.
014
015 Contributor(s): ______________________________________.
016
017 Alternatively, the contents of this file may be used under the terms of the
018 GNU General Public License (the "GPL"), in which case the provisions of the GPL are
019 applicable instead of those above. If you wish to allow use of your version of this
020 file only under the terms of the GPL and not to allow others to use your version
021 of this file under the MPL, indicate your decision by deleting the provisions above
022 and replace them with the notice and other provisions required by the GPL License.
023 If you do not delete the provisions above, a recipient may use your version of
024 this file under either the MPL or the GPL.
025 */
026 package ca.uhn.hl7v2.concurrent;
027
028 import java.util.concurrent.ExecutionException;
029 import java.util.concurrent.ExecutorService;
030 import java.util.concurrent.Future;
031 import java.util.concurrent.TimeUnit;
032 import java.util.concurrent.TimeoutException;
033
034 import org.slf4j.Logger;
035 import org.slf4j.LoggerFactory;
036
037 /**
038 * Base class for a unified management of threads with a defined lifecycle. It
039 * uses a {@link #keepRunning} flag to regularly terminate a thread. Classes
040 * implementing this class must implement {@link #handle()} to do the main
041 * processing. {@link #afterStartup()} and {@link #afterTermination()} can be
042 * overridden to acquire and release resources required for processing.
043 */
044 public abstract class Service implements Runnable {
045
046 private static final Logger log = LoggerFactory
047 .getLogger(Service.class);
048 private volatile boolean keepRunning;
049 private long shutdownTimeout = 3000L;
050 private final String name;
051 private final ExecutorService executorService;
052 private Future<?> thread;
053 private Throwable serviceExitedWithException;
054
055 public Service(String name, ExecutorService executorService) {
056 super();
057 this.name = name;
058 this.executorService = executorService;
059 }
060
061 /**
062 * @return Returns <code>true</code> if the server has been started, and has
063 * not yet been stopped.
064 */
065 public boolean isRunning() {
066 return keepRunning;
067 }
068
069 public ExecutorService getExecutorService() {
070 return executorService;
071 }
072
073 /**
074 * Sets the time in milliseconds how long {@link #stopAndWait()} should wait
075 * for the thread to terminate. Defaults to 3000ms.
076 *
077 * @param shutdownTimeout
078 */
079 public void setShutdownTimeout(long shutdownTimeout) {
080 this.shutdownTimeout = shutdownTimeout;
081 }
082
083 /**
084 * Starts the server listening for connections in a new thread. This
085 * continues until <code>stop()</code> is called.
086 */
087 public void start() {
088 log.debug("Starting service {}", name);
089 keepRunning = true;
090 thread = getExecutorService().submit(this);
091 }
092
093 /**
094 * Prepare any resources before entering the main thread.
095 *
096 * @throws RuntimeException
097 * if resources could not acquired. In this case, the thread
098 * will shutdown. Note that {@link #afterTermination()} is
099 * called before.
100 */
101 protected void afterStartup() {
102 }
103
104 /**
105 * The main task of the thread, called in a loop as long as
106 * {@link #isRunning()} returns true. Overridden methods are responsible for
107 * yielding or pausing the thread when it's idle. The method must also not
108 * block indefinitely so that a call to {@link #stop()} is able to
109 * gracefully terminate the thread.
110 *
111 * @throws Throwable
112 * any exception thrown will terminate the thread.
113 */
114 protected abstract void handle();
115
116 /**
117 * Advises the thread to leave its main loop. {@link #beforeTermination()} is
118 * called before this method returns. {@link #afterTermination()} is
119 * called after the thread has left its main loop.
120 */
121 public void stop() {
122 if (isRunning()) {
123 prepareTermination();
124 }
125 }
126
127 public void waitForTermination() {
128 if (!thread.isDone())
129 try {
130 thread.get(shutdownTimeout, TimeUnit.MILLISECONDS);
131 } catch (ExecutionException ee) {
132 } catch (TimeoutException te) {
133 log.warn(
134 "Thread did not stop after {} milliseconds. Now cancelling.",
135 shutdownTimeout);
136 thread.cancel(true);
137 } catch (InterruptedException e) {
138 }
139 }
140
141 /**
142 * Stops the thread by leaving its main loop. {@link #afterTermination()} is
143 * called before the thread is terminated. The method waits until the thread
144 * has stopped.
145 *
146 * @throws Exception
147 * if the thread has not finished within shutdownTimeout
148 */
149 public final void stopAndWait() {
150 stop();
151 waitForTermination();
152 }
153
154 /**
155 * Clean up any resources initialized in {@link #afterStartup()}.
156 */
157 protected void afterTermination() {
158 };
159
160 /**
161 * Prepare thread to leave its main loop. By default sets {@link #keepRunning}
162 * to false, but some implementations may need to do additional stuff.
163 */
164 protected void prepareTermination() {
165 log.debug("Prepare to stop thread {}", name);
166 keepRunning = false;
167 };
168
169 /**
170 * Runs the thread.
171 *
172 * @see java.lang.Runnable#run()
173 */
174 public final void run() {
175 try {
176 afterStartup();
177 log.debug("Thread {} entering main loop", name);
178 while (isRunning()) {
179 handle();
180 }
181 log.debug("Thread {} leaving main loop", name);
182 } catch (RuntimeException t) {
183 serviceExitedWithException = t.getCause();
184 log.warn("Thread exiting main loop due to exception:", t.getCause());
185 } catch (Throwable t) {
186 serviceExitedWithException = t;
187 log.warn("Thread exiting main loop due to exception:", t);
188 } finally {
189 afterTermination();
190 }
191
192 }
193
194 /**
195 * Provide the exception which caused this service to fail
196 */
197 protected void setServiceExitedWithException(Throwable theThreadExitedWithException) {
198 serviceExitedWithException = theThreadExitedWithException;
199 }
200
201
202 /**
203 * If this service exited with an exception, ths method returns that exception. This is useful for
204 * detecting if the service failed unexpectedly
205 */
206 public Throwable getServiceExitedWithException() {
207 return serviceExitedWithException;
208 }
209
210 }