View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.dbutils;
18  
19  import static org.junit.Assert.fail;
20  import static org.mockito.ArgumentMatchers.any;
21  import static org.mockito.Mockito.doThrow;
22  import static org.mockito.Mockito.mock;
23  import static org.mockito.Mockito.times;
24  import static org.mockito.Mockito.verify;
25  import static org.mockito.Mockito.when;
26  
27  import java.sql.Connection;
28  import java.sql.ParameterMetaData;
29  import java.sql.PreparedStatement;
30  import java.sql.ResultSet;
31  import java.sql.SQLException;
32  import java.sql.Statement;
33  import java.util.concurrent.ExecutionException;
34  import java.util.concurrent.Executors;
35  import java.util.concurrent.Future;
36  import java.util.concurrent.TimeUnit;
37  
38  import javax.sql.DataSource;
39  
40  import org.apache.commons.dbutils.handlers.ArrayHandler;
41  import org.junit.Before;
42  import org.junit.Test;
43  import org.junit.runner.RunWith;
44  import org.mockito.Mock;
45  import org.mockito.junit.MockitoJUnitRunner;
46  
47  @SuppressWarnings("boxing") // test code
48  @RunWith(MockitoJUnitRunner.class)
49  public class AsyncQueryRunnerTest {
50      AsyncQueryRunner runner;
51      ArrayHandler handler;
52  
53      @Mock DataSource dataSource;
54      @Mock Connection conn;
55      @Mock PreparedStatement prepStmt;
56      @Mock Statement stmt;
57      @Mock ParameterMetaData meta;
58      @Mock ResultSet results;
59  
60      // helper method for calling batch when an exception is expected
61      private void callBatchWithException(final String sql, final Object[][] params) throws Exception {
62          Future<int[]> future = null;
63          boolean caught = false;
64  
65          try {
66              future = runner.batch(sql, params);
67  
68              future.get();
69  
70              verify(prepStmt, times(2)).addBatch();
71              verify(prepStmt, times(1)).executeBatch();
72              verify(prepStmt, times(1)).close();    // make sure the statement is closed
73              verify(conn, times(1)).close();    // make sure the connection is closed
74          } catch (final Exception e) {
75              caught = true;
76          }
77  
78          if (!caught) {
79              fail("Exception never thrown, but expected");
80          }
81      }
82  
83      //
84      // Batch test cases
85      //
86      private void callGoodBatch(final Connection conn, final Object[][] params) throws Exception {
87          when(meta.getParameterCount()).thenReturn(2);
88          final Future<int[]> future = runner.batch(conn, "select * from blah where ? = ?", params);
89  
90          future.get();
91  
92          verify(prepStmt, times(2)).addBatch();
93          verify(prepStmt, times(1)).executeBatch();
94          verify(prepStmt, times(1)).close();    // make sure we closed the statement
95          verify(conn, times(0)).close();    // make sure we closed the connection
96      }
97  
98      private void callGoodBatch(final Object[][] params) throws Exception {
99          when(meta.getParameterCount()).thenReturn(2);
100         final Future<int[]> future = runner.batch("select * from blah where ? = ?", params);
101 
102         future.get();
103 
104         verify(prepStmt, times(2)).addBatch();
105         verify(prepStmt, times(1)).executeBatch();
106         verify(prepStmt, times(1)).close();    // make sure we closed the statement
107         verify(conn, times(1)).close();    // make sure we closed the connection
108     }
109 
110     private void callGoodQuery() throws Exception {
111         when(meta.getParameterCount()).thenReturn(2);
112         String sql = "select * from blah where ? = ?";
113         runner.query(sql, handler, "unit", "test").get();
114 
115         verify(prepStmt, times(1)).executeQuery();
116         verify(results, times(1)).close();
117         verify(prepStmt, times(1)).close();    // make sure we closed the statement
118         verify(conn, times(1)).close();    // make sure we closed the connection
119 
120         // call the other variation of query
121         sql = "select * from blah";
122         runner.query(sql, handler).get();
123 
124         verify(stmt, times(1)).executeQuery(sql);
125         verify(results, times(2)).close();
126         verify(stmt, times(1)).close();    // make sure we closed the statement
127         verify(conn, times(2)).close();    // make sure we closed the connection
128     }
129 
130     //
131     // Query test cases
132     //
133     private void callGoodQuery(final Connection conn) throws Exception {
134         when(meta.getParameterCount()).thenReturn(2);
135         String sql = "select * from blah where ? = ?";
136         runner.query(conn, sql, handler, "unit", "test").get();
137 
138         verify(prepStmt, times(1)).executeQuery();
139         verify(results, times(1)).close();
140         verify(prepStmt, times(1)).close();    // make sure we closed the statement
141         verify(conn, times(0)).close();    // make sure we closed the connection
142 
143         // call the other variation of query
144         sql = "select * from blah";
145         runner.query(conn, sql, handler).get();
146 
147         verify(stmt, times(1)).executeQuery(sql);
148         verify(results, times(2)).close();
149         verify(stmt, times(1)).close();    // make sure we closed the statement
150         verify(conn, times(0)).close();    // make sure we closed the connection
151     }
152 
153     private void callGoodUpdate() throws Exception {
154         when(meta.getParameterCount()).thenReturn(2);
155         String sql = "update blah set ? = ?";
156         runner.update(sql, "unit", "test").get();
157 
158         verify(prepStmt, times(1)).executeUpdate();
159         verify(prepStmt, times(1)).close();    // make sure we closed the statement
160         verify(conn, times(1)).close();    // make sure we closed the connection
161 
162         // call the other variation
163         when(meta.getParameterCount()).thenReturn(0);
164         sql = "update blah set unit = test";
165         runner.update(sql).get();
166 
167         verify(stmt, times(1)).executeUpdate(sql);
168         verify(stmt, times(1)).close();    // make sure we closed the statement
169         verify(conn, times(2)).close();    // make sure we closed the connection
170 
171         // call the other variation
172         when(meta.getParameterCount()).thenReturn(1);
173         runner.update("update blah set unit = ?", "test").get();
174 
175         verify(prepStmt, times(2)).executeUpdate();
176         verify(prepStmt, times(2)).close();    // make sure we closed the statement
177         verify(conn, times(3)).close();    // make sure we closed the connection
178     }
179 
180     //
181     // Update test cases
182     //
183     private void callGoodUpdate(final Connection conn) throws Exception {
184         when(meta.getParameterCount()).thenReturn(2);
185         String sql = "update blah set ? = ?";
186         runner.update(conn, sql, "unit", "test").get();
187 
188         verify(prepStmt, times(1)).executeUpdate();
189         verify(prepStmt, times(1)).close();    // make sure we closed the statement
190         verify(conn, times(0)).close();    // make sure we closed the connection
191 
192         // call the other variation
193         when(meta.getParameterCount()).thenReturn(0);
194         sql = "update blah set unit = test";
195         runner.update(conn, sql).get();
196 
197         verify(stmt, times(1)).executeUpdate(sql);
198         verify(stmt, times(1)).close();    // make sure we closed the statement
199         verify(conn, times(0)).close();    // make sure we closed the connection
200 
201         // call the other variation
202         when(meta.getParameterCount()).thenReturn(1);
203         sql = "update blah set unit = ?";
204         runner.update(conn, sql, "test").get();
205 
206         verify(prepStmt, times(2)).executeUpdate();
207         verify(prepStmt, times(2)).close();    // make sure we closed the statement
208         verify(conn, times(0)).close();    // make sure we closed the connection
209     }
210 
211 
212 
213     // helper method for calling batch when an exception is expected
214     private void callQueryWithException(final Object... params) throws Exception {
215         boolean caught = false;
216 
217         try {
218             when(meta.getParameterCount()).thenReturn(2);
219             runner.query("select * from blah where ? = ?", handler, params).get();
220 
221             verify(prepStmt, times(1)).executeQuery();
222             verify(results, times(1)).close();
223             verify(prepStmt, times(1)).close();    // make sure we closed the statement
224             verify(conn, times(1)).close();    // make sure we closed the connection
225         } catch (final Exception e) {
226             caught = true;
227         }
228 
229         if (!caught) {
230             fail("Exception never thrown, but expected");
231         }
232     }
233 
234     // helper method for calling batch when an exception is expected
235     private void callUpdateWithException(final Object... params) throws Exception {
236         boolean caught = false;
237 
238         try {
239             when(meta.getParameterCount()).thenReturn(2);
240             runner.update("select * from blah where ? = ?", params).get();
241 
242             verify(prepStmt, times(1)).executeUpdate();
243             verify(prepStmt, times(1)).close();    // make sure we closed the statement
244             verify(conn, times(1)).close();    // make sure we closed the connection
245         } catch (final Exception e) {
246             caught = true;
247         }
248 
249         if (!caught) {
250             fail("Exception never thrown, but expected");
251         }
252     }
253 
254     @Before
255     public void setUp() throws Exception {
256         when(dataSource.getConnection()).thenReturn(conn);
257 
258         when(conn.prepareStatement(any(String.class))).thenReturn(prepStmt);
259         when(prepStmt.getParameterMetaData()).thenReturn(meta);
260         when(prepStmt.executeQuery()).thenReturn(results);
261 
262         when(conn.createStatement()).thenReturn(stmt);
263         when(stmt.executeQuery(any(String.class))).thenReturn(results);
264 
265         when(results.next()).thenReturn(false);
266 
267          handler = new ArrayHandler();
268          runner = new AsyncQueryRunner(Executors.newFixedThreadPool(1), new QueryRunner(dataSource));
269     }
270 
271     @Test
272     public void testAddBatchException() throws Exception {
273         final String[][] params = { { "unit", "unit" }, { "test", "test" } };
274 
275         callBatchWithException("select * from blah where ? = ?", params);
276     }
277 
278     //
279     // Random tests
280     //
281     @Test(expected=ExecutionException.class)
282     public void testBadPrepareConnection() throws Exception {
283         runner = new AsyncQueryRunner(Executors.newFixedThreadPool(1));
284         runner.update("update blah set unit = test").get();
285     }
286 
287     @Test
288     public void testExecuteBatchException() throws Exception {
289         final String[][] params = { { "unit", "unit" }, { "test", "test" } };
290 
291         callBatchWithException("select * from blah where ? = ?", params);
292     }
293 
294     @Test
295     public void testExecuteQueryException() throws Exception {
296         callQueryWithException(handler, "unit", "test");
297     }
298 
299     @Test
300     public void testExecuteUpdateException() throws Exception {
301         doThrow(new SQLException()).when(prepStmt).executeUpdate();
302 
303         callUpdateWithException("unit", "test");
304     }
305 
306 
307     @Test
308     public void testGoodBatch() throws Exception {
309         final String[][] params = { { "unit", "unit" }, { "test", "test" } };
310 
311         callGoodBatch(params);
312     }
313 
314     @Test
315     public void testGoodBatchDefaultConstructor() throws Exception {
316         runner = new AsyncQueryRunner(Executors.newFixedThreadPool(1));
317         final String[][] params = { { "unit", "unit" }, { "test", "test" } };
318 
319         callGoodBatch(conn, params);
320     }
321 
322     @SuppressWarnings("deprecation") // deliberate test of deprecated code
323     @Test
324     public void testGoodBatchPmdTrue() throws Exception {
325         runner = new AsyncQueryRunner(dataSource, true, Executors.newFixedThreadPool(1));
326         final String[][] params = { { "unit", "unit" }, { "test", "test" } };
327 
328         callGoodBatch(params);
329     }
330 
331     @Test
332     public void testGoodQuery() throws Exception {
333         callGoodQuery();
334     }
335 
336     @Test
337     public void testGoodQueryDefaultConstructor() throws Exception {
338         runner = new AsyncQueryRunner(Executors.newFixedThreadPool(1));
339         callGoodQuery(conn);
340     }
341 
342 
343     @SuppressWarnings("deprecation") // deliberate test of deprecated code
344     @Test
345     public void testGoodQueryPmdTrue() throws Exception {
346         runner = new AsyncQueryRunner(true, Executors.newFixedThreadPool(1));
347         callGoodQuery(conn);
348     }
349 
350     @Test
351     public void testGoodUpdate() throws Exception {
352         callGoodUpdate();
353     }
354 
355     @Test
356     public void testGoodUpdateDefaultConstructor() throws Exception {
357         runner = new AsyncQueryRunner(Executors.newFixedThreadPool(1));
358         callGoodUpdate(conn);
359     }
360 
361     @SuppressWarnings("deprecation") // deliberate test of deprecated code
362     @Test
363     public void testGoodUpdatePmdTrue() throws Exception {
364         runner = new AsyncQueryRunner(true, Executors.newFixedThreadPool(1));
365         callGoodUpdate(conn);
366     }
367 
368     @Test
369     public void testInsertUsesGivenQueryRunner() throws Exception {
370         final QueryRunner mockQueryRunner = mock(QueryRunner.class
371                 , org.mockito.Mockito.withSettings().verboseLogging() // debug for Continuum
372                 );
373         runner = new AsyncQueryRunner(Executors.newSingleThreadExecutor(), mockQueryRunner);
374 
375         runner.insert("1", handler);
376         runner.insert("2", handler, "param1");
377         runner.insert(conn, "3", handler);
378         runner.insert(conn, "4", handler, "param1");
379 
380         // give the Executor time to submit all insert statements. Otherwise the following verify statements will fail from time to time.
381         TimeUnit.MILLISECONDS.sleep(50);
382 
383         verify(mockQueryRunner).insert("1", handler);
384         verify(mockQueryRunner).insert("2", handler, "param1");
385         verify(mockQueryRunner).insert(conn, "3", handler);
386         verify(mockQueryRunner).insert(conn, "4", handler, "param1");
387     }
388 
389     @Test
390     public void testNoParamsQuery() throws Exception {
391         callGoodQuery();
392     }
393 
394     @Test
395     public void testNoParamsUpdate() throws Exception {
396         callGoodUpdate();
397     }
398 
399     @Test(expected=ExecutionException.class)
400     public void testNullConnectionBatch() throws Exception {
401         final String[][] params = { { "unit", "unit" }, { "test", "test" } };
402 
403         when(dataSource.getConnection()).thenReturn(null);
404 
405         runner.batch("select * from blah where ? = ?", params).get();
406     }
407 
408 
409     @Test(expected=ExecutionException.class)
410     public void testNullConnectionQuery() throws Exception {
411         when(dataSource.getConnection()).thenReturn(null);
412 
413         runner.query("select * from blah where ? = ?", handler, "unit", "test").get();
414     }
415 
416     @Test(expected=ExecutionException.class)
417     public void testNullConnectionUpdate() throws Exception {
418         when(dataSource.getConnection()).thenReturn(null);
419 
420         runner.update("select * from blah where ? = ?", "unit", "test").get();
421     }
422 
423     @Test(expected=ExecutionException.class)
424     public void testNullHandlerQuery() throws Exception {
425         runner.query("select * from blah where ? = ?", null).get();
426     }
427 
428     @Test(expected=ExecutionException.class)
429     public void testNullParamsArgBatch() throws Exception {
430         runner.batch("select * from blah where ? = ?", null).get();
431     }
432 
433     @Test
434     public void testNullParamsBatch() throws Exception {
435         final String[][] params = { { null, "unit" }, { "test", null } };
436 
437         callGoodBatch(params);
438     }
439 
440     @Test(expected=ExecutionException.class)
441     public void testNullSqlBatch() throws Exception {
442         final String[][] params = { { "unit", "unit" }, { "test", "test" } };
443 
444         runner.batch(null, params).get();
445     }
446 
447     @Test(expected=ExecutionException.class)
448     public void testNullSqlQuery() throws Exception {
449         runner.query(null, handler).get();
450     }
451 
452     @Test(expected=ExecutionException.class)
453     public void testNullSqlUpdate() throws Exception {
454         runner.update(null).get();
455     }
456 
457     @Test
458     public void testTooFewParamsBatch() throws Exception {
459         final String[][] params = { { "unit" }, { "test" } };
460 
461         callBatchWithException("select * from blah where ? = ?", params);
462     }
463 
464     @Test
465     public void testTooFewParamsQuery() throws Exception {
466         callQueryWithException("unit");
467     }
468 
469     @Test
470     public void testTooFewParamsUpdate() throws Exception {
471         callUpdateWithException("unit");
472     }
473 
474     @Test
475     public void testTooManyParamsBatch() throws Exception {
476         final String[][] params = { { "unit", "unit", "unit" }, { "test", "test", "test" } };
477 
478         callBatchWithException("select * from blah where ? = ?", params);
479     }
480 
481     @Test
482     public void testTooManyParamsQuery() throws Exception {
483         callQueryWithException("unit", "test", "fail");
484     }
485 
486     @Test
487     public void testTooManyParamsUpdate() throws Exception {
488         callUpdateWithException("unit", "test", "fail");
489     }
490 }