45
45
import java .net .URL ;
46
46
import java .util .ArrayList ;
47
47
import java .util .Arrays ;
48
+ import java .util .Date ;
48
49
import java .util .HashMap ;
49
50
import java .util .List ;
50
51
import java .util .Map ;
@@ -87,6 +88,16 @@ public int hashCode() {
87
88
}
88
89
}
89
90
91
+ private static class HostStatus {
92
+ boolean isUp = true ;
93
+ long lastTryTimestamp ;
94
+
95
+ HostStatus (boolean isUp ) {
96
+ this .isUp = isUp ;
97
+ lastTryTimestamp = new Date ().getTime ();
98
+ }
99
+ }
100
+
90
101
// ----------------------------------------------------------------------
91
102
// Constants
92
103
// ----------------------------------------------------------------------
@@ -116,10 +127,14 @@ public int hashCode() {
116
127
/** Read timeout for search requests (ms). */
117
128
private int searchTimeout = 5000 ;
118
129
130
+ /** Delay to wait when a host is down before retrying it (ms). */
131
+ private int hostDownDelay = 5000 ;
132
+
119
133
private final String applicationID ;
120
134
private final String apiKey ;
121
135
private List <String > readHosts ;
122
136
private List <String > writeHosts ;
137
+ private HashMap <String , HostStatus > hostStatuses = new HashMap <>();
123
138
124
139
/**
125
140
* HTTP headers that will be sent with every request.
@@ -235,8 +250,7 @@ public int getConnectTimeout() {
235
250
* @param connectTimeout The new connection timeout (ms).
236
251
*/
237
252
public void setConnectTimeout (int connectTimeout ) {
238
- if (connectTimeout <= 0 )
239
- throw new IllegalArgumentException ();
253
+ checkTimeout (connectTimeout );
240
254
this .connectTimeout = connectTimeout ;
241
255
}
242
256
@@ -255,8 +269,7 @@ public int getReadTimeout() {
255
269
* @param readTimeout The default read timeout (ms).
256
270
*/
257
271
public void setReadTimeout (int readTimeout ) {
258
- if (readTimeout <= 0 )
259
- throw new IllegalArgumentException ();
272
+ checkTimeout (readTimeout );
260
273
this .readTimeout = readTimeout ;
261
274
}
262
275
@@ -275,11 +288,29 @@ public int getSearchTimeout() {
275
288
* @param searchTimeout The read timeout for search requests (ms).
276
289
*/
277
290
public void setSearchTimeout (int searchTimeout ) {
278
- if (searchTimeout <= 0 )
279
- throw new IllegalArgumentException ();
291
+ checkTimeout (searchTimeout );
280
292
this .searchTimeout = searchTimeout ;
281
293
}
282
294
295
+ /**
296
+ * Get the timeout for retrying connection to a down host.
297
+ *
298
+ * @return The delay before connecting again to a down host (ms).
299
+ */
300
+ public int getHostDownDelay () {
301
+ return hostDownDelay ;
302
+ }
303
+
304
+ /**
305
+ * Set the timeout for retrying connection to a down host.
306
+ *
307
+ * @param hostDownDelay The delay before connecting again to a down host (ms).
308
+ */
309
+ public void setHostDownDelay (int hostDownDelay ) {
310
+ checkTimeout (hostDownDelay );
311
+ this .hostDownDelay = hostDownDelay ;
312
+ }
313
+
283
314
/**
284
315
* Add a software library to the list of user agents.
285
316
*
@@ -333,6 +364,14 @@ private void updateUserAgents() {
333
364
userAgentRaw = s .toString ();
334
365
}
335
366
367
+ private List <String > getReadHostsThatAreUp () {
368
+ return hostsThatAreUp (readHosts );
369
+ }
370
+
371
+ private List <String > getWriteHostsThatAreUp () {
372
+ return hostsThatAreUp (writeHosts );
373
+ }
374
+
336
375
// ----------------------------------------------------------------------
337
376
// Utilities
338
377
// ----------------------------------------------------------------------
@@ -345,27 +384,27 @@ private enum Method {
345
384
}
346
385
347
386
protected byte [] getRequestRaw (String url , boolean search ) throws AlgoliaException {
348
- return _requestRaw (Method .GET , url , null , readHosts , connectTimeout , search ? searchTimeout : readTimeout );
387
+ return _requestRaw (Method .GET , url , null , getReadHostsThatAreUp () , connectTimeout , search ? searchTimeout : readTimeout );
349
388
}
350
389
351
390
protected JSONObject getRequest (String url , boolean search ) throws AlgoliaException {
352
- return _request (Method .GET , url , null , readHosts , connectTimeout , search ? searchTimeout : readTimeout );
391
+ return _request (Method .GET , url , null , getReadHostsThatAreUp () , connectTimeout , search ? searchTimeout : readTimeout );
353
392
}
354
393
355
394
protected JSONObject deleteRequest (String url ) throws AlgoliaException {
356
- return _request (Method .DELETE , url , null , writeHosts , connectTimeout , readTimeout );
395
+ return _request (Method .DELETE , url , null , getWriteHostsThatAreUp () , connectTimeout , readTimeout );
357
396
}
358
397
359
398
protected JSONObject postRequest (String url , String obj , boolean readOperation ) throws AlgoliaException {
360
- return _request (Method .POST , url , obj , (readOperation ? readHosts : writeHosts ), connectTimeout , (readOperation ? searchTimeout : readTimeout ));
399
+ return _request (Method .POST , url , obj , (readOperation ? getReadHostsThatAreUp () : getWriteHostsThatAreUp () ), connectTimeout , (readOperation ? searchTimeout : readTimeout ));
361
400
}
362
401
363
402
protected byte [] postRequestRaw (String url , String obj , boolean readOperation ) throws AlgoliaException {
364
- return _requestRaw (Method .POST , url , obj , (readOperation ? readHosts : writeHosts ), connectTimeout , (readOperation ? searchTimeout : readTimeout ));
403
+ return _requestRaw (Method .POST , url , obj , (readOperation ? getReadHostsThatAreUp () : getWriteHostsThatAreUp () ), connectTimeout , (readOperation ? searchTimeout : readTimeout ));
365
404
}
366
405
367
406
protected JSONObject putRequest (String url , String obj ) throws AlgoliaException {
368
- return _request (Method .PUT , url , obj , writeHosts , connectTimeout , readTimeout );
407
+ return _request (Method .PUT , url , obj , getWriteHostsThatAreUp () , connectTimeout , readTimeout );
369
408
}
370
409
371
410
/**
@@ -526,6 +565,7 @@ private byte[] _requestRaw(Method m, String url, String json, List<String> hosts
526
565
if (stream == null ) {
527
566
throw new IOException (String .format ("Null stream when reading connection (status %d)" , code ));
528
567
}
568
+ hostStatuses .put (host , new HostStatus (true ));
529
569
530
570
final byte [] rawResponse ;
531
571
String encoding = hostConnection .getContentEncoding ();
@@ -556,8 +596,8 @@ private byte[] _requestRaw(Method m, String url, String json, List<String> hosts
556
596
catch (UnsupportedEncodingException e ) { // fatal
557
597
consumeQuietly (hostConnection );
558
598
throw new AlgoliaException ("Invalid encoding returned by server" , e );
559
- }
560
- catch ( IOException e ) { // host error, continue on the next host
599
+ } catch ( IOException e ) { // host error, continue on the next host
600
+ hostStatuses . put ( host , new HostStatus ( false ));
561
601
consumeQuietly (hostConnection );
562
602
errors .add (e );
563
603
} finally {
@@ -599,6 +639,32 @@ private static void consumeQuietly(final HttpURLConnection connection) {
599
639
}
600
640
}
601
641
642
+ private void checkTimeout (int connectTimeout ) {
643
+ if (connectTimeout <= 0 ) {
644
+ throw new IllegalArgumentException ();
645
+ }
646
+ }
647
+
648
+ /**
649
+ * Get the hosts that are not considered down in a given list.
650
+ * @param hosts a list of hosts whose {@link HostStatus} will be checked.
651
+ * @return the hosts considered up, or all hosts if none is known to be reachable.
652
+ */
653
+ private List <String > hostsThatAreUp (List <String > hosts ) {
654
+ List <String > upHosts = new ArrayList <>();
655
+ for (String host : hosts ) {
656
+ if (isUpOrCouldBeRetried (host )) {
657
+ upHosts .add (host );
658
+ }
659
+ }
660
+ return upHosts .isEmpty () ? hosts : upHosts ;
661
+ }
662
+
663
+ boolean isUpOrCouldBeRetried (String host ) {
664
+ HostStatus status = hostStatuses .get (host );
665
+ return status == null || status .isUp || new Date ().getTime () - status .lastTryTimestamp >= hostDownDelay ;
666
+ }
667
+
602
668
// ----------------------------------------------------------------------
603
669
// Utils
604
670
// ----------------------------------------------------------------------
0 commit comments