1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.core5.pool;
28
29 import java.util.Collections;
30 import java.util.Random;
31 import java.util.concurrent.CancellationException;
32 import java.util.concurrent.CountDownLatch;
33 import java.util.concurrent.ExecutionException;
34 import java.util.concurrent.ExecutorService;
35 import java.util.concurrent.Executors;
36 import java.util.concurrent.Future;
37 import java.util.concurrent.TimeUnit;
38 import java.util.concurrent.atomic.AtomicInteger;
39 import java.util.concurrent.atomic.AtomicReference;
40
41 import org.apache.hc.core5.http.HttpConnection;
42 import org.apache.hc.core5.io.CloseMode;
43 import org.apache.hc.core5.util.DeadlineTimeoutException;
44 import org.apache.hc.core5.util.TimeValue;
45 import org.apache.hc.core5.util.Timeout;
46 import org.junit.jupiter.api.Assertions;
47 import org.junit.jupiter.api.Test;
48 import org.mockito.ArgumentMatchers;
49 import org.mockito.Mockito;
50
51 public class TestStrictConnPool {
52
53 @Test
54 public void testEmptyPool() throws Exception {
55 try (final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 10)) {
56 final PoolStats totals = pool.getTotalStats();
57 Assertions.assertEquals(0, totals.getAvailable());
58 Assertions.assertEquals(0, totals.getLeased());
59 Assertions.assertEquals(0, totals.getPending());
60 Assertions.assertEquals(10, totals.getMax());
61 Assertions.assertEquals(Collections.emptySet(), pool.getRoutes());
62 final PoolStats stats = pool.getStats("somehost");
63 Assertions.assertEquals(0, stats.getAvailable());
64 Assertions.assertEquals(0, stats.getLeased());
65 Assertions.assertEquals(0, stats.getPending());
66 Assertions.assertEquals(2, stats.getMax());
67 Assertions.assertEquals("[leased: 0][available: 0][pending: 0]", pool.toString());
68 }
69 }
70
71 @Test
72 public void testInvalidConstruction() throws Exception {
73 Assertions.assertThrows(IllegalArgumentException.class, () ->
74 new StrictConnPool<String, HttpConnection>(-1, 1));
75 Assertions.assertThrows(IllegalArgumentException.class, () ->
76 new StrictConnPool<String, HttpConnection>(1, -1));
77 }
78
79 @Test
80 public void testLeaseRelease() throws Exception {
81 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
82 final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
83 final HttpConnection conn3 = Mockito.mock(HttpConnection.class);
84
85 try (final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 10)) {
86 final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null);
87 final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null);
88 final Future<PoolEntry<String, HttpConnection>> future3 = pool.lease("otherhost", null);
89
90 final PoolEntry<String, HttpConnection> entry1 = future1.get();
91 Assertions.assertNotNull(entry1);
92 entry1.assignConnection(conn1);
93 final PoolEntry<String, HttpConnection> entry2 = future2.get();
94 Assertions.assertNotNull(entry2);
95 entry2.assignConnection(conn2);
96 final PoolEntry<String, HttpConnection> entry3 = future3.get();
97 Assertions.assertNotNull(entry3);
98 entry3.assignConnection(conn3);
99
100 pool.release(entry1, true);
101 pool.release(entry2, true);
102 pool.release(entry3, false);
103 Mockito.verify(conn1, Mockito.never()).close(ArgumentMatchers.any());
104 Mockito.verify(conn2, Mockito.never()).close(ArgumentMatchers.any());
105 Mockito.verify(conn3, Mockito.times(1)).close(CloseMode.GRACEFUL);
106
107 final PoolStats totals = pool.getTotalStats();
108 Assertions.assertEquals(2, totals.getAvailable());
109 Assertions.assertEquals(0, totals.getLeased());
110 Assertions.assertEquals(0, totals.getPending());
111 }
112 }
113
114 @Test
115 public void testLeaseReleaseMultiThreaded() throws Exception {
116 try (final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 10)) {
117
118 final int c = 10;
119 final CountDownLatch latch = new CountDownLatch(c);
120 final AtomicInteger n = new AtomicInteger(c + 100);
121 final AtomicReference<AssertionError> exRef = new AtomicReference<>();
122
123 final ExecutorService executorService = Executors.newFixedThreadPool(c);
124 try {
125 final Random rnd = new Random();
126 for (int i = 0; i < c; i++) {
127 executorService.execute(() -> {
128 try {
129 while (n.decrementAndGet() > 0) {
130 try {
131 final Future<PoolEntry<String, HttpConnection>> future = pool.lease("somehost", null);
132 final PoolEntry<String, HttpConnection> poolEntry = future.get(1, TimeUnit.MINUTES);
133 Thread.sleep(rnd.nextInt(1));
134 pool.release(poolEntry, false);
135 } catch (final Exception ex) {
136 Assertions.fail(ex.getMessage(), ex);
137 }
138 }
139 } catch (final AssertionError ex) {
140 exRef.compareAndSet(null, ex);
141 } finally {
142 latch.countDown();
143 }
144 });
145 }
146
147 Assertions.assertTrue(latch.await(5, TimeUnit.MINUTES));
148 } finally {
149 executorService.shutdownNow();
150 }
151
152 final AssertionError assertionError = exRef.get();
153 if (assertionError != null) {
154 throw assertionError;
155 }
156 }
157 }
158
159 @Test
160 public void testLeaseInvalid() throws Exception {
161 try (final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 10)) {
162 Assertions.assertThrows(NullPointerException.class, () ->
163 pool.lease(null, null, Timeout.ZERO_MILLISECONDS, null));
164 Assertions.assertThrows(NullPointerException.class, () ->
165 pool.lease("somehost", null, null, null));
166 }
167 }
168
169 @Test
170 public void testReleaseUnknownEntry() throws Exception {
171 try (final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 2)) {
172 Assertions.assertThrows(IllegalStateException.class, () ->
173 pool.release(new PoolEntry<>("somehost"), true));
174 }
175 }
176
177 @Test
178 public void testMaxLimits() throws Exception {
179 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
180 final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
181 final HttpConnection conn3 = Mockito.mock(HttpConnection.class);
182
183 try (final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 10)) {
184 pool.setMaxPerRoute("somehost", 2);
185 pool.setMaxPerRoute("otherhost", 1);
186 pool.setMaxTotal(3);
187
188 final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null);
189 final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null);
190 final Future<PoolEntry<String, HttpConnection>> future3 = pool.lease("otherhost", null);
191
192 final PoolEntry<String, HttpConnection> entry1 = future1.get();
193 Assertions.assertNotNull(entry1);
194 entry1.assignConnection(conn1);
195 final PoolEntry<String, HttpConnection> entry2 = future2.get();
196 Assertions.assertNotNull(entry2);
197 entry2.assignConnection(conn2);
198 final PoolEntry<String, HttpConnection> entry3 = future3.get();
199 Assertions.assertNotNull(entry3);
200 entry3.assignConnection(conn3);
201
202 pool.release(entry1, true);
203 pool.release(entry2, true);
204 pool.release(entry3, true);
205
206 final PoolStats totals = pool.getTotalStats();
207 Assertions.assertEquals(3, totals.getAvailable());
208 Assertions.assertEquals(0, totals.getLeased());
209 Assertions.assertEquals(0, totals.getPending());
210
211 final Future<PoolEntry<String, HttpConnection>> future4 = pool.lease("somehost", null);
212 final Future<PoolEntry<String, HttpConnection>> future5 = pool.lease("somehost", null);
213 final Future<PoolEntry<String, HttpConnection>> future6 = pool.lease("otherhost", null);
214 final Future<PoolEntry<String, HttpConnection>> future7 = pool.lease("somehost", null);
215 final Future<PoolEntry<String, HttpConnection>> future8 = pool.lease("somehost", null);
216 final Future<PoolEntry<String, HttpConnection>> future9 = pool.lease("otherhost", null);
217
218 Assertions.assertTrue(future4.isDone());
219 final PoolEntry<String, HttpConnection> entry4 = future4.get();
220 Assertions.assertNotNull(entry4);
221 Assertions.assertSame(conn2, entry4.getConnection());
222
223 Assertions.assertTrue(future5.isDone());
224 final PoolEntry<String, HttpConnection> entry5 = future5.get();
225 Assertions.assertNotNull(entry5);
226 Assertions.assertSame(conn1, entry5.getConnection());
227
228 Assertions.assertTrue(future6.isDone());
229 final PoolEntry<String, HttpConnection> entry6 = future6.get();
230 Assertions.assertNotNull(entry6);
231 Assertions.assertSame(conn3, entry6.getConnection());
232
233 Assertions.assertFalse(future7.isDone());
234 Assertions.assertFalse(future8.isDone());
235 Assertions.assertFalse(future9.isDone());
236
237 pool.release(entry4, true);
238 pool.release(entry5, false);
239 pool.release(entry6, true);
240
241 Assertions.assertTrue(future7.isDone());
242 final PoolEntry<String, HttpConnection> entry7 = future7.get();
243 Assertions.assertNotNull(entry7);
244 Assertions.assertSame(conn2, entry7.getConnection());
245
246 Assertions.assertTrue(future8.isDone());
247 final PoolEntry<String, HttpConnection> entry8 = future8.get();
248 Assertions.assertNotNull(entry8);
249 Assertions.assertNull(entry8.getConnection());
250
251 Assertions.assertTrue(future9.isDone());
252 final PoolEntry<String, HttpConnection> entry9 = future9.get();
253 Assertions.assertNotNull(entry9);
254 Assertions.assertSame(conn3, entry9.getConnection());
255 }
256 }
257
258 @Test
259 public void testConnectionRedistributionOnTotalMaxLimit() throws Exception {
260 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
261 final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
262 final HttpConnection conn3 = Mockito.mock(HttpConnection.class);
263 final HttpConnection conn4 = Mockito.mock(HttpConnection.class);
264 final HttpConnection conn5 = Mockito.mock(HttpConnection.class);
265
266 try (final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 10)) {
267 pool.setMaxPerRoute("somehost", 2);
268 pool.setMaxPerRoute("otherhost", 2);
269 pool.setMaxTotal(2);
270
271 final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null);
272 final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null);
273 final Future<PoolEntry<String, HttpConnection>> future3 = pool.lease("otherhost", null);
274 final Future<PoolEntry<String, HttpConnection>> future4 = pool.lease("otherhost", null);
275
276 Assertions.assertTrue(future1.isDone());
277 final PoolEntry<String, HttpConnection> entry1 = future1.get();
278 Assertions.assertNotNull(entry1);
279 Assertions.assertFalse(entry1.hasConnection());
280 entry1.assignConnection(conn1);
281 Assertions.assertTrue(future2.isDone());
282 final PoolEntry<String, HttpConnection> entry2 = future2.get();
283 Assertions.assertNotNull(entry2);
284 Assertions.assertFalse(entry2.hasConnection());
285 entry2.assignConnection(conn2);
286
287 Assertions.assertFalse(future3.isDone());
288 Assertions.assertFalse(future4.isDone());
289
290 PoolStats totals = pool.getTotalStats();
291 Assertions.assertEquals(0, totals.getAvailable());
292 Assertions.assertEquals(2, totals.getLeased());
293 Assertions.assertEquals(2, totals.getPending());
294
295 pool.release(entry1, true);
296 pool.release(entry2, true);
297
298 Assertions.assertTrue(future3.isDone());
299 final PoolEntry<String, HttpConnection> entry3 = future3.get();
300 Assertions.assertNotNull(entry3);
301 Assertions.assertFalse(entry3.hasConnection());
302 entry3.assignConnection(conn3);
303 Assertions.assertTrue(future4.isDone());
304 final PoolEntry<String, HttpConnection> entry4 = future4.get();
305 Assertions.assertNotNull(entry4);
306 Assertions.assertFalse(entry4.hasConnection());
307 entry4.assignConnection(conn4);
308
309 totals = pool.getTotalStats();
310 Assertions.assertEquals(0, totals.getAvailable());
311 Assertions.assertEquals(2, totals.getLeased());
312 Assertions.assertEquals(0, totals.getPending());
313
314 final Future<PoolEntry<String, HttpConnection>> future5 = pool.lease("somehost", null);
315 final Future<PoolEntry<String, HttpConnection>> future6 = pool.lease("otherhost", null);
316
317 pool.release(entry3, true);
318 pool.release(entry4, true);
319
320 Assertions.assertTrue(future5.isDone());
321 final PoolEntry<String, HttpConnection> entry5 = future5.get();
322 Assertions.assertNotNull(entry5);
323 Assertions.assertFalse(entry5.hasConnection());
324 entry5.assignConnection(conn5);
325 Assertions.assertTrue(future6.isDone());
326 final PoolEntry<String, HttpConnection> entry6 = future6.get();
327 Assertions.assertNotNull(entry6);
328 Assertions.assertTrue(entry6.hasConnection());
329 Assertions.assertSame(conn4, entry6.getConnection());
330
331 totals = pool.getTotalStats();
332 Assertions.assertEquals(0, totals.getAvailable());
333 Assertions.assertEquals(2, totals.getLeased());
334 Assertions.assertEquals(0, totals.getPending());
335
336 pool.release(entry5, true);
337 pool.release(entry6, true);
338
339 totals = pool.getTotalStats();
340 Assertions.assertEquals(2, totals.getAvailable());
341 Assertions.assertEquals(0, totals.getLeased());
342 Assertions.assertEquals(0, totals.getPending());}
343 }
344
345 @Test
346 public void testStatefulConnectionRedistributionOnPerRouteMaxLimit() throws Exception {
347 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
348 final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
349
350 try (final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 10)) {
351 pool.setMaxPerRoute("somehost", 2);
352 pool.setMaxTotal(2);
353
354 final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null);
355 final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null);
356
357 Assertions.assertTrue(future1.isDone());
358 final PoolEntry<String, HttpConnection> entry1 = future1.get();
359 entry1.assignConnection(conn1);
360 Assertions.assertNotNull(entry1);
361 Assertions.assertTrue(future2.isDone());
362 final PoolEntry<String, HttpConnection> entry2 = future2.get();
363 Assertions.assertNotNull(entry2);
364 entry2.assignConnection(conn2);
365
366 PoolStats totals = pool.getTotalStats();
367 Assertions.assertEquals(0, totals.getAvailable());
368 Assertions.assertEquals(2, totals.getLeased());
369 Assertions.assertEquals(0, totals.getPending());
370
371 entry1.updateState("some-stuff");
372 pool.release(entry1, true);
373 entry2.updateState("some-stuff");
374 pool.release(entry2, true);
375
376 final Future<PoolEntry<String, HttpConnection>> future3 = pool.lease("somehost", "some-stuff");
377 final Future<PoolEntry<String, HttpConnection>> future4 = pool.lease("somehost", "some-stuff");
378
379 Assertions.assertTrue(future1.isDone());
380 final PoolEntry<String, HttpConnection> entry3 = future3.get();
381 Assertions.assertNotNull(entry3);
382 Assertions.assertSame(conn2, entry3.getConnection());
383 Assertions.assertTrue(future4.isDone());
384 final PoolEntry<String, HttpConnection> entry4 = future4.get();
385 Assertions.assertNotNull(entry4);
386 Assertions.assertSame(conn1, entry4.getConnection());
387
388 pool.release(entry3, true);
389 pool.release(entry4, true);
390
391 totals = pool.getTotalStats();
392 Assertions.assertEquals(2, totals.getAvailable());
393 Assertions.assertEquals(0, totals.getLeased());
394 Assertions.assertEquals(0, totals.getPending());
395
396 final Future<PoolEntry<String, HttpConnection>> future5 = pool.lease("somehost", "some-other-stuff");
397
398 Assertions.assertTrue(future5.isDone());
399
400 Mockito.verify(conn2).close(CloseMode.GRACEFUL);
401 Mockito.verify(conn1, Mockito.never()).close(ArgumentMatchers.any());
402
403 totals = pool.getTotalStats();
404 Assertions.assertEquals(1, totals.getAvailable());
405 Assertions.assertEquals(1, totals.getLeased());
406 }
407 }
408
409 @Test
410 public void testCreateNewIfExpired() throws Exception {
411 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
412
413 try (final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 2)) {
414
415 final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null);
416
417 Assertions.assertTrue(future1.isDone());
418 final PoolEntry<String, HttpConnection> entry1 = future1.get();
419 Assertions.assertNotNull(entry1);
420 entry1.assignConnection(conn1);
421
422 entry1.updateExpiry(TimeValue.of(1, TimeUnit.MILLISECONDS));
423 pool.release(entry1, true);
424
425 Thread.sleep(200L);
426
427 final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null);
428
429 Assertions.assertTrue(future2.isDone());
430
431 Mockito.verify(conn1).close(CloseMode.GRACEFUL);
432
433 final PoolStats totals = pool.getTotalStats();
434 Assertions.assertEquals(0, totals.getAvailable());
435 Assertions.assertEquals(1, totals.getLeased());
436 Assertions.assertEquals(Collections.singleton("somehost"), pool.getRoutes());
437 final PoolStats stats = pool.getStats("somehost");
438 Assertions.assertEquals(0, stats.getAvailable());
439 Assertions.assertEquals(1, stats.getLeased());
440 }
441 }
442
443 @Test
444 public void testCloseExpired() throws Exception {
445 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
446 final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
447
448 try (final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 2)) {
449
450 final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null);
451 final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null);
452
453 Assertions.assertTrue(future1.isDone());
454 final PoolEntry<String, HttpConnection> entry1 = future1.get();
455 Assertions.assertNotNull(entry1);
456 entry1.assignConnection(conn1);
457 Assertions.assertTrue(future2.isDone());
458 final PoolEntry<String, HttpConnection> entry2 = future2.get();
459 Assertions.assertNotNull(entry2);
460 entry2.assignConnection(conn2);
461
462 entry1.updateExpiry(TimeValue.of(1, TimeUnit.MILLISECONDS));
463 pool.release(entry1, true);
464
465 Thread.sleep(200);
466
467 entry2.updateExpiry(TimeValue.of(1000, TimeUnit.SECONDS));
468 pool.release(entry2, true);
469
470 pool.closeExpired();
471
472 Mockito.verify(conn1).close(CloseMode.GRACEFUL);
473 Mockito.verify(conn2, Mockito.never()).close(ArgumentMatchers.any());
474
475 final PoolStats totals = pool.getTotalStats();
476 Assertions.assertEquals(1, totals.getAvailable());
477 Assertions.assertEquals(0, totals.getLeased());
478 Assertions.assertEquals(0, totals.getPending());
479 final PoolStats stats = pool.getStats("somehost");
480 Assertions.assertEquals(1, stats.getAvailable());
481 Assertions.assertEquals(0, stats.getLeased());
482 Assertions.assertEquals(0, stats.getPending());
483 }
484 }
485
486 @Test
487 public void testCloseIdle() throws Exception {
488 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
489 final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
490
491 try (final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 2)) {
492
493 final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null);
494 final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null);
495
496 Assertions.assertTrue(future1.isDone());
497 final PoolEntry<String, HttpConnection> entry1 = future1.get();
498 Assertions.assertNotNull(entry1);
499 entry1.assignConnection(conn1);
500 Assertions.assertTrue(future2.isDone());
501 final PoolEntry<String, HttpConnection> entry2 = future2.get();
502 Assertions.assertNotNull(entry2);
503 entry2.assignConnection(conn2);
504
505 entry1.updateState(null);
506 pool.release(entry1, true);
507
508 Thread.sleep(200L);
509
510 entry2.updateState(null);
511 pool.release(entry2, true);
512
513 pool.closeIdle(TimeValue.of(50, TimeUnit.MILLISECONDS));
514
515 Mockito.verify(conn1).close(CloseMode.GRACEFUL);
516 Mockito.verify(conn2, Mockito.never()).close(ArgumentMatchers.any());
517
518 PoolStats totals = pool.getTotalStats();
519 Assertions.assertEquals(1, totals.getAvailable());
520 Assertions.assertEquals(0, totals.getLeased());
521 Assertions.assertEquals(0, totals.getPending());
522 PoolStats stats = pool.getStats("somehost");
523 Assertions.assertEquals(1, stats.getAvailable());
524 Assertions.assertEquals(0, stats.getLeased());
525 Assertions.assertEquals(0, stats.getPending());
526
527 pool.closeIdle(TimeValue.of(-1, TimeUnit.MILLISECONDS));
528
529 Mockito.verify(conn2).close(CloseMode.GRACEFUL);
530
531 totals = pool.getTotalStats();
532 Assertions.assertEquals(0, totals.getAvailable());
533 Assertions.assertEquals(0, totals.getLeased());
534 Assertions.assertEquals(0, totals.getPending());
535 stats = pool.getStats("somehost");
536 Assertions.assertEquals(0, stats.getAvailable());
537 Assertions.assertEquals(0, stats.getLeased());
538 Assertions.assertEquals(0, stats.getPending());
539 }
540 }
541
542 @Test
543 public void testLeaseRequestTimeout() throws Exception {
544 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
545
546 try (final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(1, 1)) {
547
548 final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null, Timeout.ofMilliseconds(0), null);
549 final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null, Timeout.ofMilliseconds(0), null);
550 final Future<PoolEntry<String, HttpConnection>> future3 = pool.lease("somehost", null, Timeout.ofMilliseconds(10), null);
551
552 Assertions.assertTrue(future1.isDone());
553 final PoolEntry<String, HttpConnection> entry1 = future1.get();
554 Assertions.assertNotNull(entry1);
555 entry1.assignConnection(conn1);
556 Assertions.assertFalse(future2.isDone());
557 Assertions.assertFalse(future3.isDone());
558
559 Thread.sleep(100);
560
561 pool.validatePendingRequests();
562
563 Assertions.assertFalse(future2.isDone());
564 Assertions.assertTrue(future3.isDone());
565 }
566 }
567
568 private static class HoldInternalLockThread extends Thread {
569 private HoldInternalLockThread(final StrictConnPool<String, HttpConnection> pool, final CountDownLatch lockHeld) {
570 super(() -> {
571 pool.lease("somehost", null);
572 pool.enumLeased(object -> {
573 try {
574 lockHeld.countDown();
575 Thread.sleep(Long.MAX_VALUE);
576 } catch (final InterruptedException ignored) {
577 }
578 });
579 });
580 }
581 }
582
583 @Test
584 public void testLeaseRequestLockTimeout() throws Exception {
585 final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(1, 1);
586 final CountDownLatch lockHeld = new CountDownLatch(1);
587 final Thread holdInternalLock = new HoldInternalLockThread(pool, lockHeld);
588
589 holdInternalLock.start();
590 lockHeld.await();
591
592
593 final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null, Timeout.ofMilliseconds(10), null);
594
595 final ExecutionException executionException = Assertions.assertThrows(ExecutionException.class, () ->
596 future2.get());
597 Assertions.assertTrue(executionException.getCause() instanceof DeadlineTimeoutException);
598 holdInternalLock.interrupt();
599 }
600
601 @Test
602 public void testLeaseRequestInterrupted() throws Exception {
603 final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(1, 1);
604 final CountDownLatch lockHeld = new CountDownLatch(1);
605 final Thread holdInternalLock = new HoldInternalLockThread(pool, lockHeld);
606
607 holdInternalLock.start();
608 lockHeld.await();
609
610 Thread.currentThread().interrupt();
611
612 final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null, Timeout.ofMilliseconds(10), null);
613
614 Assertions.assertTrue(Thread.interrupted());
615 Assertions.assertThrows(CancellationException.class, () -> future2.get());
616 holdInternalLock.interrupt();
617 }
618
619 @Test
620 public void testLeaseRequestCanceled() throws Exception {
621 try (final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(1, 1)) {
622
623 final Future<PoolEntry<String, HttpConnection>> future1 = pool.lease("somehost", null,
624 Timeout.ofMilliseconds(0), null);
625
626 Assertions.assertTrue(future1.isDone());
627 final PoolEntry<String, HttpConnection> entry1 = future1.get();
628 Assertions.assertNotNull(entry1);
629 entry1.assignConnection(Mockito.mock(HttpConnection.class));
630
631 final Future<PoolEntry<String, HttpConnection>> future2 = pool.lease("somehost", null,
632 Timeout.ofMilliseconds(0), null);
633 future2.cancel(true);
634
635 pool.release(entry1, true);
636
637 final PoolStats totals = pool.getTotalStats();
638 Assertions.assertEquals(1, totals.getAvailable());
639 Assertions.assertEquals(0, totals.getLeased());
640 }
641 }
642
643 @Test
644 public void testGetStatsInvalid() throws Exception {
645 try (final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 2)) {
646 Assertions.assertThrows(NullPointerException.class, () -> pool.getStats(null));
647 }
648 }
649
650 @Test
651 public void testSetMaxInvalid() throws Exception {
652 try (final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 2)) {
653 Assertions.assertThrows(IllegalArgumentException.class, () ->
654 pool.setMaxTotal(-1));
655 Assertions.assertThrows(NullPointerException.class, () ->
656 pool.setMaxPerRoute(null, 1));
657 Assertions.assertThrows(IllegalArgumentException.class, () ->
658 pool.setDefaultMaxPerRoute(-1));
659 }
660 }
661
662 @Test
663 public void testSetMaxPerRoute() throws Exception {
664 try (final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 2)) {
665 pool.setMaxPerRoute("somehost", 1);
666 Assertions.assertEquals(1, pool.getMaxPerRoute("somehost"));
667 pool.setMaxPerRoute("somehost", 0);
668 Assertions.assertEquals(0, pool.getMaxPerRoute("somehost"));
669 pool.setMaxPerRoute("somehost", -1);
670 Assertions.assertEquals(2, pool.getMaxPerRoute("somehost"));
671 }
672 }
673
674 @Test
675 public void testShutdown() throws Exception {
676 final StrictConnPool<String, HttpConnection> pool = new StrictConnPool<>(2, 2);
677 pool.close(CloseMode.GRACEFUL);
678 Assertions.assertThrows(IllegalStateException.class, () -> pool.lease("somehost", null));
679
680 pool.release(new PoolEntry<>("somehost"), true);
681 }
682
683 }