KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > teamkonzept > db > TKDBConnectionManager


1 /*
2  * $Header: /cvsroot/webman-cms/source/webman/com/teamkonzept/db/TKDBConnectionManager.java,v 1.28 2002/01/28 14:41:54 alex Exp $
3  *
4  */

5 package com.teamkonzept.db;
6
7 import java.sql.*;
8 import java.util.Enumeration JavaDoc;
9 import java.util.Stack JavaDoc;
10 import java.util.Properties JavaDoc;
11
12 import com.teamkonzept.lib.*;
13 import org.apache.log4j.Category;
14
15 /**
16  * Manages database connections for an application context.
17  * Each application context has its TKDBConnectionManager, which
18  * provides its threads with database connections.
19  * <br>
20  * The manager contains all data needed to maintain connections to a database like
21  * <ul><li>database name, <li>driver, <li>username, <li>password in {@link TKConnectData TKConnectData}</ul>
22  * <br>
23  * Furthermore the manager contains information about data types supported by the database in {@link TKSQLTypeConverter TKSQLTypeConverter},
24  * <br>
25  * a hash for the used query classes, a hash for the connections of threads,
26  * <br>
27  * a stack with currently unused connections,
28  * <br>
29  * and a mechanism for limiting simultaneously opened connections {@link TKLimiter TKLimiter}.
30  *
31  * <br>
32  *
33  * @author
34  * @version
35  */

36 public class TKDBConnectionManager
37 {
38     private static final Category CAT = Category.getInstance(TKDBConnectionManager.class);
39
40     /**
41      * currently unused
42      */

43     //private String queryClassBasePackage = "";
44

45     /**
46      * currently unused
47      */

48     private int maxConnections = 0;
49
50     /**
51      * ID of a database.
52      * <br>(e.g. "oracle", "sybase", "mssql") as a string.
53      */

54     private String JavaDoc dbIdentifier = null;
55
56     /**
57      * Container of data needed for database connections, containing database name, user and password.
58      */

59     private TKConnectData jdbcConnectData;
60
61     /**
62      * A hashtable, which maps a thread to its currently used database connection
63      * <br><br>
64      * key(Thread aThread) =&gt; value(TKDBConnection aConnection)
65      */

66     private TKHashtable tkConnectionHash = new TKHashtable();
67
68     /**
69      * A hashtable, which maps a query's class name to the query class
70      * <br><br>
71      * key(String aClassName) =&gt; value(Class aQueryClass)
72      */

73     private TKHashtable queryClasses = new TKHashtable();
74
75     /**
76      * A stack, which stores currently unused, released, but opened connections.
77      */

78     private Stack JavaDoc tkConnectionStack = new Stack JavaDoc();
79
80     /**
81      * TKLimiter controls the maximum of simultaneously opened connections.
82      */

83     private TKLimiter limiter = new TKLimiter();
84
85     /**
86      * A Vector, which stores after <code>_resetConnections</code> is called,
87      * all connections from <code>tkConnectionsHash</code>.
88      * <br>
89      * A connection is removed from the vector after call of <code>_freeConnection</code>
90      */

91     private TKVector connectionsToClose = new TKVector();
92
93     //public TKHashtable prepQueries = new TKHashtable(); // of Connection, TKHashtable of queryID, PreparedQuery for reuse of Prepared Statements
94
//private TKHashtable tkdbConnectionHash = new TKHashtable(); // of Connection, TKDBConnection
95

96     /**
97      * Returns the connection of the current thread.
98      *
99      * @return Connection the connection of the current thread.
100      */

101     public Connection getConnection() throws SQLException{
102         return getTKDBConnection().getConnection();
103     }
104
105     /**
106      * Returns the connect data of the <code>TKDBConnectionManager</code>.
107      *
108      * @return TKConnectData the connect data of this manager.
109      */

110     public TKConnectData getConnectionData(){
111         return jdbcConnectData;
112     }
113
114     /**
115      * Returns the <code>TKDBConnection</code> of the current thread.
116      * <br>
117      * If there is no connection available in {@link TKHashtable TKHashtable} <code>tkConnectionHash</code>
118      * and <code>Stack tkConnectionStack</code> is empty, a new connection will be opened.
119      * <br>
120      * If the limit, defined in the {@link TKLimiter TKLimiter} <code>limiter</code>, of opened connections
121      * will be exceeded, the current thread has to wait.
122      * @return TKDBConnection {@link TKDBConnection TKDBConnection} of current thread.
123      * @throws SQLException
124      */

125     public TKDBConnection getTKDBConnection() throws SQLException
126     {
127         try
128         {
129             Thread JavaDoc currentThread = Thread.currentThread();
130             TKDBConnection tkConn = (TKDBConnection) tkConnectionHash.get(currentThread);
131
132             // debug cached threads/connections
133
if (CAT.isDebugEnabled()) {
134                 CAT.debug("Current Thread: [" + Thread.currentThread() + "]"); // XTODO
135
CAT.debug("Limiter: (oid=" + limiter +
136                           ", used=" + limiter.used +
137                           ", pending=" + limiter.pending +
138                           ", limit=" + limiter.limit +
139                           ") !=? CACHE SIZE: " + tkConnectionHash.size()); // XTODO
140
Enumeration JavaDoc en = tkConnectionHash.keys();
141                 int cnt = 0;
142                 while (en.hasMoreElements()) {
143                     CAT.debug("|<" + cnt + "> " + en.nextElement() + " |"); // XTODO
144
cnt++;
145                 }
146             }
147             // check limit of opened connections
148
if (tkConn == null) {
149                 limiter.take();
150             }
151             synchronized (this) {
152                 if( tkConn == null || tkConn.getConnection().isClosed() ) {
153                     if( !tkConnectionStack.empty() ) {
154                         try {
155                             tkConn = (TKDBConnection) tkConnectionStack.pop();
156                             tkConnectionHash.put( currentThread, tkConn);
157                         }
158                         catch( Throwable JavaDoc t) {
159                             CAT.error(t);
160                         }
161                     }
162                     else {
163                         CAT.debug("stack empty");
164                     }
165                     if( tkConn == null || tkConn.getConnection().isClosed() ) {
166                         if (CAT.isDebugEnabled()) {
167                             CAT.debug("open new connection now ...");
168                             CAT.debug("using connectData:" + jdbcConnectData);
169                         }
170                         Connection conn
171                             = DriverManager.getConnection(jdbcConnectData.getConnectString(),
172                                                           jdbcConnectData.getConnectProperties());
173                         tkConn = new TKDBConnection(conn, this);
174                         tkConnectionHash.put( currentThread, tkConn );
175                     }
176                 }
177                 else {
178                     CAT.debug("reuse connection");
179                 }
180             }
181             return tkConn;
182         }
183         catch (SQLException e)
184         {
185             CAT.error("Error during connection creation : ", e);
186             throw e;
187         }
188     }
189
190     /**
191      * Instantiates an object of the specified class <code>inQueryClass</code> and adds it
192      * to the queries maintained by this <code>TKDBConnectionManager</code>
193      * within the {@link TKHashtable TKHashtable} <code>queryClasses</code>.
194      * <br>
195      * @param inQueryClass the class definition for the <code>TKQuery</code> to instantiate.
196      * @return the instantiated <code>TKQuery</code>
197      *
198      */

199     public TKQuery newQuery(Class JavaDoc inQueryClass) throws SQLException
200     {
201         TKDBConnection tkConn = getTKDBConnection(); // Muss noch vereinfacht werden!
202
TKQuery query;
203         Class JavaDoc queryClass;
204         String JavaDoc queryId = TKLib.getClassName(inQueryClass);
205         // String basePackage = queryClass.getPackage().getName(); Oops, erst in Java 1.2 // NullPointerException falls kein Package! marwan
206
String JavaDoc basePackage = TKLib.getPackageName (inQueryClass) ;
207
208         synchronized (this) { // Warum synchronized ??? -alex
209
query = getInitializedPrepQuery(queryId, tkConn); // Attempt to reuse a TKPrepQuery
210
if(query != null) {
211                 return query;
212             }
213             queryClass = (Class JavaDoc) queryClasses.get( queryId );
214             if( queryClass == null ) {
215                 if( dbIdentifier != null && dbIdentifier.length() > 0 ) {
216                     try {
217                         String JavaDoc classToLoad = "";
218                         if(basePackage==null) {
219                             classToLoad = dbIdentifier + '.' + queryId;
220                         }else{
221                             classToLoad = basePackage + '.' + dbIdentifier + '.' + queryId;
222                         }
223                         queryClass = Class.forName(classToLoad);
224                     }
225                     catch(ClassNotFoundException JavaDoc e) {
226                         CAT.debug(e);
227                     }
228                 }
229
230                 if( queryClass == null ) {
231                     queryClass = inQueryClass;
232                 }
233                 queryClasses.put(queryId, queryClass);
234             }
235         }
236
237         try {
238             if (query == null) {
239                 query = (TKQuery) queryClass.newInstance();
240             }
241             query.initQuery(jdbcConnectData.getTypeConverter(), tkConn, queryId);
242
243             return query;
244         }
245         catch(Throwable JavaDoc t) {
246             CAT.error("closeConnection", t);
247             throw new InstantiationError JavaDoc(t.getMessage());
248         }
249     }
250
251     /*
252     public synchronized void setBasePackage( String basePackage )
253     {
254         queryClassBasePackage = basePackage;
255     }
256     */

257
258     /**
259      * Sets the connection data object of this manager.
260      * <br>
261      * @param data the {@link TKConnectData TKConnectData} object to set.
262      *
263      */

264     public synchronized void setConnectionData( TKConnectData data )
265     {
266         CAT.debug("jdbcConnectData set in setConnectionData: with: " + data);
267         jdbcConnectData = data;
268     }
269
270     /**
271      *
272      *
273      */

274     public synchronized void prepareConnection( Properties JavaDoc prop )
275         throws SQLException
276     {
277         jdbcConnectData = null;
278
279         dbIdentifier = prop.getProperty("database").toLowerCase();
280         CAT.debug("got DB Identifier "+ dbIdentifier);
281         if( dbIdentifier.startsWith( "mssql" ) ) {
282             jdbcConnectData = new TKMSSQLConnectData( prop );
283         }
284         else if( dbIdentifier.startsWith( "sybase" ) ) {
285             jdbcConnectData = new TKSybaseConnectData( prop );
286         }
287         else if( dbIdentifier.startsWith( "oracle" ) ) {
288             jdbcConnectData = new TKOracleConnectData( prop );
289         }
290         else if (dbIdentifier.startsWith( "postgresql" ) ) {
291             jdbcConnectData = new TKPostgreSQLConnectData( prop );
292         }
293         if ( jdbcConnectData != null )
294             jdbcConnectData.initTypeConverter( getConnection() );
295
296     }
297
298
299     public synchronized void closeConnection() throws SQLException{
300         Thread JavaDoc currentThread = Thread.currentThread();
301         TKDBConnection tkConn = (TKDBConnection) tkConnectionHash.remove( currentThread );
302         if (tkConn != null) {
303             limiter.free();
304             closeConnection(tkConn);
305         }
306         else{
307             CAT.warn(" closeConnection: no Connection found! ");
308         }
309     }
310
311     public synchronized void closeConnection(TKDBConnection tkConn)
312         throws SQLException
313     {
314         limiter.free();
315         try{
316             tkConn.closeNonsensitiveQueries(false);
317         }
318         finally{// Die Connection wird in jedem Fall geschlossen!
319
Connection conn = tkConn.getConnection();
320             if (!conn.isClosed()) {
321                 conn.close();
322
323
324             }
325         }
326     }
327
328
329     public synchronized void freeConnection()
330         throws SQLException
331     {
332         Thread JavaDoc currentThread = Thread.currentThread();
333
334         TKDBConnection tkConn = (TKDBConnection) tkConnectionHash.remove( currentThread );
335         if (tkConn != null) {
336             try{
337                 tkConn.closeNonsensitiveQueries(false);
338             }
339             catch(SQLException e){
340                 closeConnection(tkConn);
341             }
342             limiter.free();
343             Connection conn = tkConn.getConnection();
344             if( connectionsToClose.removeElement( conn ) ) {
345                 // die Verbindung ist als zu schliessen markiert!
346
conn.close();
347
348             }
349             if (!conn.isClosed()) {
350                 tkConnectionStack.push( tkConn );
351             }
352         }
353     }
354
355
356     public synchronized void resetConnections()
357     {
358         maxConnections = 0;
359         dbIdentifier = null;
360         jdbcConnectData = null;
361
362         // alle noch in Gebrauch befindlichen Verbindungen merken
363
Enumeration JavaDoc e = tkConnectionHash.elements();
364         while( e.hasMoreElements() ) {
365             connectionsToClose.addElement( ((TKDBConnection) e.nextElement()).getConnection() );
366         }
367
368         // alle Query-Klassen vergessen
369
queryClasses = new TKHashtable();
370
371         // alle offenen Verbindungen schliessen
372
while( !tkConnectionStack.empty() ) {
373             Connection conn = ((TKDBConnection)tkConnectionStack.pop()).getConnection();
374             try {
375                 conn.close();
376
377             }
378             catch( SQLException sqle ) {
379                 throw new TKSQLError(sqle.getMessage(), sqle);
380             }
381         }
382     }
383
384     public synchronized void limitConnections( int count )
385     {
386         limiter.newLimit( count );
387     }
388
389
390
391     public void beginTransaction() throws SQLException
392     {
393         getTKDBConnection().beginTransaction();
394     }
395
396     public void commitTransaction() throws SQLException
397     {
398
399         getTKDBConnection().commitTransaction();
400     }
401
402
403
404     public void rollbackTransaction() throws SQLException
405     {
406         getTKDBConnection().rollbackTransaction();
407     }
408
409
410     public void closeNonsensitiveQueries() throws SQLException
411         { getTKDBConnection().closeNonsensitiveQueries();}
412
413
414
415
416
417
418   /** Method to get a TKPreparedQuery Object. Returns an initialized TKPrerQueyQuery
419     * of the class queryID, if a query of that class has allready been executed on the
420     * Connection. Otherwise null is returned.
421     */

422     private TKPrepQuery getInitializedPrepQuery(Object JavaDoc queryID, TKDBConnection tkConn){
423         TKHashtable queries = (TKHashtable) tkConn.getPrepQueries();
424         TKPrepQuery query = (TKPrepQuery) queries.remove(queryID);
425         return query;
426     }
427
428 // protected Object clone()
429
// {
430
// final TKDBConnectionManager clone = new TKDBConnectionManager();
431

432 // clone.jdbcConnectData = this.jdbcConnectData;
433
// clone.limiter = this.limiter;
434
// // clone.queryClassBasePackage = this.queryClassBasePackage;
435
// clone.maxConnections = this.maxConnections;
436
// clone.dbIdentifier = this.dbIdentifier;
437
// clone.tkConnectionHash = new TKHashtable();
438
// clone.queryClasses = this.queryClasses;
439
// clone.tkConnectionStack = this.tkConnectionStack;
440
// clone.connectionsToClose = this.connectionsToClose;
441
// // clone.transaction_level = this.transaction_level;
442

443 // return clone;
444
// }
445

446
447
448     private static TKDBConnectionManager singleton = null;
449     public static TKDBConnectionManager getInstance() {
450         if (singleton == null)
451             singleton = new TKDBConnectionManager();
452         return singleton;
453     }
454
455     private TKDBConnectionManager() {
456     }
457 }
458
459
Popular Tags