KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > slide > transaction > SlideTransaction


1 /*
2  * $Header: /home/cvs/jakarta-slide/src/share/org/apache/slide/transaction/SlideTransaction.java,v 1.23 2004/07/28 09:34:33 ib Exp $
3  * $Revision: 1.23 $
4  * $Date: 2004/07/28 09:34:33 $
5  *
6  * ====================================================================
7  *
8  * Copyright 1999-2002 The Apache Software Foundation
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  * http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  *
22  */

23
24 package org.apache.slide.transaction;
25
26 import java.io.PrintWriter;
27 import java.io.StringWriter;
28 import java.util.Enumeration;
29 import java.util.Hashtable;
30 import java.util.Vector;
31
32 import javax.transaction.HeuristicMixedException;
33 import javax.transaction.HeuristicRollbackException;
34 import javax.transaction.RollbackException;
35 import javax.transaction.Status;
36 import javax.transaction.Synchronization;
37 import javax.transaction.SystemException;
38 import javax.transaction.Transaction;
39 import javax.transaction.xa.XAException;
40 import javax.transaction.xa.XAResource;
41 import javax.transaction.xa.Xid;
42
43 import org.apache.slide.util.Messages;
44 import org.apache.slide.util.logger.Logger;
45
46 /**
47  * JTA Transaction implementation.
48  *
49  * @version $Revision: 1.23 $
50  */

51 public final class SlideTransaction implements Transaction {
52     
53     
54     // -------------------------------------------------------------- Constants
55

56     
57     protected static final String LOG_CHANNEL =
58         SlideTransaction.class.getName();
59     
60     
61     // ------------------------------------------------------------ Constructor
62

63     
64     /**
65      * Constructor.
66      */

67     public SlideTransaction(SlideTransactionManager transactionManager) {
68         // Generate the transaction id
69
globalCreatedTransactions++;
70         currentTransactionNumber = globalCreatedTransactions;
71         currentThreadName = Thread.currentThread().getName();
72         xid = new SlideXid
73             ((currentThreadName + "-" + System.currentTimeMillis() + "-"
74                   + currentTransactionNumber).getBytes(),
75              0, new byte[0]);
76         this.transactionManager = transactionManager;
77     }
78     
79     
80     // ----------------------------------------------------- Instance Variables
81

82     
83     /**
84      * Global transaction id.
85      */

86     private SlideXid xid;
87     
88     
89     /**
90      * Branches.
91      * Keyed : branch xid -> resource manager.
92      */

93     private Hashtable branches = new Hashtable();
94     
95     
96     /**
97      * Active branches.
98      * Keyed : resource manager -> branches xid.
99      */

100     private Hashtable activeBranches = new Hashtable();
101     
102     
103     /**
104      * Enlisted resources.
105      */

106     private Vector enlistedResources = new Vector();
107     
108     
109     /**
110      * Suspended resources.
111      * Keyed : resource manager -> branches xid.
112      */

113     private Hashtable suspendedResources = new Hashtable();
114     
115     
116     /**
117      * Transaction status.
118      */

119     private int status = Status.STATUS_ACTIVE;
120     
121     
122     /**
123      * Synchronization objects.
124      */

125     private Vector synchronizationObjects = new Vector();
126     
127     
128     /**
129      * Branch counter.
130      */

131     private int branchCounter = 1;
132     
133     
134     /**
135      * Number of transactions created.
136      */

137     private static int globalCreatedTransactions = 0;
138     
139     
140     /**
141      * Transaction number.
142      */

143     private int currentTransactionNumber = 0;
144     
145     
146     /**
147      * Name of the thread bound to the transaction.
148      */

149     private String currentThreadName = null;
150     
151     
152     /**
153      * Associated transaction manager.
154      */

155     private SlideTransactionManager transactionManager;
156     
157     
158     // ------------------------------------------------------------- Properties
159

160     
161     // ---------------------------------------------------- Transaction Methods
162

163     
164     /**
165      * Complete the transaction represented by this Transaction object.
166      *
167      * @exception RollbackException Thrown to indicate that the transaction
168      * has been rolled back rather than committed.
169      * @exception HeuristicMixedException Thrown to indicate that a heuristic
170      * decision was made and that some relevant updates have been committed
171      * while others have been rolled back.
172      * @exception HeuristicRollbackException Thrown to indicate that a
173      * heuristic decision was made and that some relevant updates have been
174      * rolled back.
175      * @exception SecurityException Thrown to indicate that the thread is not
176      * allowed to commit the transaction.
177      * @exception IllegalStateException Thrown if the current thread is not
178      * associated with a transaction.
179      * @exception SystemException Thrown if the transaction manager encounters
180      * an unexpected error condition.
181      */

182     public void commit()
183         throws RollbackException, HeuristicMixedException,
184         HeuristicRollbackException, SecurityException, IllegalStateException,
185         SystemException {
186         
187         //System.out.println(this + " COMMIT ");
188

189         if (status == Status.STATUS_MARKED_ROLLBACK) {
190             rollback();
191             return;
192         }
193         
194         // Check status ACTIVE
195
if (status != Status.STATUS_ACTIVE)
196             throw new IllegalStateException();
197         
198         // Call synchronized objects beforeCompletion
199
Enumeration syncList = synchronizationObjects.elements();
200         while (syncList.hasMoreElements()) {
201             Synchronization sync = (Synchronization) syncList.nextElement();
202             sync.beforeCompletion();
203         }
204         
205         Vector exceptions = new Vector();
206         boolean fail = false;
207         
208         Enumeration enum = branches.keys();
209         
210         if (enlistedResources.size() == 1) {
211             
212             // One phase commit
213
status = Status.STATUS_COMMITTING;
214             while (enum.hasMoreElements()) {
215                 Object key = enum.nextElement();
216                 XAResource resourceManager =
217                     (XAResource) branches.get(key);
218                 try {
219                     if (!fail)
220                         resourceManager.commit(xid, true);
221                     else
222                         resourceManager.rollback(xid);
223                 } catch (Throwable e) {
224                     // Adding the exception to the error code list
225
String logMessage = Messages.format
226                         (SlideTransaction.class.getName() + ".commitFail",
227                          resourceManager, getXAErrorCode(e), toString());
228                     transactionManager.getLogger().log
229                         (logMessage, e, LOG_CHANNEL, Logger.WARNING);
230                     exceptions.addElement(e);
231                     fail = true;
232                     status = Status.STATUS_MARKED_ROLLBACK;
233                 }
234             }
235             if (!fail) {
236                 status = Status.STATUS_COMMITTED;
237             } else {
238                 status = Status.STATUS_ROLLEDBACK;
239             }
240             
241         } else if (enlistedResources.size() != 0) {
242             
243             // Prepare each enlisted resource
244
status = Status.STATUS_PREPARING;
245             while ((!fail) && (enum.hasMoreElements())) {
246                 Object key = enum.nextElement();
247                 XAResource resourceManager =
248                     (XAResource) branches.get(key);
249                 try {
250                     // Preparing the resource manager using its branch xid
251
resourceManager.prepare((Xid) key);
252                 } catch (Throwable e) {
253                     String logMessage = Messages.format
254                         (SlideTransaction.class.getName() + ".prepareFail",
255                          resourceManager, getXAErrorCode(e), toString());
256                     transactionManager.getLogger().log
257                         (logMessage, e, LOG_CHANNEL, Logger.WARNING);
258                     // Adding the exception to the error code list
259
exceptions.addElement(e);
260                     fail = true;
261                     status = Status.STATUS_MARKED_ROLLBACK;
262                 }
263             }
264             
265             if (!fail)
266                 status = Status.STATUS_PREPARED;
267             
268             // If fail, rollback
269
if (fail) {
270                 status = Status.STATUS_ROLLING_BACK;
271                 fail = false;
272                 // Rolling back all the prepared (and unprepared) branches
273
enum = branches.keys();
274                 while (enum.hasMoreElements()) {
275                     Object key = enum.nextElement();
276                     XAResource resourceManager =
277                         (XAResource) branches.get(key);
278                     try {
279                         resourceManager.rollback((Xid) key);
280                     } catch(Throwable e) {
281                         String logMessage = Messages.format
282                             (SlideTransaction.class.getName() + ".rollbackFail",
283                              resourceManager, getXAErrorCode(e), toString());
284                         transactionManager.getLogger().log
285                             (logMessage, e, LOG_CHANNEL, Logger.WARNING);
286                         exceptions.addElement(e);
287                         fail = true;
288                     }
289                 }
290                 status = Status.STATUS_ROLLEDBACK;
291             } else {
292                 status = Status.STATUS_COMMITTING;
293                 // Commit each enlisted resource
294
enum = branches.keys();
295                 while (enum.hasMoreElements()) {
296                     Object key = enum.nextElement();
297                     XAResource resourceManager =
298                         (XAResource) branches.get(key);
299                     try {
300                         resourceManager.commit((Xid) key, false);
301                     } catch(Throwable e) {
302                         String logMessage = Messages.format
303                             (SlideTransaction.class.getName() + ".commitFail",
304                              resourceManager, getXAErrorCode(e), toString());
305                         transactionManager.getLogger().log
306                             (logMessage, e, LOG_CHANNEL, Logger.WARNING);
307                         exceptions.addElement(e);
308                         fail = true;
309                     }
310                 }
311                 status = Status.STATUS_COMMITTED;
312             }
313             
314         }
315         
316         // Call synchronized objects afterCompletion
317
syncList = synchronizationObjects.elements();
318         while (syncList.hasMoreElements()) {
319             Synchronization sync =
320                 (Synchronization) syncList.nextElement();
321             sync.afterCompletion(status);
322         }
323         
324         // Parsing exception and throwing an appropriate exception
325
enum = exceptions.elements();
326         if (enum.hasMoreElements()) {
327             if ((status == Status.STATUS_ROLLEDBACK) && (!fail))
328                 throw new RollbackException();
329             if (status == Status.STATUS_ROLLEDBACK)
330                 throw new HeuristicRollbackException();
331             if ((status == Status.STATUS_COMMITTED) && (fail))
332                 throw new HeuristicMixedException();
333         }
334         
335     }
336     
337     
338     /**
339      * Delist the resource specified from the current transaction associated
340      * with the calling thread.
341      *
342      * @param xaRes The XAResource object representing the resource to delist
343      * @param flag One of the values of TMSUCCESS, TMSUSPEND, or TMFAIL
344      * @exception IllegalStateException Thrown if the transaction in the
345      * target object is inactive.
346      * @exception SystemException Thrown if the transaction manager encounters
347      * an unexpected error condition.
348      */

349     public boolean delistResource(XAResource xaRes, int flag)
350         throws IllegalStateException, SystemException {
351         
352         //System.out.println(this + " DELIST " + xaRes);
353

354         // Check status ACTIVE
355
if (status != Status.STATUS_ACTIVE)
356             throw new IllegalStateException();
357         
358         Xid xid = (Xid) activeBranches.get(xaRes);
359         
360         if (xid == null)
361             throw new IllegalStateException();
362         
363         activeBranches.remove(xaRes);
364         
365         if (transactionManager.getLogger().isEnabled
366                 (LOG_CHANNEL, Logger.DEBUG)) {
367             String logMessage = Messages.format
368                 (SlideTransaction.class.getName() + ".delist", xaRes,
369                  getXAFlag(flag), toString());
370             transactionManager.getLogger().log
371                 (logMessage, LOG_CHANNEL, Logger.DEBUG);
372         }
373         
374         XAException exception = null;
375         
376         try {
377             xaRes.end(xid, flag);
378         } catch (XAException e) {
379             exception = e;
380         }
381         
382         if (exception != null) {
383             String logMessage = Messages.format
384                 (SlideTransaction.class.getName() + ".delistFail",
385                  xaRes, getXAErrorCode(exception), toString());
386             transactionManager.getLogger().log
387                 (logMessage, LOG_CHANNEL, Logger.WARNING);
388             return false;
389         }
390         
391         if (flag == XAResource.TMSUSPEND)
392             suspendedResources.put(xaRes, xid);
393         
394         //System.out.println("Delisted ok(" + this + ") = " + xaRes + " xid: " + xid);
395

396         return true;
397         
398     }
399     
400     
401     /**
402      * Enlist the resource specified with the current transaction context of
403      * the calling thread.
404      *
405      * @param xaRes The XAResource object representing the resource to delist
406      * @return true if the resource was enlisted successfully; otherwise false.
407      * @exception RollbackException Thrown to indicate that the transaction
408      * has been marked for rollback only.
409      * @exception IllegalStateException Thrown if the transaction in the
410      * target object is in prepared state or the transaction is inactive.
411      * @exception SystemException Thrown if the transaction manager
412      * encounters an unexpected error condition.
413      */

414     public boolean enlistResource(XAResource xaRes)
415         throws RollbackException, IllegalStateException, SystemException {
416         
417         //System.out.println(this + " ENLIST " + xaRes);
418

419         if (status == Status.STATUS_MARKED_ROLLBACK)
420             throw new RollbackException();
421         
422         // Check status ACTIVE
423
if (status != Status.STATUS_ACTIVE)
424             throw new IllegalStateException();
425         
426         // Preventing two branches from being active at the same time on the
427
// same resource manager
428
Xid activeXid = (Xid) activeBranches.get(xaRes);
429         if (activeXid != null)
430             return false;
431         
432         boolean alreadyEnlisted = false;
433         int flag = XAResource.TMNOFLAGS;
434         
435         Xid branchXid = (Xid) suspendedResources.get(xaRes);
436         
437         if (branchXid == null) {
438             Enumeration enum = enlistedResources.elements();
439             while ((!alreadyEnlisted) && (enum.hasMoreElements())) {
440                 XAResource resourceManager = (XAResource) enum.nextElement();
441                 try {
442                     if (resourceManager.isSameRM(xaRes)) {
443                         flag = XAResource.TMJOIN;
444                         alreadyEnlisted = true;
445                     }
446                 } catch (XAException e) {
447                 }
448             }
449             branchXid = this.xid.newBranch(branchCounter++);
450             
451             //System.out.println(this + " Creating new branch for " + xaRes);
452

453         } else {
454             alreadyEnlisted = true;
455             flag = XAResource.TMRESUME;
456             suspendedResources.remove(xaRes);
457         }
458         
459         if (transactionManager.getLogger().isEnabled
460                 (LOG_CHANNEL, Logger.DEBUG)) {
461             String logMessage = Messages.format
462                 (SlideTransaction.class.getName() + ".enlist", xaRes,
463                  getXAFlag(flag), toString());
464             transactionManager.getLogger().log
465                 (logMessage, LOG_CHANNEL, Logger.DEBUG);
466         }
467         
468         try {
469             //System.out.println("Starting(" + this + ") = " + xaRes + " Branch: " + branchXid + " Flag: " + flag);
470

471             xaRes.start(branchXid, flag);
472         } catch (XAException e) {
473             String logMessage = Messages.format
474                 (SlideTransaction.class.getName() + ".enlistFail",
475                  xaRes, getXAErrorCode(e), toString());
476             transactionManager.getLogger().log
477                 (logMessage, LOG_CHANNEL, Logger.WARNING);
478             return false;
479         }
480         
481         if (!alreadyEnlisted) {
482             enlistedResources.addElement(xaRes);
483         }
484         
485         branches.put(branchXid, xaRes);
486         activeBranches.put(xaRes, branchXid);
487         
488         return true;
489         
490     }
491     
492     
493     /**
494      * Roll back the transaction associated with the current thread. When
495      * this method completes, the thread becomes associated with no
496      * transaction.
497      *
498      * @exception SecurityException Thrown to indicate that the thread is not
499      * allowed to commit the transaction.
500      * @exception IllegalStateException Thrown if the current thread is not
501      * associated with a transaction.
502      * @exception SystemException Thrown if the transaction manager encounters
503      * an unexpected error condition.
504      */

505     public void rollback()
506         throws SecurityException, IllegalStateException, SystemException {
507         
508         //System.out.println(this + " ROLLBACK ");
509

510         // Check status ACTIVE
511
if (status != Status.STATUS_ACTIVE && status != Status.STATUS_MARKED_ROLLBACK)
512             throw new IllegalStateException();
513         
514         Vector exceptions = new Vector();
515         
516         Enumeration enum = branches.keys();
517         
518         status = Status.STATUS_ROLLING_BACK;
519         while (enum.hasMoreElements()) {
520             Xid xid = (Xid) enum.nextElement();
521             XAResource resourceManager = (XAResource) branches.get(xid);
522             try {
523                 resourceManager.rollback(xid);
524             } catch (Throwable e) {
525                 exceptions.addElement(e);
526                 String logMessage = Messages.format
527                     (SlideTransaction.class.getName() + ".rollbackFail",
528                      resourceManager, getXAErrorCode(e), toString());
529                 transactionManager.getLogger().log
530                     (logMessage, LOG_CHANNEL, Logger.WARNING);
531             }
532         }
533         status = Status.STATUS_ROLLEDBACK;
534         
535     }
536     
537     
538     /**
539      * Modify the transaction associated with the current thread such that
540      * the only possible outcome of the transaction is to roll back the
541      * transaction.
542      *
543      * @exception IllegalStateException Thrown if the current thread is not
544      * associated with a transaction.
545      * @exception SystemException Thrown if the transaction manager encounters
546      * an unexpected error condition.
547      */

548     public void setRollbackOnly()
549         throws IllegalStateException, SystemException {
550         status = Status.STATUS_MARKED_ROLLBACK;
551     }
552     
553     
554     /**
555      * Obtain the status of the transaction associated with the current thread.
556      *
557      * @exception SystemException Thrown if the transaction manager encounters
558      * an unexpected error condition.
559      * @return The transaction status. If no transaction is associated with
560      * the current thread, this method returns the Status.NoTransaction value.
561      */

562     public int getStatus()
563         throws SystemException {
564         return status;
565     }
566     
567     
568     /**
569      * Register a synchronization object for the transaction currently
570      * associated with the calling thread. The transction manager invokes the
571      * beforeCompletion method prior to starting the transaction commit
572      * process. After the transaction is completed, the transaction manager
573      * invokes the afterCompletion method.
574      *
575      * @param sync The Synchronization object for the transaction associated
576      * with the target object.
577      * @exception RollbackException Thrown to indicate that the transaction
578      * has been marked for rollback only.
579      * @exception IllegalStateException Thrown if the transaction in the
580      * target object is in prepared state or the transaction is inactive.
581      * @exception SystemException Thrown if the transaction manager encounters
582      * an unexpected error condition.
583      */

584     public void registerSynchronization(Synchronization sync)
585         throws RollbackException, IllegalStateException, SystemException {
586         
587         if (status == Status.STATUS_MARKED_ROLLBACK)
588             throw new RollbackException();
589         
590         if (status != Status.STATUS_ACTIVE)
591             throw new IllegalStateException();
592         
593         synchronizationObjects.addElement(sync);
594         
595     }
596     
597     
598     // --------------------------------------------------------- Public Methods
599

600     
601     /**
602      * Return a String representation of the error code contained in a
603      * XAException.
604      */

605     public static String getXAErrorCode(Throwable throww) {
606         String result = null;
607         if (throww instanceof XAException)
608             result = getXAErrorCode((XAException)throww);
609         else {
610             StringWriter sw = new StringWriter();
611             throww.printStackTrace( new PrintWriter(sw, true) ); //autoFlush=true
612
result = sw.toString();
613         }
614         return result;
615     }
616     /**
617      * Return a String representation of the error code contained in a
618      * XAException.
619      */

620     public static String getXAErrorCode(XAException xae) {
621         switch (xae.errorCode) {
622             case XAException.XA_HEURCOM:
623                 return "XA_HEURCOM";
624             case XAException.XA_HEURHAZ:
625                 return "XA_HEURHAZ";
626             case XAException.XA_HEURMIX:
627                 return "XA_HEURMIX";
628             case XAException.XA_HEURRB:
629                 return "XA_HEURRB";
630             case XAException.XA_NOMIGRATE:
631                 return "XA_NOMIGRATE";
632             case XAException.XA_RBBASE:
633                 return "XA_RBBASE";
634             case XAException.XA_RBCOMMFAIL:
635                 return "XA_RBCOMMFAIL";
636             case XAException.XA_RBDEADLOCK:
637                 return "XA_RBBEADLOCK";
638             case XAException.XA_RBEND:
639                 return "XA_RBEND";
640             case XAException.XA_RBINTEGRITY:
641                 return "XA_RBINTEGRITY";
642             case XAException.XA_RBOTHER:
643                 return "XA_RBOTHER";
644             case XAException.XA_RBPROTO:
645                 return "XA_RBPROTO";
646             case XAException.XA_RBTIMEOUT:
647                 return "XA_RBTIMEOUT";
648             case XAException.XA_RDONLY:
649                 return "XA_RDONLY";
650             case XAException.XA_RETRY:
651                 return "XA_RETRY";
652             case XAException.XAER_ASYNC:
653                 return "XAER_ASYNC";
654             case XAException.XAER_DUPID:
655                 return "XAER_DUPID";
656             case XAException.XAER_INVAL:
657                 return "XAER_INVAL";
658             case XAException.XAER_NOTA:
659                 return "XAER_NOTA";
660             case XAException.XAER_OUTSIDE:
661                 return "XAER_OUTSIDE";
662             case XAException.XAER_PROTO:
663                 return "XAER_PROTO";
664             case XAException.XAER_RMERR:
665                 return "XAER_RMERR";
666             case XAException.XAER_RMFAIL:
667                 return "XAER_RMFAIL";
668             default:
669                 return "UNKNOWN";
670         }
671     }
672     
673     
674     /**
675      * Return a String representation of a flag.
676      */

677     public static String getXAFlag(int flag) {
678         switch (flag) {
679             case XAResource.TMENDRSCAN:
680                 return "TMENDRSCAN";
681             case XAResource.TMFAIL:
682                 return "TMFAIL";
683             case XAResource.TMJOIN:
684                 return "TMJOIN";
685             case XAResource.TMNOFLAGS:
686                 return "TMNOFLAGS";
687             case XAResource.TMONEPHASE:
688                 return "TMONEPHASE";
689             case XAResource.TMRESUME:
690                 return "TMRESUME";
691             case XAResource.TMSTARTRSCAN:
692                 return "TMSTARTRSCAN";
693             case XAResource.TMSUCCESS:
694                 return "TMSUCCESS";
695             case XAResource.TMSUSPEND:
696                 return "TMSUSPEND";
697             default:
698                 return "UNKNOWN";
699         }
700     }
701     
702     
703     /**
704      * Print the Transaction object in a debugger friendly manner
705      */

706     public String toString() {
707         return "Transaction " + currentTransactionNumber
708             + " xid " + xid + " in thread " + currentThreadName +
709             (currentThreadName.equals(Thread.currentThread().getName())?"":
710                  " current= " + Thread.currentThread().getName());
711     }
712     
713     
714 }
715
716
717
Popular Tags