View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.http.nio.pool;
28  
29  import java.io.IOException;
30  import java.net.ConnectException;
31  import java.net.InetAddress;
32  import java.net.InetSocketAddress;
33  import java.net.SocketAddress;
34  import java.net.UnknownHostException;
35  import java.util.Collections;
36  import java.util.concurrent.CancellationException;
37  import java.util.concurrent.ExecutionException;
38  import java.util.concurrent.Future;
39  import java.util.concurrent.TimeUnit;
40  
41  import org.apache.http.concurrent.BasicFuture;
42  import org.apache.http.nio.reactor.ConnectingIOReactor;
43  import org.apache.http.nio.reactor.IOReactorStatus;
44  import org.apache.http.nio.reactor.IOSession;
45  import org.apache.http.nio.reactor.SessionRequest;
46  import org.apache.http.nio.reactor.SessionRequestCallback;
47  import org.apache.http.pool.PoolEntry;
48  import org.apache.http.pool.PoolStats;
49  import org.junit.Assert;
50  import org.junit.Test;
51  import org.mockito.Matchers;
52  import org.mockito.Mockito;
53  
54  public class TestNIOConnPool {
55  
56      static class LocalPoolEntry extends PoolEntry<String, IOSession> {
57  
58          private boolean closed;
59  
60          public LocalPoolEntry(final String route, final IOSession conn) {
61              super(null, route, conn);
62          }
63  
64          @Override
65          public void close() {
66              if (this.closed) {
67                  return;
68              }
69              this.closed = true;
70              getConnection().close();
71          }
72  
73          @Override
74          public boolean isClosed() {
75              return this.closed;
76          }
77  
78      }
79  
80      static class LocalConnFactory implements NIOConnFactory<String, IOSession> {
81  
82          @Override
83          public IOSession create(final String route, final IOSession session) throws IOException {
84              return session;
85          }
86  
87      }
88  
89      static class LocalAddressResolver implements SocketAddressResolver<String> {
90  
91          @Override
92          public SocketAddress resolveLocalAddress(final String route) {
93              return null;
94          }
95  
96          @Override
97          public SocketAddress resolveRemoteAddress(final String route) {
98              return InetSocketAddress.createUnresolved(route, 80);
99          }
100 
101     }
102 
103     static class LocalSessionPool extends AbstractNIOConnPool<String, IOSession, LocalPoolEntry> {
104 
105         public LocalSessionPool(
106                 final ConnectingIOReactor ioReactor, final int defaultMaxPerRoute, final int maxTotal) {
107             super(ioReactor, new LocalConnFactory(), new LocalAddressResolver(), defaultMaxPerRoute, maxTotal);
108         }
109 
110         public LocalSessionPool(
111                 final ConnectingIOReactor ioReactor,
112                 final SocketAddressResolver<String> addressResolver,
113                 final int defaultMaxPerRoute, final int maxTotal) {
114             super(ioReactor, new LocalConnFactory(), addressResolver, defaultMaxPerRoute, maxTotal);
115         }
116 
117         @Override
118         protected LocalPoolEntry createEntry(final String route, final IOSession session) {
119             return new LocalPoolEntry(route, session);
120         }
121 
122     }
123 
124     @Test
125     public void testEmptyPool() throws Exception {
126         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
127         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 10);
128         final PoolStats totals = pool.getTotalStats();
129         Assert.assertEquals(0, totals.getAvailable());
130         Assert.assertEquals(0, totals.getLeased());
131         Assert.assertEquals(0, totals.getPending());
132         Assert.assertEquals(10, totals.getMax());
133         Assert.assertEquals(Collections.emptySet(), pool.getRoutes());
134         final PoolStats stats = pool.getStats("somehost");
135         Assert.assertEquals(0, stats.getAvailable());
136         Assert.assertEquals(0, stats.getLeased());
137         Assert.assertEquals(0, stats.getPending());
138         Assert.assertEquals(2, stats.getMax());
139         Assert.assertEquals("[leased: []][available: []][pending: []]", pool.toString());
140     }
141 
142     @Test
143     public void testInternalLeaseRequest() throws Exception {
144         final LeaseRequest<String, IOSession, LocalPoolEntry> leaseRequest =
145             new LeaseRequest<String, IOSession, LocalPoolEntry>("somehost", null, 0, 0,
146                     new BasicFuture<LocalPoolEntry>(null));
147         Assert.assertEquals("[somehost][null]", leaseRequest.toString());
148     }
149 
150     @Test
151     public void testInvalidConstruction() throws Exception {
152         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
153         try {
154             new LocalSessionPool(null, 1, 1);
155             Assert.fail("IllegalArgumentException should have been thrown");
156         } catch (final IllegalArgumentException expected) {
157         }
158         try {
159             new LocalSessionPool(ioReactor, -1, 1);
160             Assert.fail("IllegalArgumentException should have been thrown");
161         } catch (final IllegalArgumentException expected) {
162         }
163         try {
164             new LocalSessionPool(ioReactor, 1, -1);
165             Assert.fail("IllegalArgumentException should have been thrown");
166         } catch (final IllegalArgumentException expected) {
167         }
168     }
169 
170     @Test
171     public void testSuccessfulConnect() throws Exception {
172         final IOSession ioSession = Mockito.mock(IOSession.class);
173         final SessionRequest sessionRequest = Mockito.mock(SessionRequest.class);
174         Mockito.when(sessionRequest.getAttachment()).thenReturn("somehost");
175         Mockito.when(sessionRequest.getSession()).thenReturn(ioSession);
176         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
177         Mockito.when(ioReactor.connect(
178                 Matchers.any(SocketAddress.class),
179                 Matchers.any(SocketAddress.class),
180                 Matchers.any(), Matchers.any(SessionRequestCallback.class))).
181                 thenReturn(sessionRequest);
182         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 10);
183         final Future<LocalPoolEntry> future = pool.lease("somehost", null, 100, TimeUnit.MILLISECONDS, null);
184         Mockito.verify(sessionRequest).setConnectTimeout(100);
185 
186         PoolStats totals = pool.getTotalStats();
187         Assert.assertEquals(0, totals.getAvailable());
188         Assert.assertEquals(0, totals.getLeased());
189         Assert.assertEquals(1, totals.getPending());
190 
191         pool.requestCompleted(sessionRequest);
192 
193         Assert.assertTrue(future.isDone());
194         Assert.assertFalse(future.isCancelled());
195         final LocalPoolEntry entry = future.get();
196         Assert.assertNotNull(entry);
197 
198         totals = pool.getTotalStats();
199         Assert.assertEquals(0, totals.getAvailable());
200         Assert.assertEquals(1, totals.getLeased());
201         Assert.assertEquals(0, totals.getPending());
202     }
203 
204     @Test
205     public void testFailedConnect() throws Exception {
206         final SessionRequest sessionRequest = Mockito.mock(SessionRequest.class);
207         Mockito.when(sessionRequest.getAttachment()).thenReturn("somehost");
208         Mockito.when(sessionRequest.getException()).thenReturn(new IOException());
209         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
210         Mockito.when(ioReactor.connect(
211                 Matchers.any(SocketAddress.class),
212                 Matchers.any(SocketAddress.class),
213                 Matchers.any(), Matchers.any(SessionRequestCallback.class))).
214                 thenReturn(sessionRequest);
215         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 10);
216         final Future<LocalPoolEntry> future = pool.lease("somehost", null);
217 
218         PoolStats totals = pool.getTotalStats();
219         Assert.assertEquals(0, totals.getAvailable());
220         Assert.assertEquals(0, totals.getLeased());
221         Assert.assertEquals(1, totals.getPending());
222 
223         pool.requestFailed(sessionRequest);
224 
225         Assert.assertTrue(future.isDone());
226         Assert.assertFalse(future.isCancelled());
227         try {
228             future.get();
229             Assert.fail("ExecutionException should have been thrown");
230         } catch (final ExecutionException ex) {
231             Assert.assertTrue(ex.getCause() instanceof IOException);
232         }
233 
234         totals = pool.getTotalStats();
235         Assert.assertEquals(0, totals.getAvailable());
236         Assert.assertEquals(0, totals.getLeased());
237         Assert.assertEquals(0, totals.getPending());
238     }
239 
240     @Test
241     public void testCencelledConnect() throws Exception {
242         final IOSession ioSession = Mockito.mock(IOSession.class);
243         final SessionRequest sessionRequest = Mockito.mock(SessionRequest.class);
244         Mockito.when(sessionRequest.getAttachment()).thenReturn("somehost");
245         Mockito.when(sessionRequest.getSession()).thenReturn(ioSession);
246         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
247         Mockito.when(ioReactor.connect(
248                 Matchers.any(SocketAddress.class),
249                 Matchers.any(SocketAddress.class),
250                 Matchers.any(), Matchers.any(SessionRequestCallback.class))).
251                 thenReturn(sessionRequest);
252         Mockito.when(ioReactor.getStatus()).thenReturn(IOReactorStatus.ACTIVE);
253         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 10);
254         final Future<LocalPoolEntry> future = pool.lease("somehost", null);
255 
256         PoolStats totals = pool.getTotalStats();
257         Assert.assertEquals(0, totals.getAvailable());
258         Assert.assertEquals(0, totals.getLeased());
259         Assert.assertEquals(1, totals.getPending());
260 
261         pool.requestCancelled(sessionRequest);
262 
263         Assert.assertTrue(future.isDone());
264         Assert.assertTrue(future.isCancelled());
265         try {
266             future.get();
267             Assert.fail("CancellationException expected");
268         } catch (final CancellationException ignore) {
269         }
270 
271         totals = pool.getTotalStats();
272         Assert.assertEquals(0, totals.getAvailable());
273         Assert.assertEquals(0, totals.getLeased());
274         Assert.assertEquals(0, totals.getPending());
275     }
276 
277     @Test
278     public void testTimeoutConnect() throws Exception {
279         final IOSession ioSession = Mockito.mock(IOSession.class);
280         final SessionRequest sessionRequest = Mockito.mock(SessionRequest.class);
281         Mockito.when(sessionRequest.getAttachment()).thenReturn("somehost");
282         Mockito.when(sessionRequest.getRemoteAddress())
283                 .thenReturn(new InetSocketAddress(InetAddress.getByAddress(new byte[]{127, 0, 0, 1}), 80));
284         Mockito.when(sessionRequest.getSession()).thenReturn(ioSession);
285         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
286         Mockito.when(ioReactor.connect(
287                 Matchers.any(SocketAddress.class),
288                 Matchers.any(SocketAddress.class),
289                 Matchers.any(), Matchers.any(SessionRequestCallback.class))).
290                 thenReturn(sessionRequest);
291         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 10);
292         final Future<LocalPoolEntry> future = pool.lease("somehost", null);
293 
294         PoolStats totals = pool.getTotalStats();
295         Assert.assertEquals(0, totals.getAvailable());
296         Assert.assertEquals(0, totals.getLeased());
297         Assert.assertEquals(1, totals.getPending());
298 
299         pool.requestTimeout(sessionRequest);
300 
301         Assert.assertTrue(future.isDone());
302         Assert.assertFalse(future.isCancelled());
303         try {
304             future.get();
305             Assert.fail("ExecutionException should have been thrown");
306         } catch (final ExecutionException ex) {
307             Assert.assertTrue(ex.getCause() instanceof ConnectException);
308             Assert.assertEquals("Timeout connecting to [/127.0.0.1:80]", ex.getCause().getMessage());
309         }
310 
311         totals = pool.getTotalStats();
312         Assert.assertEquals(0, totals.getAvailable());
313         Assert.assertEquals(0, totals.getLeased());
314         Assert.assertEquals(0, totals.getPending());
315     }
316 
317     @Test
318     public void testConnectUnknownHost() throws Exception {
319         final SessionRequest sessionRequest = Mockito.mock(SessionRequest.class);
320         Mockito.when(sessionRequest.getAttachment()).thenReturn("somehost");
321         Mockito.when(sessionRequest.getException()).thenReturn(new IOException());
322         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
323         final SocketAddressResolver<String> addressResolver = Mockito.mock(SocketAddressResolver.class);
324         Mockito.when(addressResolver.resolveRemoteAddress("somehost")).thenThrow(new UnknownHostException());
325         final LocalSessionPool pool = new LocalSessionPool(ioReactor, addressResolver, 2, 10);
326         final Future<LocalPoolEntry> future = pool.lease("somehost", null);
327 
328         Assert.assertTrue(future.isDone());
329         Assert.assertFalse(future.isCancelled());
330         try {
331             future.get();
332             Assert.fail("ExecutionException should have been thrown");
333         } catch (final ExecutionException ex) {
334             Assert.assertTrue(ex.getCause() instanceof UnknownHostException);
335         }
336     }
337 
338     @Test
339     public void testLeaseRelease() throws Exception {
340         final IOSession ioSession1 = Mockito.mock(IOSession.class);
341         final SessionRequest sessionRequest1 = Mockito.mock(SessionRequest.class);
342         Mockito.when(sessionRequest1.getAttachment()).thenReturn("somehost");
343         Mockito.when(sessionRequest1.getSession()).thenReturn(ioSession1);
344 
345         final IOSession ioSession2 = Mockito.mock(IOSession.class);
346         final SessionRequest sessionRequest2 = Mockito.mock(SessionRequest.class);
347         Mockito.when(sessionRequest2.getAttachment()).thenReturn("otherhost");
348         Mockito.when(sessionRequest2.getSession()).thenReturn(ioSession2);
349 
350         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
351         Mockito.when(ioReactor.connect(
352                 Matchers.eq(InetSocketAddress.createUnresolved("somehost", 80)),
353                 Matchers.any(SocketAddress.class),
354                 Matchers.any(), Matchers.any(SessionRequestCallback.class))).
355                 thenReturn(sessionRequest1);
356         Mockito.when(ioReactor.connect(
357                 Matchers.eq(InetSocketAddress.createUnresolved("otherhost", 80)),
358                 Matchers.any(SocketAddress.class),
359                 Matchers.any(), Matchers.any(SessionRequestCallback.class))).
360                 thenReturn(sessionRequest2);
361 
362         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 10);
363         final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
364         pool.requestCompleted(sessionRequest1);
365         final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
366         pool.requestCompleted(sessionRequest1);
367         final Future<LocalPoolEntry> future3 = pool.lease("otherhost", null);
368         pool.requestCompleted(sessionRequest2);
369 
370         final LocalPoolEntry entry1 = future1.get();
371         Assert.assertNotNull(entry1);
372         final LocalPoolEntry entry2 = future2.get();
373         Assert.assertNotNull(entry2);
374         final LocalPoolEntry entry3 = future3.get();
375         Assert.assertNotNull(entry3);
376 
377         pool.release(entry1, true);
378         pool.release(entry2, true);
379         pool.release(entry3, false);
380         Mockito.verify(ioSession1, Mockito.never()).close();
381         Mockito.verify(ioSession2, Mockito.times(1)).close();
382 
383         final PoolStats totals = pool.getTotalStats();
384         Assert.assertEquals(2, totals.getAvailable());
385         Assert.assertEquals(0, totals.getLeased());
386         Assert.assertEquals(0, totals.getPending());
387     }
388 
389     @Test
390     public void testLeaseIllegal() throws Exception {
391         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
392         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 10);
393         try {
394             pool.lease(null, null, 0, TimeUnit.MILLISECONDS, null);
395             Assert.fail("IllegalArgumentException should have been thrown");
396         } catch (final IllegalArgumentException expected) {
397         }
398         try {
399             pool.lease("somehost", null, 0, null, null);
400             Assert.fail("IllegalArgumentException should have been thrown");
401         } catch (final IllegalArgumentException expected) {
402         }
403     }
404 
405     @Test
406     public void testReleaseUnknownEntry() throws Exception {
407         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
408         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 2);
409         pool.release(new LocalPoolEntry("somehost", Mockito.mock(IOSession.class)), true);
410     }
411 
412     @Test
413     public void testMaxLimits() throws Exception {
414         final IOSession ioSession1 = Mockito.mock(IOSession.class);
415         final SessionRequest sessionRequest1 = Mockito.mock(SessionRequest.class);
416         Mockito.when(sessionRequest1.getAttachment()).thenReturn("somehost");
417         Mockito.when(sessionRequest1.getSession()).thenReturn(ioSession1);
418 
419         final IOSession ioSession2 = Mockito.mock(IOSession.class);
420         final SessionRequest sessionRequest2 = Mockito.mock(SessionRequest.class);
421         Mockito.when(sessionRequest2.getAttachment()).thenReturn("otherhost");
422         Mockito.when(sessionRequest2.getSession()).thenReturn(ioSession2);
423 
424         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
425         Mockito.when(ioReactor.connect(
426                 Matchers.eq(InetSocketAddress.createUnresolved("somehost", 80)),
427                 Matchers.any(SocketAddress.class),
428                 Matchers.any(), Matchers.any(SessionRequestCallback.class))).
429                 thenReturn(sessionRequest1);
430         Mockito.when(ioReactor.connect(
431                 Matchers.eq(InetSocketAddress.createUnresolved("otherhost", 80)),
432                 Matchers.any(SocketAddress.class),
433                 Matchers.any(), Matchers.any(SessionRequestCallback.class))).
434                 thenReturn(sessionRequest2);
435 
436         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 10);
437         pool.setMaxPerRoute("somehost", 2);
438         pool.setMaxPerRoute("otherhost", 1);
439         pool.setMaxTotal(3);
440 
441         final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
442         pool.requestCompleted(sessionRequest1);
443         final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
444         pool.requestCompleted(sessionRequest1);
445         final Future<LocalPoolEntry> future3 = pool.lease("otherhost", null);
446         pool.requestCompleted(sessionRequest2);
447 
448         final LocalPoolEntry entry1 = future1.get();
449         Assert.assertNotNull(entry1);
450         final LocalPoolEntry entry2 = future2.get();
451         Assert.assertNotNull(entry2);
452         final LocalPoolEntry entry3 = future3.get();
453         Assert.assertNotNull(entry3);
454 
455         pool.release(entry1, true);
456         pool.release(entry2, true);
457         pool.release(entry3, true);
458 
459         final PoolStats totals = pool.getTotalStats();
460         Assert.assertEquals(3, totals.getAvailable());
461         Assert.assertEquals(0, totals.getLeased());
462         Assert.assertEquals(0, totals.getPending());
463 
464         final Future<LocalPoolEntry> future4 = pool.lease("somehost", null);
465         final Future<LocalPoolEntry> future5 = pool.lease("somehost", null);
466         final Future<LocalPoolEntry> future6 = pool.lease("otherhost", null);
467         final Future<LocalPoolEntry> future7 = pool.lease("somehost", null);
468         final Future<LocalPoolEntry> future8 = pool.lease("somehost", null);
469         final Future<LocalPoolEntry> future9 = pool.lease("otherhost", null);
470 
471         Assert.assertTrue(future4.isDone());
472         final LocalPoolEntry entry4 = future4.get();
473         Assert.assertNotNull(entry4);
474         Assert.assertTrue(future5.isDone());
475         final LocalPoolEntry entry5 = future5.get();
476         Assert.assertNotNull(entry5);
477         Assert.assertTrue(future6.isDone());
478         final LocalPoolEntry entry6 = future6.get();
479         Assert.assertNotNull(entry6);
480         Assert.assertFalse(future7.isDone());
481         Assert.assertFalse(future8.isDone());
482         Assert.assertFalse(future9.isDone());
483 
484         Mockito.verify(ioReactor, Mockito.times(3)).connect(
485                 Matchers.any(SocketAddress.class), Matchers.any(SocketAddress.class),
486                 Matchers.any(), Matchers.any(SessionRequestCallback.class));
487 
488         pool.release(entry4, true);
489         pool.release(entry5, false);
490         pool.release(entry6, true);
491 
492         Assert.assertTrue(future7.isDone());
493         Assert.assertFalse(future8.isDone());
494         Assert.assertTrue(future9.isDone());
495 
496         Mockito.verify(ioReactor, Mockito.times(4)).connect(
497                 Matchers.any(SocketAddress.class), Matchers.any(SocketAddress.class),
498                 Matchers.any(), Matchers.any(SessionRequestCallback.class));
499     }
500 
501     @Test
502     public void testConnectionRedistributionOnTotalMaxLimit() throws Exception {
503         final IOSession ioSession1 = Mockito.mock(IOSession.class);
504         final SessionRequest sessionRequest1 = Mockito.mock(SessionRequest.class);
505         Mockito.when(sessionRequest1.getAttachment()).thenReturn("somehost");
506         Mockito.when(sessionRequest1.getSession()).thenReturn(ioSession1);
507 
508         final IOSession ioSession2 = Mockito.mock(IOSession.class);
509         final SessionRequest sessionRequest2 = Mockito.mock(SessionRequest.class);
510         Mockito.when(sessionRequest2.getAttachment()).thenReturn("somehost");
511         Mockito.when(sessionRequest2.getSession()).thenReturn(ioSession2);
512 
513         final IOSession ioSession3 = Mockito.mock(IOSession.class);
514         final SessionRequest sessionRequest3 = Mockito.mock(SessionRequest.class);
515         Mockito.when(sessionRequest3.getAttachment()).thenReturn("otherhost");
516         Mockito.when(sessionRequest3.getSession()).thenReturn(ioSession3);
517 
518         final IOSession ioSession4 = Mockito.mock(IOSession.class);
519         final SessionRequest sessionRequest4 = Mockito.mock(SessionRequest.class);
520         Mockito.when(sessionRequest4.getAttachment()).thenReturn("otherhost");
521         Mockito.when(sessionRequest4.getSession()).thenReturn(ioSession4);
522 
523         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
524         Mockito.when(ioReactor.connect(
525                 Matchers.eq(InetSocketAddress.createUnresolved("somehost", 80)),
526                 Matchers.any(SocketAddress.class),
527                 Matchers.any(), Matchers.any(SessionRequestCallback.class))).
528                 thenReturn(sessionRequest1, sessionRequest2, sessionRequest1);
529         Mockito.when(ioReactor.connect(
530                 Matchers.eq(InetSocketAddress.createUnresolved("otherhost", 80)),
531                 Matchers.any(SocketAddress.class),
532                 Matchers.any(), Matchers.any(SessionRequestCallback.class))).
533                 thenReturn(sessionRequest3, sessionRequest4, sessionRequest3);
534 
535         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 10);
536         pool.setMaxPerRoute("somehost", 2);
537         pool.setMaxPerRoute("otherhost", 2);
538         pool.setMaxTotal(2);
539 
540         final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
541         final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
542         final Future<LocalPoolEntry> future3 = pool.lease("otherhost", null);
543         final Future<LocalPoolEntry> future4 = pool.lease("otherhost", null);
544 
545         Mockito.verify(ioReactor, Mockito.times(2)).connect(
546                 Matchers.eq(InetSocketAddress.createUnresolved("somehost", 80)),
547                 Matchers.any(SocketAddress.class),
548                 Matchers.any(), Matchers.any(SessionRequestCallback.class));
549 
550         Mockito.verify(ioReactor, Mockito.never()).connect(
551                 Matchers.eq(InetSocketAddress.createUnresolved("otherhost", 80)),
552                 Matchers.any(SocketAddress.class),
553                 Matchers.any(), Matchers.any(SessionRequestCallback.class));
554 
555         pool.requestCompleted(sessionRequest1);
556         pool.requestCompleted(sessionRequest2);
557 
558         Assert.assertTrue(future1.isDone());
559         final LocalPoolEntry entry1 = future1.get();
560         Assert.assertNotNull(entry1);
561         Assert.assertTrue(future2.isDone());
562         final LocalPoolEntry entry2 = future2.get();
563         Assert.assertNotNull(entry2);
564 
565         Assert.assertFalse(future3.isDone());
566         Assert.assertFalse(future4.isDone());
567 
568         PoolStats totals = pool.getTotalStats();
569         Assert.assertEquals(0, totals.getAvailable());
570         Assert.assertEquals(2, totals.getLeased());
571         Assert.assertEquals(0, totals.getPending());
572 
573         pool.release(entry1, true);
574         pool.release(entry2, true);
575 
576         Mockito.verify(ioReactor, Mockito.times(2)).connect(
577                 Matchers.eq(InetSocketAddress.createUnresolved("somehost", 80)),
578                 Matchers.any(SocketAddress.class),
579                 Matchers.any(), Matchers.any(SessionRequestCallback.class));
580 
581         Mockito.verify(ioReactor, Mockito.times(2)).connect(
582                 Matchers.eq(InetSocketAddress.createUnresolved("otherhost", 80)),
583                 Matchers.any(SocketAddress.class),
584                 Matchers.any(), Matchers.any(SessionRequestCallback.class));
585 
586         pool.requestCompleted(sessionRequest3);
587         pool.requestCompleted(sessionRequest4);
588 
589         Assert.assertTrue(future3.isDone());
590         final LocalPoolEntry entry3 = future3.get();
591         Assert.assertNotNull(entry3);
592         Assert.assertTrue(future4.isDone());
593         final LocalPoolEntry entry4 = future4.get();
594         Assert.assertNotNull(entry4);
595 
596         totals = pool.getTotalStats();
597         Assert.assertEquals(0, totals.getAvailable());
598         Assert.assertEquals(2, totals.getLeased());
599         Assert.assertEquals(0, totals.getPending());
600 
601         final Future<LocalPoolEntry> future5 = pool.lease("somehost", null);
602         final Future<LocalPoolEntry> future6 = pool.lease("otherhost", null);
603 
604         Mockito.verify(ioReactor, Mockito.times(2)).connect(
605                 Matchers.eq(InetSocketAddress.createUnresolved("somehost", 80)),
606                 Matchers.any(SocketAddress.class),
607                 Matchers.any(), Matchers.any(SessionRequestCallback.class));
608 
609         Mockito.verify(ioReactor, Mockito.times(2)).connect(
610                 Matchers.eq(InetSocketAddress.createUnresolved("otherhost", 80)),
611                 Matchers.any(SocketAddress.class),
612                 Matchers.any(), Matchers.any(SessionRequestCallback.class));
613 
614         pool.release(entry3, true);
615         pool.release(entry4, true);
616 
617         Mockito.verify(ioReactor, Mockito.times(3)).connect(
618                 Matchers.eq(InetSocketAddress.createUnresolved("somehost", 80)),
619                 Matchers.any(SocketAddress.class),
620                 Matchers.any(), Matchers.any(SessionRequestCallback.class));
621 
622         Mockito.verify(ioReactor, Mockito.times(2)).connect(
623                 Matchers.eq(InetSocketAddress.createUnresolved("otherhost", 80)),
624                 Matchers.any(SocketAddress.class),
625                 Matchers.any(), Matchers.any(SessionRequestCallback.class));
626 
627         pool.requestCompleted(sessionRequest1);
628 
629         Assert.assertTrue(future5.isDone());
630         final LocalPoolEntry entry5 = future5.get();
631         Assert.assertNotNull(entry5);
632         Assert.assertTrue(future6.isDone());
633         final LocalPoolEntry entry6 = future6.get();
634         Assert.assertNotNull(entry6);
635 
636         totals = pool.getTotalStats();
637         Assert.assertEquals(0, totals.getAvailable());
638         Assert.assertEquals(2, totals.getLeased());
639         Assert.assertEquals(0, totals.getPending());
640 
641         pool.release(entry5, true);
642         pool.release(entry6, true);
643 
644         Mockito.verify(ioReactor, Mockito.times(3)).connect(
645                 Matchers.eq(InetSocketAddress.createUnresolved("somehost", 80)),
646                 Matchers.any(SocketAddress.class),
647                 Matchers.any(), Matchers.any(SessionRequestCallback.class));
648 
649         Mockito.verify(ioReactor, Mockito.times(2)).connect(
650                 Matchers.eq(InetSocketAddress.createUnresolved("otherhost", 80)),
651                 Matchers.any(SocketAddress.class),
652                 Matchers.any(), Matchers.any(SessionRequestCallback.class));
653 
654         totals = pool.getTotalStats();
655         Assert.assertEquals(2, totals.getAvailable());
656         Assert.assertEquals(0, totals.getLeased());
657         Assert.assertEquals(0, totals.getPending());
658     }
659 
660     @Test
661     public void testStatefulConnectionRedistributionOnPerRouteMaxLimit() throws Exception {
662         final IOSession ioSession1 = Mockito.mock(IOSession.class);
663         final SessionRequest sessionRequest1 = Mockito.mock(SessionRequest.class);
664         Mockito.when(sessionRequest1.getAttachment()).thenReturn("somehost");
665         Mockito.when(sessionRequest1.getSession()).thenReturn(ioSession1);
666 
667         final IOSession ioSession2 = Mockito.mock(IOSession.class);
668         final SessionRequest sessionRequest2 = Mockito.mock(SessionRequest.class);
669         Mockito.when(sessionRequest2.getAttachment()).thenReturn("somehost");
670         Mockito.when(sessionRequest2.getSession()).thenReturn(ioSession2);
671 
672         final IOSession ioSession3 = Mockito.mock(IOSession.class);
673         final SessionRequest sessionRequest3 = Mockito.mock(SessionRequest.class);
674         Mockito.when(sessionRequest3.getAttachment()).thenReturn("somehost");
675         Mockito.when(sessionRequest3.getSession()).thenReturn(ioSession3);
676 
677         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
678         Mockito.when(ioReactor.connect(
679                 Matchers.eq(InetSocketAddress.createUnresolved("somehost", 80)),
680                 Matchers.any(SocketAddress.class),
681                 Matchers.any(), Matchers.any(SessionRequestCallback.class))).
682                 thenReturn(sessionRequest1, sessionRequest2, sessionRequest3);
683 
684         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 10);
685         pool.setMaxPerRoute("somehost", 2);
686         pool.setMaxTotal(2);
687 
688         final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
689         final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
690 
691         Mockito.verify(ioReactor, Mockito.times(2)).connect(
692                 Matchers.eq(InetSocketAddress.createUnresolved("somehost", 80)),
693                 Matchers.any(SocketAddress.class),
694                 Matchers.any(), Matchers.any(SessionRequestCallback.class));
695 
696         pool.requestCompleted(sessionRequest1);
697         pool.requestCompleted(sessionRequest2);
698 
699         Assert.assertTrue(future1.isDone());
700         final LocalPoolEntry entry1 = future1.get();
701         Assert.assertNotNull(entry1);
702         Assert.assertTrue(future2.isDone());
703         final LocalPoolEntry entry2 = future2.get();
704         Assert.assertNotNull(entry2);
705 
706         PoolStats totals = pool.getTotalStats();
707         Assert.assertEquals(0, totals.getAvailable());
708         Assert.assertEquals(2, totals.getLeased());
709         Assert.assertEquals(0, totals.getPending());
710 
711         entry1.setState("some-stuff");
712         pool.release(entry1, true);
713         entry2.setState("some-stuff");
714         pool.release(entry2, true);
715 
716         final Future<LocalPoolEntry> future3 = pool.lease("somehost", "some-stuff");
717         final Future<LocalPoolEntry> future4 = pool.lease("somehost", "some-stuff");
718 
719         Assert.assertTrue(future1.isDone());
720         final LocalPoolEntry entry3 = future3.get();
721         Assert.assertNotNull(entry3);
722         Assert.assertTrue(future4.isDone());
723         final LocalPoolEntry entry4 = future4.get();
724         Assert.assertNotNull(entry4);
725 
726         Mockito.verify(ioReactor, Mockito.times(2)).connect(
727                 Matchers.eq(InetSocketAddress.createUnresolved("somehost", 80)),
728                 Matchers.any(SocketAddress.class),
729                 Matchers.any(), Matchers.any(SessionRequestCallback.class));
730 
731         pool.release(entry3, true);
732         pool.release(entry4, true);
733 
734         totals = pool.getTotalStats();
735         Assert.assertEquals(2, totals.getAvailable());
736         Assert.assertEquals(0, totals.getLeased());
737         Assert.assertEquals(0, totals.getPending());
738 
739         final Future<LocalPoolEntry> future5 = pool.lease("somehost", "some-other-stuff");
740 
741         Assert.assertFalse(future5.isDone());
742 
743         Mockito.verify(ioReactor, Mockito.times(3)).connect(
744                 Matchers.eq(InetSocketAddress.createUnresolved("somehost", 80)),
745                 Matchers.any(SocketAddress.class),
746                 Matchers.any(), Matchers.any(SessionRequestCallback.class));
747 
748         Mockito.verify(ioSession2).close();
749         Mockito.verify(ioSession1, Mockito.never()).close();
750 
751         totals = pool.getTotalStats();
752         Assert.assertEquals(1, totals.getAvailable());
753         Assert.assertEquals(0, totals.getLeased());
754         Assert.assertEquals(1, totals.getPending());
755     }
756 
757     @Test
758     public void testCreateNewIfExpired() throws Exception {
759         final IOSession ioSession1 = Mockito.mock(IOSession.class);
760         Mockito.when(ioSession1.isClosed()).thenReturn(Boolean.TRUE);
761         final SessionRequest sessionRequest1 = Mockito.mock(SessionRequest.class);
762         Mockito.when(sessionRequest1.getAttachment()).thenReturn("somehost");
763         Mockito.when(sessionRequest1.getSession()).thenReturn(ioSession1);
764 
765         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
766         Mockito.when(ioReactor.connect(
767                 Matchers.eq(InetSocketAddress.createUnresolved("somehost", 80)),
768                 Matchers.any(SocketAddress.class),
769                 Matchers.any(), Matchers.any(SessionRequestCallback.class))).
770                 thenReturn(sessionRequest1);
771 
772         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 2);
773 
774         final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
775 
776         Mockito.verify(ioReactor, Mockito.times(1)).connect(
777                 Matchers.any(SocketAddress.class), Matchers.any(SocketAddress.class),
778                 Matchers.any(), Matchers.any(SessionRequestCallback.class));
779 
780         pool.requestCompleted(sessionRequest1);
781 
782         Assert.assertTrue(future1.isDone());
783         final LocalPoolEntry entry1 = future1.get();
784         Assert.assertNotNull(entry1);
785 
786         entry1.updateExpiry(1, TimeUnit.MILLISECONDS);
787         pool.release(entry1, true);
788 
789         Thread.sleep(200L);
790 
791         final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
792 
793         Assert.assertFalse(future2.isDone());
794 
795         Mockito.verify(ioSession1).close();
796         Mockito.verify(ioReactor, Mockito.times(2)).connect(
797                 Matchers.any(SocketAddress.class), Matchers.any(SocketAddress.class),
798                 Matchers.any(), Matchers.any(SessionRequestCallback.class));
799 
800         final PoolStats totals = pool.getTotalStats();
801         Assert.assertEquals(0, totals.getAvailable());
802         Assert.assertEquals(0, totals.getLeased());
803         Assert.assertEquals(1, totals.getPending());
804         Assert.assertEquals(Collections.singleton("somehost"), pool.getRoutes());
805         final PoolStats stats = pool.getStats("somehost");
806         Assert.assertEquals(0, stats.getAvailable());
807         Assert.assertEquals(0, stats.getLeased());
808         Assert.assertEquals(1, stats.getPending());
809     }
810 
811     @Test
812     public void testCloseExpired() throws Exception {
813         final IOSession ioSession1 = Mockito.mock(IOSession.class);
814         Mockito.when(ioSession1.isClosed()).thenReturn(Boolean.TRUE);
815         final SessionRequest sessionRequest1 = Mockito.mock(SessionRequest.class);
816         Mockito.when(sessionRequest1.getAttachment()).thenReturn("somehost");
817         Mockito.when(sessionRequest1.getSession()).thenReturn(ioSession1);
818 
819         final IOSession ioSession2 = Mockito.mock(IOSession.class);
820         final SessionRequest sessionRequest2 = Mockito.mock(SessionRequest.class);
821         Mockito.when(sessionRequest2.getAttachment()).thenReturn("somehost");
822         Mockito.when(sessionRequest2.getSession()).thenReturn(ioSession2);
823 
824         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
825         Mockito.when(ioReactor.connect(
826                 Matchers.any(SocketAddress.class), Matchers.any(SocketAddress.class),
827                 Matchers.any(), Matchers.any(SessionRequestCallback.class))).
828                 thenReturn(sessionRequest1, sessionRequest2);
829 
830         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 2);
831 
832         final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
833         final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
834 
835         pool.requestCompleted(sessionRequest1);
836         pool.requestCompleted(sessionRequest2);
837 
838         Assert.assertTrue(future1.isDone());
839         final LocalPoolEntry entry1 = future1.get();
840         Assert.assertNotNull(entry1);
841         Assert.assertTrue(future2.isDone());
842         final LocalPoolEntry entry2 = future2.get();
843         Assert.assertNotNull(entry2);
844 
845         entry1.updateExpiry(1, TimeUnit.MILLISECONDS);
846         pool.release(entry1, true);
847 
848         Thread.sleep(200);
849 
850         entry2.updateExpiry(1000, TimeUnit.SECONDS);
851         pool.release(entry2, true);
852 
853         pool.closeExpired();
854 
855         Mockito.verify(ioSession1).close();
856         Mockito.verify(ioSession2, Mockito.never()).close();
857 
858         final PoolStats totals = pool.getTotalStats();
859         Assert.assertEquals(1, totals.getAvailable());
860         Assert.assertEquals(0, totals.getLeased());
861         Assert.assertEquals(0, totals.getPending());
862         final PoolStats stats = pool.getStats("somehost");
863         Assert.assertEquals(1, stats.getAvailable());
864         Assert.assertEquals(0, stats.getLeased());
865         Assert.assertEquals(0, stats.getPending());
866     }
867 
868     @Test
869     public void testCloseIdle() throws Exception {
870         final IOSession ioSession1 = Mockito.mock(IOSession.class);
871         final SessionRequest sessionRequest1 = Mockito.mock(SessionRequest.class);
872         Mockito.when(sessionRequest1.getAttachment()).thenReturn("somehost");
873         Mockito.when(sessionRequest1.getSession()).thenReturn(ioSession1);
874 
875         final IOSession ioSession2 = Mockito.mock(IOSession.class);
876         final SessionRequest sessionRequest2 = Mockito.mock(SessionRequest.class);
877         Mockito.when(sessionRequest2.getAttachment()).thenReturn("somehost");
878         Mockito.when(sessionRequest2.getSession()).thenReturn(ioSession2);
879 
880         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
881         Mockito.when(ioReactor.connect(
882                 Matchers.any(SocketAddress.class), Matchers.any(SocketAddress.class),
883                 Matchers.any(), Matchers.any(SessionRequestCallback.class))).
884                 thenReturn(sessionRequest1, sessionRequest2);
885 
886         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 2);
887 
888         final Future<LocalPoolEntry> future1 = pool.lease("somehost", null);
889         final Future<LocalPoolEntry> future2 = pool.lease("somehost", null);
890 
891         pool.requestCompleted(sessionRequest1);
892         pool.requestCompleted(sessionRequest2);
893 
894         Assert.assertTrue(future1.isDone());
895         final LocalPoolEntry entry1 = future1.get();
896         Assert.assertNotNull(entry1);
897         Assert.assertTrue(future2.isDone());
898         final LocalPoolEntry entry2 = future2.get();
899         Assert.assertNotNull(entry2);
900 
901         entry1.updateExpiry(0, TimeUnit.MILLISECONDS);
902         pool.release(entry1, true);
903 
904         Thread.sleep(200L);
905 
906         entry2.updateExpiry(0, TimeUnit.MILLISECONDS);
907         pool.release(entry2, true);
908 
909         pool.closeIdle(50, TimeUnit.MILLISECONDS);
910 
911         Mockito.verify(ioSession1).close();
912         Mockito.verify(ioSession2, Mockito.never()).close();
913 
914         PoolStats totals = pool.getTotalStats();
915         Assert.assertEquals(1, totals.getAvailable());
916         Assert.assertEquals(0, totals.getLeased());
917         Assert.assertEquals(0, totals.getPending());
918         PoolStats stats = pool.getStats("somehost");
919         Assert.assertEquals(1, stats.getAvailable());
920         Assert.assertEquals(0, stats.getLeased());
921         Assert.assertEquals(0, stats.getPending());
922 
923         pool.closeIdle(-1, TimeUnit.MILLISECONDS);
924 
925         Mockito.verify(ioSession2).close();
926 
927         totals = pool.getTotalStats();
928         Assert.assertEquals(0, totals.getAvailable());
929         Assert.assertEquals(0, totals.getLeased());
930         Assert.assertEquals(0, totals.getPending());
931         stats = pool.getStats("somehost");
932         Assert.assertEquals(0, stats.getAvailable());
933         Assert.assertEquals(0, stats.getLeased());
934         Assert.assertEquals(0, stats.getPending());
935     }
936 
937     @Test
938     public void testLeaseRequestTimeout() throws Exception {
939         final IOSession ioSession1 = Mockito.mock(IOSession.class);
940         Mockito.when(ioSession1.isClosed()).thenReturn(Boolean.TRUE);
941         final SessionRequest sessionRequest1 = Mockito.mock(SessionRequest.class);
942         Mockito.when(sessionRequest1.getAttachment()).thenReturn("somehost");
943         Mockito.when(sessionRequest1.getSession()).thenReturn(ioSession1);
944 
945         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
946         Mockito.when(ioReactor.connect(
947                 Matchers.any(SocketAddress.class), Matchers.any(SocketAddress.class),
948                 Matchers.any(), Matchers.any(SessionRequestCallback.class))).
949                 thenReturn(sessionRequest1);
950 
951         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 1, 1);
952 
953         final Future<LocalPoolEntry> future1 = pool.lease("somehost", null, 0, TimeUnit.MILLISECONDS, null);
954         final Future<LocalPoolEntry> future2 = pool.lease("somehost", null, 0, TimeUnit.MILLISECONDS, null);
955         final Future<LocalPoolEntry> future3 = pool.lease("somehost", null, 10, TimeUnit.MILLISECONDS, null);
956 
957         pool.requestCompleted(sessionRequest1);
958 
959         Assert.assertTrue(future1.isDone());
960         final LocalPoolEntry entry1 = future1.get();
961         Assert.assertNotNull(entry1);
962         Assert.assertFalse(future2.isDone());
963         Assert.assertFalse(future3.isDone());
964 
965         Thread.sleep(100);
966 
967         pool.validatePendingRequests();
968 
969         Assert.assertFalse(future2.isDone());
970         Assert.assertTrue(future3.isDone());
971     }
972 
973     @Test(expected=IllegalArgumentException.class)
974     public void testCloseIdleInvalid() throws Exception {
975         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
976         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 2);
977         pool.closeIdle(50, null);
978     }
979 
980     @Test(expected=IllegalArgumentException.class)
981     public void testGetStatsInvalid() throws Exception {
982         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
983         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 2);
984         pool.getStats(null);
985     }
986 
987     @Test
988     public void testSetMaxInvalid() throws Exception {
989         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
990         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 2);
991         try {
992             pool.setMaxTotal(-1);
993             Assert.fail("IllegalArgumentException should have been thrown");
994         } catch (final IllegalArgumentException expected) {
995         }
996         try {
997             pool.setMaxPerRoute(null, 1);
998             Assert.fail("IllegalArgumentException should have been thrown");
999         } catch (final IllegalArgumentException expected) {
1000         }
1001         try {
1002             pool.setDefaultMaxPerRoute(-1);
1003             Assert.fail("IllegalArgumentException should have been thrown");
1004         } catch (final IllegalArgumentException expected) {
1005         }
1006     }
1007 
1008     @Test
1009     public void testSetMaxPerRoute() throws Exception {
1010         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
1011         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 2);
1012         pool.setMaxPerRoute("somehost", 1);
1013         Assert.assertEquals(1, pool.getMaxPerRoute("somehost"));
1014         pool.setMaxPerRoute("somehost", 0);
1015         Assert.assertEquals(0, pool.getMaxPerRoute("somehost"));
1016         pool.setMaxPerRoute("somehost", -1);
1017         Assert.assertEquals(2, pool.getMaxPerRoute("somehost"));
1018     }
1019 
1020     @Test
1021     public void testShutdown() throws Exception {
1022         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
1023         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 2, 2);
1024         pool.shutdown(1000);
1025         Mockito.verify(ioReactor, Mockito.times(1)).shutdown(1000);
1026         pool.shutdown(1000);
1027         Mockito.verify(ioReactor, Mockito.times(1)).shutdown(1000);
1028         try {
1029             pool.lease("somehost", null);
1030             Assert.fail("IllegalStateException should have been thrown");
1031         } catch (final IllegalStateException expected) {
1032         }
1033         // Ignored if shut down
1034         pool.release(new LocalPoolEntry("somehost", Mockito.mock(IOSession.class)), true);
1035         pool.requestCompleted(Mockito.mock(SessionRequest.class));
1036         pool.requestFailed(Mockito.mock(SessionRequest.class));
1037         pool.requestCancelled(Mockito.mock(SessionRequest.class));
1038         pool.requestTimeout(Mockito.mock(SessionRequest.class));
1039     }
1040 
1041     @Test
1042     public void testLeaseRequestCanceled() throws Exception {
1043         final IOSession ioSession1 = Mockito.mock(IOSession.class);
1044         Mockito.when(ioSession1.isClosed()).thenReturn(Boolean.TRUE);
1045         final SessionRequest sessionRequest1 = Mockito.mock(SessionRequest.class);
1046         Mockito.when(sessionRequest1.getAttachment()).thenReturn("somehost");
1047         Mockito.when(sessionRequest1.getSession()).thenReturn(ioSession1);
1048 
1049         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
1050         Mockito.when(ioReactor.connect(
1051                 Matchers.any(SocketAddress.class), Matchers.any(SocketAddress.class),
1052                 Matchers.any(), Matchers.any(SessionRequestCallback.class))).
1053                 thenReturn(sessionRequest1);
1054         Mockito.when(ioReactor.getStatus()).thenReturn(IOReactorStatus.ACTIVE);
1055 
1056         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 1, 1);
1057 
1058         final Future<LocalPoolEntry> future1 = pool.lease("somehost", null, 0, TimeUnit.MILLISECONDS, null);
1059         future1.cancel(true);
1060 
1061         pool.requestCompleted(sessionRequest1);
1062 
1063         Assert.assertTrue(future1.isDone());
1064         try {
1065             future1.get();
1066             Assert.fail("CancellationException expected");
1067         } catch (final CancellationException ignore) {
1068         }
1069 
1070         final PoolStats totals = pool.getTotalStats();
1071         Assert.assertEquals(1, totals.getAvailable());
1072         Assert.assertEquals(0, totals.getLeased());
1073     }
1074 
1075     @Test
1076     public void testLeaseRequestCanceledWhileConnecting() throws Exception {
1077         final IOSession ioSession1 = Mockito.mock(IOSession.class);
1078         Mockito.when(ioSession1.isClosed()).thenReturn(Boolean.TRUE);
1079         final SessionRequest sessionRequest1 = Mockito.mock(SessionRequest.class);
1080         Mockito.when(sessionRequest1.getAttachment()).thenReturn("somehost");
1081         Mockito.when(sessionRequest1.getSession()).thenReturn(ioSession1);
1082 
1083         final ConnectingIOReactor ioReactor = Mockito.mock(ConnectingIOReactor.class);
1084         Mockito.when(ioReactor.connect(
1085                 Matchers.any(SocketAddress.class), Matchers.any(SocketAddress.class),
1086                 Matchers.any(), Matchers.any(SessionRequestCallback.class))).
1087                 thenReturn(sessionRequest1);
1088         Mockito.when(ioReactor.getStatus()).thenReturn(IOReactorStatus.ACTIVE);
1089 
1090         final LocalSessionPool pool = new LocalSessionPool(ioReactor, 1, 1);
1091 
1092         final Future<LocalPoolEntry> future1 = pool.lease("somehost", null, 0, TimeUnit.MILLISECONDS, null);
1093 
1094         pool.requestCompleted(sessionRequest1);
1095 
1096         Assert.assertTrue(future1.isDone());
1097         final LocalPoolEntry entry1 = future1.get();
1098         Assert.assertNotNull(entry1);
1099 
1100         final Future<LocalPoolEntry> future2 = pool.lease("somehost", null, 0, TimeUnit.MILLISECONDS, null);
1101         future2.cancel(true);
1102 
1103         pool.release(entry1, true);
1104 
1105         final PoolStats totals = pool.getTotalStats();
1106         Assert.assertEquals(1, totals.getAvailable());
1107         Assert.assertEquals(0, totals.getLeased());
1108     }
1109 
1110 }