1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.dbcp2.datasources;
18
19 import java.sql.Connection;
20 import java.sql.SQLException;
21 import java.time.Duration;
22
23 import javax.sql.ConnectionEvent;
24 import javax.sql.ConnectionEventListener;
25 import javax.sql.ConnectionPoolDataSource;
26 import javax.sql.PooledConnection;
27
28 import org.apache.commons.dbcp2.PoolableConnectionFactory;
29 import org.apache.commons.pool2.ObjectPool;
30 import org.apache.commons.pool2.PooledObject;
31 import org.apache.commons.pool2.PooledObjectFactory;
32 import org.apache.commons.pool2.impl.DefaultPooledObject;
33
34
35
36
37
38
39 final class CPDSConnectionFactory extends AbstractConnectionFactory
40 implements PooledObjectFactory<PooledConnectionAndInfo>, ConnectionEventListener, PooledConnectionManager {
41
42 private static final String NO_KEY_MESSAGE = "close() was called on a Connection, but I have no record of the underlying PooledConnection.";
43
44 private ObjectPool<PooledConnectionAndInfo> pool;
45 private UserPassKey userPassKey;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 public CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery,
67 final Duration validationQueryTimeoutDuration, final boolean rollbackAfterValidation, final String userName,
68 final char[] userPassword) {
69 super(cpds, validationQuery, validationQueryTimeoutDuration, rollbackAfterValidation);
70 this.userPassKey = new UserPassKey(userName, userPassword);
71 }
72
73 @Override
74 public void activateObject(final PooledObject<PooledConnectionAndInfo> pooledObject) throws SQLException {
75 validateLifetime(pooledObject);
76 }
77
78
79
80
81
82 @Override
83 public void closePool(final String userName) throws SQLException {
84 synchronized (this) {
85 if (userName == null || !userName.equals(this.userPassKey.getUserName())) {
86 return;
87 }
88 }
89 try {
90 pool.close();
91 } catch (final Exception ex) {
92 throw new SQLException("Error closing connection pool", ex);
93 }
94 }
95
96
97
98
99
100
101 @Override
102 public void connectionClosed(final ConnectionEvent event) {
103 final PooledConnection pc = (PooledConnection) event.getSource();
104
105
106 if (!validatingSet.contains(pc)) {
107 final PooledConnectionAndInfo pci = pcMap.get(pc);
108 if (pci == null) {
109 throw new IllegalStateException(NO_KEY_MESSAGE);
110 }
111
112 try {
113 pool.returnObject(pci);
114 } catch (final Exception e) {
115 System.err.println("CLOSING DOWN CONNECTION AS IT COULD NOT BE RETURNED TO THE POOL");
116 pc.removeConnectionEventListener(this);
117 try {
118 doDestroyObject(pci);
119 } catch (final Exception e2) {
120 System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + pci);
121 e2.printStackTrace();
122 }
123 }
124 }
125 }
126
127
128
129
130 @Override
131 public void connectionErrorOccurred(final ConnectionEvent event) {
132 final PooledConnection pc = (PooledConnection) event.getSource();
133 if (null != event.getSQLException()) {
134 System.err.println("CLOSING DOWN CONNECTION DUE TO INTERNAL ERROR (" + event.getSQLException() + ")");
135 }
136 pc.removeConnectionEventListener(this);
137
138 final PooledConnectionAndInfo pci = pcMap.get(pc);
139 if (pci == null) {
140 throw new IllegalStateException(NO_KEY_MESSAGE);
141 }
142 try {
143 pool.invalidateObject(pci);
144 } catch (final Exception e) {
145 System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + pci);
146 e.printStackTrace();
147 }
148 }
149
150
151
152
153 @Override
154 public void destroyObject(final PooledObject<PooledConnectionAndInfo> p) throws SQLException {
155 doDestroyObject(p.getObject());
156 }
157
158 private void doDestroyObject(final PooledConnectionAndInfo pci) throws SQLException {
159 final PooledConnection pc = pci.getPooledConnection();
160 pc.removeConnectionEventListener(this);
161 pcMap.remove(pc);
162 pc.close();
163 }
164
165
166
167
168
169
170 char[] getPasswordCharArray() {
171 return userPassKey.getPasswordCharArray();
172 }
173
174
175
176
177
178
179 public ObjectPool<PooledConnectionAndInfo> getPool() {
180 return pool;
181 }
182
183
184
185
186
187
188 @Override
189 public void invalidate(final PooledConnection pc) throws SQLException {
190 final PooledConnectionAndInfo pci = pcMap.get(pc);
191 if (pci == null) {
192 throw new IllegalStateException(NO_KEY_MESSAGE);
193 }
194 try {
195 pool.close();
196
197 pool.invalidateObject(pci);
198 } catch (final Exception ex) {
199 throw new SQLException("Error invalidating connection", ex);
200 }
201 }
202
203 @Override
204 public synchronized PooledObject<PooledConnectionAndInfo> makeObject() throws SQLException {
205 PooledConnection pc = null;
206 if (userPassKey.getUserName() == null) {
207 pc = cpds.getPooledConnection();
208 } else {
209 pc = cpds.getPooledConnection(userPassKey.getUserName(), userPassKey.getPassword());
210 }
211 if (pc == null) {
212 throw new IllegalStateException("Connection pool data source returned null from getPooledConnection");
213 }
214
215
216 pc.addConnectionEventListener(this);
217 final PooledConnectionAndInfo pci = new PooledConnectionAndInfo(pc, userPassKey);
218 pcMap.put(pc, pci);
219 return new DefaultPooledObject<>(pci);
220 }
221
222 @Override
223 public void passivateObject(final PooledObject<PooledConnectionAndInfo> p) throws SQLException {
224 validateLifetime(p);
225 }
226
227
228
229
230
231
232
233 @Override
234 public synchronized void setPassword(final char[] userPassword) {
235 this.userPassKey = new UserPassKey(userPassKey.getUserName(), userPassword);
236 }
237
238
239
240
241
242
243
244 @Override
245 public synchronized void setPassword(final String userPassword) {
246 this.userPassKey = new UserPassKey(userPassKey.getUserName(), userPassword);
247 }
248
249
250
251
252
253
254 public void setPool(final ObjectPool<PooledConnectionAndInfo> pool) {
255 this.pool = pool;
256 }
257
258
259
260
261 @Override
262 public synchronized String toString() {
263 final StringBuilder builder = new StringBuilder(super.toString());
264 builder.append("[cpds=");
265 builder.append(cpds);
266 builder.append(", validationQuery=");
267 builder.append(validationQuery);
268 builder.append(", validationQueryTimeoutDuration=");
269 builder.append(validationQueryTimeoutDuration);
270 builder.append(", rollbackAfterValidation=");
271 builder.append(rollbackAfterValidation);
272 builder.append(", pool=");
273 builder.append(pool);
274 builder.append(", maxConnDuration=");
275 builder.append(maxConnDuration);
276 builder.append(", validatingSet=");
277 builder.append(validatingSet);
278 builder.append(", pcMap=");
279 builder.append(pcMap);
280 builder.append("]");
281 return builder.toString();
282 }
283 }