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.hc.core5.reactor;
28  
29  import java.util.concurrent.Future;
30  
31  import org.apache.hc.core5.concurrent.FutureCallback;
32  import org.apache.hc.core5.function.Callback;
33  import org.apache.hc.core5.io.CloseMode;
34  import org.apache.hc.core5.util.TimeValue;
35  import org.apache.hc.core5.util.Timeout;
36  import org.hamcrest.CoreMatchers;
37  import org.hamcrest.MatcherAssert;
38  import org.junit.Before;
39  import org.junit.Test;
40  import org.junit.runner.RunWith;
41  import org.mockito.Answers;
42  import org.mockito.ArgumentMatcher;
43  import org.mockito.ArgumentMatchers;
44  import org.mockito.Mock;
45  import org.mockito.Mockito;
46  import org.mockito.invocation.InvocationOnMock;
47  import org.mockito.junit.MockitoJUnitRunner;
48  import org.mockito.stubbing.Answer;
49  
50  @RunWith(MockitoJUnitRunner.class)
51  public class TestAbstractIOSessionPool {
52  
53      @Mock
54      private Future<IOSession> connectFuture;
55      @Mock
56      private FutureCallback<IOSession> callback1;
57      @Mock
58      private FutureCallback<IOSession> callback2;
59      @Mock
60      private IOSession ioSession1;
61      @Mock
62      private IOSession ioSession2;
63  
64      private AbstractIOSessionPool<String> impl;
65  
66      @Before
67      public void setup() {
68          impl = Mockito.mock(AbstractIOSessionPool.class, Mockito.withSettings()
69                  .defaultAnswer(Answers.CALLS_REAL_METHODS)
70                  .useConstructor());
71      }
72  
73      @Test
74      public void testGetSessions() throws Exception {
75  
76          Mockito.when(impl.connectSession(
77                  ArgumentMatchers.anyString(),
78                  ArgumentMatchers.<Timeout>any(),
79                  ArgumentMatchers.<FutureCallback<IOSession>>any())).thenReturn(connectFuture);
80  
81          Mockito.doAnswer(new Answer() {
82  
83              @Override
84              public Object answer(final InvocationOnMock invocation) throws Throwable {
85                  final Callback<Boolean> callback = invocation.getArgument(1);
86                  callback.execute(true);
87                  return null;
88              }
89  
90          }).when(impl).validateSession(ArgumentMatchers.<IOSession>any(), ArgumentMatchers.<Callback<Boolean>>any());
91  
92          Mockito.when(ioSession1.isOpen()).thenReturn(true);
93  
94          final Future<IOSession> future1 = impl.getSession("somehost", Timeout.ofSeconds(123L), null);
95          MatcherAssert.assertThat(future1, CoreMatchers.notNullValue());
96          MatcherAssert.assertThat(future1.isDone(), CoreMatchers.equalTo(false));
97          MatcherAssert.assertThat(impl.getRoutes(), CoreMatchers.hasItem("somehost"));
98  
99          Mockito.verify(impl).connectSession(
100                 ArgumentMatchers.eq("somehost"),
101                 ArgumentMatchers.eq(Timeout.ofSeconds(123L)),
102                 ArgumentMatchers.<FutureCallback<IOSession>>any());
103 
104         final Future<IOSession> future2 = impl.getSession("somehost", Timeout.ofSeconds(123L), null);
105         MatcherAssert.assertThat(future2, CoreMatchers.notNullValue());
106         MatcherAssert.assertThat(future2.isDone(), CoreMatchers.equalTo(false));
107         MatcherAssert.assertThat(impl.getRoutes(), CoreMatchers.hasItem("somehost"));
108 
109         Mockito.verify(impl, Mockito.times(1)).connectSession(
110                 ArgumentMatchers.eq("somehost"),
111                 ArgumentMatchers.<Timeout>any(),
112                 ArgumentMatchers.argThat(new ArgumentMatcher<FutureCallback<IOSession>>() {
113 
114                     @Override
115                     public boolean matches(final FutureCallback<IOSession> callback) {
116                         callback.completed(ioSession1);
117                         return true;
118                     }
119 
120                 }));
121 
122         MatcherAssert.assertThat(future1.isDone(), CoreMatchers.equalTo(true));
123         MatcherAssert.assertThat(future1.get(), CoreMatchers.sameInstance(ioSession1));
124 
125         MatcherAssert.assertThat(future2.isDone(), CoreMatchers.equalTo(true));
126         MatcherAssert.assertThat(future2.get(), CoreMatchers.sameInstance(ioSession1));
127 
128         Mockito.verify(impl, Mockito.times(2)).validateSession(ArgumentMatchers.<IOSession>any(), ArgumentMatchers.<Callback<Boolean>>any());
129 
130         final Future<IOSession> future3 = impl.getSession("somehost", Timeout.ofSeconds(123L), null);
131 
132         Mockito.verify(impl, Mockito.times(1)).connectSession(
133                 ArgumentMatchers.eq("somehost"),
134                 ArgumentMatchers.<Timeout>any(),
135                 ArgumentMatchers.<FutureCallback<IOSession>>any());
136 
137         Mockito.verify(impl, Mockito.times(3)).validateSession(ArgumentMatchers.<IOSession>any(), ArgumentMatchers.<Callback<Boolean>>any());
138 
139         MatcherAssert.assertThat(future3.isDone(), CoreMatchers.equalTo(true));
140         MatcherAssert.assertThat(future3.get(), CoreMatchers.sameInstance(ioSession1));
141     }
142 
143     @Test
144     public void testGetSessionFailure() throws Exception {
145 
146         Mockito.when(impl.connectSession(
147                 ArgumentMatchers.anyString(),
148                 ArgumentMatchers.<Timeout>any(),
149                 ArgumentMatchers.<FutureCallback<IOSession>>any())).thenReturn(connectFuture);
150 
151         final Future<IOSession> future1 = impl.getSession("somehost", Timeout.ofSeconds(123L), null);
152         MatcherAssert.assertThat(future1, CoreMatchers.notNullValue());
153         MatcherAssert.assertThat(future1.isDone(), CoreMatchers.equalTo(false));
154         MatcherAssert.assertThat(impl.getRoutes(), CoreMatchers.hasItem("somehost"));
155 
156         Mockito.verify(impl).connectSession(
157                 ArgumentMatchers.eq("somehost"),
158                 ArgumentMatchers.eq(Timeout.ofSeconds(123L)),
159                 ArgumentMatchers.<FutureCallback<IOSession>>any());
160 
161         final Future<IOSession> future2 = impl.getSession("somehost", Timeout.ofSeconds(123L), null);
162         MatcherAssert.assertThat(future2, CoreMatchers.notNullValue());
163         MatcherAssert.assertThat(future2.isDone(), CoreMatchers.equalTo(false));
164         MatcherAssert.assertThat(impl.getRoutes(), CoreMatchers.hasItem("somehost"));
165 
166         Mockito.verify(impl, Mockito.times(1)).connectSession(
167                 ArgumentMatchers.eq("somehost"),
168                 ArgumentMatchers.<Timeout>any(),
169                 ArgumentMatchers.argThat(new ArgumentMatcher<FutureCallback<IOSession>>() {
170 
171                     @Override
172                     public boolean matches(final FutureCallback<IOSession> callback) {
173                         callback.failed(new Exception("Boom"));
174                         return true;
175                     }
176 
177                 }));
178 
179         MatcherAssert.assertThat(future1.isDone(), CoreMatchers.equalTo(true));
180         MatcherAssert.assertThat(future2.isDone(), CoreMatchers.equalTo(true));
181     }
182 
183     @Test
184     public void testShutdownPool() throws Exception {
185         final AbstractIOSessionPool.PoolEntry entry1 = impl.getPoolEntry("host1");
186         MatcherAssert.assertThat(entry1, CoreMatchers.notNullValue());
187         entry1.session = ioSession1;
188 
189         final AbstractIOSessionPool.PoolEntry entry2 = impl.getPoolEntry("host2");
190         MatcherAssert.assertThat(entry2, CoreMatchers.notNullValue());
191         entry2.session = ioSession2;
192 
193         final AbstractIOSessionPool.PoolEntry entry3 = impl.getPoolEntry("host3");
194         MatcherAssert.assertThat(entry3, CoreMatchers.notNullValue());
195         entry3.sessionFuture = connectFuture;
196         entry3.requestQueue.add(callback1);
197         entry3.requestQueue.add(callback2);
198 
199         impl.close(CloseMode.GRACEFUL);
200 
201         Mockito.verify(impl).closeSession(ioSession1, CloseMode.GRACEFUL);
202         Mockito.verify(impl).closeSession(ioSession2, CloseMode.GRACEFUL);
203         Mockito.verify(connectFuture).cancel(ArgumentMatchers.anyBoolean());
204         Mockito.verify(callback1).cancelled();
205         Mockito.verify(callback2).cancelled();
206     }
207 
208     @Test
209     public void testCloseIdleSessions() throws Exception {
210         final AbstractIOSessionPool.PoolEntry entry1 = impl.getPoolEntry("host1");
211         MatcherAssert.assertThat(entry1, CoreMatchers.notNullValue());
212         entry1.session = ioSession1;
213 
214         final AbstractIOSessionPool.PoolEntry entry2 = impl.getPoolEntry("host2");
215         MatcherAssert.assertThat(entry2, CoreMatchers.notNullValue());
216         entry2.session = ioSession2;
217 
218         impl.closeIdle(TimeValue.ZERO_MILLISECONDS);
219 
220         Mockito.verify(impl).closeSession(ioSession1, CloseMode.GRACEFUL);
221         Mockito.verify(impl).closeSession(ioSession2, CloseMode.GRACEFUL);
222 
223         MatcherAssert.assertThat(entry1.session, CoreMatchers.nullValue());
224         MatcherAssert.assertThat(entry2.session, CoreMatchers.nullValue());
225     }
226 
227     @Test
228     public void testEnumSessions() throws Exception {
229         final AbstractIOSessionPool.PoolEntry entry1 = impl.getPoolEntry("host1");
230         MatcherAssert.assertThat(entry1, CoreMatchers.notNullValue());
231         entry1.session = ioSession1;
232 
233         final AbstractIOSessionPool.PoolEntry entry2 = impl.getPoolEntry("host2");
234         MatcherAssert.assertThat(entry2, CoreMatchers.notNullValue());
235         entry2.session = ioSession2;
236 
237         impl.enumAvailable(new Callback<IOSession>() {
238 
239             @Override
240             public void execute(final IOSession ioSession) {
241                 ioSession.close(CloseMode.GRACEFUL);
242             }
243 
244         });
245         Mockito.verify(ioSession1).close(CloseMode.GRACEFUL);
246         Mockito.verify(ioSession2).close(CloseMode.GRACEFUL);
247     }
248 
249     @Test
250     public void testGetSessionReconnectAfterValidate() throws Exception {
251         final AbstractIOSessionPool.PoolEntry entry1 = impl.getPoolEntry("somehost");
252         MatcherAssert.assertThat(entry1, CoreMatchers.notNullValue());
253         entry1.session = ioSession1;
254 
255         Mockito.when(ioSession1.isOpen()).thenReturn(true);
256         Mockito.doAnswer(new Answer() {
257 
258             @Override
259             public Object answer(final InvocationOnMock invocation) throws Throwable {
260                 final Callback<Boolean> callback = invocation.getArgument(1);
261                 callback.execute(false);
262                 return null;
263             }
264 
265         }).when(impl).validateSession(ArgumentMatchers.<IOSession>any(), ArgumentMatchers.<Callback<Boolean>>any());
266 
267         impl.getSession("somehost", Timeout.ofSeconds(123L), null);
268 
269         Mockito.verify(impl, Mockito.times(1)).connectSession(
270                 ArgumentMatchers.eq("somehost"),
271                 ArgumentMatchers.eq(Timeout.ofSeconds(123L)),
272                 ArgumentMatchers.<FutureCallback<IOSession>>any());
273     }
274 
275     @Test
276     public void testGetSessionReconnectIfClosed() throws Exception {
277         final AbstractIOSessionPool.PoolEntry entry1 = impl.getPoolEntry("somehost");
278         MatcherAssert.assertThat(entry1, CoreMatchers.notNullValue());
279         entry1.session = ioSession1;
280 
281         Mockito.when(ioSession1.isOpen()).thenReturn(false);
282 
283         impl.getSession("somehost", Timeout.ofSeconds(123L), null);
284 
285         Mockito.verify(impl).connectSession(
286                 ArgumentMatchers.eq("somehost"),
287                 ArgumentMatchers.eq(Timeout.ofSeconds(123L)),
288                 ArgumentMatchers.<FutureCallback<IOSession>>any());
289     }
290 
291 }