001         package com.croftsoft.core.net.http.msg;
002    
003         import java.io.*;
004         import java.net.*;
005    
006         import com.croftsoft.core.io.Parser;
007         import com.croftsoft.core.lang.NullArgumentException;
008         import com.croftsoft.core.lang.lifecycle.Lifecycle;
009         import com.croftsoft.core.math.MathConstants;
010         import com.croftsoft.core.net.http.HttpLib;
011         import com.croftsoft.core.util.loop.FixedDelayLoopGovernor;
012         import com.croftsoft.core.util.loop.Loopable;
013         import com.croftsoft.core.util.loop.Looper;
014         import com.croftsoft.core.util.queue.Queue;
015    
016         /*********************************************************************
017         * Polls a server for messages and downloads them to a local queue.
018         *
019         * @version
020         *   2003-06-12
021         * @since
022         *   2000-04-27
023         * @author
024         *   <a href="https://www.croftsoft.com/">David Wallace Croft</a>
025         *********************************************************************/
026    
027         public final class  HttpMessagePoller
028           implements Lifecycle
029         //////////////////////////////////////////////////////////////////////
030         //////////////////////////////////////////////////////////////////////
031         {
032    
033         public static final long     DEFAULT_POLLING_PERIOD_MIN
034           = MathConstants.MILLISECONDS_PER_SECOND;
035    
036         public static final long     DEFAULT_POLLING_PERIOD_MAX
037           = MathConstants.MILLISECONDS_PER_DAY;
038                                    
039         public static final long     DEFAULT_POLLING_PERIOD_INIT
040           = DEFAULT_POLLING_PERIOD_MIN;
041    
042         public static final double   DEFAULT_POLLING_PERIOD_MULT
043           = 1.1;
044    
045         public static final double   DEFAULT_POLLING_PERIOD_DIVI
046           = 1.1;
047    
048         public static final long     DEFAULT_POLLING_PERIOD_INCR
049           = MathConstants.MILLISECONDS_PER_SECOND;
050    
051         public static final String   THREAD_NAME = "HttpMessagePoller";
052    
053         public static final int      THREAD_PRIORITY = Thread.MIN_PRIORITY;
054    
055         public static final boolean  USE_DAEMON_THREAD = true;
056    
057         //
058    
059         private static final boolean  DEBUG = false;
060    
061         //
062    
063         private final URL     url;
064    
065         private final String  userAgent;
066    
067         private final String  contentType;
068    
069         private final Parser  parser;
070    
071         private final Looper  looper;
072    
073         private final Queue   incomingQueue;
074    
075         private final long    pollingPeriodMin;
076    
077         private final long    pollingPeriodMax;
078    
079         private final double  pollingPeriodMult;
080    
081         private final double  pollingPeriodDivi;
082    
083         private final long    pollingPeriodIncr;
084    
085         //
086    
087         private byte [ ]  requestBytes;
088    
089         private long      pollingPeriod;
090    
091         //////////////////////////////////////////////////////////////////////
092         // constructor methods
093         //////////////////////////////////////////////////////////////////////
094    
095         /*********************************************************************
096         * All polling period values are in milliseconds.
097         *
098         * @param  pollingPeriodIncr
099         *
100         *   Minimum incremental change whenever the polling period is
101         *   increased or decreased, regardless of the pollingPeriodMult
102         *   or pollingPeriodDivi value.
103         *********************************************************************/
104         public  HttpMessagePoller (
105           URL       url,
106           String    userAgent,
107           String    contentType,
108           byte [ ]  requestBytes,
109           Parser    parser,
110           Queue     incomingQueue,
111           long      pollingPeriodMin,
112           long      pollingPeriodMax,
113           long      pollingPeriodInit,
114           double    pollingPeriodMult,
115           double    pollingPeriodDivi,
116           long      pollingPeriodIncr )
117         //////////////////////////////////////////////////////////////////////
118         {
119           NullArgumentException.check ( this.url           = url           );
120    
121           NullArgumentException.check ( this.userAgent     = userAgent     );
122    
123           NullArgumentException.check ( this.contentType   = contentType   );
124    
125           NullArgumentException.check ( this.parser        = parser        );
126    
127           NullArgumentException.check ( this.incomingQueue = incomingQueue );
128    
129           setRequestBytes ( requestBytes );
130    
131           if ( pollingPeriodMin < 0 )
132           {
133             throw new IllegalArgumentException ( "pollingPeriodMin < 0" );
134           }
135    
136           if ( pollingPeriodMax < pollingPeriodMin )
137           {
138             throw new IllegalArgumentException (
139               "pollingPeriodMax < pollingPeriodMin" );
140           }
141    
142           if ( pollingPeriodInit < pollingPeriodMin )
143           {
144             throw new IllegalArgumentException (
145               "pollingPeriodInit < pollingPeriodMin" );
146           }
147    
148           if ( pollingPeriodInit > pollingPeriodMax )
149           {
150             throw new IllegalArgumentException (
151               "pollingPeriodInit > pollingPeriodMax" );
152           }
153    
154           if ( pollingPeriodMult < 1.0 )
155           {
156             throw new IllegalArgumentException ( "pollingPeriodMult < 1.0" );
157           }
158    
159           if ( pollingPeriodDivi < 1.0 )
160           {
161             throw new IllegalArgumentException ( "pollingPeriodDivi < 1.0" );
162           }
163    
164           if ( pollingPeriodIncr < 0 )
165           {
166             throw new IllegalArgumentException ( "pollingPeriodIncr < 0" );
167           }
168    
169           looper = new Looper (
170             new Loopable ( )
171             {
172               public boolean  loop ( )
173               {
174                 return HttpMessagePoller.this.loop ( );
175               }
176             },
177             new FixedDelayLoopGovernor ( pollingPeriodInit, 0 ),
178             null,
179             THREAD_NAME,
180             THREAD_PRIORITY,
181             USE_DAEMON_THREAD );
182    
183           this.pollingPeriodMin  = pollingPeriodMin;
184    
185           this.pollingPeriodMax  = pollingPeriodMax;
186    
187           this.pollingPeriodMult = pollingPeriodMult;
188    
189           this.pollingPeriodDivi = pollingPeriodDivi;
190    
191           this.pollingPeriodIncr = pollingPeriodIncr;
192    
193           pollingPeriod = pollingPeriodInit;
194         }
195    
196         /*********************************************************************
197         * Convenience constructor using default polling period values.
198         *********************************************************************/
199         public  HttpMessagePoller (
200           URL       url,
201           String    userAgent,
202           String    contentType,
203           byte [ ]  requestBytes,
204           Parser    contentParser,
205           Queue     incomingQueue )
206         //////////////////////////////////////////////////////////////////////
207         {
208           this (
209             url,
210             userAgent,
211             contentType,
212             requestBytes,
213             contentParser,
214             incomingQueue,
215             DEFAULT_POLLING_PERIOD_MIN,
216             DEFAULT_POLLING_PERIOD_MAX,
217             DEFAULT_POLLING_PERIOD_INIT,
218             DEFAULT_POLLING_PERIOD_MULT,
219             DEFAULT_POLLING_PERIOD_DIVI,
220             DEFAULT_POLLING_PERIOD_INCR );
221         }
222    
223         //////////////////////////////////////////////////////////////////////
224         // mutator methods
225         //////////////////////////////////////////////////////////////////////
226    
227         public void  setRequestBytes ( byte [ ]  requestBytes )
228         //////////////////////////////////////////////////////////////////////
229         {
230           NullArgumentException.check ( this.requestBytes = requestBytes );
231         }
232    
233         public synchronized long  setPollingPeriod ( long  pollingPeriod )
234         //////////////////////////////////////////////////////////////////////
235         {
236           if ( pollingPeriod < pollingPeriodMin )
237           {
238             pollingPeriod = pollingPeriodMin;
239           }
240    
241           if ( pollingPeriod > pollingPeriodMax )
242           {
243             pollingPeriod = pollingPeriodMax;
244           }
245    
246           this.pollingPeriod = pollingPeriod;
247    
248           looper.setLoopGovernor (
249             new FixedDelayLoopGovernor ( pollingPeriod, 0 ) );
250    
251           return pollingPeriod;       
252         }
253    
254         //////////////////////////////////////////////////////////////////////
255         // lifecycle methods
256         //////////////////////////////////////////////////////////////////////
257    
258         public void  init ( )
259         //////////////////////////////////////////////////////////////////////
260         {
261           looper.init ( );
262         }
263    
264         public void  start ( )
265         //////////////////////////////////////////////////////////////////////
266         {
267           looper.start ( );
268         }
269    
270         public void  stop ( )
271         //////////////////////////////////////////////////////////////////////
272         {
273           looper.stop ( );
274         }
275    
276         public void  destroy ( )
277         //////////////////////////////////////////////////////////////////////
278         {
279           looper.destroy ( );
280         }
281    
282         //////////////////////////////////////////////////////////////////////
283         // private methods
284         //////////////////////////////////////////////////////////////////////
285    
286         private boolean  loop ( )
287         //////////////////////////////////////////////////////////////////////
288         {
289           try
290           {
291             Object  response = HttpLib.post ( 
292               url,
293               requestBytes,
294               userAgent,
295               contentType,
296               parser );
297    
298             if ( response != null )
299             {
300               incomingQueue.append ( response );
301    
302               decreasePollingPeriod ( );
303             }
304             else
305             {
306               increasePollingPeriod ( );
307             }
308    
309             return true;
310           }
311           catch ( Exception  ex )
312           {
313             ex.printStackTrace ( );
314    
315             increasePollingPeriod ( );
316    
317             return true;
318           }
319         }
320    
321         private void  increasePollingPeriod ( )
322         //////////////////////////////////////////////////////////////////////
323         {
324           long  newPollingPeriod
325             = ( long ) ( pollingPeriod * pollingPeriodMult );
326    
327           if ( newPollingPeriod < pollingPeriod + pollingPeriodIncr )
328           {
329             newPollingPeriod = pollingPeriod + pollingPeriodIncr;
330           }
331    
332           if ( newPollingPeriod > pollingPeriodMax )
333           {
334             newPollingPeriod = pollingPeriodMax;
335           }
336    
337           if ( pollingPeriod != newPollingPeriod )
338           {
339             if ( DEBUG )
340             {
341               System.out.println (
342                 "Increasing polling period from " + pollingPeriod
343                 + " to " + newPollingPeriod + " milliseconds." );
344             }
345    
346             looper.setLoopGovernor (
347               new FixedDelayLoopGovernor ( newPollingPeriod, 0 ) );
348    
349             pollingPeriod = newPollingPeriod;
350           }
351         }
352    
353         private void  decreasePollingPeriod ( )
354         //////////////////////////////////////////////////////////////////////
355         {
356           long  newPollingPeriod
357             = ( long ) ( pollingPeriod / pollingPeriodDivi );
358    
359           if ( newPollingPeriod > pollingPeriod - pollingPeriodIncr )
360           {
361             newPollingPeriod = pollingPeriod - pollingPeriodIncr;
362           }
363    
364           if ( newPollingPeriod < pollingPeriodMin )
365           {
366             newPollingPeriod = pollingPeriodMin;
367           }
368    
369           if ( pollingPeriod != newPollingPeriod )
370           {
371             if ( DEBUG )
372             {
373               System.out.println (
374                 "Decreasing polling period from " + pollingPeriod
375                 + " to " + newPollingPeriod + " milliseconds." );
376             }
377    
378             looper.setLoopGovernor (
379               new FixedDelayLoopGovernor ( newPollingPeriod, 0 ) );
380    
381             pollingPeriod = newPollingPeriod;
382           }
383         }
384    
385         //////////////////////////////////////////////////////////////////////
386         //////////////////////////////////////////////////////////////////////
387         }