1 package org.springframework.batch.item.database;
2
3 import static org.easymock.EasyMock.createMock;
4 import static org.easymock.EasyMock.expect;
5 import static org.easymock.EasyMock.replay;
6 import static org.easymock.EasyMock.verify;
7 import static org.junit.Assert.assertEquals;
8 import static org.junit.Assert.assertFalse;
9 import static org.junit.Assert.assertNotSame;
10 import static org.junit.Assert.assertSame;
11 import static org.junit.Assert.assertTrue;
12 import static org.junit.Assert.fail;
13
14 import java.io.PrintWriter;
15 import java.sql.Connection;
16 import java.sql.ResultSet;
17 import java.sql.SQLException;
18 import java.sql.Statement;
19 import java.util.logging.Logger;
20
21 import javax.sql.DataSource;
22
23 import org.junit.Test;
24 import org.junit.internal.runners.JUnit4ClassRunner;
25 import org.junit.runner.RunWith;
26 import org.springframework.jdbc.core.JdbcTemplate;
27 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
28 import org.springframework.jdbc.datasource.DataSourceUtils;
29 import org.springframework.jdbc.datasource.SmartDataSource;
30 import org.springframework.transaction.PlatformTransactionManager;
31 import org.springframework.transaction.TransactionDefinition;
32 import org.springframework.transaction.TransactionStatus;
33 import org.springframework.transaction.support.TransactionCallback;
34 import org.springframework.transaction.support.TransactionTemplate;
35
36 @RunWith(JUnit4ClassRunner.class)
37 public class ExtendedConnectionDataSourceProxyTests {
38
39 @Test
40 public void testOperationWithDataSourceUtils() throws SQLException {
41 Connection con = createMock(Connection.class);
42 DataSource ds = createMock(DataSource.class);
43
44 expect(ds.getConnection()).andReturn(con);
45 con.close();
46 expect(ds.getConnection()).andReturn(con);
47 con.close();
48
49 expect(ds.getConnection()).andReturn(con);
50 con.close();
51 expect(ds.getConnection()).andReturn(con);
52 con.close();
53
54 replay(ds);
55 replay(con);
56
57 final ExtendedConnectionDataSourceProxy csds = new ExtendedConnectionDataSourceProxy(ds);
58
59 Connection con1 = csds.getConnection();
60 Connection con2 = csds.getConnection();
61 assertNotSame("shouldn't be the same connection", con1, con2);
62
63 assertTrue("should be able to close connection", csds.shouldClose(con1));
64 con1.close();
65 assertTrue("should be able to close connection", csds.shouldClose(con2));
66 con2.close();
67
68 Connection con3 = csds.getConnection();
69 csds.startCloseSuppression(con3);
70 Connection con3_1 = csds.getConnection();
71 assertSame("should be same connection", con3_1, con3);
72 assertFalse("should not be able to close connection", csds.shouldClose(con3));
73 con3_1.close();
74 Connection con3_2 = csds.getConnection();
75 assertSame("should be same connection", con3_2, con3);
76 Connection con4 = csds.getConnection();
77 assertNotSame("shouldn't be same connection", con4, con3);
78 csds.stopCloseSuppression(con3);
79 assertTrue("should be able to close connection", csds.shouldClose(con3));
80 con3_1 = null;
81 con3_2 = null;
82 con3.close();
83 assertTrue("should be able to close connection", csds.shouldClose(con4));
84 con4.close();
85
86 verify(ds);
87 verify(con);
88
89 }
90
91 @Test
92 public void testOperationWithDirectCloseCall() throws SQLException {
93 Connection con = createMock(Connection.class);
94 DataSource ds = createMock(DataSource.class);
95
96 expect(ds.getConnection()).andReturn(con);
97 con.close();
98 expect(ds.getConnection()).andReturn(con);
99 con.close();
100
101 replay(ds);
102 replay(con);
103
104 final ExtendedConnectionDataSourceProxy csds = new ExtendedConnectionDataSourceProxy(ds);
105
106 Connection con1 = csds.getConnection();
107 csds.startCloseSuppression(con1);
108 Connection con1_1 = csds.getConnection();
109 assertSame("should be same connection", con1_1, con1);
110 con1_1.close();
111 Connection con1_2 = csds.getConnection();
112 assertSame("should be same connection", con1_2, con1);
113 Connection con2 = csds.getConnection();
114 assertNotSame("shouldn't be same connection", con2, con1);
115 csds.stopCloseSuppression(con1);
116 assertTrue("should be able to close connection", csds.shouldClose(con1));
117 con1_1 = null;
118 con1_2 = null;
119 con1.close();
120 assertTrue("should be able to close connection", csds.shouldClose(con2));
121 con2.close();
122
123 verify(ds);
124 verify(con);
125
126 }
127
128 @Test
129 public void testSupressOfCloseWithJdbcTemplate() throws Exception {
130
131 Connection con = createMock(Connection.class);
132 DataSource ds = createMock(DataSource.class);
133 Statement stmt = createMock(Statement.class);
134 ResultSet rs = createMock(ResultSet.class);
135
136
137 expect(ds.getConnection()).andReturn(con);
138
139
140 expect(con.getAutoCommit()).andReturn(false);
141 expect(con.createStatement()).andReturn(stmt);
142 expect(stmt.executeQuery("select baz from bar")).andReturn(rs);
143 expect(rs.next()).andReturn(false);
144 expect(con.createStatement()).andReturn(stmt);
145 expect(stmt.executeQuery("select foo from bar")).andReturn(rs);
146 expect(rs.next()).andReturn(false);
147 con.commit();
148
149
150 expect(con.getAutoCommit()).andReturn(false);
151 expect(con.createStatement()).andReturn(stmt);
152 expect(stmt.executeQuery("select ham from foo")).andReturn(rs);
153 expect(rs.next()).andReturn(false);
154
155 expect(ds.getConnection()).andReturn(con);
156 expect(con.getAutoCommit()).andReturn(false);
157 expect(con.createStatement()).andReturn(stmt);
158 expect(stmt.executeQuery("select 1 from eggs")).andReturn(rs);
159 expect(rs.next()).andReturn(false);
160 con.commit();
161 con.close();
162
163 expect(con.createStatement()).andReturn(stmt);
164 expect(stmt.executeQuery("select more, ham from foo")).andReturn(rs);
165 expect(rs.next()).andReturn(false);
166 con.commit();
167
168
169 expect(con.getAutoCommit()).andReturn(false);
170 expect(con.createStatement()).andReturn(stmt);
171 expect(stmt.executeQuery("select spam from ham")).andReturn(rs);
172 expect(rs.next()).andReturn(false);
173 con.commit();
174
175
176 con.close();
177
178
179 expect(ds.getConnection()).andReturn(con);
180 expect(con.createStatement()).andReturn(stmt);
181 expect(stmt.executeQuery("select egg from bar")).andReturn(rs);
182 expect(rs.next()).andReturn(false);
183 con.close();
184
185 replay(rs);
186 replay(stmt);
187 replay(con);
188 replay(ds);
189
190 final ExtendedConnectionDataSourceProxy csds = new ExtendedConnectionDataSourceProxy();
191 csds.setDataSource(ds);
192 PlatformTransactionManager tm = new DataSourceTransactionManager(csds);
193 TransactionTemplate tt = new TransactionTemplate(tm);
194 final TransactionTemplate tt2 = new TransactionTemplate(tm);
195 tt2.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
196 final JdbcTemplate template = new JdbcTemplate(csds);
197
198 Connection connection = DataSourceUtils.getConnection(csds);
199 csds.startCloseSuppression(connection);
200 tt.execute(new TransactionCallback() {
201 public Object doInTransaction(TransactionStatus status) {
202 template.queryForList("select baz from bar");
203 template.queryForList("select foo from bar");
204 return null;
205 }
206 });
207 tt.execute(new TransactionCallback() {
208 public Object doInTransaction(TransactionStatus status) {
209 template.queryForList("select ham from foo");
210 tt2.execute(new TransactionCallback() {
211 public Object doInTransaction(TransactionStatus status) {
212 template.queryForList("select 1 from eggs");
213 return null;
214 }
215 });
216 template.queryForList("select more, ham from foo");
217 return null;
218 }
219 });
220 tt.execute(new TransactionCallback() {
221 public Object doInTransaction(TransactionStatus status) {
222 template.queryForList("select spam from ham");
223 return null;
224 }
225 });
226 csds.stopCloseSuppression(connection);
227 DataSourceUtils.releaseConnection(connection, csds);
228 template.queryForList("select egg from bar");
229
230 verify(rs);
231 verify(stmt);
232 verify(con);
233 verify(ds);
234 }
235
236 @Test(expected = IllegalArgumentException.class)
237 public void delegateIsRequired() throws Exception {
238
239 ExtendedConnectionDataSourceProxy tested = new ExtendedConnectionDataSourceProxy(null);
240 tested.afterPropertiesSet();
241 }
242
243 @Test
244 public void unwrapForUnsupportedInterface() throws Exception {
245
246 ExtendedConnectionDataSourceProxy tested = new ExtendedConnectionDataSourceProxy(new DataSourceStub());
247
248 assertFalse(tested.isWrapperFor(Unsupported.class));
249
250 try {
251 tested.unwrap(Unsupported.class);
252 fail();
253 }
254 catch (SQLException expected) {
255
256
257 assertEquals("Unsupported class " + Unsupported.class.getSimpleName(), expected.getMessage());
258 }
259 }
260
261 @Test
262 public void unwrapForSupportedInterface() throws Exception {
263
264 DataSourceStub ds = new DataSourceStub();
265 ExtendedConnectionDataSourceProxy tested = new ExtendedConnectionDataSourceProxy(ds);
266
267 assertTrue(tested.isWrapperFor(Supported.class));
268 assertEquals(ds, tested.unwrap(Supported.class));
269 }
270
271 @Test
272 public void unwrapForSmartDataSource() throws Exception {
273
274 ExtendedConnectionDataSourceProxy tested = new ExtendedConnectionDataSourceProxy(new DataSourceStub());
275
276 assertTrue(tested.isWrapperFor(DataSource.class));
277 assertEquals(tested, tested.unwrap(DataSource.class));
278
279 assertTrue(tested.isWrapperFor(SmartDataSource.class));
280 assertEquals(tested, tested.unwrap(SmartDataSource.class));
281 }
282
283
284
285
286 private static interface Supported {
287 }
288
289
290
291
292 private static interface Unsupported {
293 }
294
295
296
297
298
299
300 private static class DataSourceStub implements DataSource, Supported {
301
302 private static final String UNWRAP_ERROR_MESSAGE = "supplied type is not implemented by this class";
303
304 public Connection getConnection() throws SQLException {
305 throw new UnsupportedOperationException();
306 }
307
308 public Connection getConnection(String username, String password) throws SQLException {
309 throw new UnsupportedOperationException();
310 }
311
312 public PrintWriter getLogWriter() throws SQLException {
313 throw new UnsupportedOperationException();
314 }
315
316 public int getLoginTimeout() throws SQLException {
317 throw new UnsupportedOperationException();
318 }
319
320 public void setLogWriter(PrintWriter out) throws SQLException {
321 throw new UnsupportedOperationException();
322 }
323
324 public void setLoginTimeout(int seconds) throws SQLException {
325 throw new UnsupportedOperationException();
326 }
327
328 public boolean isWrapperFor(Class<?> iface) throws SQLException {
329 if (iface.equals(Supported.class) || (iface.equals(DataSource.class))) {
330 return true;
331 }
332 return false;
333 }
334
335 @SuppressWarnings("unchecked")
336 public <T> T unwrap(Class<T> iface) throws SQLException {
337 if (iface.equals(Supported.class) || iface.equals(DataSource.class)) {
338 return (T) this;
339 }
340 throw new SQLException(UNWRAP_ERROR_MESSAGE);
341 }
342
343
344
345
346
347
348 public Logger getParentLogger() {
349 throw new UnsupportedOperationException();
350 }
351
352 }
353 }