1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.dbunit.junit.jupiter;
22
23 import java.io.PrintWriter;
24 import java.lang.reflect.InvocationHandler;
25 import java.lang.reflect.InvocationTargetException;
26 import java.lang.reflect.Proxy;
27 import java.sql.Connection;
28 import java.sql.DriverManager;
29 import java.sql.SQLException;
30 import java.util.concurrent.atomic.AtomicInteger;
31 import java.util.logging.Logger;
32
33 import javax.sql.DataSource;
34
35 import org.dbunit.DatabaseProfile;
36
37
38
39
40
41
42
43
44
45
46
47
48
49 final class CountingDataSource implements DataSource
50 {
51 private final DatabaseProfile profile;
52 private final int maxConcurrent;
53 private final AtomicInteger inUse = new AtomicInteger();
54 private final AtomicInteger peak = new AtomicInteger();
55 private final AtomicInteger opened = new AtomicInteger();
56 private final AtomicInteger closed = new AtomicInteger();
57
58 CountingDataSource(final DatabaseProfile profile)
59 {
60 this(profile, 0);
61 }
62
63 CountingDataSource(final DatabaseProfile profile, final int maxConcurrent)
64 {
65 this.profile = profile;
66 this.maxConcurrent = maxConcurrent;
67 }
68
69 int peakConcurrent()
70 {
71 return peak.get();
72 }
73
74 int opened()
75 {
76 return opened.get();
77 }
78
79 int leaked()
80 {
81 return opened.get() - closed.get();
82 }
83
84 @Override
85 public Connection getConnection() throws SQLException
86 {
87 final int now = inUse.incrementAndGet();
88 if (maxConcurrent > 0 && now > maxConcurrent)
89 {
90 inUse.decrementAndGet();
91 throw new SQLException("CountingDataSource capped at " + maxConcurrent
92 + " concurrent connections; connection " + now + " was requested while "
93 + (now - 1) + " were still open");
94 }
95 peak.accumulateAndGet(now, Math::max);
96 final Connection real;
97 try
98 {
99 real = DriverManager.getConnection(profile.getConnectionUrl(), profile.getUser(),
100 profile.getPassword());
101 }
102 catch (final SQLException e)
103 {
104 inUse.decrementAndGet();
105 throw e;
106 }
107 opened.incrementAndGet();
108 return slotFreeingProxy(real);
109 }
110
111 private Connection slotFreeingProxy(final Connection real)
112 {
113 final InvocationHandler handler = (proxy, method, args) ->
114 {
115 if ("close".equals(method.getName()) && (args == null || args.length == 0))
116 {
117 if (!real.isClosed())
118 {
119 real.close();
120 closed.incrementAndGet();
121 inUse.decrementAndGet();
122 }
123 return null;
124 }
125 try
126 {
127 return method.invoke(real, args);
128 }
129 catch (final InvocationTargetException e)
130 {
131 throw e.getCause();
132 }
133 };
134 return (Connection) Proxy.newProxyInstance(getClass().getClassLoader(),
135 new Class<?>[] {Connection.class}, handler);
136 }
137
138 @Override
139 public Connection getConnection(final String username, final String password)
140 throws SQLException
141 {
142 return getConnection();
143 }
144
145 @Override
146 public PrintWriter getLogWriter()
147 {
148 return null;
149 }
150
151 @Override
152 public void setLogWriter(final PrintWriter out)
153 {
154 }
155
156 @Override
157 public void setLoginTimeout(final int seconds)
158 {
159 }
160
161 @Override
162 public int getLoginTimeout()
163 {
164 return 0;
165 }
166
167 @Override
168 public Logger getParentLogger()
169 {
170 return Logger.getLogger("");
171 }
172
173 @Override
174 public <T> T unwrap(final Class<T> iface)
175 {
176 throw new UnsupportedOperationException();
177 }
178
179 @Override
180 public boolean isWrapperFor(final Class<?> iface)
181 {
182 return false;
183 }
184 }