View Javadoc

1   package org.csc.phynixx.xa;
2   
3   /*
4    * #%L
5    * phynixx-xa
6    * %%
7    * Copyright (C) 2014 csc
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  
23  
24  import org.csc.phynixx.common.cast.ImplementorUtils;
25  import org.csc.phynixx.common.exceptions.DelegatedRuntimeException;
26  import org.csc.phynixx.common.exceptions.ExceptionUtils;
27  import org.csc.phynixx.common.logger.IPhynixxLogger;
28  import org.csc.phynixx.common.logger.PhynixxLogManager;
29  import org.csc.phynixx.connection.IPhynixxConnection;
30  import org.csc.phynixx.connection.IPhynixxManagedConnection;
31  import org.csc.phynixx.connection.IPhynixxManagedConnectionFactory;
32  
33  import javax.transaction.RollbackException;
34  import javax.transaction.SystemException;
35  import javax.transaction.Transaction;
36  import javax.transaction.TransactionManager;
37  import javax.transaction.xa.XAResource;
38  import javax.transaction.xa.Xid;
39  
40  
41  /**
42   * keeps the XAresource's association to the transactional branch (given via XID).
43   *
44   * @author Christoph Schmidt-Casdorff
45   */
46  class PhynixxManagedXAConnection<C extends IPhynixxConnection> implements IPhynixxXAConnection<C> {
47  
48      private static final IPhynixxLogger LOG = PhynixxLogManager.getLogger(PhynixxManagedXAConnection.class);
49  
50      private final PhynixxXAResource<C> xaResource;
51  
52      private IPhynixxManagedConnectionFactory<C> managedConnectionFactory;
53  
54      private transient ITransactionBinding<C> transactionBinding = null;
55  
56      private TransactionManager transactionManager = null;
57  
58      private final IXATransactionalBranchRepository<C> xaTransactionalBranchDictionary;
59  
60      private boolean enlisted;
61  
62      PhynixxManagedXAConnection(PhynixxXAResource<C> xaResource,
63                                 TransactionManager transactionManager,
64                                 IXATransactionalBranchRepository<C> xaTransactionalBranchDictionary,
65                                 IPhynixxManagedConnectionFactory<C> managedConnectionFactory) {
66          this.xaResource = xaResource;
67          this.xaTransactionalBranchDictionary = xaTransactionalBranchDictionary;
68          this.managedConnectionFactory = managedConnectionFactory;
69          this.transactionManager = transactionManager;
70      }
71  
72      public XAResource getXAResource() {
73          return xaResource;
74      }
75  
76  
77      /**
78       * @return !=null if an if the XAConnection is bound to a global transaction
79       */
80      XATransactionalBranch<C> toGlobalTransactionBranch() {
81          if (this.transactionBinding != null && this.transactionBinding.getTransactionBindingType() == TransactionBindingType.GlobalTransaction) {
82              return ImplementorUtils.cast(this.transactionBinding, GlobalTransactionProxy.class).getGlobalTransactionalBranch();
83          }
84          return null;
85      }
86  
87  
88      /**
89       * call by the XCAResource when {@link javax.transaction.xa.XAResource#start(javax.transaction.xa.Xid, int)}  is called.
90       * A TransactionalBranch for the given XID ist expected to be created.
91       *
92       * @param xid
93       */
94      void startTransactionalBranch(Xid xid) {
95  
96  
97          cleanupTransactionBinding();
98  
99          final TransactionBindingType transactionBindingType = getTransactionBindingType();
100 
101 
102         // already associated to a local transaction
103         if (transactionBindingType == TransactionBindingType.LocalTransaction) {
104             LocalTransactionProxy<C> localTransactionProxy = ImplementorUtils.cast(this.transactionBinding, LocalTransactionProxy.class);
105             if (localTransactionProxy.hasTransactionalData()) {
106                 throw new IllegalStateException("Connection is associated to a local transaction and has uncommitted transactional data");
107             }
108 
109             XATransactionalBranch<C> xaTransactionalBranch = this.xaTransactionalBranchDictionary.findTransactionalBranch(xid);
110             // xaTransactionalBranch!=null => joining a existing transactional branch
111             if (xaTransactionalBranch == null) {
112                 xaTransactionalBranch= this.xaTransactionalBranchDictionary.instanciateTransactionalBranch(xid, localTransactionProxy.getConnection());
113             }
114             this.transactionBinding = new GlobalTransactionProxy<C>().assignTransactionalBranch(this.xaTransactionalBranchDictionary.findTransactionalBranch(xid));
115 
116             // no transaction binding
117         } else if (transactionBindingType == TransactionBindingType.NoTransaction) {
118             XATransactionalBranch<C> xaTransactionalBranch = this.xaTransactionalBranchDictionary.findTransactionalBranch(xid);
119 
120             /// xaTransactionalBranch!=null => joining a existing transactional branch
121             if (xaTransactionalBranch == null) {
122                 IPhynixxManagedConnection<C> physicalConnection = this.createPhysicalConnection();
123                 xaTransactionalBranch =
124                         this.xaTransactionalBranchDictionary.instanciateTransactionalBranch(xid, physicalConnection);
125                 this.xaTransactionalBranchDictionary.instanciateTransactionalBranch(xid, physicalConnection);
126             }
127             this.transactionBinding  = new GlobalTransactionProxy<C>().assignTransactionalBranch(this.xaTransactionalBranchDictionary.findTransactionalBranch(xid));
128 
129             // if bound to a global TX it has to be the same XID
130         } else if (transactionBindingType == TransactionBindingType.GlobalTransaction) {
131             GlobalTransactionProxy<C> globalTransactionProxy = ImplementorUtils.cast(this.transactionBinding, GlobalTransactionProxy.class);
132             XATransactionalBranch<C> xaTransactionalBranch = this.xaTransactionalBranchDictionary.findTransactionalBranch(xid);
133 
134             /// xaTransactionalBranch!=null => joining a existing transactional branch
135             if (xaTransactionalBranch == null) {
136                 IPhynixxManagedConnection<C> physicalConnection = this.createPhysicalConnection();
137                 xaTransactionalBranch =
138                         this.xaTransactionalBranchDictionary.instanciateTransactionalBranch(xid, physicalConnection);
139                 this.xaTransactionalBranchDictionary.instanciateTransactionalBranch(xid, physicalConnection);
140             } else {
141 
142                 if (globalTransactionProxy.getGlobalTransactionalBranch()==null) {
143                     throw new IllegalStateException("XAConnection already associated toXID but not to a global Transaction ");
144                 }
145                 if (!globalTransactionProxy.getXid().equals(xid)) {
146                     throw new IllegalStateException("XAConnection already associated to a global Transaction "+globalTransactionProxy.getXid());
147                 }
148             }
149             this.transactionBinding  = globalTransactionProxy.assignTransactionalBranch(this.xaTransactionalBranchDictionary.findTransactionalBranch(xid));
150 
151 
152         }
153 
154 
155     }
156 
157     private void cleanupTransactionBinding() {
158         // cleanup
159         if (getTransactionBindingType() == TransactionBindingType.LocalTransaction) {
160             LocalTransactionProxy<C> localTransactionProxy = ImplementorUtils.cast(this.transactionBinding, LocalTransactionProxy.class);
161             if (localTransactionProxy.isClosed()) {
162                 this.transactionBinding.release();
163                 this.transactionBinding = null;
164             }
165         }
166     }
167 
168     private TransactionBindingType getTransactionBindingType() {
169         return (this.transactionBinding != null) ? this.transactionBinding.getTransactionBindingType() : TransactionBindingType.NoTransaction;
170     }
171 
172     private IPhynixxManagedConnection<C> createPhysicalConnection() {
173         return this.managedConnectionFactory.getManagedConnection();
174     }
175 
176 
177     void resumeTransactionalBranch(Xid xid) {
178 
179         this.cleanupTransactionBinding();
180 
181         TransactionBindingType transactionBindingType = getTransactionBindingType();
182         if (transactionBindingType == TransactionBindingType.GlobalTransaction) {
183             throw new IllegalStateException("XAConnection associated to a global transaction and can not be resumed");
184         }
185         XATransactionalBranch<C> transactionalBranch = this.xaTransactionalBranchDictionary.findTransactionalBranch(xid);
186         if (transactionalBranch == null) {
187             throw new IllegalStateException("No suspended branch for XID " + xid);
188         }
189         transactionalBranch.resume();
190         this.transactionBinding = new GlobalTransactionProxy<C>().assignTransactionalBranch(transactionalBranch);
191 
192     }
193 
194     void suspendTransactionalBranch(Xid xid) {
195 
196         this.cleanupTransactionBinding();
197 
198         TransactionBindingType transactionBindingType = getTransactionBindingType();
199         if (transactionBindingType != TransactionBindingType.GlobalTransaction) {
200             throw new IllegalStateException("XAConnection not associated to a global transaction and can not be suspended");
201         }
202         GlobalTransactionProxy<C> globalTransactionProxy = ImplementorUtils.cast(this.transactionBinding, GlobalTransactionProxy.class);
203         if (!globalTransactionProxy.getXid().equals(xid)) {
204             throw new IllegalStateException("XAConnection already associated to a global Transaction");
205         }
206         globalTransactionProxy.getGlobalTransactionalBranch().suspend();
207 
208         this.transactionBinding.release();
209         this.transactionBinding = null;
210 
211     }
212 
213     void joinTransactionalBranch(Xid xid) {
214 
215         cleanupTransactionBinding();
216 
217         TransactionBindingType transactionBindingType = getTransactionBindingType();
218         if (transactionBindingType == TransactionBindingType.GlobalTransaction) {
219             throw new IllegalStateException("XAConnection already associated to a global transaction and can not be joined to XID " + xid);
220         }
221 
222 
223         this.transactionBinding.release();
224         this.transactionBinding = null;
225 
226     }
227 
228     /**
229      * call by the XCAResource when {@link javax.transaction.xa.XAResource#end(javax.transaction.xa.Xid, int)} is called
230      *
231      * @param xid
232      */
233     void closeTransactionalBranch(Xid xid) {
234 
235         if (this.getTransactionBindingType() != TransactionBindingType.GlobalTransaction) {
236             throw new IllegalStateException("XAConnection not associated to a global transaction");
237         }
238 
239         GlobalTransactionProxy<C> globalTransactionProxy = ImplementorUtils.cast(this.transactionBinding, GlobalTransactionProxy.class);
240 
241         XATransactionalBranch<C> transactionalBranch = globalTransactionProxy.getGlobalTransactionalBranch();
242         transactionalBranch.close();
243 
244         delistTransaction();
245     }
246 
247 
248     @Override
249     public void close() {
250         if(this.transactionBinding!=null) {
251             this.transactionBinding.close();
252             this.transactionBinding=null;
253         }
254     }
255 
256     private void delistTransaction() {
257         if (this.transactionBinding != null) {
258             this.transactionBinding.release();
259         }
260         this.transactionBinding = null;
261     }
262 
263     public IPhynixxManagedConnection<C> getManagedConnection() {
264 
265         // Connection wid in TX eingetragen ....
266         this.checkTransactionBinding();
267 
268         return this.transactionBinding.getConnection();
269     }
270 
271     public C getConnection() {
272         return this.getManagedConnection().toConnection();
273     }
274 
275 
276     boolean isInGlobalTransaction() {
277         try {
278             return transactionManager!=null && this.transactionManager.getTransaction()!=null;
279         } catch (SystemException e) {
280             throw new DelegatedRuntimeException(e);
281         }
282     }
283 
284 
285     /**
286      * if necessary the current xa resource is enlisted in the current TX.
287      * <p/>
288      * The enlistment calls the{@link javax.transaction.xa.XAResource#start(javax.transaction.xa.Xid, int)}. This call associates the Xid with the current instance
289      */
290     private void enlistTransaction() {
291 
292         this.cleanupTransactionBinding();
293 
294         TransactionBindingType transactionBindingType = this.getTransactionBindingType();
295 
296         // not associated to global transaction, try to associate a global transaction
297         if (this.isInGlobalTransaction() && transactionBindingType != TransactionBindingType.GlobalTransaction) {
298             try {
299                 Transaction ntx = this.transactionManager.getTransaction();
300 
301                 // Bitronix calls start on reaction of enlist --- check if cycle
302                 if (!enlisted && ntx != null) {
303                     this.enlisted=true;
304                     // enlisted makes startTransaactionalBranch calling
305                     this.enlisted = ntx.enlistResource(this.xaResource);
306                     if (!enlisted) {
307                         LOG.error("Enlisting " + xaResource + " failed");
308                     } else {
309 
310                     }
311                 } else {
312                     LOG.debug(
313                             "SampleXAConnection:connectionRequiresTransaction (no globalTransaction found)");
314                 }
315             } catch (RollbackException n) {
316                 LOG.error(
317                         "SampleXAConnection:prevokeAction enlistResource exception : "
318                                 + n.toString());
319             } catch (SystemException n) {
320                 LOG.error("SampleXAConnection:connectionRequiresTransaction " + n + "\n" + ExceptionUtils.getStackTrace(n));
321                 throw new DelegatedRuntimeException(n);
322             }
323         } else if (transactionBindingType == TransactionBindingType.NoTransaction) {
324             this.transactionBinding = new LocalTransactionProxy<C>(this.managedConnectionFactory.getManagedConnection());
325         } else     // In Global Transaction and associated to a global transaction => nothing to do
326             if (this.isInGlobalTransaction() && transactionBindingType == TransactionBindingType.GlobalTransaction) {
327             } else
328 
329                 // Not in Global Transaction and associated to a local transaction => nothing to do
330                 if (!this.isInGlobalTransaction() && transactionBindingType == TransactionBindingType.LocalTransaction) {
331                 }
332 
333     }
334 
335 
336     @Override
337     public boolean equals(Object o) {
338         if (this == o) return true;
339         if (o == null || getClass() != o.getClass()) return false;
340 
341         PhynixxManagedXAConnection that = (PhynixxManagedXAConnection) o;
342 
343         return xaResource.equals(that.xaResource);
344 
345     }
346 
347     @Override
348     public int hashCode() {
349         return xaResource.hashCode();
350     }
351 
352 
353     @Override
354     public String toString() {
355         return "PhynixxManagedXAConnection{" +
356                 "xaResource=" + xaResource +
357                 ", transactionBinding=" + this.transactionBinding +
358                 '}';
359     }
360 
361 
362     void checkTransactionBinding() {
363 
364 
365             if( this.isInGlobalTransaction()) {
366                 this.enlistTransaction();
367             }
368         if (this.transactionBinding == null || this.transactionBinding.getTransactionBindingType() == TransactionBindingType.NoTransaction) {
369             this.transactionBinding = new LocalTransactionProxy<C>(this.createPhysicalConnection());
370 
371         }
372     }
373 
374 }