~ubuntu-branches/ubuntu/wily/libpgjava/wily

« back to all changes in this revision

Viewing changes to org/postgresql/ds/PGPoolingDataSource.java

  • Committer: Bazaar Package Importer
  • Author(s): Michael Koch
  • Date: 2008-04-26 22:01:11 UTC
  • mfrom: (3.1.4 gutsy)
  • Revision ID: james.westby@ubuntu.com-20080426220111-yasgxtas5smx2qm3
Tags: 8.2-504-2
* Updated description to mention PostgreSQL 8.3 as supported.
  Closes: #398348
* Removed libpgjava transitional package. Closes: #477557
* Moved debhelper and cdbs from Build-Depends-Indep to Build-Depends.
* Added Homepage, Vcs-Svn and Vcs-Browser fields.
* Added watch file.
* Added myself to Uploaders.
* Removed Stafan and Wolfgang from Uploaders.
* Updated Standards-Version to 3.7.3
* Updated debhelper level to 5.

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
* Copyright (c) 2004-2005, PostgreSQL Global Development Group
4
4
*
5
5
* IDENTIFICATION
6
 
*   $PostgreSQL: pgjdbc/org/postgresql/ds/PGPoolingDataSource.java,v 1.7 2005/01/14 01:20:17 oliver Exp $
 
6
*   $PostgreSQL: pgjdbc/org/postgresql/ds/PGPoolingDataSource.java.in,v 1.1 2006/11/29 04:00:16 jurka Exp $
7
7
*
8
8
*-------------------------------------------------------------------------
9
9
*/
10
10
package org.postgresql.ds;
11
11
 
12
 
import javax.sql.*;
13
 
import javax.naming.*;
14
 
import java.util.*;
15
 
import java.sql.Connection;
16
 
import java.sql.SQLException;
17
 
 
18
 
import org.postgresql.util.GT;
19
 
import org.postgresql.util.PSQLState;
20
 
import org.postgresql.util.PSQLException;
21
 
import org.postgresql.ds.common.*;
 
12
import javax.sql.DataSource;
22
13
 
23
14
/**
24
15
 * DataSource which uses connection pooling.  <font color="red">Don't use this if
48
39
 *
49
40
 * @author Aaron Mulder (ammulder@chariotsolutions.com)
50
41
 */
51
 
public class PGPoolingDataSource extends BaseDataSource implements DataSource
 
42
public class PGPoolingDataSource
 
43
    extends org.postgresql.ds.jdbc23.AbstractJdbc23PoolingDataSource
 
44
    implements DataSource
52
45
{
53
 
    private static Map dataSources = new HashMap();
54
 
 
55
 
    public static PGPoolingDataSource getDataSource(String name)
56
 
    {
57
 
        return (PGPoolingDataSource)dataSources.get(name);
58
 
    }
59
 
 
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;
64
 
    // State variables
65
 
    private boolean initialized = false;
66
 
    private Stack available = new Stack();
67
 
    private Stack used = new Stack();
68
 
    private Object lock = new Object()
69
 
                              ;
70
 
    private PGConnectionPoolDataSource source;
71
 
 
72
 
    /**
73
 
     * Gets a description of this DataSource.
74
 
     */
75
 
    public String getDescription()
76
 
    {
77
 
        return "Pooling DataSource '" + dataSourceName + " from " + org.postgresql.Driver.getVersion();
78
 
    }
79
 
 
80
 
    /**
81
 
     * Ensures the DataSource properties are not changed after the DataSource has
82
 
     * been used.
83
 
     *
84
 
     * @throws java.lang.IllegalStateException
85
 
     *     The Server Name cannot be changed after the DataSource has been
86
 
     *     used.
87
 
     */
88
 
    public void setServerName(String serverName)
89
 
    {
90
 
        if (initialized)
91
 
        {
92
 
            throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
93
 
        }
94
 
        super.setServerName(serverName);
95
 
    }
96
 
 
97
 
    /**
98
 
     * Ensures the DataSource properties are not changed after the DataSource has
99
 
     * been used.
100
 
     *
101
 
     * @throws java.lang.IllegalStateException
102
 
     *     The Database Name cannot be changed after the DataSource has been
103
 
     *     used.
104
 
     */
105
 
    public void setDatabaseName(String databaseName)
106
 
    {
107
 
        if (initialized)
108
 
        {
109
 
            throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
110
 
        }
111
 
        super.setDatabaseName(databaseName);
112
 
    }
113
 
 
114
 
    /**
115
 
     * Ensures the DataSource properties are not changed after the DataSource has
116
 
     * been used.
117
 
     *
118
 
     * @throws java.lang.IllegalStateException
119
 
     *     The User cannot be changed after the DataSource has been
120
 
     *     used.
121
 
     */
122
 
    public void setUser(String user)
123
 
    {
124
 
        if (initialized)
125
 
        {
126
 
            throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
127
 
        }
128
 
        super.setUser(user);
129
 
    }
130
 
 
131
 
    /**
132
 
     * Ensures the DataSource properties are not changed after the DataSource has
133
 
     * been used.
134
 
     *
135
 
     * @throws java.lang.IllegalStateException
136
 
     *     The Password cannot be changed after the DataSource has been
137
 
     *     used.
138
 
     */
139
 
    public void setPassword(String password)
140
 
    {
141
 
        if (initialized)
142
 
        {
143
 
            throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
144
 
        }
145
 
        super.setPassword(password);
146
 
    }
147
 
 
148
 
    /**
149
 
     * Ensures the DataSource properties are not changed after the DataSource has
150
 
     * been used.
151
 
     *
152
 
     * @throws java.lang.IllegalStateException
153
 
     *     The Port Number cannot be changed after the DataSource has been
154
 
     *     used.
155
 
     */
156
 
    public void setPortNumber(int portNumber)
157
 
    {
158
 
        if (initialized)
159
 
        {
160
 
            throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
161
 
        }
162
 
        super.setPortNumber(portNumber);
163
 
    }
164
 
 
165
 
    /**
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.
169
 
     */
170
 
    public int getInitialConnections()
171
 
    {
172
 
        return initialConnections;
173
 
    }
174
 
 
175
 
    /**
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.
179
 
     *
180
 
     * @throws java.lang.IllegalStateException
181
 
     *     The Initial Connections cannot be changed after the DataSource has been
182
 
     *     used.
183
 
     */
184
 
    public void setInitialConnections(int initialConnections)
185
 
    {
186
 
        if (initialized)
187
 
        {
188
 
            throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
189
 
        }
190
 
        this.initialConnections = initialConnections;
191
 
    }
192
 
 
193
 
    /**
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.
198
 
     *
199
 
     * @return The maximum number of pooled connection allowed, or 0 for no maximum.
200
 
     */
201
 
    public int getMaxConnections()
202
 
    {
203
 
        return maxConnections;
204
 
    }
205
 
 
206
 
    /**
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.
211
 
     *
212
 
     * @param maxConnections The maximum number of pooled connection to allow, or
213
 
     *    0 for no maximum.
214
 
     *
215
 
     * @throws java.lang.IllegalStateException
216
 
     *     The Maximum Connections cannot be changed after the DataSource has been
217
 
     *     used.
218
 
     */
219
 
    public void setMaxConnections(int maxConnections)
220
 
    {
221
 
        if (initialized)
222
 
        {
223
 
            throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
224
 
        }
225
 
        this.maxConnections = maxConnections;
226
 
    }
227
 
 
228
 
    /**
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.
231
 
     */
232
 
    public String getDataSourceName()
233
 
    {
234
 
        return dataSourceName;
235
 
    }
236
 
 
237
 
    /**
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.
241
 
     *
242
 
     * @throws java.lang.IllegalStateException
243
 
     *     The Data Source Name cannot be changed after the DataSource has been
244
 
     *     used.
245
 
     * @throws java.lang.IllegalArgumentException
246
 
     *     Another PoolingDataSource with the same dataSourceName already
247
 
     *     exists.
248
 
     */
249
 
    public void setDataSourceName(String dataSourceName)
250
 
    {
251
 
        if (initialized)
252
 
        {
253
 
            throw new IllegalStateException("Cannot set Data Source properties after DataSource has been used");
254
 
        }
255
 
        if (this.dataSourceName != null && dataSourceName != null && dataSourceName.equals(this.dataSourceName))
256
 
        {
257
 
            return ;
258
 
        }
259
 
        synchronized (dataSources)
260
 
        {
261
 
            if (getDataSource(dataSourceName) != null)
262
 
            {
263
 
                throw new IllegalArgumentException("DataSource with name '" + dataSourceName + "' already exists!");
264
 
            }
265
 
            if (this.dataSourceName != null)
266
 
            {
267
 
                dataSources.remove(this.dataSourceName);
268
 
            }
269
 
            this.dataSourceName = dataSourceName;
270
 
            dataSources.put(dataSourceName, this);
271
 
        }
272
 
    }
273
 
 
274
 
    /**
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
279
 
     * DataSource.
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.
283
 
     */
284
 
    public void initialize() throws SQLException
285
 
    {
286
 
        synchronized (lock )
287
 
        {
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)
295
 
            {
296
 
                available.push(source.getPooledConnection());
297
 
            }
298
 
            initialized = true;
299
 
        }
300
 
    }
301
 
 
302
 
    protected boolean isInitialized() {
303
 
        return initialized;
304
 
    }
305
 
 
306
 
    /**
307
 
     * Creates the appropriate ConnectionPool to use for this DataSource.
308
 
     */
309
 
    protected PGConnectionPoolDataSource createConnectionPool() {
310
 
        return new PGConnectionPoolDataSource();
311
 
    }
312
 
 
313
 
    /**
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.
316
 
     *
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.
321
 
     */
322
 
    public Connection getConnection(String user, String password) throws SQLException
323
 
    {
324
 
        // If this is for the default user/password, use a pooled connection
325
 
        if (user == null ||
326
 
                (user.equals(getUser()) && ((password == null && getPassword() == null) || (password != null && password.equals(getPassword())))))
327
 
        {
328
 
            return getConnection();
329
 
        }
330
 
        // Otherwise, use a non-pooled connection
331
 
        if (!initialized)
332
 
        {
333
 
            initialize();
334
 
        }
335
 
        return super.getConnection(user, password);
336
 
    }
337
 
 
338
 
    /**
339
 
     * Gets a connection from the connection pool.
340
 
     *
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.
345
 
     */
346
 
    public Connection getConnection() throws SQLException
347
 
    {
348
 
        if (!initialized)
349
 
        {
350
 
            initialize();
351
 
        }
352
 
        return getPooledConnection();
353
 
    }
354
 
 
355
 
    /**
356
 
     * Closes this DataSource, and all the pooled connections, whether in use or not.
357
 
     */
358
 
    public void close()
359
 
    {
360
 
        synchronized (lock )
361
 
        {
362
 
            while (available.size() > 0)
363
 
            {
364
 
                PooledConnectionImpl pci = (PooledConnectionImpl)available.pop();
365
 
                try
366
 
                {
367
 
                    pci.close();
368
 
                }
369
 
                catch (SQLException e)
370
 
                {
371
 
                }
372
 
            }
373
 
            available = null;
374
 
            while (used.size() > 0)
375
 
            {
376
 
                PooledConnectionImpl pci = (PooledConnectionImpl)used.pop();
377
 
                pci.removeConnectionEventListener(connectionEventListener);
378
 
                try
379
 
                {
380
 
                    pci.close();
381
 
                }
382
 
                catch (SQLException e)
383
 
                {
384
 
                }
385
 
            }
386
 
            used = null;
387
 
        }
388
 
        removeStoredDataSource();
389
 
    }
390
 
 
391
 
    protected void removeStoredDataSource() {
392
 
        synchronized (dataSources)
393
 
        {
394
 
            dataSources.remove(dataSourceName);
395
 
        }
396
 
    }
397
 
 
398
 
    /**
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.
402
 
    */
403
 
    private Connection getPooledConnection() throws SQLException
404
 
    {
405
 
        PooledConnection pc = null;
406
 
        synchronized (lock )
407
 
        {
408
 
            if (available == null)
409
 
            {
410
 
                throw new PSQLException(GT.tr("DataSource has been closed."),
411
 
                                        PSQLState.CONNECTION_DOES_NOT_EXIST);
412
 
            }
413
 
            while (true)
414
 
            {
415
 
                if (available.size() > 0)
416
 
                {
417
 
                    pc = (PooledConnection)available.pop();
418
 
                    used.push(pc);
419
 
                    break;
420
 
                }
421
 
                if (maxConnections == 0 || used.size() < maxConnections)
422
 
                {
423
 
                    pc = source.getPooledConnection();
424
 
                    used.push(pc);
425
 
                    break;
426
 
                }
427
 
                else
428
 
                {
429
 
                    try
430
 
                    {
431
 
                        // Wake up every second at a minimum
432
 
                        lock.wait(1000L);
433
 
                    }
434
 
                    catch (InterruptedException e)
435
 
                    {
436
 
                    }
437
 
                }
438
 
            }
439
 
        }
440
 
        pc.addConnectionEventListener(connectionEventListener);
441
 
        return pc.getConnection();
442
 
    }
443
 
 
444
 
    /**
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
447
 
     * as unused.
448
 
     */
449
 
    private ConnectionEventListener connectionEventListener = new ConnectionEventListener()
450
 
            {
451
 
                public void connectionClosed(ConnectionEvent event)
452
 
                {
453
 
                    ((PooledConnection)event.getSource()).removeConnectionEventListener(this);
454
 
                    synchronized (lock )
455
 
                    {
456
 
                        if (available == null)
457
 
                        {
458
 
                            return ; // DataSource has been closed
459
 
                        }
460
 
                        boolean removed = used.remove(event.getSource());
461
 
                        if (removed)
462
 
                        {
463
 
                            available.push(event.getSource());
464
 
                            // There's now a new connection available
465
 
                            lock.notify();
466
 
                        }
467
 
                        else
468
 
                        {
469
 
                            // a connection error occured
470
 
                        }
471
 
                    }
472
 
                }
473
 
 
474
 
                /**
475
 
                 * This is only called for fatal errors, where the physical connection is
476
 
                 * useless afterward and should be removed from the pool.
477
 
                 */
478
 
                public void connectionErrorOccurred(ConnectionEvent event)
479
 
                {
480
 
                    ((PooledConnection) event.getSource()).removeConnectionEventListener(this);
481
 
                    synchronized (lock )
482
 
                    {
483
 
                        if (available == null)
484
 
                        {
485
 
                            return ; // DataSource has been closed
486
 
                        }
487
 
                        used.remove(event.getSource());
488
 
                        // We're now at least 1 connection under the max
489
 
                        lock.notify();
490
 
                    }
491
 
                }
492
 
            };
493
 
 
494
 
    /**
495
 
     * Adds custom properties for this DataSource to the properties defined in
496
 
     * the superclass.
497
 
     */
498
 
    public Reference getReference() throws NamingException
499
 
    {
500
 
        Reference ref = super.getReference();
501
 
        ref.add(new StringRefAddr("dataSourceName", dataSourceName));
502
 
        if (initialConnections > 0)
503
 
        {
504
 
            ref.add(new StringRefAddr("initialConnections", Integer.toString(initialConnections)));
505
 
        }
506
 
        if (maxConnections > 0)
507
 
        {
508
 
            ref.add(new StringRefAddr("maxConnections", Integer.toString(maxConnections)));
509
 
        }
510
 
        return ref;
511
 
    }
 
46
 
 
47
    protected void addDataSource(String dataSourceName)
 
48
    {
 
49
        dataSources.put(dataSourceName, this);
 
50
    }
 
51
 
512
52
}