KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > mq > pm > TxManager


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software 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 GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.mq.pm;
23
24 import java.util.Iterator JavaDoc;
25 import java.util.Map JavaDoc;
26 import java.util.Set JavaDoc;
27
28 import javax.jms.JMSException JavaDoc;
29 import javax.transaction.xa.Xid JavaDoc;
30
31 import org.jboss.logging.Logger;
32 import org.jboss.mq.ConnectionToken;
33 import org.jboss.mq.Recoverable;
34 import org.jboss.mq.SpyJMSException;
35
36 import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
37 import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArraySet;
38
39 /**
40  * This class allows provides the base for user supplied persistence packages.
41  *
42  * @author Hiram Chirino (Cojonudo14@hotmail.com)
43  * @author Paul Kendall (paul.kendall@orion.co.nz)
44  * @author <a HREF="mailto:adrian@jboss.org">Adrian Brock</a>
45  * @version $Revision: 45317 $
46  */

47 public class TxManager implements Recoverable
48 {
49    /** The log */
50    private static final Logger log = Logger.getLogger(TxManager.class);
51    
52    /** The persistence manager */
53    PersistenceManager persistenceManager;
54    
55    /** Maps Global transactions to local transactions */
56    ConcurrentHashMap globalToLocal = new ConcurrentHashMap();
57    
58    /** Prepared Transactions Map<Xid, PreparedInfo<Tx>> */
59    ConcurrentHashMap prepared = new ConcurrentHashMap();
60    
61    /**
62     * Create a new TxManager
63     *
64     * @param pm the persistence manager
65     */

66    public TxManager(PersistenceManager pm)
67    {
68       persistenceManager = pm;
69    }
70
71    /**
72      * Return the local transaction id for a distributed transaction id.
73      *
74      * @deprecated
75      * @param dc the connection
76      * @param xid the transaction id
77      * @return The Prepared transaction
78      * @exception javax.jms.JMSException Description of Exception
79      */

80    public final Tx getPrepared(ConnectionToken dc, Object JavaDoc xid) throws JMSException JavaDoc
81    {
82       GlobalXID gxid = new GlobalXID(dc, xid);
83       Tx txid = (Tx) globalToLocal.get(gxid);
84       if (txid == null)
85          throw new SpyJMSException("Transaction does not exist from: " + dc.getClientID() + " xid=" + xid);
86
87       return txid;
88    }
89
90    /**
91      * Create and return a unique transaction id.
92      *
93      * @return the transaction id
94      * @exception JMSException for any error
95      */

96    public final Tx createTx() throws JMSException JavaDoc
97    {
98       Tx txId = persistenceManager.createPersistentTx();
99       return txId;
100    }
101
102    /**
103      * Commit the transaction to the persistent store.
104      *
105      * @param txId the transaction
106      * @exception JMSException for any error
107      */

108    public final void commitTx(Tx txId) throws JMSException JavaDoc
109    {
110       boolean trace = log.isTraceEnabled();
111       if (trace)
112          log.trace("Commit branch=" + txId.longValue());
113       txId.commit(persistenceManager);
114    }
115
116    /**
117     * Commit the transaction to the persistent store.
118     *
119     * @param dc the connection token
120     * @param xid the transaction
121     * @exception JMSException for any error
122     */

123    public final void commitTx(ConnectionToken dc, Object JavaDoc xid) throws JMSException JavaDoc
124    {
125       boolean trace = log.isTraceEnabled();
126       GlobalXID gxid = new GlobalXID(dc, xid);
127       Tx txid = (Tx) globalToLocal.get(gxid);
128       if (txid == null)
129       {
130          PreparedInfo preparedInfo = (PreparedInfo) prepared.get(xid);
131          if (preparedInfo == null)
132             throw new SpyJMSException("Transaction does not exist from: " + dc.getClientID() + " xid=" + xid);
133          Set JavaDoc txids = preparedInfo.getTxids();
134          for (Iterator JavaDoc i = txids.iterator(); i.hasNext();)
135          {
136             txid = (Tx) i.next();
137             if (trace)
138                log.trace("Commit xid=" + xid + " branch=" + txid.longValue());
139             txid.commit(persistenceManager);
140          }
141          prepared.remove(xid);
142       }
143       else
144       {
145          if (trace)
146             log.trace("Commit xid=" + xid + " branch=" + txid.longValue());
147          txid.commit(persistenceManager);
148       }
149    }
150    
151    /**
152     * Add an operation for after a commit
153     *
154     * @param txId the transaction
155     * @param task the task
156     * @throws JMSException for any error
157     */

158    public void addPostCommitTask(Tx txId, Runnable JavaDoc task) throws JMSException JavaDoc
159    {
160       if (txId == null)
161       {
162          task.run();
163          return;
164       }
165
166       txId.addPostCommitTask(task);
167    }
168
169    /**
170      * Rollback the transaction.
171      *
172      * @param txId the transaction
173      * @exception JMSException for any error
174      */

175    public void rollbackTx(Tx txId) throws JMSException JavaDoc
176    {
177       boolean trace = log.isTraceEnabled();
178       if (trace)
179          log.trace("Rollback branch=" + txId.longValue());
180       txId.rollback(persistenceManager);
181    }
182
183    /**
184     * Rollback the transaction
185     *
186     * @param dc the connection token
187     * @param xid the transaction
188     * @exception JMSException for any error
189     */

190    public final void rollbackTx(ConnectionToken dc, Object JavaDoc xid) throws JMSException JavaDoc
191    {
192       boolean trace = log.isTraceEnabled();
193       GlobalXID gxid = new GlobalXID(dc, xid);
194       Tx txid = (Tx) globalToLocal.get(gxid);
195       if (txid == null)
196       {
197          PreparedInfo preparedInfo = (PreparedInfo) prepared.get(xid);
198          if (preparedInfo == null)
199             throw new SpyJMSException("Transaction does not exist from: " + dc.getClientID() + " xid=" + xid);
200          Set JavaDoc txids = preparedInfo.getTxids();
201          for (Iterator JavaDoc i = txids.iterator(); i.hasNext();)
202          {
203             txid = (Tx) i.next();
204             if (trace)
205                log.trace("Rolling back xid=" + xid + " branch=" + txid.longValue());
206             txid.rollback(persistenceManager);
207          }
208          prepared.remove(xid);
209       }
210       else
211       {
212          if (trace)
213             log.trace("Rolling back xid=" + xid + " branch=" + txid.longValue());
214          txid.rollback(persistenceManager);
215       }
216    }
217
218    /**
219     * Add an operation for after a rollback
220     *
221     * @param txId the transaction
222     * @param task the task
223     * @throws JMSException for any error
224     */

225    public void addPostRollbackTask(Tx txId, Runnable JavaDoc task) throws JMSException JavaDoc
226    {
227       if (txId == null)
228          return;
229
230       txId.addPostRollbackTask(task);
231    }
232
233    /**
234      * Create and return a unique transaction id. Given a distributed connection
235      * and a transaction id object, allocate a unique local transaction id if
236      * the remote id is not already known.
237      *
238      * @param dc the connection token
239      * @param xid the xid
240      * @return the transaction
241      * @exception JMSException for any error
242      */

243    public Tx createTx(ConnectionToken dc, Object JavaDoc xid) throws JMSException JavaDoc
244    {
245       GlobalXID gxid = new GlobalXID(dc, xid);
246       if (globalToLocal.containsKey(gxid))
247          throw new SpyJMSException("Duplicate transaction from: " + dc.getClientID() + " xid=" + xid);
248
249       Tx txId = createTx();
250       if (xid != null && xid instanceof Xid JavaDoc)
251          txId.setXid((Xid JavaDoc) xid);
252       globalToLocal.put(gxid, txId);
253
254       //Tasks to remove the global to local mappings on commit/rollback
255
txId.addPostCommitTask(gxid);
256       txId.addPostRollbackTask(gxid);
257
258       return txId;
259    }
260
261    /**
262     * Restore a prepared transaction
263     *
264     * @param txId the transaction id
265     * @throws JMSException for any error
266     */

267    public void restoreTx(Tx txId) throws JMSException JavaDoc
268    {
269       addPreparedTx(txId, txId.getXid(), true);
270    }
271
272    /**
273     * Add a prepared transactions
274     *
275     * @param txId the transaction id
276     * @param xid the xid
277     * @param inDoubt whether it is in doubt
278     * @throws JMSException for any error
279     */

280    void addPreparedTx(Tx txId, Xid JavaDoc xid, boolean inDoubt) throws JMSException JavaDoc
281    {
282       PreparedInfo preparedInfo = (PreparedInfo) prepared.get(xid);
283       if (preparedInfo == null)
284       {
285          preparedInfo = new PreparedInfo(xid, false);
286          prepared.put(xid, preparedInfo);
287       }
288       preparedInfo.add(txId);
289       if (inDoubt)
290          preparedInfo.setInDoubt(true);
291    }
292    
293    /**
294     * Mark the transaction branch as prepared
295     *
296     * @param dc the connection token
297     * @param xid the xid
298     * @param txId the transaction
299     * @throws JMSException for any error
300     */

301    public void markPrepared(ConnectionToken dc, Object JavaDoc xid, Tx txId) throws JMSException JavaDoc
302    {
303       try
304       {
305          if (xid instanceof Xid JavaDoc)
306             addPreparedTx(txId, (Xid JavaDoc) xid, false);
307       }
308       catch (Throwable JavaDoc t)
309       {
310          SpyJMSException.rethrowAsJMSException("Error marking transaction as prepared xid=" + xid + " tx=" + txId, t);
311       }
312    }
313
314    public Xid JavaDoc[] recover(ConnectionToken dc, int flags) throws Exception JavaDoc
315    {
316       Set JavaDoc preparedXids = prepared.keySet();
317       Xid JavaDoc[] xids = (Xid JavaDoc[]) preparedXids.toArray(new Xid JavaDoc[preparedXids.size()]);
318       return xids;
319    }
320    
321    /**
322     * Get the prepared transactions
323     *
324     * @return
325     */

326    public Map JavaDoc getPreparedTransactions()
327    {
328       return prepared;
329    }
330    
331    /**
332      * A global transaction
333      */

334    class GlobalXID implements Runnable JavaDoc
335    {
336       ConnectionToken dc;
337       Object JavaDoc xid;
338
339       GlobalXID(ConnectionToken dc, Object JavaDoc xid)
340       {
341          this.dc = dc;
342          this.xid = xid;
343       }
344
345       public boolean equals(Object JavaDoc obj)
346       {
347          if (obj == null)
348          {
349             return false;
350          }
351          if (obj.getClass() != GlobalXID.class)
352          {
353             return false;
354          }
355          return ((GlobalXID) obj).xid.equals(xid) && ((GlobalXID) obj).dc.equals(dc);
356       }
357
358       public int hashCode()
359       {
360          return xid.hashCode();
361       }
362
363       public void run()
364       {
365          Tx txId = (Tx) globalToLocal.remove(this);
366          // Tidyup the prepared transactions
367
if (txId != null)
368          {
369             PreparedInfo preparedInfo = (PreparedInfo) prepared.get(xid);
370             if (preparedInfo != null)
371             {
372                preparedInfo.remove(txId);
373                if (preparedInfo.isEmpty())
374                   prepared.remove(xid);
375             }
376          }
377       }
378    }
379
380    /**
381     * Information about a prepared global transaction
382     *
383     * @author <a HREF="adrian@jboss.com">Adrian Brock</a>
384     * @version $Revision: 45317 $
385     */

386    public static class PreparedInfo
387    {
388       /** The XID */
389       private Xid JavaDoc xid;
390       
391       /** Whether the transaction is in doubt */
392       private boolean inDoubt;
393       
394       /** The local transaction branches */
395       private Set JavaDoc txids = new CopyOnWriteArraySet();
396
397       /**
398        * Create a new PreparedInfo.
399        *
400        * @param xid the xid
401        * @param indoubt whether the transaction is in doubt
402        */

403       public PreparedInfo(Xid JavaDoc xid, boolean inDoubt)
404       {
405          this.xid = xid;
406          this.inDoubt = inDoubt;
407       }
408
409       /**
410        * Whether the transaction is in doubt
411        *
412        * @return true when in doubt
413        */

414       public boolean isInDoubt()
415       {
416          return inDoubt;
417       }
418       
419       /**
420        * Set the in doubt
421        *
422        * @param inDoubt the in doubt value
423        */

424       public void setInDoubt(boolean inDoubt)
425       {
426          this.inDoubt = inDoubt;
427       }
428       
429       /**
430        * Get the XID
431        *
432        * @return
433        */

434       public Xid JavaDoc getXid()
435       {
436          return xid;
437       }
438
439       /**
440        * Get the local branches
441        *
442        * @return the local branches
443        */

444       public Set JavaDoc getTxids()
445       {
446          return txids;
447       }
448       
449       /**
450        * Add a local branch
451        *
452        * @param txid the local branch
453        */

454       public void add(Tx txid)
455       {
456          txids.add(txid);
457       }
458       
459       /**
460        * Remove a local branch
461        *
462        * @param txid the local branch
463        */

464       public void remove(Tx txid)
465       {
466          txids.remove(txid);
467       }
468       
469       /**
470        * Whether there are no local branches
471        *
472        * @return true when there no local branches
473        */

474       public boolean isEmpty()
475       {
476          return txids.isEmpty();
477       }
478    }
479 }
Popular Tags