| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| Service |
|
| 1.8125;1.812 |
| 1 | /** | |
| 2 | The contents of this file are subject to the Mozilla Public License Version 1.1 | |
| 3 | (the "License"); you may not use this file except in compliance with the License. | |
| 4 | You may obtain a copy of the License at http://www.mozilla.org/MPL/ | |
| 5 | Software distributed under the License is distributed on an "AS IS" basis, | |
| 6 | WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the | |
| 7 | specific language governing rights and limitations under the License. | |
| 8 | ||
| 9 | The Original Code is "ManagedRunnable.java". Description: | |
| 10 | "Base class for a unified management of threads with a defined lifecycle." | |
| 11 | ||
| 12 | The Initial Developer of the Original Code is University Health Network. Copyright (C) | |
| 13 | 2001. All Rights Reserved. | |
| 14 | ||
| 15 | Contributor(s): ______________________________________. | |
| 16 | ||
| 17 | Alternatively, the contents of this file may be used under the terms of the | |
| 18 | GNU General Public License (the "GPL"), in which case the provisions of the GPL are | |
| 19 | applicable instead of those above. If you wish to allow use of your version of this | |
| 20 | file only under the terms of the GPL and not to allow others to use your version | |
| 21 | of this file under the MPL, indicate your decision by deleting the provisions above | |
| 22 | and replace them with the notice and other provisions required by the GPL License. | |
| 23 | If you do not delete the provisions above, a recipient may use your version of | |
| 24 | this file under either the MPL or the GPL. | |
| 25 | */ | |
| 26 | package ca.uhn.hl7v2.concurrent; | |
| 27 | ||
| 28 | import java.util.concurrent.CountDownLatch; | |
| 29 | import java.util.concurrent.ExecutionException; | |
| 30 | import java.util.concurrent.ExecutorService; | |
| 31 | import java.util.concurrent.Future; | |
| 32 | import java.util.concurrent.TimeUnit; | |
| 33 | import java.util.concurrent.TimeoutException; | |
| 34 | ||
| 35 | import org.slf4j.Logger; | |
| 36 | import org.slf4j.LoggerFactory; | |
| 37 | ||
| 38 | /** | |
| 39 | * Base class for a unified management of threads with a defined lifecycle. It | |
| 40 | * uses a {@link #keepRunning} flag to regularly terminate a thread. Classes | |
| 41 | * implementing this class must implement {@link #handle()} to do the main | |
| 42 | * processing. {@link #afterStartup()} and {@link #afterTermination()} can be | |
| 43 | * overridden to acquire and release resources required for processing. | |
| 44 | */ | |
| 45 | public abstract class Service implements Runnable { | |
| 46 | ||
| 47 | 10 | private static final Logger log = LoggerFactory |
| 48 | 5 | .getLogger(Service.class); |
| 49 | private volatile boolean keepRunning; | |
| 50 | 993 | private long shutdownTimeout = 3000L; |
| 51 | private final String name; | |
| 52 | private final ExecutorService executorService; | |
| 53 | private Future<?> thread; | |
| 54 | private Throwable serviceExitedWithException; | |
| 55 | 993 | private CountDownLatch startupLatch = new CountDownLatch(1); |
| 56 | ||
| 57 | public Service(String name, ExecutorService executorService) { | |
| 58 | 993 | super(); |
| 59 | 993 | this.name = name; |
| 60 | 993 | this.executorService = executorService; |
| 61 | 993 | } |
| 62 | ||
| 63 | /** | |
| 64 | * @return Returns <code>true</code> if the server has been started, and has | |
| 65 | * not yet been stopped. | |
| 66 | */ | |
| 67 | public boolean isRunning() { | |
| 68 | 15299 | return keepRunning; |
| 69 | } | |
| 70 | ||
| 71 | public ExecutorService getExecutorService() { | |
| 72 | 2145 | return executorService; |
| 73 | } | |
| 74 | ||
| 75 | /** | |
| 76 | * Sets the time in milliseconds how long {@link #stopAndWait()} should wait | |
| 77 | * for the thread to terminate. Defaults to 3000ms. | |
| 78 | * | |
| 79 | * @param shutdownTimeout timout in milliseconds | |
| 80 | */ | |
| 81 | public void setShutdownTimeout(long shutdownTimeout) { | |
| 82 | 0 | this.shutdownTimeout = shutdownTimeout; |
| 83 | 0 | } |
| 84 | ||
| 85 | /** | |
| 86 | * Starts the server listening for connections in a new thread. This | |
| 87 | * continues until <code>stop()</code> is called. | |
| 88 | * | |
| 89 | * @throws IllegalStateException If the service is already running (i.e. | |
| 90 | * start() has already been called. | |
| 91 | * | |
| 92 | */ | |
| 93 | public void start() { | |
| 94 | 978 | if (keepRunning) { |
| 95 | 5 | throw new IllegalStateException("Service is already running"); |
| 96 | } | |
| 97 | 973 | log.debug("Starting service {}", name); |
| 98 | 973 | keepRunning = true; |
| 99 | 973 | ExecutorService service = getExecutorService(); |
| 100 | 973 | if (service.isShutdown()) { |
| 101 | 0 | throw new IllegalStateException("ExecutorService is shut down"); |
| 102 | } | |
| 103 | 973 | thread = service.submit(this); |
| 104 | 973 | } |
| 105 | ||
| 106 | /** | |
| 107 | * <p> | |
| 108 | * Starts the server listening for connections in a new thread. This | |
| 109 | * continues until <code>stop()</code> is called. | |
| 110 | * </p> | |
| 111 | * <p> | |
| 112 | * Unlike {@link #start()}, this method will not return until the processing | |
| 113 | * loop has completed at least once. This does not imply any kind of successful | |
| 114 | * processing, but should at least provide a guarantee that the service | |
| 115 | * has finished initializing itself. | |
| 116 | * </p> | |
| 117 | */ | |
| 118 | public void startAndWait() throws InterruptedException { | |
| 119 | 115 | start(); |
| 120 | 110 | startupLatch.await(); |
| 121 | 110 | } |
| 122 | ||
| 123 | /** | |
| 124 | * Prepare any resources before entering the main thread. | |
| 125 | * | |
| 126 | * @throws RuntimeException | |
| 127 | * if resources could not acquired. In this case, the thread | |
| 128 | * will shutdown. Note that {@link #afterTermination()} is | |
| 129 | * called before. | |
| 130 | */ | |
| 131 | protected void afterStartup() { | |
| 132 | 838 | } |
| 133 | ||
| 134 | /** | |
| 135 | * The main task of the thread, called in a loop as long as | |
| 136 | * {@link #isRunning()} returns true. Overridden methods are responsible for | |
| 137 | * yielding or pausing the thread when it's idle. The method must also not | |
| 138 | * block indefinitely so that a call to {@link #stop()} is able to | |
| 139 | * gracefully terminate the thread. | |
| 140 | */ | |
| 141 | protected abstract void handle(); | |
| 142 | ||
| 143 | /** | |
| 144 | * Advises the thread to leave its main loop. {@link #prepareTermination()} is | |
| 145 | * called before this method returns. {@link #afterTermination()} is | |
| 146 | * called after the thread has left its main loop. | |
| 147 | */ | |
| 148 | public void stop() { | |
| 149 | 878 | if (isRunning()) { |
| 150 | 878 | prepareTermination(); |
| 151 | } | |
| 152 | 878 | } |
| 153 | ||
| 154 | public void waitForTermination() { | |
| 155 | 205 | if (!thread.isDone()) |
| 156 | try { | |
| 157 | 205 | thread.get(shutdownTimeout, TimeUnit.MILLISECONDS); |
| 158 | 0 | } catch (ExecutionException ee) { |
| 159 | // empty | |
| 160 | 0 | } catch (TimeoutException te) { |
| 161 | 0 | log.warn( |
| 162 | "Thread did not stop after {} milliseconds. Now cancelling.", | |
| 163 | 0 | shutdownTimeout); |
| 164 | 0 | thread.cancel(true); |
| 165 | 0 | } catch (InterruptedException e) { |
| 166 | // empty | |
| 167 | 205 | } |
| 168 | 205 | } |
| 169 | ||
| 170 | /** | |
| 171 | * Stops the thread by leaving its main loop. {@link #afterTermination()} is | |
| 172 | * called before the thread is terminated. The method waits until the thread | |
| 173 | * has stopped. | |
| 174 | */ | |
| 175 | public final void stopAndWait() { | |
| 176 | 205 | stop(); |
| 177 | 205 | waitForTermination(); |
| 178 | 205 | } |
| 179 | ||
| 180 | /** | |
| 181 | * Clean up any resources initialized in {@link #afterStartup()}. | |
| 182 | */ | |
| 183 | protected void afterTermination() { | |
| 184 | 878 | } |
| 185 | ||
| 186 | /** | |
| 187 | * Prepare thread to leave its main loop. By default sets {@link #keepRunning} | |
| 188 | * to false, but some implementations may need to do additional stuff. | |
| 189 | */ | |
| 190 | protected void prepareTermination() { | |
| 191 | 878 | log.debug("Prepare to stop thread {}", name); |
| 192 | 878 | keepRunning = false; |
| 193 | 878 | } |
| 194 | ||
| 195 | /** | |
| 196 | * Runs the thread. | |
| 197 | * | |
| 198 | * @see java.lang.Runnable#run() | |
| 199 | */ | |
| 200 | public final void run() { | |
| 201 | try { | |
| 202 | 973 | afterStartup(); |
| 203 | 968 | log.debug("Thread {} entering main loop", name); |
| 204 | 13267 | while (isRunning()) { |
| 205 | 12394 | handle(); |
| 206 | 12299 | startupLatch.countDown(); |
| 207 | } | |
| 208 | 873 | log.debug("Thread {} leaving main loop", name); |
| 209 | 5 | } catch (RuntimeException t) { |
| 210 | 5 | if (t.getCause() != null) { |
| 211 | 5 | serviceExitedWithException = t.getCause(); |
| 212 | } else { | |
| 213 | 0 | serviceExitedWithException = t; |
| 214 | } | |
| 215 | 5 | log.warn("Thread exiting main loop due to exception:", t); |
| 216 | 0 | } catch (Throwable t) { |
| 217 | 0 | serviceExitedWithException = t; |
| 218 | 0 | log.warn("Thread exiting main loop due to exception:", t); |
| 219 | } finally { | |
| 220 | 878 | startupLatch.countDown(); |
| 221 | 878 | afterTermination(); |
| 222 | 878 | } |
| 223 | ||
| 224 | 878 | } |
| 225 | ||
| 226 | /** | |
| 227 | * Provide the exception which caused this service to fail | |
| 228 | */ | |
| 229 | protected void setServiceExitedWithException(Throwable theThreadExitedWithException) { | |
| 230 | 5 | serviceExitedWithException = theThreadExitedWithException; |
| 231 | 5 | } |
| 232 | ||
| 233 | ||
| 234 | /** | |
| 235 | * If this service exited with an exception, ths method returns that exception. This is useful for | |
| 236 | * detecting if the service failed unexpectedly | |
| 237 | */ | |
| 238 | public Throwable getServiceExitedWithException() { | |
| 239 | 3594 | return serviceExitedWithException; |
| 240 | } | |
| 241 | ||
| 242 | } |