49
40
* @author Aaron Mulder (ammulder@chariotsolutions.com)
51
public class PGPoolingDataSource extends BaseDataSource implements DataSource
42
public class PGPoolingDataSource
43
extends org.postgresql.ds.jdbc23.AbstractJdbc23PoolingDataSource
53
private static Map dataSources = new HashMap();
55
public static PGPoolingDataSource getDataSource(String name)
57
return (PGPoolingDataSource)dataSources.get(name);
60
// Additional Data Source properties
61
protected String dataSourceName; // Must be protected for subclasses to sync updates to it
62
private int initialConnections = 0;
63
private int maxConnections = 0;
65
private boolean initialized = false;
66
private Stack available = new Stack();
67
private Stack used = new Stack();
68
private Object lock = new Object()
70
private PGConnectionPoolDataSource source;
73
* Gets a description of this DataSource.
75
public String getDescription()
77
return "Pooling DataSource '" + dataSourceName + " from " + org.postgresql.Driver.getVersion();
81
* Ensures the DataSource properties are not changed after the DataSource has
84
* @throws java.lang.IllegalStateException
85
* The Server Name cannot be changed after the DataSource has been
88
public void setServerName(String serverName)
92
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
94
super.setServerName(serverName);
98
* Ensures the DataSource properties are not changed after the DataSource has
101
* @throws java.lang.IllegalStateException
102
* The Database Name cannot be changed after the DataSource has been
105
public void setDatabaseName(String databaseName)
109
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
111
super.setDatabaseName(databaseName);
115
* Ensures the DataSource properties are not changed after the DataSource has
118
* @throws java.lang.IllegalStateException
119
* The User cannot be changed after the DataSource has been
122
public void setUser(String user)
126
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
132
* Ensures the DataSource properties are not changed after the DataSource has
135
* @throws java.lang.IllegalStateException
136
* The Password cannot be changed after the DataSource has been
139
public void setPassword(String password)
143
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
145
super.setPassword(password);
149
* Ensures the DataSource properties are not changed after the DataSource has
152
* @throws java.lang.IllegalStateException
153
* The Port Number cannot be changed after the DataSource has been
156
public void setPortNumber(int portNumber)
160
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
162
super.setPortNumber(portNumber);
166
* Gets the number of connections that will be created when this DataSource
167
* is initialized. If you do not call initialize explicitly, it will be
168
* initialized the first time a connection is drawn from it.
170
public int getInitialConnections()
172
return initialConnections;
176
* Sets the number of connections that will be created when this DataSource
177
* is initialized. If you do not call initialize explicitly, it will be
178
* initialized the first time a connection is drawn from it.
180
* @throws java.lang.IllegalStateException
181
* The Initial Connections cannot be changed after the DataSource has been
184
public void setInitialConnections(int initialConnections)
188
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
190
this.initialConnections = initialConnections;
194
* Gets the maximum number of connections that the pool will allow. If a request
195
* comes in and this many connections are in use, the request will block until a
196
* connection is available. Note that connections for a user other than the
197
* default user will not be pooled and don't count against this limit.
199
* @return The maximum number of pooled connection allowed, or 0 for no maximum.
201
public int getMaxConnections()
203
return maxConnections;
207
* Sets the maximum number of connections that the pool will allow. If a request
208
* comes in and this many connections are in use, the request will block until a
209
* connection is available. Note that connections for a user other than the
210
* default user will not be pooled and don't count against this limit.
212
* @param maxConnections The maximum number of pooled connection to allow, or
215
* @throws java.lang.IllegalStateException
216
* The Maximum Connections cannot be changed after the DataSource has been
219
public void setMaxConnections(int maxConnections)
223
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
225
this.maxConnections = maxConnections;
229
* Gets the name of this DataSource. This uniquely identifies the DataSource.
230
* You cannot use more than one DataSource in the same VM with the same name.
232
public String getDataSourceName()
234
return dataSourceName;
238
* Sets the name of this DataSource. This is required, and uniquely identifies
239
* the DataSource. You cannot create or use more than one DataSource in the
240
* same VM with the same name.
242
* @throws java.lang.IllegalStateException
243
* The Data Source Name cannot be changed after the DataSource has been
245
* @throws java.lang.IllegalArgumentException
246
* Another PoolingDataSource with the same dataSourceName already
249
public void setDataSourceName(String dataSourceName)
253
throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
255
if (this.dataSourceName != null && dataSourceName != null && dataSourceName.equals(this.dataSourceName))
259
synchronized (dataSources)
261
if (getDataSource(dataSourceName) != null)
263
throw new IllegalArgumentException("DataSource with name '" + dataSourceName + "' already exists!");
265
if (this.dataSourceName != null)
267
dataSources.remove(this.dataSourceName);
269
this.dataSourceName = dataSourceName;
270
dataSources.put(dataSourceName, this);
275
* Initializes this DataSource. If the initialConnections is greater than zero,
276
* that number of connections will be created. After this method is called,
277
* the DataSource properties cannot be changed. If you do not call this
278
* explicitly, it will be called the first time you get a connection from the
280
* @throws java.sql.SQLException
281
* Occurs when the initialConnections is greater than zero, but the
282
* DataSource is not able to create enough physical connections.
284
public void initialize() throws SQLException
288
source = createConnectionPool();
289
source.setDatabaseName(getDatabaseName());
290
source.setPassword(getPassword());
291
source.setPortNumber(getPortNumber());
292
source.setServerName(getServerName());
293
source.setUser(getUser());
294
while (available.size() < initialConnections)
296
available.push(source.getPooledConnection());
302
protected boolean isInitialized() {
307
* Creates the appropriate ConnectionPool to use for this DataSource.
309
protected PGConnectionPoolDataSource createConnectionPool() {
310
return new PGConnectionPoolDataSource();
314
* Gets a <b>non-pooled</b> connection, unless the user and password are the
315
* same as the default values for this connection pool.
317
* @return A pooled connection.
318
* @throws SQLException
319
* Occurs when no pooled connection is available, and a new physical
320
* connection cannot be created.
322
public Connection getConnection(String user, String password) throws SQLException
324
// If this is for the default user/password, use a pooled connection
326
(user.equals(getUser()) && ((password == null && getPassword() == null) || (password != null && password.equals(getPassword())))))
328
return getConnection();
330
// Otherwise, use a non-pooled connection
335
return super.getConnection(user, password);
339
* Gets a connection from the connection pool.
341
* @return A pooled connection.
342
* @throws SQLException
343
* Occurs when no pooled connection is available, and a new physical
344
* connection cannot be created.
346
public Connection getConnection() throws SQLException
352
return getPooledConnection();
356
* Closes this DataSource, and all the pooled connections, whether in use or not.
362
while (available.size() > 0)
364
PooledConnectionImpl pci = (PooledConnectionImpl)available.pop();
369
catch (SQLException e)
374
while (used.size() > 0)
376
PooledConnectionImpl pci = (PooledConnectionImpl)used.pop();
377
pci.removeConnectionEventListener(connectionEventListener);
382
catch (SQLException e)
388
removeStoredDataSource();
391
protected void removeStoredDataSource() {
392
synchronized (dataSources)
394
dataSources.remove(dataSourceName);
399
* Gets a connection from the pool. Will get an available one if
400
* present, or create a new one if under the max limit. Will
401
* block if all used and a new one would exceed the max.
403
private Connection getPooledConnection() throws SQLException
405
PooledConnection pc = null;
408
if (available == null)
410
throw new PSQLException(GT.tr("DataSource has been closed."),
411
PSQLState.CONNECTION_DOES_NOT_EXIST);
415
if (available.size() > 0)
417
pc = (PooledConnection)available.pop();
421
if (maxConnections == 0 || used.size() < maxConnections)
423
pc = source.getPooledConnection();
431
// Wake up every second at a minimum
434
catch (InterruptedException e)
440
pc.addConnectionEventListener(connectionEventListener);
441
return pc.getConnection();
445
* Notified when a pooled connection is closed, or a fatal error occurs
446
* on a pooled connection. This is the only way connections are marked
449
private ConnectionEventListener connectionEventListener = new ConnectionEventListener()
451
public void connectionClosed(ConnectionEvent event)
453
((PooledConnection)event.getSource()).removeConnectionEventListener(this);
456
if (available == null)
458
return ; // DataSource has been closed
460
boolean removed = used.remove(event.getSource());
463
available.push(event.getSource());
464
// There's now a new connection available
469
// a connection error occured
475
* This is only called for fatal errors, where the physical connection is
476
* useless afterward and should be removed from the pool.
478
public void connectionErrorOccurred(ConnectionEvent event)
480
((PooledConnection) event.getSource()).removeConnectionEventListener(this);
483
if (available == null)
485
return ; // DataSource has been closed
487
used.remove(event.getSource());
488
// We're now at least 1 connection under the max
495
* Adds custom properties for this DataSource to the properties defined in
498
public Reference getReference() throws NamingException
500
Reference ref = super.getReference();
501
ref.add(new StringRefAddr("dataSourceName", dataSourceName));
502
if (initialConnections > 0)
504
ref.add(new StringRefAddr("initialConnections", Integer.toString(initialConnections)));
506
if (maxConnections > 0)
508
ref.add(new StringRefAddr("maxConnections", Integer.toString(maxConnections)));
47
protected void addDataSource(String dataSourceName)
49
dataSources.put(dataSourceName, this);