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    }