001: package com.teamkonzept.db;
002:
003: import java.sql.*;
004: import java.util.Enumeration;
005: import java.util.Properties;
006:
007: import com.teamkonzept.lib.*;
008: import org.apache.log4j.Category;
009:
010: /**
011: * A manager for database connections of an application context.
012: * <br><br>
013: * Each thread is assigned to an application context. For each context this class
014: * manages a pool of database connections using an object of class {@link TKDBConnectionManager TKDBConnectionManager}.
015: * <br><br>
016: * Having initialized the database pool for each context,
017: * a concrete connection is assigned to each thread after the first request,
018: * until an explicit release of the connection.
019: * <br><br>
020: * Simultaneously, a thread can maintain several opened connections to different databases.
021: * Each context is then assigned to a single connection. By changing the context a thread
022: * may switch to another database connection.
023: * <br><br>
024: * This class is never instantiated.
025: * <br><br>
026: * @author $Author: alex $
027: * @version $Revision: 1.26 $
028: * @see TKDBConnectionManager
029: */
030: public class TKDBManager {
031:
032: /** Logging Category */
033: private static final Category CAT = Category
034: .getInstance(TKDBManager.class);
035:
036: /**
037: * A hashtable, which maps each context to its connection manager.
038: * <br><br>
039: * key(Object aContext) => value(TKDBConnectionManager aConnectionManager)
040: */
041: // private static TKHashtable contextManager = new TKHashtable();
042: /**
043: * A hashtable, which maps each thread to the connection manager
044: * of its actual context.
045: * <br><br>
046: * key(Thread aThread) => value(TKDBConnectionManager aConnectionManager)
047: */
048: // private static TKHashtable threadManager = new TKHashtable();
049: /**
050: * aktueller Kontext
051: */
052: // Thread, Context
053: // private static TKHashtable currentContext = new TKHashtable();
054: /**
055: * A hashtable, which maps each thread to a hashtable of all contexts and the
056: * connection managers assigned to the contexts, where the thread has registered.
057: * <br><br>
058: * key(Thread aThread) => value(TKHashtable aTKHashtable(key (Object aContext) =>
059: * value(TKDBConnectionManager aConnectionManager)))
060: */
061: // private static TKHashtable threadContexts = new TKHashtable();
062: /**
063: * Default Context
064: */
065: // public final static Object DEFAULT_CONTEXT = "com.teamkonzept.db.TKDBManagerDefaultContext";
066: /**
067: * Ensures, that for the specified context exists a <code>TKDBConnectionManager</code>.
068: * <br><br>
069: * The specified context becomes the actual context of the specified thread.
070: * <br>
071: * Returns the responsible <code>TKDBConnectionManager</code>.
072: *
073: * @param thread gets the specified context as actual context
074: * @return the responsible <code>TKDBConnectionManager</code> of the specified thread.
075: */
076: // private static synchronized TKDBConnectionManager ensureManager( Thread thread, Object context )
077: // {
078: // TKDBConnectionManager currManager = (TKDBConnectionManager)contextManager.get(context);
079: // if ( currManager == null )
080: // {
081: // final Object currContext = getCurrentContext();
082: // if (currContext != null) {
083: // // hole den Manager zum aktuellen Kontext
084: // currManager = (TKDBConnectionManager)contextManager.get( currContext );
085: // }
086: // // final boolean toClone;
087: // if (currManager == null) {
088: // // hole den default-Manager
089: // currManager = (TKDBConnectionManager)contextManager.get(DEFAULT_CONTEXT);
090: // if (currManager == null) {
091: // // erzeuge einen neuen Manager
092: // // currManager = new TKDBConnectionManager();
093: // currManager = TKDBConnectionManager.getInstance();
094: // // toClone = false;
095: // }
096: // // else {
097: // // toClone = true;
098: // // }
099: // }
100: // // else {
101: // // toClone = true;
102: // // }
103: // // if ( toClone )
104: // // {
105: // // currManager = (TKDBConnectionManager)currManager.clone();
106: // // }
107: // contextManager.put(context, currManager);
108: // }
109: // threadManager.put(thread, currManager);
110: // return currManager;
111: // return TKDBConnectionManager.getInstance();
112: // }
113: /**
114: * Registers the specified thread at this <code>TKDBManager</code>.
115: * @param currThread the thread to be registered.
116: */
117: protected static final void register(Thread currThread) {
118: // threadContexts.put( currThread, new TKHashtable() );
119: }
120:
121: /**
122: * Registers the current thread at this <code>TKDBManager</code>.
123: */
124: public static void register() {
125: // register( Thread.currentThread() );
126: }
127:
128: /**
129: * Registers the current thread at this <code>TKDBManager</code>
130: * and sets the specified context as its initial context.
131: *
132: * @param initialContext the initial context to be assigned to the current thread.
133: */
134: public static void register(Object initialContext) {
135: // Thread currThread = Thread.currentThread();
136: // register( currThread );
137: // try {
138: // enterContext( currThread, initialContext, false );
139: // }
140: // catch( SQLException e ) {
141: // throw new Error("Impossible error");
142: // }
143: }
144:
145: /**
146: * Changes the context of the specified thread to the specified nextContext.
147: * Depending on the specified value of leaveOldContext, the connection in the
148: * previous context is released.
149: *
150: * @param currThread the thread, of which the context has to be changed.
151: * @param nextContext this context replaces the previous context of the specified thread.
152: * @param leaveOldContext indicates if the connection in the previous context is released.
153: * @throws SQLException normaler SQL Fehler
154: */
155: protected static void enterContext(Thread currThread,
156: Object nextContext, boolean leaveOldContext)
157: throws SQLException {
158: if (leaveOldContext) {
159: // TKDBConnectionManager connManager =
160: // (TKDBConnectionManager) threadManager.get( currThread );
161: // if( connManager != null ) {
162: // connManager.freeConnection();
163: // }
164: TKDBConnectionManager.getInstance().freeConnection();
165: }
166:
167: // TKHashtable contextHash = (TKHashtable)threadContexts.get( currThread );
168: // contextHash.put( nextContext, ensureManager( currThread, nextContext ) );
169: // currentContext.put( currThread, nextContext );
170: }
171:
172: /**
173: * Replaces the context of the current thread with the specified nextContext.
174: */
175: /*
176: private static void enterContext( Object nextContext )
177: {
178: try {
179: enterContext( Thread.currentThread(), nextContext, false );
180: }
181: catch( SQLException e ) {
182: throw new Error("Impossible error");
183: }
184: }
185: */
186: /**
187: * Replaces the context of the current thread with the specified nextContext.
188: * Depending on the specified value of leaveOldContext, the connection in the
189: * previous context is released.
190: *
191: * @param nextContext this context replaces the previous context of the specified thread.
192: * @param leaveOldContext indicates if the connection in the previous context is released.
193: * @throws SQLException
194: */
195: /*
196: private static void enterContext( Object nextContext, boolean leaveOldContext )
197: throws SQLException
198: {
199: enterContext( Thread.currentThread(), nextContext, leaveOldContext );
200: }
201: */
202: /**
203: * Closes all opened connections in the <code>TKDBConnectionManager</code> assigned to
204: * the current thread.
205: */
206: protected static void resetContext() {
207: getManager().resetConnections();
208: }
209:
210: /**
211: * Deregisters the current thread from this <code>TKDBManager</code>.
212: * The specified value of doClose indicates, if all the connections contained
213: * in the <code>TKDBConnectionManager</code> assigned to the current thread are
214: * closed or only released.
215: *
216: * @param doClose if <code>true</code> close connections of the current thread
217: * else only release them for other threads.
218: * @throws SLQException normaler SQL Fehler
219: */
220: public static synchronized void deregister(boolean doClose)
221: throws SQLException {
222:
223: // Thread currThread = Thread.currentThread();
224:
225: // threadManager.remove( currThread );
226: // TKHashtable contexts = (TKHashtable)threadContexts.remove( currThread );
227: // if( contexts == null ) return;
228:
229: // Enumeration e = contexts.elements();
230: // if( doClose ) {
231: // while( e.hasMoreElements() ) {
232: // ((TKDBConnectionManager)e.nextElement()).closeConnection();
233: // }
234: // }
235: // else {
236: // while( e.hasMoreElements() ) {
237: // ((TKDBConnectionManager)e.nextElement()).freeConnection();
238: // }
239: // }
240: TKDBConnectionManager.getInstance().freeConnection();
241: }
242:
243: /**
244: * Returns the <code>TKDBConnectionManager</code> assigned to the actual context of the current thread.
245: * @return the connection manager of the current thread.
246: */
247: public static TKDBConnectionManager getManager() {
248: // Thread currThread = Thread.currentThread();
249: // TKDBConnectionManager currManager = (TKDBConnectionManager)threadManager.get( currThread );
250: // if( currManager == null ) {
251: // currManager = ensureManager( currThread, DEFAULT_CONTEXT );
252: // }
253: // return currManager;
254: return TKDBConnectionManager.getInstance();
255: }
256:
257: /**
258: * Returns the connection of the current thread in the actual context
259: * @throws SQLException
260: */
261: public static Connection getConnection() throws SQLException {
262: return getManager().getConnection();
263: }
264:
265: /**
266: * Using the methods <code>beginTransaction</code>, <code>commitTransaction</code>
267: * and <code>rollbackTransaction</code>, it is possible to encapsulate several queries
268: * in one transaction.
269: * <br>
270: * Usage:
271: * <br>
272: * <pre>
273: * TKDBManager.beginTransaction();
274: * try{
275: * q_1.execute();
276: * q_1.close();
277: * .
278: * .
279: * .
280: * q_n.execute();
281: * q_n.close();
282: * commitTransaction();
283: * }
284: * catch(Throwable e){
285: * TKDBManager.rollbackTransaction();
286: * }
287: * </pre>
288: * <br>
289: * It is important to call <code>close</code> for each <code>TKQuery</code>,
290: * because only then the statement will be completed and SQLExceptions will be thrown.
291: * <br>
292: * As well important is to catch <code>Throwable</code>, because <code>close</close> may
293: * throw a <code>SQLError</code>!
294: * <br>
295: *
296: * For details see method {@link #executeAsTran(TKQuery [] queryArray) executeAsTran}
297: * @throws SQLException
298: */
299: public static void beginTransaction() throws SQLException {
300: getManager().beginTransaction();
301: }
302:
303: /**
304: * Commits a transaction begun in the <code>TKDBConnection</code>
305: * in the actual context of the current thread
306: * <i>(connection is retrieved over <code>TKDBConnectionManager</code>)</i>
307: * @throws SQLException
308: */
309: public static void commitTransaction() throws SQLException {
310: getManager().commitTransaction();
311: }
312:
313: /**
314: * Rollbacks a transaction begun in the <code>TKDBConnection</code>
315: * in the actual context of the current thread
316: * <i>(connection is retrieved over <code>TKDBConnectionManager</code>)</i>
317: */
318: public static void rollbackTransaction() throws SQLException {
319: getManager().rollbackTransaction();
320: }
321:
322: /**
323: * Rollbacks a transaction begun in the <code>TKDBConnection</code>
324: * in the actual context of the current thread
325: * <i>(connection is retrieved over <code>TKDBConnectionManager</code>)</i>.
326: * <br>
327: * Is called by {@link executeAsTran(TKQuery [] queryArray) executeAsTran}
328: * in the catch-clause and analyzes the specified <code>Throwable</code>.
329: * Throws a <code>TKSQLError</code>, which encapsulates a <code>SQLException</code>.
330: * @param t Ursprungsexception
331: */
332: public static void safeRollbackTransaction(Throwable t) {
333: try {
334: CAT.error("Rollback Reason: ", t);
335: rollbackTransaction();
336:
337: } catch (SQLException e) {
338: CAT.debug("SQLException during rollback", e);
339: }
340:
341: if (t instanceof SQLException) {
342: throw new TKSQLError("Rollback: " + t.getMessage(),
343: (SQLException) t);
344: }
345:
346: if (t instanceof TKSQLError) {
347: throw new TKSQLError("Rollback: " + t.getMessage(),
348: ((TKSQLError) t).getSQLException());
349: }
350:
351: throw new TKSQLError("Rollback: " + t.getMessage());
352: }
353:
354: /**
355: * Closes all queries within the connection of the actual context,
356: * not implementing the interface <code>SensitiveQuery</code>
357: * by calling the queries´ <code>close</code> method.
358: * @throws SQLException
359: */
360: public static void closeNonsensitiveQueries() throws SQLException {
361: getManager().closeNonsensitiveQueries();
362: }
363:
364: /**
365: * Executes the queries in the specified queryArray within one transaction.
366: * If one <code>TKQuery</code> throws an exception, all
367: * queries are rollbacked.
368: * <br>
369: * This method should be used, if the returned resultsets are not needed.
370: * @param queryArray Array of TKQuery, to be executed within a transaction
371: */
372: public static void executeAsTran(TKQuery[] queryArray) {
373: try {
374: beginTransaction();
375: int i;
376: for (i = 0; i < queryArray.length; i++) {
377: queryArray[i].execute();
378: queryArray[i].close();
379: }
380: commitTransaction();
381: } catch (Throwable t) {
382: safeRollbackTransaction(t);
383: }
384: }
385:
386: //***************************************************************************
387: /**
388: Erzeugt ein neues Query-Objekt im aktuellen Kontext des laufenden Threads.
389: Die Query-Klasse wird im uebergebenen Java-Package gesucht.
390: */
391: /*
392: public static TKQuery newQuery( String queryId, String basePackage )
393: throws SQLException
394: {return getManager()._newQuery( queryId, basePackage );}
395: */
396: /**
397: Erzeugt ein neues Query-Objekt im aktuellen Kontext des laufenden Threads.
398: Die Query-Klasse wird im default-Base-Package gesucht.
399: */
400: /*
401: public static TKQuery newQuery( String queryId ) throws SQLException
402: {return getManager()._newQuery( queryId );}
403: */
404: /**
405: Erzeugt ein neues Query-Objekt im aktuellen Kontext des laufenden Threads.
406: Die Query-Klasse wird übergeben oder anhand der uebergebenen Objektes gesucht.
407: */
408: /*
409: public static TKQuery newQuery( TKPackageInformer query ) throws SQLException
410: { return getManager()._newQuery( query ); }
411: */
412: //***************************************************************************
413: /**
414: * Returns an instance of the specified queryClass registered in the
415: * <code>TKDBConnectionManager</code> responsible for the actual context
416: * of the current thread.
417: *
418: * @param queryClass the class to instantiate a new <code>TKQuery</code>
419: * @see TKQuery
420: * @see TKDBConnectionManager
421: * @see TKDBConnection
422: * @return die erzeugte Query
423: */
424: public static TKQuery newQuery(Class queryClass)
425: throws SQLException {
426: return getManager().newQuery(queryClass);
427: }
428:
429: /**
430: Setzt das Default-Java-Package in dem nach Query-Klassen fuer den
431: aktuellen Kontext des laufenden Threads gesucht wird
432: */
433: /*
434: public static void setBasePackage( String basePackage )
435: {getManager()._setBasePackage( basePackage );}
436: */
437:
438: /**
439: * Sets all values within the <code>TKDBConnectionManager</code> responsible
440: * for the context of the current thread, necessary to connect to database.
441: * @param dbId a string which determines the vendor of the used database
442: * @param serverId the connect string
443: * @param prop properties of the used database connection
444: * (database, user_name, password, server, ...)
445: */
446: public static void prepareConnection(Properties prop)
447: throws SQLException {
448: getManager().prepareConnection(prop);
449: }
450:
451: /**
452: * Closes the connection of the current thread.
453: */
454: public static void closeConnection() throws SQLException {
455: getManager().closeConnection();
456: }
457:
458: /**
459: * Releases the connection of the current thread, so it can be used later.
460: */
461: public static void freeConnection() throws SQLException {
462: getManager().freeConnection();
463: }
464:
465: /**
466: * Limits the number of opened connections of the current thread to
467: * the specified value of count.
468: */
469: public static void limitConnections(int count) {
470: getManager().limitConnections(count);
471: }
472:
473: /**
474: * Liefert den aktuellen Kontext des Threads
475: */
476: // private static Object getCurrentContext()
477: // {
478: //return currentContext.get( Thread.currentThread() );
479: // return TKDBConnectionManager.getInstance();
480: // }
481: /**
482: * Returns the vendor of database out of the connect data within
483: * the <code>TKDBConnectionManager</code> responsible for the actual context
484: * of the current thread.
485: */
486: public static int getDBVendor() {
487: TKConnectData connectData = getManager().getConnectionData();
488:
489: if (connectData == null) {
490: return QueryConstants.DONTKNOW;
491: }
492: if (connectData instanceof TKOracleConnectData) {
493: return QueryConstants.ORACLE;
494: }
495: if (connectData instanceof TKSybaseConnectData) {
496: return QueryConstants.SYBASE;
497: }
498: if (connectData instanceof TKPostgreSQLConnectData) {
499: return QueryConstants.POSTGRESQL;
500: } else {
501: return QueryConstants.DONTKNOW;
502: }
503:
504: }
505:
506: }
|