KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > oracle > toplink > essentials > transaction > AbstractTransactionController


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the "License"). You may not use this file except
5  * in compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * glassfish/bootstrap/legal/CDDLv1.0.txt or
9  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * HEADER in each file and include the License file at
15  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
16  * add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your
18  * own identifying information: Portions Copyright [yyyy]
19  * [name of copyright owner]
20  */

21 // Copyright (c) 1998, 2005, Oracle. All rights reserved.
22
package oracle.toplink.essentials.transaction;
23
24 import java.util.*;
25 import javax.naming.*;
26 import oracle.toplink.essentials.sessions.ExternalTransactionController;
27 import oracle.toplink.essentials.logging.SessionLog;
28 import oracle.toplink.essentials.exceptions.TransactionException;
29 import oracle.toplink.essentials.exceptions.DatabaseException;
30 import oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl;
31 import oracle.toplink.essentials.internal.sessions.AbstractSession;
32
33 /**
34  * <p>
35  * <b>Purpose</b>: Abstract implementation of an ExternalTransactionController.
36  * <p>
37  * <b>Description</b>: This class implements the ExternalTransactionController
38  * interface. Concrete implementations of this class are responsible for performing
39  * the coordination with the external transaction manager through whatever means
40  * available to that manager. Different transaction services may do this in slightly
41  * different ways, but most common ones (JTA and JTS/OTS) will use a synchronization
42  * interface.
43  * <p>
44  * @see AbstractSynchronizationListener
45  * @see oracle.toplink.essentials.sessions.ExternalTransactionController
46  */

47 public abstract class AbstractTransactionController implements ExternalTransactionController {
48
49     /** Table of external transaction object keys and unit of work values */
50     protected Hashtable unitsOfWork;
51
52     /** The session this controller is responsible for controlling */
53     protected AbstractSession session;
54
55     /** Generates listener instances for synchronization */
56     protected SynchronizationListenerFactory listenerFactory;
57
58     /** PERF: Cache the active uow in a thread local. */
59     protected ThreadLocal JavaDoc activeUnitOfWorkThreadLocal;
60
61     /**
62      * INTERNAL:
63      * Return a new controller.
64      */

65     public AbstractTransactionController() {
66         this.unitsOfWork = new Hashtable();
67         this.activeUnitOfWorkThreadLocal = new ThreadLocal JavaDoc();
68     }
69
70     /**
71      * INTERNAL:
72      * Associate the given unit of work and TopLink session with the active external
73      * transaction. This may be done by whatever means supported by the transaction
74      * manager (i.e. through a synchronization protocol).
75      *
76      * @param unitOfWork The unit of work that is to be bound to the active transaction
77      * @param session The session ancestor of the unit of work
78      */

79     public void bindToCurrentTransaction(UnitOfWorkImpl unitOfWork, AbstractSession session) {
80         Object JavaDoc status = getTransactionStatus();
81         logTxStateTrace(unitOfWork, "TX_bind", status);
82         try {
83             Object JavaDoc txn = getTransaction();
84             if (txn == null) {
85                 // If no external transaction is active then start one through the uow
86
unitOfWork.beginTransaction();
87                 txn = getTransaction();
88             }
89
90             // If there still is no active txn then something is wrong
91
if (txn == null) {
92                 throw TransactionException.externalTransactionNotActive();
93             }
94
95             // Create and register the synchronization listener
96
AbstractSynchronizationListener listener = getListenerFactory().newSynchronizationListener(unitOfWork, session, txn, this);
97
98             registerSynchronization_impl(listener, txn);
99             unitOfWork.setSynchronized(true);
100
101         } catch (Exception JavaDoc exception) {
102             throw TransactionException.errorBindingToExternalTransaction(exception);
103         }
104     }
105
106     /**
107      * INTERNAL:
108      * Begin an external transaction.
109      *
110      * @param session The session for which the transaction is being begun.
111      */

112     public void beginTransaction(AbstractSession session) {
113         try {
114             Object JavaDoc status = getTransactionStatus();
115             logTxStateTrace(session, "TX_begin", status);
116
117             // Make sure that we are in a state that we can actually start
118
// a transaction (e.g. ensure one is not already in progress)
119
if (canBeginTransaction_impl(status)) {
120                 logTxTrace(session, "TX_beginningTxn", null);
121                 beginTransaction_impl();
122                 session.setWasJTSTransactionInternallyStarted(true);
123             }
124         } catch (Exception JavaDoc exception) {
125             throw TransactionException.errorBeginningExternalTransaction(exception);
126         }
127     }
128
129     /**
130      * INTERNAL:
131      * Commit the external transaction.
132      *
133      * @param session The session for which the transaction is being committed.
134      */

135     public void commitTransaction(AbstractSession session) {
136         try {
137             Object JavaDoc status = getTransactionStatus();
138             logTxStateTrace(session, "TX_commit", status);
139
140             if (canCommitTransaction_impl(status)) {
141                 logTxTrace(session, "TX_committingTxn", null);
142
143                 session.setWasJTSTransactionInternallyStarted(false);
144                 commitTransaction_impl();
145             }
146         } catch (Exception JavaDoc exception) {
147             throw TransactionException.errorCommittingExternalTransaction(exception);
148         }
149     }
150
151     /**
152      * INTERNAL:
153      * Roll back the external transaction.
154      *
155      * @param session The session for which the transaction is being rolled back.
156      */

157     public void rollbackTransaction(AbstractSession session) {
158         try {
159             Object JavaDoc status = getTransactionStatus();
160             logTxStateTrace(session, "TX_rollback", status);
161
162             session.setWasJTSTransactionInternallyStarted(false);
163
164             // Only roll back if there is a transaction to roll back
165
if ((canRollbackTransaction_impl(status)) && (getTransaction() != null)) {
166                 logTxTrace(session, "TX_rollingBackTxn", null);
167                 rollbackTransaction_impl();
168             }
169         } catch (Exception JavaDoc exception) {
170             throw TransactionException.errorRollingBackExternalTransaction(exception);
171         }
172     }
173
174     /**
175      * INTERNAL:
176      * Mark the external transaction for rollback.
177      */

178     public void markTransactionForRollback() {
179         try {
180             markTransactionForRollback_impl();
181         } catch (Exception JavaDoc exception) {
182             throw TransactionException.errorMarkingTransactionForRollback(exception);
183         }
184     }
185
186     /**
187      * INTERNAL:
188      * Return the active external transaction object, or null if
189      * none is active. This may be in whatever form the transaction system uses.
190      */

191     public Object JavaDoc getTransaction() {
192         try {
193             return getTransaction_impl();
194         } catch (Exception JavaDoc exception) {
195             throw TransactionException.errorGettingExternalTransaction(exception);
196         }
197     }
198
199     /**
200      * INTERNAL:
201      * Return a key for the specified external transaction object.
202      * The key is just something that can be inserted into a hashtable (must support
203      * hashCode() and equals() methods).
204      */

205     public Object JavaDoc getTransactionKey(Object JavaDoc transaction) {
206         try {
207             return getTransactionKey_impl(transaction);
208         } catch (Exception JavaDoc exception) {
209             throw TransactionException.errorGettingExternalTransaction(exception);
210         }
211     }
212
213     /**
214      * INTERNAL:
215      * Return the transaction status. This may be any type of status or value,
216      * depending upon the transaction system.
217      */

218     public Object JavaDoc getTransactionStatus() {
219         try {
220             return getTransactionStatus_impl();
221         } catch (Exception JavaDoc exception) {
222             throw TransactionException.errorGettingExternalTransactionStatus(exception);
223         }
224     }
225
226     /**
227     * INTERNAL:
228     * Used the EJB 3.0 to determine if a transaction is in a state where an EntityManager can
229     * be closed
230     */

231     public boolean noTransactionOrRolledBackOrCommited() {
232         try {
233             Object JavaDoc status = getTransactionStatus();
234             return canBeginTransaction_impl(status) || canMergeUnitOfWork_impl(status) || isRolledBack_impl(status);
235         } catch (Exception JavaDoc exception) {
236             throw TransactionException.errorGettingExternalTransactionStatus(exception);
237         }
238     }
239
240     /**
241      * INTERNAL:
242      * Return true if the transaction is in the roleld back state.
243      */

244     public abstract boolean isRolledBack_impl(Object JavaDoc status);
245
246     /**
247      * INTERNAL:
248      * Return true if there is a unit of work associated with the active external
249      * transaction. Return false if no transaction is current, or if no uow has
250      * been associated with the active transaction yet.
251      */

252     public boolean hasActiveUnitOfWork() {
253         return this.lookupActiveUnitOfWork() != null;
254     }
255
256     /**
257      * INTERNAL:
258      * Return the active unit of work for the current external transaction.
259      * If no transaction is active then return null. If a transaction is active
260      * but no unit of work has been bound to it then create and return one.
261      */

262     public UnitOfWorkImpl getActiveUnitOfWork() {
263         Object JavaDoc transaction = getTransaction();
264         if (transaction == null) {
265             return null;
266         }
267         
268         UnitOfWorkImpl activeUnitOfWork = lookupActiveUnitOfWork(transaction);
269         if (activeUnitOfWork == null) {
270             // Txn is active but no UoW has been associated with it yet, so create one.
271
activeUnitOfWork = (UnitOfWorkImpl) getSession().acquireUnitOfWork();
272             Object JavaDoc transactionKey = getTransactionKey(transaction);
273             addUnitOfWork(transactionKey, activeUnitOfWork);
274             activeUnitOfWork.setTransaction(transaction);
275             this.activeUnitOfWorkThreadLocal.set(activeUnitOfWork);
276         }
277         return activeUnitOfWork;
278     }
279
280     /**
281      * INTERNAL:
282      * Return the unit of work associated with the active external transaction.
283      * Return null if no transaction is active, or if no uow has been associated with
284      * the active transaction yet.
285      */

286     public UnitOfWorkImpl lookupActiveUnitOfWork() {
287         return lookupActiveUnitOfWork(getTransaction());
288     }
289
290     /**
291      * INTERNAL:
292      * Return the unit of work associated with the active external transaction.
293      * Return null if no transaction is active, or if no uow has been associated with
294      * the active transaction yet.
295      */

296     public UnitOfWorkImpl lookupActiveUnitOfWork(Object JavaDoc transaction) {
297         if (transaction == null) {
298             return null;
299         }
300         Object JavaDoc transactionKey = getTransactionKey(transaction);
301
302         // PERF: Cache the active unit of work in a thread local.
303
// This is just a heuristic, so uses == and no tx-key and direct access as extreamly high throughput.
304
UnitOfWorkImpl activeUnitOfWork = (UnitOfWorkImpl)this.activeUnitOfWorkThreadLocal.get();
305         if (activeUnitOfWork != null) {
306             if (transaction == activeUnitOfWork.getTransaction()) {
307                 return activeUnitOfWork;
308             }
309         }
310         activeUnitOfWork = (UnitOfWorkImpl)getUnitsOfWork().get(transactionKey);
311         if (activeUnitOfWork != null) {
312             activeUnitOfWork.setTransaction(transaction);
313         }
314         this.activeUnitOfWorkThreadLocal.set(activeUnitOfWork);
315         return activeUnitOfWork;
316     }
317
318     /**
319      * INTERNAL:
320      * Add a UnitOfWork object to the Hashtable keyed on the external transaction object.
321      */

322     public void addUnitOfWork(Object JavaDoc transaction, UnitOfWorkImpl activeUnitOfWork) {
323         this.activeUnitOfWorkThreadLocal.set(null);
324         getUnitsOfWork().put(transaction, activeUnitOfWork);
325     }
326
327     /**
328      * INTERNAL:
329      * Remove the unit of work associated with the transaction passed in.
330      */

331     public void removeUnitOfWork(Object JavaDoc transaction) {
332         if (transaction != null) {
333             getUnitsOfWork().remove(transaction);
334         }
335         this.activeUnitOfWorkThreadLocal.set(null);
336     }
337
338     /**
339      * INTERNAL:
340      * Return the manager's session.
341      */

342     public AbstractSession getSession() {
343         return session;
344     }
345
346     /**
347      * INTERNAL:
348      * Set the manager's session.
349      */

350     public void setSession(AbstractSession session) {
351         this.session = session;
352     }
353
354     /**
355      * INTERNAL:
356      * Return the hashtable keyed on the external transaction objectss with values
357      * that are the associated units of work.
358      */

359     public Hashtable getUnitsOfWork() {
360         return unitsOfWork;
361     }
362
363     /**
364      * INTERNAL:
365      * Set the table of transactions to units of work.
366      */

367     protected void setUnitsOfWork(Hashtable unitsOfWork) {
368         this.unitsOfWork = unitsOfWork;
369     }
370
371     /**
372      * INTERNAL:
373      * Get the factory used to generate synchronization listeners.
374      */

375     public SynchronizationListenerFactory getListenerFactory() {
376         return listenerFactory;
377     }
378
379     /**
380      * INTERNAL:
381      * Set the factory used to generate synchronization listeners. This should be
382      * set if a listener other than the default one is being used.
383      */

384     public void setListenerFactory(SynchronizationListenerFactory factory) {
385         listenerFactory = factory;
386     }
387
388     /**
389      * INTERNAL:
390      * Associate the given unit of work and TopLink session with the current external
391      * transaction. This method is offered only for backwards compatibility.
392      */

393     public void registerSynchronizationListener(UnitOfWorkImpl uow, AbstractSession session) throws DatabaseException {
394         this.bindToCurrentTransaction(uow, session);
395     }
396
397     /**
398      * PUBLIC:
399      * Look up a given name in JNDI. This can be used by a subclass or even an
400      * application to look up transaction artifacts that are required for the
401      * implementation.
402      * <p>
403      * The lookup assumes that it is taking place on the server side, and that the
404      * InitialContext can be used without requiring any special properties.
405      *
406      * @param jndiName The name to look up
407      * @return The object bound in JNDI to the specified name
408      * @exception TransactionException Thrown in case of lookup failure
409      */

410     public Object JavaDoc jndiLookup(String JavaDoc jndiName) {
411         Context context = null;
412         Object JavaDoc jndiObject = null;
413         try {
414             context = new InitialContext();
415             jndiObject = context.lookup(jndiName);
416         } catch (NamingException ex) {
417             throw TransactionException.jndiLookupException(jndiName, ex);
418         } finally {
419             try {
420                 context.close();
421             } catch (Exception JavaDoc ex2) {/* ignore */
422             }
423         }
424         return jndiObject;
425     }
426
427     /*
428      * INTERNAL:
429      * Helper method to log trace statements to the transaction channel.
430      */

431     public void logTxTrace(AbstractSession session, String JavaDoc msgInd, Object JavaDoc[] args) {
432         session.log(SessionLog.FINER, SessionLog.TRANSACTION, msgInd, args);
433     }
434
435     /*
436      * INTERNAL:
437      * Helper method to log transaction state to the transaction channel.
438      */

439     public void logTxStateTrace(AbstractSession session, String JavaDoc msgInd, Object JavaDoc status) {
440         if (session.shouldLog(SessionLog.FINER, SessionLog.TRANSACTION)) {
441             String JavaDoc statusString = statusToString_impl(status);
442             Object JavaDoc[] args = { statusString };
443             session.log(SessionLog.FINER, SessionLog.TRANSACTION, msgInd, args);
444         }
445     }
446
447     //---------------------------------------------------------------------------------
448
// The following methods must be implemented by subclass implementations.
449
//---------------------------------------------------------------------------------
450

451     /**
452      * INTERNAL:
453      * Register the specified synchronization listener with the given active
454      * transaction.
455      *
456      * @param listener The synchronization listener created for this transaction
457      * @param txn The active transaction for which notification is being requested
458      */

459     protected abstract void registerSynchronization_impl(AbstractSynchronizationListener listener, Object JavaDoc txn) throws Exception JavaDoc;
460
461     /**
462      * INTERNAL:
463      * Return the active external transaction for the calling thread, or null if
464      * none is currently active for this thread.
465      *
466      * @return The active transaction object, or null if no transaction is active
467      */

468     protected abstract Object JavaDoc getTransaction_impl() throws Exception JavaDoc;
469
470     /**
471      * INTERNAL:
472      * Return a key for the specified external transaction object.
473      * The key is just something that can be inserted into a hashtable (must support
474      * hashCode() and equals() methods).
475      *
476      * @param transaction The transaction to which the returned key applies (may be null)
477      * @return A key for the passed in transaction, or null if no transaction specified
478      */

479     protected abstract Object JavaDoc getTransactionKey_impl(Object JavaDoc transaction) throws Exception JavaDoc;
480
481     /**
482      * INTERNAL:
483      * Return the transaction status. This may be any type of status or value,
484      * depending upon the transaction system.
485      *
486      * @return The current transaction status
487      */

488     protected abstract Object JavaDoc getTransactionStatus_impl() throws Exception JavaDoc;
489
490     /**
491      * INTERNAL:
492      * Begin an external transaction. Do this in a way appropriate to the
493      * transaction subsystem.
494      */

495     protected abstract void beginTransaction_impl() throws Exception JavaDoc;
496
497     /**
498      * INTERNAL:
499      * Commit the external transaction. Do this in a way appropriate to the
500      * transaction subsystem.
501      */

502     protected abstract void commitTransaction_impl() throws Exception JavaDoc;
503
504     /**
505      * INTERNAL:
506      * Roll back the external transaction. Do this in a way appropriate to the
507      * transaction subsystem.
508      */

509     protected abstract void rollbackTransaction_impl() throws Exception JavaDoc;
510
511     /**
512      * INTERNAL:
513      * Mark the external transaction for rollback. Do this in a way appropriate to the
514      * transaction subsystem.
515      */

516     protected abstract void markTransactionForRollback_impl() throws Exception JavaDoc;
517
518     /**
519      * INTERNAL:
520      * Return true if the status indicates that a transaction can be started. This
521      * would normally mean that no transaction is currently active.
522      * The status is interpreted by the transaction subsystem.
523      *
524      * @param status The current transaction status
525      * @return true if the current state allows for a transaction to be started
526      */

527     protected abstract boolean canBeginTransaction_impl(Object JavaDoc status);
528
529     /**
530      * INTERNAL:
531      * Return true if the status indicates that a transaction can be committed. This
532      * would normally mean that a transaction is currently active.
533      * The status is interpreted by the transaction subsystem.
534      *
535      * @param status The current transaction status
536      * @return true if the current state allows for a transaction to be committed
537      */

538     protected abstract boolean canCommitTransaction_impl(Object JavaDoc status);
539
540     /**
541      * INTERNAL:
542      * Return true if the status indicates that a transaction can be rolled back. This
543      * would normally mean that a transaction is currently active.
544      * The status is interpreted by the transaction subsystem.
545      *
546      * @param status The current transaction status
547      * @return true if the current state allows for a transaction to be rolled back
548      */

549     protected abstract boolean canRollbackTransaction_impl(Object JavaDoc status);
550
551     /**
552      * INTERNAL:
553      * Return true if the status indicates that the SQL should be issued to the db.
554      * This would normally mean that a transaction was active and not being rolled
555      * back or marked for rollback.
556      * The status is interpreted by the transaction subsystem.
557      *
558      * @param status The current transaction status
559      * @return true if the current state allows for the SQL to be sent to the database
560      */

561     protected abstract boolean canIssueSQLToDatabase_impl(Object JavaDoc status);
562
563     /**
564      * INTERNAL:
565      * Return true if the status indicates that the unit of work should be merged
566      * into the shared cache. This would normally mean that the transaction was
567      * committed successfully.
568      * The status is interpreted by the transaction subsystem.
569      *
570      * @param status The current transaction status
571      * @return true if the current state dictates that the unit of work should be merged
572      */

573     protected abstract boolean canMergeUnitOfWork_impl(Object JavaDoc status);
574
575     /**
576      * INTERNAL:
577      * Convert the status to a string for tracing.
578      */

579     protected abstract String JavaDoc statusToString_impl(Object JavaDoc status);
580 }
581
Popular Tags