001         package com.croftsoft.core.util.loop;
002    
003         import com.croftsoft.core.lang.NullArgumentException;
004         import com.croftsoft.core.lang.ex.ExceptionHandler;
005         import com.croftsoft.core.lang.lifecycle.Lifecycle;
006    
007         /*********************************************************************
008         * Periodically runs a task in a loop using a separate thread.
009         *
010         * @version
011         *   $Id: Looper.java,v 1.3 2008/09/20 05:01:27 croft Exp $
012         * @since
013         *   2000-04-27
014         * @author
015         *   <a href="https://www.croftsoft.com/">David Wallace Croft</a>
016         *********************************************************************/
017    
018         public final class  Looper
019           implements Lifecycle
020         //////////////////////////////////////////////////////////////////////
021         //////////////////////////////////////////////////////////////////////
022         {
023    
024         private final Loopable          loopable;
025    
026         private final ExceptionHandler  exceptionHandler;
027    
028         private final String            threadName;
029    
030         private final int               threadPriority;
031    
032         private final boolean           useDaemonThread;
033    
034         //
035    
036         private LoopGovernor  loopGovernor;
037    
038         private Thread        thread;
039    
040         private boolean       stopRequested;
041    
042         //////////////////////////////////////////////////////////////////////
043         //////////////////////////////////////////////////////////////////////
044    
045         /*********************************************************************
046         * Main constructor.
047         *
048         * <p>
049         * Call the start() method after construction to begin looping.
050         * </p>
051         *
052         * @param  loopable
053         *
054         *   The loop() method of this object will be called periodically.
055         *   Looping will stop if this method returns false.
056         *
057         * @param  exceptionHandler
058         *
059         *   Handles all Exceptions generated by runnable.run().
060         *   The handleException() Object argument passed will be the loopable.
061         *   If null, the default behavior of printing any Exception to the
062         *   standard error output and stopping looping will be used.
063         *********************************************************************/
064         public  Looper (
065           Loopable          loopable,
066           LoopGovernor      loopGovernor,
067           ExceptionHandler  exceptionHandler,
068           String            threadName,
069           int               threadPriority,
070           boolean           useDaemonThread )
071         //////////////////////////////////////////////////////////////////////
072         {
073           NullArgumentException.check ( this.loopable = loopable );
074    
075           setLoopGovernor ( loopGovernor );
076    
077           this.exceptionHandler = exceptionHandler;
078    
079           this.threadName       = threadName;
080    
081           this.threadPriority   = threadPriority;
082    
083           this.useDaemonThread  = useDaemonThread;
084         }
085    
086         public  Looper ( Loopable  loopable )
087         //////////////////////////////////////////////////////////////////////
088         {
089           this (
090             loopable,
091             new FixedDelayLoopGovernor ( 0L, 0 ),
092             ( ExceptionHandler ) null,
093             ( String ) null,
094             Thread.MIN_PRIORITY,
095             true );
096         }
097    
098         //////////////////////////////////////////////////////////////////////
099         //////////////////////////////////////////////////////////////////////
100    
101         public void  setLoopGovernor ( LoopGovernor  loopGovernor )
102         //////////////////////////////////////////////////////////////////////
103         {
104           NullArgumentException.check ( this.loopGovernor = loopGovernor );
105         }
106    
107         //////////////////////////////////////////////////////////////////////
108         //////////////////////////////////////////////////////////////////////
109    
110         public void  init ( )
111         //////////////////////////////////////////////////////////////////////
112         {
113           // empty
114         }
115    
116         public synchronized void  start ( )
117         //////////////////////////////////////////////////////////////////////
118         {
119           stopRequested = false;
120    
121           if ( thread == null )
122           {
123             Runnable  runnable =
124               new Runnable ( )
125               {
126                 public void  run ( )
127                 {
128                   loop ( );
129                 }
130               };
131    
132             if ( threadName == null )
133             {
134               thread = new Thread ( runnable );
135             }
136             else
137             {
138               thread = new Thread ( runnable, threadName );
139             }
140    
141             thread.setPriority ( threadPriority );
142    
143             thread.setDaemon ( useDaemonThread );
144    
145             thread.start ( );
146           }
147           else
148           {
149             notify ( );
150           }
151         }
152    
153         public synchronized void  stop ( )
154         //////////////////////////////////////////////////////////////////////
155         {
156           stopRequested = true;
157    
158           thread.interrupt ( );
159         }
160    
161         public synchronized void  destroy ( )
162         //////////////////////////////////////////////////////////////////////
163         {
164           thread = null;
165    
166           stopRequested = false;
167    
168           notify ( );
169         }
170    
171         //////////////////////////////////////////////////////////////////////
172         //////////////////////////////////////////////////////////////////////
173    
174         private void  loop ( )
175         //////////////////////////////////////////////////////////////////////
176         {
177           while ( thread != null )
178           {
179             try
180             {
181               if ( loopable.loop ( ) )
182               {
183                 loopGovernor.govern ( );
184               }
185               else
186               {
187                 stopRequested = true;
188               }
189             }
190             catch ( InterruptedException  ex )
191             {
192               // ignore
193             }
194             catch ( Exception  ex )
195             {
196               if ( ( exceptionHandler == null )
197                 || !exceptionHandler.handleException ( ex, loopable ) )
198               {
199                 stopRequested = true;
200    
201                 ex.printStackTrace ( );
202               }
203             }
204    
205             if ( stopRequested )
206             {
207               synchronized ( this )
208               {
209                 while ( stopRequested )
210                 {
211                   try
212                   {
213                     wait ( );
214                   }
215                   catch ( InterruptedException  ex )
216                   {
217                     // ignore
218                   }
219                 }
220               }
221             }
222           }
223         }
224    
225         //////////////////////////////////////////////////////////////////////
226         //////////////////////////////////////////////////////////////////////
227         }