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.http.pool;
28
29 import java.io.IOException;
30 import java.util.Collections;
31 import java.util.concurrent.ExecutionException;
32 import java.util.concurrent.Future;
33 import java.util.concurrent.TimeUnit;
34 import java.util.concurrent.TimeoutException;
35
36 import org.apache.http.HttpConnection;
37 import org.junit.Assert;
38 import org.junit.Test;
39 import org.mockito.Matchers;
40 import org.mockito.Mockito;
41
42 public class TestConnPool {
43
44 private static final int GRACE_PERIOD = 10000;
45
46 interface LocalConnFactory extends ConnFactory<String, HttpConnection> {
47 }
48
49 static class LocalPoolEntry extends PoolEntry<String, HttpConnection> {
50
51 private boolean closed;
52
53 public LocalPoolEntry(final String route, final HttpConnection conn) {
54 super(null, route, conn);
55 }
56
57 @Override
58 public void close() {
59 if (this.closed) {
60 return;
61 }
62 this.closed = true;
63 try {
64 getConnection().close();
65 } catch (final IOException ignore) {
66 }
67 }
68
69 @Override
70 public boolean isClosed() {
71 return this.closed;
72 }
73
74 }
75
76 static class LocalConnPool extends AbstractConnPool<String, HttpConnection, LocalPoolEntry> {
77
78 public LocalConnPool(
79 final ConnFactory<String, HttpConnection> connFactory,
80 final int defaultMaxPerRoute, final int maxTotal) {
81 super(connFactory, defaultMaxPerRoute, maxTotal);
82 }
83
84 @Override
85 protected LocalPoolEntry createEntry(final String route, final HttpConnection conn) {
86 return new LocalPoolEntry(route, conn);
87 }
88
89 @Override
90 protected boolean validate(final LocalPoolEntry entry) {
91 return !entry.getConnection().isStale();
92 }
93 }
94
95 @Test
96 public void testEmptyPool() throws Exception {
97 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
98 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
99 pool.setDefaultMaxPerRoute(5);
100 pool.setMaxPerRoute("somehost", 3);
101 final PoolStats totals = pool.getTotalStats();
102 Assert.assertEquals(0, totals.getAvailable());
103 Assert.assertEquals(0, totals.getLeased());
104 Assert.assertEquals(10, totals.getMax());
105 Assert.assertEquals(Collections.emptySet(), pool.getRoutes());
106 final PoolStats stats = pool.getStats("somehost");
107 Assert.assertEquals(0, stats.getAvailable());
108 Assert.assertEquals(0, stats.getLeased());
109 Assert.assertEquals(3, stats.getMax());
110 Assert.assertEquals("[leased: []][available: []][pending: []]", pool.toString());
111 }
112
113 @Test
114 public void testInvalidConstruction() throws Exception {
115 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
116 try {
117 new LocalConnPool(connFactory, -1, 1);
118 Assert.fail("IllegalArgumentException should have been thrown");
119 } catch (final IllegalArgumentException expected) {
120 }
121 try {
122 new LocalConnPool(connFactory, 1, -1);
123 Assert.fail("IllegalArgumentException should have been thrown");
124 } catch (final IllegalArgumentException expected) {
125 }
126 }
127
128 @Test
129 public void testLeaseRelease() throws Exception {
130 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
131 Mockito.when(conn1.isOpen()).thenReturn(true);
132 final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
133 Mockito.when(conn2.isOpen()).thenReturn(true);
134
135 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
136 Mockito.when(connFactory.create(Matchers.eq("somehost"))).thenReturn(conn1);
137 Mockito.when(connFactory.create(Matchers.eq("otherhost"))).thenReturn(conn2);
138
139 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
140 final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
141 final LocalPoolEntry entry1 = future1.get(1, TimeUnit.SECONDS);
142 Assert.assertNotNull(entry1);
143 final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
144 final LocalPoolEntry entry2 = future2.get(1, TimeUnit.SECONDS);
145 Assert.assertNotNull(entry2);
146 final Future<LocalPoolEntry> future3 = pool.lease("otherhost", null);
147 final LocalPoolEntry entry3 = future3.get(1, TimeUnit.SECONDS);
148 Assert.assertNotNull(entry3);
149
150 PoolStats totals = pool.getTotalStats();
151 Assert.assertEquals(0, totals.getAvailable());
152 Assert.assertEquals(3, totals.getLeased());
153
154 final LocalPoolEntry entry = future1.get();
155 Assert.assertSame(entry1, entry);
156
157 pool.release(entry1, true);
158 pool.release(entry2, true);
159 pool.release(entry3, false);
160 Mockito.verify(conn1, Mockito.never()).close();
161 Mockito.verify(conn2, Mockito.times(1)).close();
162
163 totals = pool.getTotalStats();
164 Assert.assertEquals(2, totals.getAvailable());
165 Assert.assertEquals(0, totals.getLeased());
166 }
167
168 @Test
169 public void testLeaseIllegal() throws Exception {
170 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
171 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
172 try {
173 pool.lease(null, null);
174 Assert.fail("IllegalArgumentException should have been thrown");
175 } catch (final IllegalArgumentException expected) {
176 }
177 }
178
179 @Test
180 public void testReleaseUnknownEntry() throws Exception {
181 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
182 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
183 pool.release(new LocalPoolEntry("somehost", Mockito.mock(HttpConnection.class)), true);
184 }
185
186 static class GetPoolEntryThread extends Thread {
187
188 private final Future<LocalPoolEntry> future;
189 private final long time;
190 private final TimeUnit timeUnit;
191
192 private volatile LocalPoolEntry entry;
193 private volatile Exception ex;
194
195 GetPoolEntryThread(final Future<LocalPoolEntry> future, final long time, final TimeUnit timeUnit) {
196 super();
197 this.future = future;
198 this.time = time;
199 this.timeUnit = timeUnit;
200 setDaemon(true);
201 }
202
203 GetPoolEntryThread(final Future<LocalPoolEntry> future) {
204 this(future, 1000, TimeUnit.SECONDS);
205 }
206
207 @Override
208 public void run() {
209 try {
210 this.entry = this.future.get(this.time, this.timeUnit);
211 } catch (final Exception ex) {
212 this.ex = ex;
213 }
214 }
215
216 public boolean isDone() {
217 return this.future.isDone();
218 }
219
220 public LocalPoolEntry getEntry() {
221 return this.entry;
222 }
223
224 public Exception getException() {
225 return this.ex;
226 }
227
228 }
229
230 @Test
231 public void testMaxLimits() throws Exception {
232 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
233
234 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
235 Mockito.when(conn1.isOpen()).thenReturn(true);
236 Mockito.when(connFactory.create(Matchers.eq("somehost"))).thenReturn(conn1);
237
238 final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
239 Mockito.when(conn2.isOpen()).thenReturn(true);
240 Mockito.when(connFactory.create(Matchers.eq("otherhost"))).thenReturn(conn2);
241
242 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
243 pool.setMaxPerRoute("somehost", 2);
244 pool.setMaxPerRoute("otherhost", 1);
245 pool.setMaxTotal(3);
246
247 final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
248 final GetPoolEntryThread t1 = new GetPoolEntryThread(future1);
249 t1.start();
250 final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
251 final GetPoolEntryThread t2 = new GetPoolEntryThread(future2);
252 t2.start();
253 final Future<LocalPoolEntry> future3 = pool.lease("otherhost", null);
254 final GetPoolEntryThread t3 = new GetPoolEntryThread(future3);
255 t3.start();
256
257 t1.join(GRACE_PERIOD);
258 Assert.assertTrue(future1.isDone());
259 final LocalPoolEntry entry1 = t1.getEntry();
260 Assert.assertNotNull(entry1);
261 t2.join(GRACE_PERIOD);
262 Assert.assertTrue(future2.isDone());
263 final LocalPoolEntry entry2 = t2.getEntry();
264 Assert.assertNotNull(entry2);
265 t3.join(GRACE_PERIOD);
266 Assert.assertTrue(future3.isDone());
267 final LocalPoolEntry entry3 = t3.getEntry();
268 Assert.assertNotNull(entry3);
269
270 pool.release(entry1, true);
271 pool.release(entry2, true);
272 pool.release(entry3, true);
273
274 final PoolStats totals = pool.getTotalStats();
275 Assert.assertEquals(3, totals.getAvailable());
276 Assert.assertEquals(0, totals.getLeased());
277
278 final Future<LocalPoolEntry> future4 = pool.lease("somehost", null);
279 final GetPoolEntryThread t4 = new GetPoolEntryThread(future4);
280 t4.start();
281 final Future<LocalPoolEntry> future5 = pool.lease("somehost", null);
282 final GetPoolEntryThread t5 = new GetPoolEntryThread(future5);
283 t5.start();
284 final Future<LocalPoolEntry> future6 = pool.lease("otherhost", null);
285 final GetPoolEntryThread t6 = new GetPoolEntryThread(future6);
286 t6.start();
287
288 t4.join(GRACE_PERIOD);
289 Assert.assertTrue(future4.isDone());
290 final LocalPoolEntry entry4 = t4.getEntry();
291 Assert.assertNotNull(entry4);
292 t5.join(GRACE_PERIOD);
293 Assert.assertTrue(future5.isDone());
294 final LocalPoolEntry entry5 = t5.getEntry();
295 Assert.assertNotNull(entry5);
296 t6.join(GRACE_PERIOD);
297 Assert.assertTrue(future6.isDone());
298 final LocalPoolEntry entry6 = t6.getEntry();
299 Assert.assertNotNull(entry6);
300
301 final Future<LocalPoolEntry> future7 = pool.lease("somehost", null);
302 final GetPoolEntryThread t7 = new GetPoolEntryThread(future7);
303 t7.start();
304 final Future<LocalPoolEntry> future8 = pool.lease("somehost", null);
305 final GetPoolEntryThread t8 = new GetPoolEntryThread(future8);
306 t8.start();
307 final Future<LocalPoolEntry> future9 = pool.lease("otherhost", null);
308 final GetPoolEntryThread t9 = new GetPoolEntryThread(future9);
309 t9.start();
310
311 Assert.assertFalse(t7.isDone());
312 Assert.assertFalse(t8.isDone());
313 Assert.assertFalse(t9.isDone());
314
315 Mockito.verify(connFactory, Mockito.times(3)).create(Matchers.any(String.class));
316
317 pool.release(entry4, true);
318 pool.release(entry5, false);
319 pool.release(entry6, true);
320
321 t7.join();
322 Assert.assertTrue(future7.isDone());
323 t8.join();
324 Assert.assertTrue(future8.isDone());
325 t9.join();
326 Assert.assertTrue(future9.isDone());
327
328 Mockito.verify(connFactory, Mockito.times(4)).create(Matchers.any(String.class));
329 }
330
331 @Test
332 public void testConnectionRedistributionOnTotalMaxLimit() throws Exception {
333 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
334
335 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
336 Mockito.when(conn1.isOpen()).thenReturn(true);
337 final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
338 Mockito.when(conn2.isOpen()).thenReturn(true);
339 final HttpConnection conn3 = Mockito.mock(HttpConnection.class);
340 Mockito.when(conn3.isOpen()).thenReturn(true);
341 Mockito.when(connFactory.create(Matchers.eq("somehost"))).thenReturn(conn1, conn2, conn3);
342
343 final HttpConnection conn4 = Mockito.mock(HttpConnection.class);
344 Mockito.when(conn4.isOpen()).thenReturn(true);
345 final HttpConnection conn5 = Mockito.mock(HttpConnection.class);
346 Mockito.when(conn5.isOpen()).thenReturn(true);
347 Mockito.when(connFactory.create(Matchers.eq("otherhost"))).thenReturn(conn4, conn5);
348
349 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
350 pool.setMaxPerRoute("somehost", 2);
351 pool.setMaxPerRoute("otherhost", 2);
352 pool.setMaxTotal(2);
353
354 final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
355 final GetPoolEntryThread t1 = new GetPoolEntryThread(future1);
356 t1.start();
357 final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
358 final GetPoolEntryThread t2 = new GetPoolEntryThread(future2);
359 t2.start();
360
361 t1.join(GRACE_PERIOD);
362 Assert.assertTrue(future1.isDone());
363 final LocalPoolEntry entry1 = t1.getEntry();
364 Assert.assertNotNull(entry1);
365 t2.join(GRACE_PERIOD);
366 Assert.assertTrue(future2.isDone());
367 final LocalPoolEntry entry2 = t2.getEntry();
368 Assert.assertNotNull(entry2);
369
370 final Future<LocalPoolEntry> future3 = pool.lease("otherhost", null);
371 final GetPoolEntryThread t3 = new GetPoolEntryThread(future3);
372 t3.start();
373 final Future<LocalPoolEntry> future4 = pool.lease("otherhost", null);
374 final GetPoolEntryThread t4 = new GetPoolEntryThread(future4);
375 t4.start();
376
377 Assert.assertFalse(t3.isDone());
378 Assert.assertFalse(t4.isDone());
379
380 Mockito.verify(connFactory, Mockito.times(2)).create(Matchers.eq("somehost"));
381 Mockito.verify(connFactory, Mockito.never()).create(Matchers.eq("otherhost"));
382
383 PoolStats totals = pool.getTotalStats();
384 Assert.assertEquals(0, totals.getAvailable());
385 Assert.assertEquals(2, totals.getLeased());
386
387 pool.release(entry1, true);
388 pool.release(entry2, true);
389
390 t3.join(GRACE_PERIOD);
391 Assert.assertTrue(future3.isDone());
392 final LocalPoolEntry entry3 = t3.getEntry();
393 Assert.assertNotNull(entry3);
394 t4.join(GRACE_PERIOD);
395 Assert.assertTrue(future4.isDone());
396 final LocalPoolEntry entry4 = t4.getEntry();
397 Assert.assertNotNull(entry4);
398
399 Mockito.verify(connFactory, Mockito.times(2)).create(Matchers.eq("somehost"));
400 Mockito.verify(connFactory, Mockito.times(2)).create(Matchers.eq("otherhost"));
401
402 totals = pool.getTotalStats();
403 Assert.assertEquals(0, totals.getAvailable());
404 Assert.assertEquals(2, totals.getLeased());
405
406 final Future<LocalPoolEntry> future5 = pool.lease("somehost", null);
407 final GetPoolEntryThread t5 = new GetPoolEntryThread(future5);
408 t5.start();
409 final Future<LocalPoolEntry> future6 = pool.lease("otherhost", null);
410 final GetPoolEntryThread t6 = new GetPoolEntryThread(future6);
411 t6.start();
412
413 pool.release(entry3, true);
414 pool.release(entry4, true);
415
416 t5.join(GRACE_PERIOD);
417 Assert.assertTrue(future5.isDone());
418 final LocalPoolEntry entry5 = t5.getEntry();
419 Assert.assertNotNull(entry5);
420 t6.join(GRACE_PERIOD);
421 Assert.assertTrue(future6.isDone());
422 final LocalPoolEntry entry6 = t6.getEntry();
423 Assert.assertNotNull(entry6);
424
425 Mockito.verify(connFactory, Mockito.times(3)).create(Matchers.eq("somehost"));
426 Mockito.verify(connFactory, Mockito.times(2)).create(Matchers.eq("otherhost"));
427
428 totals = pool.getTotalStats();
429 Assert.assertEquals(0, totals.getAvailable());
430 Assert.assertEquals(2, totals.getLeased());
431
432 pool.release(entry5, true);
433 pool.release(entry6, true);
434
435 totals = pool.getTotalStats();
436 Assert.assertEquals(2, totals.getAvailable());
437 Assert.assertEquals(0, totals.getLeased());
438 }
439
440 @Test
441 public void testStatefulConnectionRedistributionOnPerRouteMaxLimit() throws Exception {
442 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
443
444 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
445 Mockito.when(conn1.isOpen()).thenReturn(true);
446 final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
447 Mockito.when(conn2.isOpen()).thenReturn(true);
448 final HttpConnection conn3 = Mockito.mock(HttpConnection.class);
449 Mockito.when(conn3.isOpen()).thenReturn(true);
450 Mockito.when(connFactory.create(Matchers.eq("somehost"))).thenReturn(conn1, conn2, conn3);
451
452 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
453 pool.setMaxPerRoute("somehost", 2);
454 pool.setMaxTotal(2);
455
456 final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
457 final GetPoolEntryThread t1 = new GetPoolEntryThread(future1);
458 t1.start();
459
460 t1.join(GRACE_PERIOD);
461 Assert.assertTrue(future1.isDone());
462 final LocalPoolEntry entry1 = t1.getEntry();
463 Assert.assertNotNull(entry1);
464
465 final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
466 final GetPoolEntryThread t2 = new GetPoolEntryThread(future2);
467 t2.start();
468
469 t2.join(GRACE_PERIOD);
470 Assert.assertTrue(future2.isDone());
471 final LocalPoolEntry entry2 = t2.getEntry();
472 Assert.assertNotNull(entry2);
473
474 PoolStats totals = pool.getTotalStats();
475 Assert.assertEquals(0, totals.getAvailable());
476 Assert.assertEquals(2, totals.getLeased());
477 Assert.assertEquals(0, totals.getPending());
478
479 entry1.setState("some-stuff");
480 pool.release(entry1, true);
481 entry2.setState("some-stuff");
482 pool.release(entry2, true);
483
484 Mockito.verify(connFactory, Mockito.times(2)).create(Matchers.eq("somehost"));
485
486 final Future<LocalPoolEntry> future3 = pool.lease("somehost", "some-other-stuff");
487 final GetPoolEntryThread t3 = new GetPoolEntryThread(future3);
488 t3.start();
489
490 t3.join(GRACE_PERIOD);
491 Assert.assertTrue(future3.isDone());
492 final LocalPoolEntry entry3 = t3.getEntry();
493 Assert.assertNotNull(entry3);
494
495 Mockito.verify(connFactory, Mockito.times(3)).create(Matchers.eq("somehost"));
496
497 Mockito.verify(conn1).close();
498 Mockito.verify(conn2, Mockito.never()).close();
499
500 totals = pool.getTotalStats();
501 Assert.assertEquals(1, totals.getAvailable());
502 Assert.assertEquals(1, totals.getLeased());
503
504 }
505
506 @Test
507 public void testCreateNewIfExpired() throws Exception {
508 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
509
510 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
511 Mockito.when(conn1.isOpen()).thenReturn(true);
512 Mockito.when(connFactory.create(Matchers.eq("somehost"))).thenReturn(conn1);
513
514 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 2);
515
516 final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
517 final LocalPoolEntry entry1 = future1.get(1, TimeUnit.SECONDS);
518 Assert.assertNotNull(entry1);
519
520 Mockito.verify(connFactory, Mockito.times(1)).create(Matchers.eq("somehost"));
521
522 entry1.updateExpiry(1, TimeUnit.MILLISECONDS);
523 pool.release(entry1, true);
524
525 Thread.sleep(200L);
526
527 final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
528 final LocalPoolEntry entry2 = future2.get(1, TimeUnit.SECONDS);
529 Assert.assertNotNull(entry2);
530
531 Mockito.verify(connFactory, Mockito.times(2)).create(Matchers.eq("somehost"));
532
533 final PoolStats totals = pool.getTotalStats();
534 Assert.assertEquals(0, totals.getAvailable());
535 Assert.assertEquals(1, totals.getLeased());
536 Assert.assertEquals(Collections.singleton("somehost"), pool.getRoutes());
537 final PoolStats stats = pool.getStats("somehost");
538 Assert.assertEquals(0, stats.getAvailable());
539 Assert.assertEquals(1, stats.getLeased());
540 }
541
542 @Test
543 public void testCloseExpired() throws Exception {
544 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
545
546 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
547 Mockito.when(conn1.isOpen()).thenReturn(Boolean.FALSE);
548 final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
549 Mockito.when(conn2.isOpen()).thenReturn(Boolean.TRUE);
550
551 Mockito.when(connFactory.create(Matchers.eq("somehost"))).thenReturn(conn1, conn2);
552
553 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 2);
554
555 final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
556 final LocalPoolEntry entry1 = future1.get(1, TimeUnit.SECONDS);
557 Assert.assertNotNull(entry1);
558 final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
559 final LocalPoolEntry entry2 = future2.get(1, TimeUnit.SECONDS);
560 Assert.assertNotNull(entry2);
561
562 entry1.updateExpiry(1, TimeUnit.MILLISECONDS);
563 pool.release(entry1, true);
564
565 Thread.sleep(200);
566
567 entry2.updateExpiry(1000, TimeUnit.SECONDS);
568 pool.release(entry2, true);
569
570 pool.closeExpired();
571
572 Mockito.verify(conn1).close();
573 Mockito.verify(conn2, Mockito.never()).close();
574
575 final PoolStats totals = pool.getTotalStats();
576 Assert.assertEquals(1, totals.getAvailable());
577 Assert.assertEquals(0, totals.getLeased());
578 final PoolStats stats = pool.getStats("somehost");
579 Assert.assertEquals(1, stats.getAvailable());
580 Assert.assertEquals(0, stats.getLeased());
581 }
582
583 @Test
584 public void testLeaseTimeout() throws Exception {
585 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
586
587 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
588 Mockito.when(conn1.isOpen()).thenReturn(true);
589 Mockito.when(connFactory.create(Matchers.eq("somehost"))).thenReturn(conn1);
590
591 final LocalConnPool pool = new LocalConnPool(connFactory, 1, 1);
592
593 final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
594 final GetPoolEntryThread t1 = new GetPoolEntryThread(future1);
595 t1.start();
596
597 t1.join(GRACE_PERIOD);
598 Assert.assertTrue(future1.isDone());
599 final LocalPoolEntry entry1 = t1.getEntry();
600 Assert.assertNotNull(entry1);
601
602 final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
603 final GetPoolEntryThread t2 = new GetPoolEntryThread(future2, 50, TimeUnit.MICROSECONDS);
604 t2.start();
605
606 t2.join(GRACE_PERIOD);
607 Assert.assertTrue(t2.getException() instanceof TimeoutException);
608 Assert.assertFalse(future2.isDone());
609 Assert.assertFalse(future2.isCancelled());
610 }
611
612 @Test
613 public void testLeaseIOException() throws Exception {
614 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
615 Mockito.doThrow(new IOException("Oppsie")).when(connFactory).create("somehost");
616
617 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
618
619 final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
620 final GetPoolEntryThread t1 = new GetPoolEntryThread(future1);
621 t1.start();
622
623 t1.join(GRACE_PERIOD);
624 Assert.assertTrue(future1.isDone());
625 Assert.assertTrue(t1.getException() instanceof ExecutionException);
626 Assert.assertTrue(t1.getException().getCause() instanceof IOException);
627 Assert.assertFalse(future1.isCancelled());
628 }
629
630 @Test
631 public void testLeaseCancel() throws Exception {
632 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
633
634 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
635 Mockito.when(conn1.isOpen()).thenReturn(true);
636 Mockito.when(connFactory.create(Matchers.eq("somehost"))).thenReturn(conn1);
637
638 final LocalConnPool pool = new LocalConnPool(connFactory, 1, 1);
639
640 final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
641 final GetPoolEntryThread t1 = new GetPoolEntryThread(future1);
642 t1.start();
643
644 t1.join(GRACE_PERIOD);
645 Assert.assertTrue(future1.isDone());
646 final LocalPoolEntry entry1 = t1.getEntry();
647 Assert.assertNotNull(entry1);
648
649 final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
650 final GetPoolEntryThread t2 = new GetPoolEntryThread(future2);
651 t2.start();
652
653 Thread.sleep(5);
654
655 Assert.assertFalse(future2.isDone());
656 Assert.assertFalse(future2.isCancelled());
657
658 future2.cancel(true);
659 t2.join(GRACE_PERIOD);
660 Assert.assertTrue(future2.isDone());
661 Assert.assertTrue(future2.isCancelled());
662 future2.cancel(true);
663 future2.cancel(true);
664 }
665
666 @Test
667 public void testCloseIdle() throws Exception {
668 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
669
670 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
671 Mockito.when(conn1.isOpen()).thenReturn(true);
672 final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
673 Mockito.when(conn2.isOpen()).thenReturn(true);
674
675 Mockito.when(connFactory.create(Matchers.eq("somehost"))).thenReturn(conn1, conn2);
676
677 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 2);
678
679 final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
680 final LocalPoolEntry entry1 = future1.get(1, TimeUnit.SECONDS);
681 Assert.assertNotNull(entry1);
682 final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
683 final LocalPoolEntry entry2 = future2.get(1, TimeUnit.SECONDS);
684 Assert.assertNotNull(entry2);
685
686 entry1.updateExpiry(0, TimeUnit.MILLISECONDS);
687 pool.release(entry1, true);
688
689 Thread.sleep(200L);
690
691 entry2.updateExpiry(0, TimeUnit.MILLISECONDS);
692 pool.release(entry2, true);
693
694 pool.closeIdle(50, TimeUnit.MILLISECONDS);
695
696 Mockito.verify(conn1).close();
697 Mockito.verify(conn2, Mockito.never()).close();
698
699 PoolStats totals = pool.getTotalStats();
700 Assert.assertEquals(1, totals.getAvailable());
701 Assert.assertEquals(0, totals.getLeased());
702 PoolStats stats = pool.getStats("somehost");
703 Assert.assertEquals(1, stats.getAvailable());
704 Assert.assertEquals(0, stats.getLeased());
705
706 pool.closeIdle(-1, TimeUnit.MILLISECONDS);
707
708 Mockito.verify(conn2).close();
709
710 totals = pool.getTotalStats();
711 Assert.assertEquals(0, totals.getAvailable());
712 Assert.assertEquals(0, totals.getLeased());
713 stats = pool.getStats("somehost");
714 Assert.assertEquals(0, stats.getAvailable());
715 Assert.assertEquals(0, stats.getLeased());
716 }
717
718 @Test(expected=IllegalArgumentException.class)
719 public void testCloseIdleInvalid() throws Exception {
720 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
721 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 2);
722 pool.closeIdle(50, null);
723 }
724
725 @Test(expected=IllegalArgumentException.class)
726 public void testGetStatsInvalid() throws Exception {
727 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
728 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 2);
729 pool.getStats(null);
730 }
731
732 @Test
733 public void testSetMaxInvalid() throws Exception {
734 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
735 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 2);
736 try {
737 pool.setMaxTotal(-1);
738 Assert.fail("IllegalArgumentException should have been thrown");
739 } catch (final IllegalArgumentException expected) {
740 }
741 try {
742 pool.setMaxPerRoute(null, 1);
743 Assert.fail("IllegalArgumentException should have been thrown");
744 } catch (final IllegalArgumentException expected) {
745 }
746 try {
747 pool.setDefaultMaxPerRoute(-1);
748 Assert.fail("IllegalArgumentException should have been thrown");
749 } catch (final IllegalArgumentException expected) {
750 }
751 }
752
753 @Test
754 public void testSetMaxPerRoute() throws Exception {
755 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
756 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 2);
757 pool.setMaxPerRoute("somehost", 1);
758 Assert.assertEquals(1, pool.getMaxPerRoute("somehost"));
759 pool.setMaxPerRoute("somehost", 0);
760 Assert.assertEquals(0, pool.getMaxPerRoute("somehost"));
761 pool.setMaxPerRoute("somehost", -1);
762 Assert.assertEquals(2, pool.getMaxPerRoute("somehost"));
763 }
764
765 @Test
766 public void testShutdown() throws Exception {
767 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
768
769 final HttpConnection conn1 = Mockito.mock(HttpConnection.class);
770 Mockito.when(conn1.isOpen()).thenReturn(true);
771 Mockito.when(connFactory.create(Matchers.eq("somehost"))).thenReturn(conn1);
772 final HttpConnection conn2 = Mockito.mock(HttpConnection.class);
773 Mockito.when(conn2.isOpen()).thenReturn(true);
774 Mockito.when(connFactory.create(Matchers.eq("otherhost"))).thenReturn(conn2);
775
776 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 2);
777 final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
778 final LocalPoolEntry entry1 = future1.get(1, TimeUnit.SECONDS);
779 Assert.assertNotNull(entry1);
780 final Future<LocalPoolEntry> future2 = pool.lease("otherhost", null);
781 final LocalPoolEntry entry2 = future2.get(1, TimeUnit.SECONDS);
782 Assert.assertNotNull(entry2);
783
784 pool.release(entry2, true);
785
786 final PoolStats totals = pool.getTotalStats();
787 Assert.assertEquals(1, totals.getAvailable());
788 Assert.assertEquals(1, totals.getLeased());
789
790 pool.shutdown();
791 Assert.assertTrue(pool.isShutdown());
792 pool.shutdown();
793 pool.shutdown();
794
795 Mockito.verify(conn1, Mockito.atLeastOnce()).close();
796 Mockito.verify(conn2, Mockito.atLeastOnce()).close();
797
798 try {
799 pool.lease("somehost", null);
800 Assert.fail("IllegalStateException should have been thrown");
801 } catch (final IllegalStateException expected) {
802 }
803
804 pool.release(new LocalPoolEntry("somehost", Mockito.mock(HttpConnection.class)), true);
805 }
806
807 @Test
808 public void testValidateConnectionNotStale() throws Exception {
809 final HttpConnection conn = Mockito.mock(HttpConnection.class);
810 Mockito.when(conn.isOpen()).thenReturn(true);
811 Mockito.when(conn.isStale()).thenReturn(false);
812
813 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
814 Mockito.when(connFactory.create(Matchers.eq("somehost"))).thenReturn(conn);
815
816 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
817 pool.setValidateAfterInactivity(100);
818
819 final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
820 final LocalPoolEntry entry1 = future1.get(1, TimeUnit.SECONDS);
821 Assert.assertNotNull(entry1);
822
823 pool.release(entry1, true);
824
825 Thread.sleep(150);
826
827 final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
828 final LocalPoolEntry entry2 = future2.get(1, TimeUnit.SECONDS);
829 Assert.assertNotNull(entry2);
830 Assert.assertSame(entry1, entry2);
831
832 Mockito.verify(conn, Mockito.times(1)).isStale();
833 }
834
835 @Test
836 public void testValidateConnectionStale() throws Exception {
837 final HttpConnection conn = Mockito.mock(HttpConnection.class);
838 Mockito.when(conn.isOpen()).thenReturn(true);
839 Mockito.when(conn.isStale()).thenReturn(false);
840
841 final LocalConnFactory connFactory = Mockito.mock(LocalConnFactory.class);
842 Mockito.when(connFactory.create(Matchers.eq("somehost"))).thenReturn(conn);
843
844 final LocalConnPool pool = new LocalConnPool(connFactory, 2, 10);
845 pool.setValidateAfterInactivity(5);
846
847 final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
848 final LocalPoolEntry entry1 = future1.get(1, TimeUnit.SECONDS);
849 Assert.assertNotNull(entry1);
850
851 pool.release(entry1, true);
852
853 Thread.sleep(10);
854
855 Mockito.verify(connFactory, Mockito.times(1)).create("somehost");
856 Mockito.when(conn.isStale()).thenReturn(true);
857
858 final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
859 final LocalPoolEntry entry2 = future2.get(1, TimeUnit.SECONDS);
860 Assert.assertNotNull(entry2);
861 Assert.assertNotSame(entry1, entry2);
862
863 Mockito.verify(conn, Mockito.times(1)).isStale();
864 Mockito.verify(conn, Mockito.times(1)).close();
865 Mockito.verify(connFactory, Mockito.times(2)).create("somehost");
866 }
867
868 }