KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > opensubsystems > core > persist > db > DatabaseTransactionFactoryImpl


1 /*
2  * Copyright (c) 2003 - 2007 OpenSubsystems s.r.o. Slovak Republic. All rights reserved.
3  *
4  * Project: OpenSubsystems
5  *
6  * $Id: DatabaseTransactionFactoryImpl.java,v 1.25 2007/01/07 06:14:18 bastafidli Exp $
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; version 2 of the License.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */

21
22 package org.opensubsystems.core.persist.db;
23
24 import java.sql.Connection JavaDoc;
25 import java.sql.SQLException JavaDoc;
26 import java.util.Properties JavaDoc;
27 import java.util.logging.Level JavaDoc;
28 import java.util.logging.Logger JavaDoc;
29
30 import javax.transaction.Status JavaDoc;
31 import javax.transaction.SystemException JavaDoc;
32 import javax.transaction.UserTransaction JavaDoc;
33
34 import org.opensubsystems.core.error.OSSDatabaseAccessException;
35 import org.opensubsystems.core.error.OSSDynamicClassException;
36 import org.opensubsystems.core.error.OSSException;
37 import org.opensubsystems.core.persist.db.transaction.J2EETransactionFactoryImpl;
38 import org.opensubsystems.core.persist.db.transaction.SimpleLocalTransactionFactoryImpl;
39 import org.opensubsystems.core.util.ClassFactory;
40 import org.opensubsystems.core.util.Config;
41 import org.opensubsystems.core.util.GlobalConstants;
42 import org.opensubsystems.core.util.J2EEUtils;
43 import org.opensubsystems.core.util.Log;
44 import org.opensubsystems.core.util.TransactionFactory;
45
46 /**
47  * Base class for implementation of database transaction factories.
48  *
49  * @version $Id: DatabaseTransactionFactoryImpl.java,v 1.25 2007/01/07 06:14:18 bastafidli Exp $
50  * @author Miro Halas
51  * @code.reviewer Miro Halas
52  * @code.reviewed 1.18 2006/05/24 15:18:33 bastafidli
53  */

54 public abstract class DatabaseTransactionFactoryImpl implements DatabaseTransactionFactory
55 {
56    // Configuration settings ///////////////////////////////////////////////////
57

58    /**
59     * Transaction timeout in seconds, which should be set for a transaction when
60     * it is started. If this is set to 0, according to documentation the transaction
61     * manager restores the default value.
62     */

63    public static final String JavaDoc TRANSACTION_TIMEOUT = "oss.transaction.timeout";
64    
65    /**
66     * Boolean flag, which specifies if the system should monitor transactions
67     * that is use a delegating class which intercepts and monitors calls to
68     * UserTransaction class. This is useful for troubleshooting and during
69     * development but can affect the system performance.
70     */

71    public static final String JavaDoc TRANSACTION_MONITOR = "oss.transaction.monitor";
72
73    // Constants ////////////////////////////////////////////////////////////////
74

75    /**
76     * Lock used in synchronized sections.
77     */

78    private static final String JavaDoc IMPL_LOCK = "IMPL_LOCK";
79
80    /**
81     * Default transaction timeout. Keep it longer so that if during debugging
82     * we need to step through the code, we have enough time to complete
83     * transaction.
84     */

85    public static final int TRANSACTION_TIMEOUT_DEFAULT = 600;
86
87    /**
88     * Default transaction monitor setting. Keep it false so that it doesn't affect
89     * performance.
90     */

91    public static final boolean TRANSACTION_MONITOR_DEFAULT = false;
92    
93    // Cached values ////////////////////////////////////////////////////////////
94

95    /**
96     * Logger for this class
97     */

98    private static Logger JavaDoc s_logger = Log.getInstance(DatabaseTransactionFactoryImpl.class);
99   
100    /**
101     * Reference to default instance.
102     */

103    private static DatabaseTransactionFactory s_defaultInstance = null;
104
105    /**
106     * Transaction timeout, which should be set for a transaction when it is
107     * started. If this is set to 0, according to documentation transaction
108     * manager restores the default value.
109     */

110    protected static int s_iTransactionTimeout;
111    
112    /**
113     * Flag, which is telling us if we should monitor transactions that is use
114     * a delegating class which intercepts and monitors calls to UserTransaction
115     * class.
116     */

117    protected static boolean s_bTransactionMonitor;
118    
119    // Constructors /////////////////////////////////////////////////////////////
120

121    /**
122     * Static initializer.
123     */

124    static
125    {
126       // Read configuration parameters
127
Properties JavaDoc prpSettings;
128
129       prpSettings = Config.getInstance().getPropertiesSafely();
130       s_iTransactionTimeout = Config.getIntPropertyInRange(
131                                         prpSettings,
132                                         TRANSACTION_TIMEOUT,
133                                         TRANSACTION_TIMEOUT_DEFAULT,
134                                         "Default transaction timout",
135                                         0, // 0 is allowed
136
Integer.MAX_VALUE);
137
138       s_bTransactionMonitor = Config.getBooleanProperty(
139                                         prpSettings,
140                                         TRANSACTION_MONITOR,
141                                         TRANSACTION_MONITOR_DEFAULT,
142                                         "Default transaction monitoring");
143    }
144    
145    /**
146     * Default constructor.
147     */

148    public DatabaseTransactionFactoryImpl(
149    )
150    {
151       super();
152    }
153
154    // Factory methods //////////////////////////////////////////////////////////
155

156    /**
157     * Get the default factory. This method is here to make the transaction factory
158     * is configurable. Once can specify in configuration file derived class to
159     * used instead of this one [DatabaseTransactionFactory.class]=new class to
160     * use or [TransactionFactory.class]=new class to use
161     *
162     * @return DatabaseTransactionFactory
163     * @throws OSSException - an error has occured
164     */

165    public static DatabaseTransactionFactory getInstance(
166    ) throws OSSException
167    {
168       if (s_defaultInstance == null)
169       {
170          // Only if the default factory wasn't set by other means create a new one
171
// Synchronize just for the creation
172
synchronized (IMPL_LOCK)
173          {
174             if (s_defaultInstance == null)
175             {
176                DatabaseTransactionFactory transactionFactory = null;
177                
178                try
179                {
180                   transactionFactory = (DatabaseTransactionFactory)ClassFactory.getInstance()
181                                .createInstance(DatabaseTransactionFactory.class);
182                }
183                catch (OSSDynamicClassException dneExc)
184                {
185                   // Cannot instantiate database transaction factory
186
// so try just the generic transaction factory
187
Class JavaDoc defaultTransactionFactoryImpl = SimpleLocalTransactionFactoryImpl.class;
188
189                   // Find out if we are running under a j2ee server. If yes, redefine
190
// default variable defaultTransactionFactoryImpl for using
191
// j2ee transaction factory implementation.
192
if (J2EEUtils.getJ2EEServerType() != J2EEUtils.J2EE_SERVER_NO)
193                   {
194                      defaultTransactionFactoryImpl = J2EETransactionFactoryImpl.class;
195                   }
196                   
197                   transactionFactory = (DatabaseTransactionFactory)ClassFactory.getInstance()
198                                           .createInstance(TransactionFactory.class,
199                                                           defaultTransactionFactoryImpl);
200                }
201                
202                setInstance(transactionFactory);
203             }
204          }
205       }
206       
207       return s_defaultInstance;
208    }
209    
210    /**
211     * Set default factory instance. This instance will be returned by
212     * getInstance method until it is changed.
213     *
214     * @param defaultFactory - new default factory instance
215     * @see #getInstance
216     */

217    public static void setInstance(
218       DatabaseTransactionFactory defaultFactory
219    )
220    {
221       if (GlobalConstants.ERROR_CHECKING)
222       {
223          
224          assert defaultFactory != null
225                 : "Default transaction factory instance cannot be null";
226       }
227       
228       synchronized (IMPL_LOCK)
229       {
230          s_defaultInstance = defaultFactory;
231          s_logger.fine("Default database transaction factory is "
232                        + s_defaultInstance.getClass().getName());
233       }
234    }
235    
236    /**
237     * {@inheritDoc}
238     */

239    public void commitTransaction(
240       Connection JavaDoc cntConnection
241    ) throws SQLException JavaDoc
242    {
243       try
244       {
245          boolean bAutoCommit = cntConnection.getAutoCommit();
246          
247          if ((!isTransactionInProgress()) && (!bAutoCommit))
248          {
249             // If there is no global transaction in progress and then can be some
250
// sql pending since the autocommit is false then just commit
251
// this connection since the transaction has to be connection based
252
if (s_bTransactionMonitor)
253             {
254                s_logger.finest("commitTransaction on connection "
255                                + cntConnection.toString());
256             }
257             cntConnection.commit();
258             if (s_bTransactionMonitor)
259             {
260                s_logger.finest("commitTransaction sucessfull on connection "
261                                + cntConnection.toString());
262             }
263          }
264          else
265          {
266             if (s_bTransactionMonitor)
267             {
268                s_logger.finest("commitTransaction on connection "
269                                + cntConnection.toString()
270                                + " ignored since either transaction in progress"
271                                + " or autoCommit is set. Autocommit value is "
272                                + bAutoCommit);
273             }
274          }
275       }
276       catch (SystemException JavaDoc seExc)
277       {
278          SQLException JavaDoc sqlExc = new SQLException JavaDoc("Cannot check state of the transaction");
279          sqlExc.initCause(seExc);
280          throw sqlExc;
281       }
282       catch (OSSException osseExc)
283       {
284          SQLException JavaDoc sqlExc = new SQLException JavaDoc("Cannot check state of the transaction");
285          sqlExc.initCause(osseExc);
286          throw sqlExc;
287       }
288    }
289
290    /**
291     * {@inheritDoc}
292     */

293    public void rollbackTransaction(
294       Connection JavaDoc cntConnection
295    ) throws SQLException JavaDoc
296    {
297       try
298       {
299          boolean bAutoCommit = cntConnection.getAutoCommit();
300          
301          // Connection can be null in case an exception is thrown when
302
// getting connection
303
if (cntConnection != null)
304          {
305             if ((!isTransactionInProgress()) && (!bAutoCommit))
306             {
307                // If there is no global transaction in progress and then can be some
308
// sql pending since the autocommit is false then just rollback
309
// this connection since the transaction has to be connection based
310
if (s_bTransactionMonitor)
311                {
312                   s_logger.finest("rollbackTransaction on connection "
313                                   + cntConnection.toString());
314                }
315                cntConnection.rollback();
316                if (s_bTransactionMonitor)
317                {
318                   s_logger.finest("rollbackTransaction sucessfull on connection "
319                                   + cntConnection.toString());
320                }
321             }
322             else
323             {
324                if (s_bTransactionMonitor)
325                {
326                   s_logger.finest("rollbackTransaction on connection "
327                                   + cntConnection.toString()
328                                   + " ignored since either transaction in progress"
329                                   + " or autoCommit is set. Autocommit value is "
330                                   + bAutoCommit);
331                }
332             }
333          }
334       }
335       catch (SystemException JavaDoc seExc)
336       {
337          s_logger.log(Level.WARNING, "Cannot check state of the transaction",
338                       seExc);
339          // Rollback anyway, since that should be better than just
340
cntConnection.rollback();
341       }
342       catch (OSSException osseExc)
343       {
344          SQLException JavaDoc sqlExc = new SQLException JavaDoc("Cannot check state of the transaction");
345          sqlExc.initCause(osseExc);
346          throw sqlExc;
347       }
348    }
349
350    /**
351     * Test if transaction is in progress.
352     *
353     * @return boolean - if true then transaction is in progress for curren thread.
354     * @throws SystemException - error occured while getting the transaction status
355     * @throws OSSException - error occured while getting the transaction status
356     */

357    public boolean isTransactionInProgress(
358    ) throws SystemException JavaDoc,
359             OSSException
360    {
361       UserTransaction JavaDoc transaction = requestTransaction();
362       int iStatus = Status.STATUS_NO_TRANSACTION;
363       
364       if (transaction != null)
365       {
366          // Transaction might be null if there is an error to initialize
367
// the persistence layer
368
iStatus = transaction.getStatus();
369       }
370
371       return ((iStatus != Status.STATUS_NO_TRANSACTION)
372               && (iStatus != Status.STATUS_COMMITTED)
373               && (iStatus != Status.STATUS_ROLLEDBACK));
374    }
375    
376    /**
377     * Test if transaction should be monitored
378     *
379     * @return boolean - if true then transactions should be monitored and log
380     * should capture additional diagnostic information about
381     * each transaction
382     */

383    public boolean isTransactionMonitored()
384    {
385       return s_bTransactionMonitor;
386    }
387
388    // Helper methods ///////////////////////////////////////////////////////////
389

390    /**
391     * This method should be exclusively used by DatabaseConnectionFactoryImpl
392     * to get a transaction aware version of a connection. If there one already
393     * exists (such as there is one associated with the current pending transaction
394     * managed by the database transaction factory) then this method will return
395     * that one and there is no need to allocate a new one. If there is not one,
396     * then this method will call back the database connection factory which
397     * allocated the connection and then create a transaction aware version of it.
398     *
399     * This method is protected so that only classes in this package can access it.
400     *
401     * @param bAutoCommit - The desired autocommit state of the connection. If
402     * this connection is invoked in global (JTA) transaction
403     * then the autocommit is false regardless of what
404     * value is specified here. Use true here if the client
405     * only reads the data and false if the client also
406     * modifies the data.
407     * @param strDataSourceName - datasource for which the connection was requested,
408     * if null then it is requested for default data source
409     * @param strUser - user for which the connection was requested, if null
410     * then it is requested for default user
411     * @param strPassword - password for which the connection was requested, if
412     * null then it is requested for default passwoed
413     * @param connectionFactory - connection factory which is requesting the connection
414     * @return Connection - transaction aware version of connection
415     * @throws OSSDatabaseAccessException - an error has occured
416     */

417    protected Connection JavaDoc requestTransactionalConnection(
418       boolean bAutoCommit,
419       String JavaDoc strDataSourceName,
420       String JavaDoc strUser,
421       String JavaDoc strPassword,
422       DatabaseConnectionFactoryImpl connectionFactory
423    ) throws OSSDatabaseAccessException
424    {
425       // Many external connection pools already integrate with transaction
426
// managers so we don't have to do anything, just call the real
427
// connection acquiring method.
428
Connection JavaDoc realConnection;
429       
430       if (strDataSourceName == null)
431       {
432          if ((strUser == null) && (strPassword == null))
433          {
434             // Get just connection for default user
435
realConnection = connectionFactory.requestNonTransactionalConnection(
436                                 bAutoCommit);
437          }
438          else
439          {
440             realConnection = connectionFactory.requestNonTransactionalConnection(
441                                 bAutoCommit, strUser, strPassword);
442          }
443       }
444       else
445       {
446          if ((strUser == null) && (strPassword == null))
447          {
448             // Get just connection for default user
449
realConnection = connectionFactory.requestNonTransactionalConnection(
450                                 bAutoCommit, strDataSourceName);
451          }
452          else
453          {
454             realConnection = connectionFactory.requestNonTransactionalConnection(
455                                 bAutoCommit, strDataSourceName, strUser, strPassword);
456          }
457       }
458       
459       return realConnection;
460    }
461
462    /**
463     * This method should be exclusively used by DatabaseConnectionFactoryImpl
464     * to get a transaction aware version of a connection. If the connection
465     * is transaction aware then it will be realy returned if it is not involved
466     * in a pending transaction.
467     *
468     * This method is protected so that only classes from this package can access it.
469     *
470     * @param cntDBConnection - connection to return, can be null
471     * @param connectionFactory - connection factory to which the connection should
472     * be returned
473     */

474    protected void returnTransactionalConnection(
475       Connection JavaDoc cntDBConnection,
476       DatabaseConnectionFactoryImpl connectionFactory
477    )
478    {
479       // Many external connection pools already integrate with transaction
480
// managers so we don't have to do anything, just call the real
481
// connection returning method.
482
connectionFactory.returnNonTransactionalConnection(cntDBConnection);
483    }
484 }
485
Popular Tags