KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lutris > appserver > server > sql > standard > SimpleConnectionAllocator


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: SimpleConnectionAllocator.java,v 1.1 2005/05/26 08:08:10 predrag Exp $
22  */

23 package com.lutris.appserver.server.sql.standard;
24
25 import java.lang.reflect.Constructor JavaDoc;
26 import java.sql.SQLException JavaDoc;
27 import java.util.Date JavaDoc;
28 import java.util.Hashtable JavaDoc;
29 import java.util.LinkedList JavaDoc;
30 import java.util.NoSuchElementException JavaDoc;
31
32 import org.enhydra.dods.CommonConstants;
33 import org.enhydra.dods.DODS;
34
35 import com.lutris.appserver.server.sql.AbstractDBConnectionFactory;
36 import com.lutris.appserver.server.sql.ConnectionAllocator;
37 import com.lutris.appserver.server.sql.DBConnection;
38 import com.lutris.appserver.server.sql.ExtendedConnectionAllocator;
39 import com.lutris.appserver.server.sql.ExtendedDBConnection;
40 import com.lutris.appserver.server.sql.LogicalDatabase;
41 import com.lutris.appserver.server.sql.SimpleDBConnectionFactory;
42 import com.lutris.logging.Logger;
43 import com.lutris.util.Config;
44 import com.lutris.util.ConfigException;
45 import com.lutris.util.KeywordValueException;
46
47 /**
48  * Manages a pool (set) of connections to a database. The pool is named
49  * as a logical database. By naming a pool, this allows connection resource
50  * control to be on a finer grain that a database and allows for easier
51  * migration to multiple databases. One or more pools can map to the same
52  * actual database. A connection considered part of the pool, even if its
53  * allocated to a thread. These objects are all publicly accessed via the
54  * Database Manager, not directly.
55  * <P>
56  * If an error occurs in a connection, it is dropped from the pool. The
57  * process using the connection has already received an error which aborts
58  * the work in progress. By dropping the connection, waiting threads are
59  * restarted. If something is wrong with the database (e.g. server is down),
60  * they will recieve errors and also be aborted. A generation number is used
61  * to close down all connections that were open when the error occured,
62  * allowing new connections to be allocated.
63  * <P>
64  * The configuration data is specified in the section:
65  * <B><CODE>DatabaseManager.DB.<I>dbName</I>.Connection</CODE></B>
66  * <P>
67  * <I>Configuration sub fields are:</I>
68  * <UL>
69  * <LI> <B><CODE>Url</CODE></B> -
70  * The JDBC URLof the database. Manditary.
71  * E.g. "jdbc:sequelink://dbHost:4000/[Informix];Database=dummy"
72  * <LI> <B><CODE>User</CODE></B> -
73  * The database users used to access the database. Manditary.
74  * <LI> <B><CODE>Password</CODE></B> -
75  * The database user's password. Manditary.
76  * <LI> <B><CODE>MaxPoolSize</CODE></B> -
77  * The maximum number of open connections to the database. Optional,
78  * if not specified, then it default to 0. A value of 0 means that
79  * connections are allocated indefinitely or until the database (JDBC)
80  * refuses any new connections.
81  * <LI> <B><CODE>Logging</CODE></B> -
82  * Specify true to enable SQL logging, false to disable it. Optional,
83  * false if not specified.
84  * <LI> <B><CODE>AllocationTimeout</CODE></B> -
85  * The Maximum amount of time that a thread will wait for
86  * a connection from the connection allocator before an exception is
87  * thrown. This will prevent possible dead locks. The time out is in
88  * milliseconds. If the time out is <= 0, the allocation of connections
89  * will wait indefinitely. Optional, if not specified, then it
90  * defaults to 1000 (ms).
91  * <LI> <B><CODE>QueryTimeout</CODE></B> - The amount of time (in seconds) that
92  * a query will block before throwing an exception. If <= 0 then the
93  * query will not block. Optional, if not specified, then the value
94  * defaults to 0. This is not implemented by all logical databases.
95  * <LI> <B><CODE>TransactionTimeout</CODE></B> - The amount of time (in seconds)
96  * that a transaction will block before throwing an exception. If
97  * <= 0 then the transaction will not block. Optional, if not specified,
98  * then the value defaults to 0. This is not implemented by all
99  * logical databases.
100  * <LI> <B><CODE>MaxPreparedStatements</CODE></B> - If specified, overrides
101  * the JDBC <CODE>Connection.getMetaData().getMaxStatements()</CODE>
102  * value. If less than zero, use the meta data value. Optional,
103  * default is to use the meta data.
104  * </UL>
105  * It would be nice to add a config parameter that would disable caching
106  * of PreparedStatements.
107  *
108  * @author Mark Diekhans
109  * @author Kyle Clark
110  * @author Paul Morgan
111  * @since LBS1.8
112  * @version $Revision: 1.1 $
113  */

114 // Excelsior, LLC. Memory leak patch begin
115
/**
116  * Brief description of the patch:
117  * Since each long-time used connection holds too much memory (about 15Mb after
118  * several hours of work) it was decided to close and recreate connections that
119  * were allocated more than the MaxConnectionUsages value set in the
120  * proper config file.
121  * We also changed the stack-like structure of the connection pool to
122  * queue-like. This guarantees that no connections become stagnant.
123  * 4.07.2003
124  */

125 // Excelsior, LLC. Memory leak patch end
126
public class SimpleConnectionAllocator implements ExtendedConnectionAllocator {
127
128     /**
129      * Reference to the logical database for easy access to the
130      * connection pool.
131      */

132     protected LogicalDatabase logicalDatabase = null;
133
134     /**
135      * JDBC URL of database.
136      */

137     protected String JavaDoc url;
138
139     /**
140      * SQL user name.
141      */

142     protected String JavaDoc user;
143
144     /**
145      * SQL password..
146      */

147     protected String JavaDoc password;
148
149     
150     /**
151      * database "ShutDown" string
152      */

153     protected String JavaDoc shutDownStr;
154     
155     /**
156      * Maximum number of connections in the pool.
157      * If this value is <= zero, then create as many
158      * connections as possible.
159      */

160     private int maxPoolSize;
161
162     /**
163      * Current size of the pool; includes allocated connections.
164      */

165     private int currentPoolSize;
166     
167     /**
168      * Maximum time that connection will wait in connection pool before they will be closed;
169      */

170     private long connectionIdileTimeout = -1;
171
172     /**
173      * Maximum size the pool ever got to, regardless of generation.
174      */

175     private int biggestPoolSize;
176
177     /**
178      * Date at which the biggest pool size occured.
179      */

180     private Date JavaDoc biggestPoolDate;
181
182     /**
183      * Number of queries or transactions on this logical database.
184      */

185     protected long numRequests;
186
187     /**
188      * The actual pool of DBConnection objects.
189      */

190     // Excelsior, LLC. Memory leak patch begin
191
/* original code
192      private Stack pool;
193      */

194     private LinkedList JavaDoc pool;
195
196     /**
197      * This hashtable holds the number of times a particular DBConnection
198      * was allocated. The key is the DBConnection object and the value is
199      * an Integer object.
200      * If the connectionUsageCounter is null, then the number of connection
201      * usages is unlimited.
202      * Maybe, it would be better to have the respective instance field in
203      * the DBConnection but it worsens locality of changes made...
204      */

205     private Hashtable JavaDoc connectionUsageCounter;
206
207     /** JP Patch
208      * Maximum number of DBConnections that will be waiting in the pool
209      */

210      protected int maxWaitingConnections;
211
212
213     /**
214      * Maximum number of times a particular connection is allowed to
215      * be allocated before it is closed and replaced with a new one.
216      * If this value is <= zero, then the number of allocations is unlimited.
217      */

218     private int maxConnectionUsages = -1;
219
220     // Excelsior, LLC. Memory leak patch end
221
/**
222      * Indicates if logging is enabled.
223      */

224     protected boolean sqlLogging;
225
226     /**
227      * Maximum amount of time in milliseconds to wait for a connection.
228      */

229     private int timeOut;
230
231     /**
232      * Maximum amount of time in seconds to block on a query. The
233      * DBQuery object will retrieve this value from the connection.
234      */

235     protected int queryTimeOut;
236
237     /**
238      * Maximum amount of time in seconds to block on a transaction. The
239      * DBTransaction object will retrieve this value from the connection.
240      */

241     protected int transactionTimeOut;
242
243     /**
244      * Maximum number of prepared statements to use; if less-than zero,
245      * then JDBC is queried for this value.
246      */

247     protected int maxPreparedStatements;
248
249     /**
250      * Generation number. When an SQL error occurs, all objects of the
251      * same generation or earlier are dropped.
252      */

253     protected int generation = 1;
254
255
256     /**
257      * Create a new connection in the pool.
258      *
259      * @exception java.sql.SQLException If a SQL error occures.
260      */

261     protected DBConnection createConnection()
262         throws java.sql.SQLException JavaDoc {
263             DBConnection dbConnection = dbConnectionFactory.createConnection((ConnectionAllocator)this, url, user,
264                     password, maxPreparedStatements, sqlLogging, generation);
265             return dbConnection;
266     }
267
268     private AbstractDBConnectionFactory dbConnectionFactory = null;
269     private String JavaDoc dbConnectionFactoryName = null;
270     private AbstractDBConnectionFactory createDBConnectionFactory(String JavaDoc factoryName){
271         Class JavaDoc connectionFactoryClass = null;
272         Constructor JavaDoc connectionFactoryConstructor = null;
273         Class JavaDoc[] methodTypes={};
274         Object JavaDoc[] methodArgs={};
275         AbstractDBConnectionFactory factory = null;
276         if (factoryName!=null){
277             try{
278                 connectionFactoryClass = Class.forName(factoryName);
279                 factory = (AbstractDBConnectionFactory)connectionFactoryClass.newInstance();
280             }catch(Exception JavaDoc e){
281                 DODS.getLogChannel().write(Logger.INFO,"Faild to make Connection Factory :"+factoryName+" creating StandardDBConnectionFactory insted");
282                 factory = null;
283             }
284         }
285         if (factoryName==null || factory == null){
286             factory = new SimpleDBConnectionFactory();
287         }
288         return factory;
289
290     }
291     
292     /**
293      * Initialize the connection allocator object. Connections are
294      * opened on demand and stored in a pool.
295      *
296      * @param logicalDatabase LogicalDatabase.
297      * @param conConfig LogicalDatabase Config object
298      * @exception ConfigException if bad configuration information is
299      * given in the config file.
300      */

301     public SimpleConnectionAllocator(LogicalDatabase logicalDatabase,
302             Config conConfig)
303         throws ConfigException {
304         this.logicalDatabase = logicalDatabase;
305         try {
306             url = conConfig.getString("Url");
307             user = conConfig.getString("User");
308             password = conConfig.getString("Password");
309             timeOut = conConfig.getInt("AllocationTimeout", 1000);
310             maxPoolSize = conConfig.getInt("MaxPoolSize", 0);
311             sqlLogging = conConfig.getBoolean("Logging", false);
312             queryTimeOut = conConfig.getInt("QueryTimeout", 0);
313             transactionTimeOut = conConfig.getInt("TransactionTimeout", 0);
314             connectionIdileTimeout = conConfig.getLong("ConnectionIdleTimeout", -1);
315       
316             shutDownStr = conConfig.getString("ShutDownString",null);
317     
318             maxPreparedStatements = conConfig.getInt("MaxPreparedStatements", -1);
319             // Excelsior, LLC. Memory leak patch begin
320
maxConnectionUsages = conConfig.getInt("MaxConnectionUsages", -1);
321             // Excelsior, LLC. Memory leak patch end
322
maxWaitingConnections = conConfig.getInt("MaxWaitingConnections", Integer.MAX_VALUE);
323             
324             dbConnectionFactoryName = conConfig.getString(CommonConstants.CONNECTION_FACTORY,null);
325             
326             dbConnectionFactory = createDBConnectionFactory(dbConnectionFactoryName);
327             
328             
329         } catch (KeywordValueException except) {
330             throw new ConfigException("Bad DatabaseManager.DB."
331                     + logicalDatabase.getName()
332                     + ".Connection section defined in config file.");
333         }
334         currentPoolSize = 0;
335         // Excelsior, LLC. Memory leak patch begin
336
/* original code
337          pool = new Stack ();
338          */

339         pool = new LinkedList JavaDoc();
340         if (maxConnectionUsages > 0) {
341             connectionUsageCounter = new Hashtable JavaDoc();
342         }
343         // Excelsior, LLC. Memory leak patch end
344
biggestPoolSize = 0;
345         biggestPoolDate = new Date JavaDoc();
346         numRequests = 0;
347     }
348
349     /**
350      * Allocate a connection to a thread. If none are available, grow the
351      * pool. If the pool is alredy its maximum size, then the thread waits.
352      *
353      * @return The allocated connection object.
354      * @exception SQLException
355      * If a SQL error occures.
356      */

357     public synchronized DBConnection allocate()
358         throws SQLException JavaDoc {
359         //
360
// It isn't always possible to determine the maximum
361
// number of connections allowed to the database because
362
// of JDBC driver differences. We assume connections are
363
// available until we fail to allocate one or we reach
364
// the maximum configured.
365
//
366
boolean createNewConn = true;
367         // Excelsior, LLC. Memory leak patch begin
368
/* original code
369          while (pool.empty()) {
370          */

371         DBConnection conn = null;
372
373         while (conn == null) {
374             while (pool.isEmpty()) {
375                 // Excelsior, LLC. Memory leak patch end
376
if (createNewConn
377                         && ((currentPoolSize < maxPoolSize)
378                                 || (maxPoolSize <= 0))) {
379                     try {
380                         // Excelsior, LLC. Memory leak patch begin
381
/* original code
382                          pool.push(createConnection());
383                          */

384                         DBConnection newConnection = createConnection();
385
386                         if (connectionUsageCounter != null) {
387                             connectionUsageCounter.put(newConnection,
388                                     new Integer JavaDoc(maxConnectionUsages));
389                         }
390                         pool.addLast(newConnection);
391                         // Excelsior, LLC. Memory leak patch end
392
currentPoolSize++;
393                         if (currentPoolSize > biggestPoolSize) {
394                             biggestPoolSize = currentPoolSize;
395                             biggestPoolDate = new Date JavaDoc();
396                         }
397                     } catch (SQLException JavaDoc e) {
398                         if (currentPoolSize > 0) {
399                             DODS.getLogChannel().write(Logger.INFO,
400                                     "ConnectionAllocator: "
401                                     + "failed to allocate a new connection due to"
402                                     + e.toString() + "Error code: "
403                                     + e.getErrorCode() + "\n" + "SQLState: "
404                                     + e.getSQLState() + "\n"
405                                     + "\nCurrent pool size is: "
406                                     + currentPoolSize
407                                     + "\nMaximum configured pool size is now "
408                                     + maxPoolSize + "\nContinuing...\n");
409                             createNewConn = false;
410                         } else {
411                             DODS.getLogChannel().write(Logger.EMERGENCY,
412                                     "ConnectionAllocator: "
413                                     + "failed to allocate a new connection"
414                                     + "\nThe connection pool is empty!\n");
415                             throw e;
416                         }
417                     }
418                 } else {
419                     try {
420                         if (timeOut > 0) {
421                             wait(timeOut);
422                             // Excelsior, LLC. Memory leak patch begin
423
/* original code
424                              if (pool.empty()) {
425                              */

426                             if (pool.isEmpty()) {
427                                 // Excelsior, LLC. Memory leak patch begin
428
DODS.getLogChannel().write(Logger.EMERGENCY,
429                                         "ConnectionAllocator: "
430                                         + "allocation of a new connection timed out."
431                                         + "Possible dead lock avoided.");
432                                 String JavaDoc msg = "Connections are currently unavailable.\n"
433                                         + "Possible dead lock avoided.";
434
435                                 throw new SQLException JavaDoc(msg);
436                             }
437                         } else {
438                             wait();
439                         }
440                     } catch (InterruptedException JavaDoc intEx) {}
441                 }
442             }
443             //
444
// A connection is available.
445
//
446
// Excelsior, LLC. Memory leak patch begin
447
/* original code
448              conn = (DBConnection)pool.pop();
449              */

450             // check
451
try {
452                 conn = (DBConnection) pool.removeFirst();
453             } catch (NoSuchElementException JavaDoc e) {}
454             if (connectionUsageCounter != null) {
455                 Integer JavaDoc connUsages = (Integer JavaDoc) connectionUsageCounter.get(conn);
456
457                 if (connUsages != null && connUsages.intValue() > 0) {
458                     // decrease connection usage counter
459
connectionUsageCounter.put(conn,
460                             new Integer JavaDoc(connUsages.intValue() - 1));
461                 } else { // the usages limit is over, close the connection
462
conn.close();
463                     currentPoolSize--;
464                     connectionUsageCounter.remove(conn);
465                     DODS.getLogChannel().write(Logger.DEBUG,
466                             "ConnectionAllocator: connection closed due to usage counter. currentPoolSize="
467                             + currentPoolSize + "\n");
468                     conn = null;
469                 }
470             }
471             if ((conn!=null) && (conn.getConnection().isClosed())){
472                 conn.close();
473                 currentPoolSize--;
474                 if (connectionUsageCounter!=null && connectionUsageCounter.contains(conn)){
475                     connectionUsageCounter.remove(conn);
476                 }
477                 DODS.getLogChannel().write(Logger.DEBUG,
478                         "ConnectionAllocator: Inactiv connection closed due allocate() operation. Geting new one. currentPoolSize="
479                         + currentPoolSize + "\n");
480                 conn = null;
481             }
482             if((conn != null)&&(connectionIdileTimeout>0)){
483                 if((System.currentTimeMillis()
484                         -((ExtendedDBConnection)conn).getConnectionEnterPoolTime())
485                                 >connectionIdileTimeout){
486                         conn.close();
487                         currentPoolSize--;
488                         DODS.getLogChannel().write(Logger.DEBUG,
489                                 "ConnectionAllocator: Connection closed due allocate() operation - long connection idile time. Geting new one. currentPoolSize="
490                                 + currentPoolSize + "\n");
491                         conn = null;
492                 }
493             }
494         }
495         // Excelsior, LLC. Memory leak patch end
496
// DBConnection conn = (DBConnection)pool.pop();
497
conn.allocate();
498         return conn;
499     }
500
501     /**
502      * Return a connection to the pool. If it is of an old generation,
503      * close and drop.
504      *
505      * @param dbConnection The connection object to return.
506      */

507     public synchronized void release(DBConnection dbConnection) {
508         try {
509             if ( (dbConnection.getGeneration() < generation)
510                 || (dbConnection.isMarkedForDrop())
511                 || (dbConnection.getConnection().isClosed())
512                 || (pool.size() >=maxWaitingConnections) ) {
513                     dbConnection.close();
514                     dbConnection = null;
515                     currentPoolSize--;
516             }
517             else {
518                 // Excelsior, LLC. Memory leak patch begin
519
/* original code
520                  pool.push (dbConnection);
521                  */

522                  if (connectionIdileTimeout>0){
523                     ((ExtendedDBConnection)dbConnection).setConnectionEnterPoolTime(System.currentTimeMillis());
524                  }
525                 pool.addLast(dbConnection);
526                 // Excelsior, LLC. Memory leak patch end
527
}
528         }
529         catch (SQLException JavaDoc ex) {
530             DODS.getLogChannel().write(Logger.DEBUG,"Eror relasing connection");
531         }
532         notify();
533     }
534
535     /**
536      * Called when a connection in this pool has an SQL error.
537      * All unallocated connections in the pool are dropped if
538      * they have a generation number less
539      * than or equal to the error connection.
540      * If the current generation number
541      * is the same as this generation number, increment it.
542      * This way so that all outstanding connections
543      * (including the one passed in) of the same generation
544      * are dropped when returned.
545      *
546      * @param dbConnection The connection object to drop.
547      * The connection should be
548      * returned to the pool after this function returns.
549      */

550     public synchronized void drop(DBConnection dbConnection) {
551         if (generation <= dbConnection.getGeneration()) {
552             generation++; // new generation
553
}
554         // Delete all entries of the last generation.
555
// Excelsior, LLC. Memory leak patch begin
556
/* original code
557          Stack holdPool = new Stack ();
558          while (!pool.empty ()) {
559          DBConnection connect = (DBConnection) pool.pop ();
560          if (connect.getGeneration () < generation) {
561          connect.close ();
562          currentPoolSize--;
563          } else {
564          holdPool.push (connect);
565          }
566          }
567          // Put all current generation entries back in the pool.
568          while (!holdPool.empty ()) {
569          pool.push (holdPool.pop ());
570          }
571          */

572         LinkedList JavaDoc newPool = new LinkedList JavaDoc();
573
574         try {
575             while (!pool.isEmpty()) {
576                 DBConnection connect = (DBConnection) pool.removeFirst();
577
578                 if (connect.getGeneration() < generation) {
579                     connect.close();
580                     currentPoolSize--;
581                 } else {
582                     newPool.addLast(connect);
583                 }
584             }
585         } catch (NoSuchElementException JavaDoc e) {}
586         // replace the original pool with the one that contains
587
// only current generation entries
588
pool = newPool;
589         // Excelsior, LLC. Memory leak patch end
590
notify();
591     }
592
593     /**
594      * Called when the database manager is shutting down:
595      * Close all connections immediately.
596      */

597     public synchronized void dropAllNow() {
598         // Excelsior, LLC. Memory leak patch begin
599
/* original code
600          while (!pool.empty ()) {
601          DBConnection connect = (DBConnection) pool.pop ();
602          connect.close ();
603          currentPoolSize--;
604          }
605          */

606         
607         if (shutDownStr!=null) {
608             try {
609                 DBConnection tmpConn = allocate();
610                 tmpConn.execute(shutDownStr);
611                 tmpConn.release();
612             } catch (SQLException JavaDoc e1) {}
613         }
614         try {
615             while (!pool.isEmpty()) {
616                 DBConnection connect = (DBConnection) pool.removeFirst();
617
618                 connect.close();
619                 currentPoolSize--;
620             }
621         } catch (NoSuchElementException JavaDoc e) {}
622         // Excelsior, LLC. Memory leak patch begin
623
}
624
625     /**
626      * Return the number of currently active connections.
627      *
628      * @return The number of connections.
629      */

630     public int getActiveCount() {
631         return currentPoolSize;
632     }
633
634     /**
635      * Return the maximum number of connections active at one time.
636      *
637      * @return The number of connections.
638      */

639     public int getMaxCount() {
640         return biggestPoolSize;
641     }
642
643     /**
644      * Return the time when the maximum connection count occured.
645      *
646      * @return The <CODE>Date</CODE> when the maximum connection
647      * count occured.
648      */

649     public Date JavaDoc getMaxCountDate() {
650         return biggestPoolDate;
651     }
652
653     /**
654      * Reset the maximum connection count and date.
655      */

656     public void resetMaxCount() {
657         biggestPoolSize = currentPoolSize;
658         biggestPoolDate = new Date JavaDoc();
659     }
660
661     /**
662      * Return the number of database requests.
663      *
664      * @return The number of database requests (queries or transactions).
665      */

666     public long getRequestCount() {
667         return numRequests;
668     }
669
670     /**
671      * Finalizer.
672      * If any connections allocated by this object have not been closed,
673      * this method ensures that garbage collection does so.
674      */

675     protected void finalize() {
676         dropAllNow();
677     }
678
679     /**
680      * @return database name of current connection
681      *
682      */

683     public String JavaDoc getDatabaseName() {
684         return logicalDatabase.getName();
685     }
686     
687     public void IncrementRequesteCount(){
688         numRequests++;
689     }
690 }
691
Popular Tags