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.PoolableConnection;
29 import org.apache.commons.pool2.KeyedObjectPool;
30 import org.apache.commons.pool2.KeyedPooledObjectFactory;
31 import org.apache.commons.pool2.PooledObject;
32 import org.apache.commons.pool2.impl.DefaultPooledObject;
33
34
35
36
37
38
39 final class KeyedCPDSConnectionFactory extends AbstractConnectionFactory
40 implements KeyedPooledObjectFactory<UserPassKey, 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 private KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> pool;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public KeyedCPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery, final Duration validationQueryTimeoutDuration,
61 final boolean rollbackAfterValidation) {
62 super(cpds, validationQuery, validationQueryTimeoutDuration, rollbackAfterValidation);
63 }
64
65 @Override
66 public void activateObject(final UserPassKey ignored, final PooledObject<PooledConnectionAndInfo> pooledObject) throws SQLException {
67 validateLifetime(pooledObject);
68 }
69
70
71
72
73
74 @Override
75 public void closePool(final String userName) throws SQLException {
76 try {
77 pool.clear(new UserPassKey(userName));
78 } catch (final Exception ex) {
79 throw new SQLException("Error closing connection pool", ex);
80 }
81 }
82
83
84
85
86
87
88 @Override
89 public void connectionClosed(final ConnectionEvent event) {
90 final PooledConnection pc = (PooledConnection) event.getSource();
91
92
93
94 if (!validatingSet.contains(pc)) {
95 final PooledConnectionAndInfo pci = pcMap.get(pc);
96 if (pci == null) {
97 throw new IllegalStateException(NO_KEY_MESSAGE);
98 }
99 try {
100 pool.returnObject(pci.getUserPassKey(), pci);
101 } catch (final Exception e) {
102 System.err.println("CLOSING DOWN CONNECTION AS IT COULD NOT BE RETURNED TO THE POOL");
103 pc.removeConnectionEventListener(this);
104 try {
105 pool.invalidateObject(pci.getUserPassKey(), pci);
106 } catch (final Exception e3) {
107 System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + pci);
108 e3.printStackTrace();
109 }
110 }
111 }
112 }
113
114
115
116
117 @Override
118 public void connectionErrorOccurred(final ConnectionEvent event) {
119 final PooledConnection pc = (PooledConnection) event.getSource();
120 if (null != event.getSQLException()) {
121 System.err.println("CLOSING DOWN CONNECTION DUE TO INTERNAL ERROR (" + event.getSQLException() + ")");
122 }
123 pc.removeConnectionEventListener(this);
124
125 final PooledConnectionAndInfo info = pcMap.get(pc);
126 if (info == null) {
127 throw new IllegalStateException(NO_KEY_MESSAGE);
128 }
129 try {
130 pool.invalidateObject(info.getUserPassKey(), info);
131 } catch (final Exception e) {
132 System.err.println("EXCEPTION WHILE DESTROYING OBJECT " + info);
133 e.printStackTrace();
134 }
135 }
136
137
138
139
140 @Override
141 public void destroyObject(final UserPassKey ignored, final PooledObject<PooledConnectionAndInfo> pooledObject) throws SQLException {
142 final PooledConnection pooledConnection = pooledObject.getObject().getPooledConnection();
143 pooledConnection.removeConnectionEventListener(this);
144 pcMap.remove(pooledConnection);
145 pooledConnection.close();
146 }
147
148
149
150
151
152
153 public KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> getPool() {
154 return pool;
155 }
156
157
158
159
160
161
162
163 @Override
164 public void invalidate(final PooledConnection pc) throws SQLException {
165 final PooledConnectionAndInfo info = pcMap.get(pc);
166 if (info == null) {
167 throw new IllegalStateException(NO_KEY_MESSAGE);
168 }
169 final UserPassKey key = info.getUserPassKey();
170 try {
171 pool.invalidateObject(key, info);
172 pool.clear(key);
173 } catch (final Exception ex) {
174 throw new SQLException("Error invalidating connection", ex);
175 }
176 }
177
178
179
180
181
182
183
184
185
186
187 @Override
188 public synchronized PooledObject<PooledConnectionAndInfo> makeObject(final UserPassKey userPassKey) throws SQLException {
189 PooledConnection pooledConnection = null;
190 final String userName = userPassKey.getUserName();
191 final String password = userPassKey.getPassword();
192 if (userName == null) {
193 pooledConnection = cpds.getPooledConnection();
194 } else {
195 pooledConnection = cpds.getPooledConnection(userName, password);
196 }
197 if (pooledConnection == null) {
198 throw new IllegalStateException("Connection pool data source returned null from getPooledConnection");
199 }
200
201
202 pooledConnection.addConnectionEventListener(this);
203 final PooledConnectionAndInfo pci = new PooledConnectionAndInfo(pooledConnection, userPassKey);
204 pcMap.put(pooledConnection, pci);
205 return new DefaultPooledObject<>(pci);
206 }
207
208 @Override
209 public void passivateObject(final UserPassKey ignored, final PooledObject<PooledConnectionAndInfo> pooledObject) throws SQLException {
210 validateLifetime(pooledObject);
211 }
212
213
214
215
216 @Override
217 public void setPassword(final char[] password) {
218
219 }
220
221
222
223
224 @Override
225 public void setPassword(final String password) {
226
227 }
228
229 public void setPool(final KeyedObjectPool<UserPassKey, PooledConnectionAndInfo> pool) {
230 this.pool = pool;
231 }
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246 @Override
247 public boolean validateObject(final UserPassKey ignored, final PooledObject<PooledConnectionAndInfo> pooledObject) {
248 return super.validateObject(pooledObject);
249 }
250 }