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 }