Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/lutris/appserver/server/sql/standard/StandardConnectionAllocator.java


1   /*
2    * Enhydra Java Application Server Project
3    * 
4    * The contents of this file are subject to the Enhydra Public License
5    * Version 1.1 (the "License"); you may not use this file except in
6    * compliance with the License. You may obtain a copy of the License on
7    * the Enhydra web site ( http://www.enhydra.org/ ).
8    * 
9    * Software distributed under the License is distributed on an "AS IS"
10   * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 
11   * the License for the specific terms governing rights and limitations
12   * under the License.
13   * 
14   * The Initial Developer of the Enhydra Application Server is Lutris
15   * Technologies, Inc. The Enhydra Application Server and portions created
16   * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
17   * All Rights Reserved.
18   * 
19   * Contributor(s):
20   * 
21   * $Id: StandardConnectionAllocator.java,v 1.10.12.1 2000/10/19 17:59:06 jasona Exp $
22   */
23  
24  
25  
26  
27  
28  package  com.lutris.appserver.server.sql.standard;
29  
30  import com.lutris.appserver.server.LBS;
31  import com.lutris.appserver.server.sql.*;
32  import com.lutris.logging.*;
33  import com.lutris.util.*;
34  import java.util.Date;  // Must appear before java.sql.Date
35  import java.util.Stack;
36  import java.sql.*;
37  
38  
39  /**
40   * Manages a pool (set) of connections to a database.  The pool is named
41   * as a logical database.  By naming a pool, this allows connection  resource
42   * control to be on a finer grain that a database and allows for easier 
43   * migration to multiple databases.  One or more pools can map to the same
44   * actual database.  A connection considered part of the pool, even if its
45   * allocated to a thread.  These objects are all publicly accessed via the
46   * Database Manager, not directly.
47   * <P>
48   * If an error occurs in a connection, it is dropped from the pool.  The
49   * process using the connection has already received an error which aborts
50   * the work in progress.  By dropping the connection, waiting threads are
51   * restarted.  If something is wrong with the database (e.g. server is down),
52   * they will recieve errors and also be aborted.  A generation number is used
53   * to close down all connections that were open when the error occured,
54   * allowing new connections to be allocated.
55   * <P>
56   * The configuration data is specified in the section:
57   * <B><CODE>DatabaseManager.DB.<I>dbName</I>.Connection</CODE></B>
58   * <P>
59   * <I>Configuration sub fields are:</I>
60   * <UL>
61   * <LI> <B><CODE>Url</CODE></B> -
62   *      The JDBC URLof the database. Manditary.
63   *      E.g. "jdbc:sequelink://dbHost:4000/[Informix];Database=dummy"
64   * <LI> <B><CODE>User</CODE></B> -
65   *      The database users used to access the database. Manditary.
66   * <LI> <B><CODE>Password</CODE></B> -
67   *      The database user's password. Manditary.
68   * <LI> <B><CODE>MaxPoolSize</CODE></B> -
69   *      The maximum number of open connections to the database. Optional,
70   *      if not specified, then it default to 0. A value of 0 means that
71   *      connections are allocated indefinitely or until the database (JDBC)
72   *      refuses any new connections.
73   * <LI> <B><CODE>Logging</CODE></B> -
74   *      Specify true to enable SQL logging, false to disable it. Optional,
75   *      false if not specified.
76   * <LI> <B><CODE>AllocationTimeout</CODE></B> -
77   *      The Maximum amount of time that a thread will wait for
78   *      a connection from the connection allocator before an exception is
79   *      thrown. This will prevent possible dead locks. The time out is in
80   *      milliseconds. If the time out is <= 0, the allocation of connections
81   *      will wait indefinitely. Optional, if not specified, then it
82   *      defaults to 1000 (ms).
83   * <LI> <B><CODE>QueryTimeout</CODE></B> - The amount of time (in seconds) that
84   *  a query will block before throwing an exception. If <= 0 then the
85   *  query will not block. Optional, if not specified, then the value
86   *  defaults to 0. This is not implemented by all logical databases.
87   * <LI> <B><CODE>TransactionTimeout</CODE></B> - The amount of time (in seconds)
88   *  that a transaction will block before throwing an exception. If
89   *  <= 0 then the transaction will not block. Optional, if not specified,
90   *  then the value defaults to 0. This is not implemented by all
91   *  logical databases.
92   * <LI> <B><CODE>MaxPreparedStatements</CODE></B> - If specified, overrides
93   *      the JDBC <CODE>Connection.getMetaData().getMaxStatements()</CODE>
94   *      value.  If less than zero, use the meta data value.  Optional,
95   *      default is to use the meta data.
96   * </UL>
97   * It would be nice to add a config parameter that would disable caching
98   * of PreparedStatements.
99   *
100  * @author  Mark Diekhans
101  * @author  Kyle Clark
102  * @author  Paul Morgan
103  * @since  LBS1.8
104  * @version  $Revision: 1.10.12.1 $
105  */
106 public class StandardConnectionAllocator implements ConnectionAllocator {
107 
108     /**
109      * Reference to the logical database for easy access to the
110      * connection pool.
111      */
112     protected LogicalDatabase logicalDatabase = null;
113 
114     /**
115      * JDBC URL of database.
116      */
117     protected String url;
118     
119     /**
120      * SQL user name.
121      */
122     protected String user;
123       
124     /**
125      * SQL password..
126      */
127     protected String password;
128 
129     /**
130      * Maximum number of connections in the pool.
131      * If this value is <= zero, then create as many
132      * connections as possible.
133      */
134     private int maxPoolSize;
135 
136     /**
137      * Current size of the pool; includes allocated connections.
138      */
139     private int currentPoolSize;
140 
141     /**
142      * Maximum size the pool ever got to, regardless of generation.
143      */
144     private int biggestPoolSize;
145 
146     /**
147      * Date at which the biggest pool size occured.
148      */
149     private Date biggestPoolDate;
150 
151     /**
152      * Number of queries or transactions on this logical database.
153      */
154     protected long numRequests;
155  
156     /**
157      * The actual pool of DBConnection objects.
158      */
159     private Stack pool;
160 
161     /**
162      * Indicates if logging is enabled.
163      */
164     protected boolean sqlLogging;
165 
166     /**
167      * Maximum amount of time in milliseconds to wait for a connection.
168      */
169     private int timeOut;
170 
171     /**
172      * Maximum amount of time in seconds to block on a query. The
173      * DBQuery object will retrieve this value from the connection.
174      */
175     protected int queryTimeOut;
176 
177     /**
178      * Maximum amount of time in seconds to block on a transaction. The
179      * DBTransaction object will retrieve this value from the connection.
180      */
181     protected int transactionTimeOut;
182 
183     /**
184      * Maximum number of prepared statements to use; if less-than zero,
185      * then JDBC is queried for this value.
186      */
187     protected int maxPreparedStatements;
188 
189     /**
190      * Generation number.  When an SQL error occurs, all objects of the
191      * same generation or earlier are dropped.
192      */
193     protected int generation = 1;
194 
195     /**
196      * The log channel.
197      */
198     private LogChannel log = LBS.getLogChannel();
199 
200 
201     /**
202      * Create a new connection in the pool.
203      *
204      * @exception java.sql.SQLException If a SQL error occures.
205      */
206     protected DBConnection createConnection ()
207         throws java.sql.SQLException {
208         DBConnection dbConnection =
209             new StandardDBConnection (this, url, user, password,
210                                       maxPreparedStatements,
211               sqlLogging, generation);
212         return dbConnection;
213     }
214 
215 
216     /**
217      * Initialize the connection allocator object.  Connections are
218      * opened on demand and stored in a pool.
219      *
220      * @param url JDBC URL of database.
221      * @param user SQL user name.
222      * @param password SQL password.
223      * @param maxPoolSize Maximum number of connections.
224      * @param sqlLogging Specifying <CODE>true</CODE> enables SQL logging.
225      * @param timeOut Set the maximum amount of time in milliseconds
226      *  to wait for a new connection.
227      * @exception ConfigException if bad configuration information is
228      *  given in the config file.
229      */
230     protected StandardConnectionAllocator (LogicalDatabase logicalDatabase,
231                                            Config conConfig)
232         throws ConfigException
233     {
234   this.logicalDatabase = logicalDatabase;
235 
236   try {
237       url = conConfig.getString ("Url");
238       user = conConfig.getString ("User");
239       password = conConfig.getString ("Password");
240       timeOut = conConfig.getInt("AllocationTimeout", 1000);
241       maxPoolSize = conConfig.getInt ("MaxPoolSize", 0);
242       sqlLogging = conConfig.getBoolean ("Logging", false);
243       queryTimeOut = conConfig.getInt("QueryTimeout", 0);
244       transactionTimeOut = conConfig.getInt("TransactionTimeout", 0);
245       maxPreparedStatements = conConfig.getInt("MaxPreparedStatements", -1);
246   } catch (KeywordValueException except) {
247       throw new ConfigException ("Bad DatabaseManager.DB." + logicalDatabase.getName() + ".Connection section defined in config file.");
248   }
249 
250         currentPoolSize = 0;
251         pool = new Stack ();
252 
253   biggestPoolSize = 0;
254   biggestPoolDate = new Date();
255   numRequests = 0;
256     }
257 
258 
259     /**
260      * Allocate a connection to a thread.  If none are available, grow the
261      * pool.  If the pool is alredy its maximum size, then the thread waits.
262      *
263      * @return The allocated connection object.
264      * @exception SQLException
265      *   If a SQL error occures.
266      */
267     public synchronized DBConnection allocate ()
268         throws SQLException
269     {
270         //
271         // It isn't always possible to determine the maximum
272         // number of connections allowed to the database because
273   // of JDBC driver differences. We assume connections are
274   // available until we fail to allocate one or we reach
275   // the maximum configured.
276         //
277         boolean createNewConn = true;
278         while (pool.empty()) {
279             if (createNewConn &&
280                 ((currentPoolSize < maxPoolSize) || (maxPoolSize <= 0))) {
281                 try {
282                     pool.push(createConnection());
283                     currentPoolSize++;
284         if (currentPoolSize > biggestPoolSize) {
285       biggestPoolSize = currentPoolSize;
286       biggestPoolDate = new Date();
287         }
288                 } catch (SQLException e) {
289                     if (currentPoolSize > 0) {
290                         log.write(Logger.NOTICE,
291                              "ConnectionAllocator: " +
292                              "failed to allocate a new connection due to" + 
293                              e.toString() +
294                              "Error code: " + e.getErrorCode() + "\n" +
295                              "SQLState: " + e.getSQLState() + "\n" +
296                              "\nCurrent pool size is: " + currentPoolSize +
297                              "\nMaximum configured pool size is now " +
298                              maxPoolSize + "\nContinuing...\n");
299                         createNewConn = false;
300                     } else {
301                         log.write(Logger.ALERT,
302                              "ConnectionAllocator: " +
303                              "failed to allocate a new connection" + 
304                              "\nThe connection pool is empty!\n");
305                         throw e;
306                     }
307                 }
308             } else {
309                 try {
310                     if (timeOut > 0) {
311                         wait(timeOut);
312                         if (pool.empty()) {
313                             log.write(Logger.ALERT,
314                                  "ConnectionAllocator: " +
315                                  "allocation of a new connection timed out." +
316                                  "Possible dead lock avoided.");
317                             String msg =
318                                 "Connections are currently unavailable.\n" +
319                                 "Possible dead lock avoided.";
320                             throw new SQLException(msg);
321                         }
322                     }
323                     else {
324                         wait();
325                     }
326                 }
327                 catch (InterruptedException intEx) {}
328                 }
329             }
330 
331         //
332         // A connection is available.
333         //
334         DBConnection conn = (DBConnection)pool.pop();
335         conn.allocate();
336         return conn;
337     }
338 
339 
340     /**
341      * Return a connection to the pool.  If it is of an old generation,
342      * close and drop.
343      *
344      * @param dbConnection The connection object to return.
345      */
346     public synchronized void release (DBConnection dbConnection) {
347 
348         if (dbConnection.getGeneration () < generation) {
349             dbConnection.close ();
350             currentPoolSize--;
351         } else {
352             pool.push (dbConnection);
353         }
354         notify ();
355     }
356 
357 
358     /**
359      * Called when a connection in this pool has an SQL error.
360      * All unallocated connections in the pool are dropped if
361      * they have a generation number less
362      * than or equal to the error connection.
363      * If the current generation number
364      * is the same as this generation number, increment it.
365      * This way so that all outstanding connections
366      * (including the one passed in) of the same generation
367      * are dropped when returned.
368      *
369      * @param dbConnection The connection object to drop.
370      *   The connection should be
371      *   returned to the pool after this function returns.
372      */
373     public synchronized void drop (DBConnection dbConnection) {
374 
375         if (generation <= dbConnection.getGeneration ()) {
376             generation++; // new generation
377         }
378         
379         // Delete all entries of the last generation.
380         Stack holdPool = new Stack ();
381         while (!pool.empty ()) {
382             DBConnection connect = (DBConnection) pool.pop ();
383             if (connect.getGeneration () < generation) {
384                 connect.close ();
385                 currentPoolSize--;
386             } else {
387                 holdPool.push (connect);
388             }
389         }
390 
391         // Put all current generation entries back in the pool.
392         while (!holdPool.empty ()) {
393             pool.push (holdPool.pop ());
394         }
395         notify ();
396     }
397     
398 
399     /**
400      * Called when the database manager is shutting down:
401      * Close all connections immediately.
402      */
403     public synchronized void dropAllNow () {
404 
405         while (!pool.empty ()) {
406             DBConnection connect = (DBConnection) pool.pop ();
407             connect.close ();
408             currentPoolSize--;
409   }
410     }
411 
412 
413     /**
414      * Return the number of currently active connections.
415      *
416      * @return The number of connections.
417      */
418     public int getActiveCount () {
419   return currentPoolSize;
420     }
421 
422 
423     /**
424      * Return the maximum number of connections active at one time.
425      *
426      * @return The number of connections.
427      */
428     public int getMaxCount () {
429   return biggestPoolSize;
430     }
431 
432 
433     /**
434      * Return the time when the maximum connection count occured.
435      *
436      * @return The <CODE>Date</CODE> when the maximum connection
437      *  count occured.
438      */
439     public Date getMaxCountDate () {
440   return biggestPoolDate;
441     }   
442 
443 
444     /**
445      * Reset the maximum connection count and date.
446      */
447     public void resetMaxCount () {
448   biggestPoolSize = currentPoolSize;
449   biggestPoolDate = new Date();
450     }
451 
452 
453     /**
454      * Return the number of database requests.
455      *
456      * @return The number of database requests (queries or transactions).
457      */
458     public long getRequestCount () {
459   return numRequests;
460     }
461     
462 
463     /**
464      * Finalizer.
465      * If any connections allocated by this object have not been closed,
466      * this method ensures that garbage collection does so.
467      */
468     protected void finalize() {
469   dropAllNow();
470     }
471 }