View Javadoc
1   package org.synchronoss.cpo.jdbc.jta;
2   
3   /*-
4    * [[
5    * jdbc
6    * ==
7    * Copyright (C) 2003 - 2026 Exaxis LLC, Synchronoss Technologies Inc
8    * ==
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   *
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * ]]
23   */
24  
25  import static org.testng.Assert.*;
26  
27  import java.util.ArrayList;
28  import java.util.Collection;
29  import java.util.stream.Stream;
30  import javax.transaction.xa.XAException;
31  import javax.transaction.xa.XAResource;
32  import javax.transaction.xa.Xid;
33  import org.slf4j.Logger;
34  import org.slf4j.LoggerFactory;
35  import org.synchronoss.cpo.core.CpoAdapter;
36  import org.synchronoss.cpo.core.CpoAdapterFactoryManager;
37  import org.synchronoss.cpo.core.CpoOrderBy;
38  import org.synchronoss.cpo.core.CpoQuery;
39  import org.synchronoss.cpo.core.helper.ExceptionHelper;
40  import org.synchronoss.cpo.core.jta.CpoXaError;
41  import org.synchronoss.cpo.jdbc.ValueObject;
42  import org.synchronoss.cpo.jdbc.adapter.JdbcStatics;
43  import org.synchronoss.cpo.jdbc.adapter.ValueObjectFactory;
44  import org.testng.annotations.AfterMethod;
45  import org.testng.annotations.BeforeMethod;
46  import org.testng.annotations.Parameters;
47  import org.testng.annotations.Test;
48  
49  /** Created by dberry on 12/8/15. */
50  public class JdbcXaResourceTest {
51  
52    // unique id base so this class's rows never collide with another test class's
53    private static final int IDB = 2600000;
54    private static final Logger logger = LoggerFactory.getLogger(JdbcXaResourceTest.class);
55    private CpoAdapter cpoAdapter = null;
56    private JdbcCpoXaAdapter cpoXaAdapter1 = null;
57    private JdbcCpoXaAdapter cpoXaAdapter2 = null;
58    private ArrayList<ValueObject> al = new ArrayList<>();
59    private boolean isXaSupported = true;
60  
61    /** Creates a new XaResourceTest object. */
62    public JdbcXaResourceTest() {}
63  
64    /**
65     * <code>setUp</code> Load the datasource from the properties in the property file
66     * jdbc_en_US.properties
67     */
68    @Parameters({"db.xasupport"})
69    @BeforeMethod
70    public void setUp(boolean xaSupport) {
71      String method = "setUp:";
72      isXaSupported = xaSupport;
73  
74      try {
75        cpoAdapter = CpoAdapterFactoryManager.getCpoAdapter(JdbcStatics.ADAPTER_CONTEXT_JDBC);
76        cpoXaAdapter1 =
77            (JdbcCpoXaAdapter)
78                CpoAdapterFactoryManager.getCpoXaAdapter(JdbcStatics.ADAPTER_CONTEXT_JDBC);
79        cpoXaAdapter2 =
80            (JdbcCpoXaAdapter)
81                CpoAdapterFactoryManager.getCpoXaAdapter(JdbcStatics.ADAPTER_CONTEXT_JDBC);
82        assertNotNull(cpoXaAdapter1, method + "CpoXaAdapter1 is null");
83        assertNotNull(cpoXaAdapter2, method + "CpoXaAdapter2 is null");
84      } catch (Exception e) {
85        fail(method + ExceptionHelper.getLocalizedMessage(e));
86      }
87    }
88  
89    /** DOCUMENT ME! */
90    @AfterMethod
91    public void tearDown() {
92      String method = "tearDown:";
93      try {
94        cpoAdapter.deleteBeans(al);
95      } catch (Exception e) {
96        fail(method + e.getMessage());
97      } finally {
98        al.clear();
99      }
100 
101     cpoAdapter = null;
102     cpoXaAdapter1 = null;
103     cpoXaAdapter2 = null;
104   }
105 
106   /** Tests that simple distributed transaction processing works as expected. */
107   @Test
108   public void testCoordination() {
109     if (isXaSupported) {
110       String method = "testCoordination:";
111       ValueObject valObj1 = ValueObjectFactory.createValueObject(IDB + 1);
112       ValueObject valObj2 = ValueObjectFactory.createValueObject(IDB + 2);
113       al.add(valObj1);
114       al.add(valObj2);
115 
116       Xid xid1 = new MyXid(100, new byte[] {0x01}, new byte[] {0x02});
117       Xid xid2 = new MyXid(100, new byte[] {0x11}, new byte[] {0x22});
118 
119       try {
120         cpoXaAdapter1.start(xid1, XAResource.TMNOFLAGS);
121         cpoXaAdapter2.start(xid2, XAResource.TMNOFLAGS);
122         cpoXaAdapter1.insertBean(valObj1);
123         cpoXaAdapter2.insertBean(valObj2);
124         cpoXaAdapter1.end(xid1, XAResource.TMSUCCESS);
125         cpoXaAdapter2.end(xid2, XAResource.TMSUCCESS);
126 
127         cpoXaAdapter1.prepare(xid1);
128         cpoXaAdapter2.prepare(xid2);
129 
130         cpoXaAdapter1.commit(xid1, false);
131         cpoXaAdapter2.commit(xid2, false);
132 
133         CpoOrderBy cob = cpoXaAdapter1.newOrderBy(ValueObject.ATTR_ID, true);
134         Collection<CpoOrderBy> colCob = new ArrayList<>();
135         colCob.add(cob);
136 
137         ValueObject valObj = ValueObjectFactory.createValueObject();
138         try (Stream<ValueObject> beans =
139             cpoXaAdapter1.retrieveBeans(
140                 CpoQuery.group(ValueObject.FG_LIST_NULL).orderBys(colCob), valObj)) {
141           var list =
142               beans
143                   .filter(b -> Math.abs(b.getId()) >= IDB && Math.abs(b.getId()) < IDB + 100000)
144                   .toList();
145           var rvo = list.getFirst();
146           assertEquals(rvo.getId(), IDB + 1, "1 != " + rvo.getId());
147           assertEquals(list.size(), 2, "list size is " + list.size());
148           assertEquals(list.get(0).getId(), IDB + 1, "ValuObject(1) is missing");
149           assertEquals(list.get(1).getId(), IDB + 2, "ValuObject(2) is missing");
150           assertEquals(cpoXaAdapter1.deleteBeans(al), 2);
151         }
152       } catch (Exception e) {
153         fail(method + ExceptionHelper.getLocalizedMessage(e));
154       } finally {
155         try {
156           cpoXaAdapter1.close(xid1);
157         } catch (Exception ignored) {
158         }
159         try {
160           cpoXaAdapter1.close(xid2);
161         } catch (Exception ignored) {
162         }
163       }
164     } else {
165       logger.error(cpoAdapter.getDataSourceName() + " does not support XA Transactions");
166     }
167   }
168 
169   /** Tests that simple distributed transaction processing works as expected. */
170   @Test
171   public void testRollback() {
172     if (isXaSupported) {
173       String method = "testRollback:";
174       ValueObject valObj1 = ValueObjectFactory.createValueObject(IDB + 1);
175       ValueObject valObj2 = ValueObjectFactory.createValueObject(IDB + 2);
176       al.add(valObj1);
177       al.add(valObj2);
178 
179       Xid xid1 = new MyXid(100, new byte[] {0x01}, new byte[] {0x02});
180       Xid xid2 = new MyXid(100, new byte[] {0x11}, new byte[] {0x22});
181 
182       try {
183         cpoXaAdapter1.start(xid1, XAResource.TMNOFLAGS);
184         cpoXaAdapter2.start(xid2, XAResource.TMNOFLAGS);
185         cpoXaAdapter1.insertBean(valObj1);
186         cpoXaAdapter2.insertBean(valObj2);
187         cpoXaAdapter1.end(xid1, XAResource.TMSUCCESS);
188         cpoXaAdapter2.end(xid2, XAResource.TMSUCCESS);
189 
190         cpoXaAdapter1.prepare(xid1);
191         cpoXaAdapter2.prepare(xid2);
192 
193         cpoXaAdapter1.rollback(xid1);
194         cpoXaAdapter2.rollback(xid2);
195 
196         ValueObject valObj = ValueObjectFactory.createValueObject();
197         try (Stream<ValueObject> beans =
198             cpoAdapter.retrieveBeans(ValueObject.FG_LIST_NULL, valObj)) {
199           var list =
200               beans
201                   .filter(b -> Math.abs(b.getId()) >= IDB && Math.abs(b.getId()) < IDB + 100000)
202                   .toList();
203           assertTrue(list.isEmpty(), "list SHOULD BE EMPTY");
204         }
205       } catch (Exception e) {
206         fail(method + ExceptionHelper.getLocalizedMessage(e));
207       } finally {
208         try {
209           cpoXaAdapter1.close(xid1);
210         } catch (Exception ignored) {
211         }
212         try {
213           cpoXaAdapter1.close(xid2);
214         } catch (Exception ignored) {
215         }
216       }
217     } else {
218       logger.error(cpoAdapter.getDataSourceName() + " does not support XA Transactions");
219     }
220   }
221 
222   /** Tests that XA RECOVER works as expected. */
223   @Test
224   public void testRecover() {
225     if (isXaSupported) {
226       String method = "testRecover:";
227       ValueObject valObj = ValueObjectFactory.createValueObject(IDB + 1);
228       al.add(valObj);
229 
230       Xid xid = new MyXid(100, new byte[] {0x01}, new byte[] {0x02});
231       try {
232 
233         cpoXaAdapter1.start(xid, XAResource.TMNOFLAGS);
234         cpoXaAdapter1.insertBean(valObj);
235         cpoXaAdapter1.end(xid, XAResource.TMSUCCESS);
236         cpoXaAdapter1.prepare(xid);
237 
238         // Now try and recover
239 
240         Xid[] recoveredXids =
241             cpoXaAdapter2.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN);
242 
243         assertNotNull(recoveredXids);
244         assertTrue(recoveredXids.length > 0);
245 
246         boolean xidFound = false;
247 
248         for (int i = 0; i < recoveredXids.length; i++) {
249           if (recoveredXids[i] != null && recoveredXids[i].equals(xid)) {
250             xidFound = true;
251 
252             break;
253           }
254         }
255 
256         assertTrue(xidFound);
257 
258         recoveredXids = cpoXaAdapter1.recover(XAResource.TMSTARTRSCAN);
259 
260         assertNotNull(recoveredXids);
261         assertTrue(recoveredXids.length > 0);
262 
263         xidFound = false;
264 
265         for (int i = 0; i < recoveredXids.length; i++) {
266           if (recoveredXids[i] != null && recoveredXids[i].equals(xid)) {
267             xidFound = true;
268 
269             break;
270           }
271         }
272 
273         assertTrue(xidFound);
274 
275         // Test flags
276         cpoXaAdapter1.recover(XAResource.TMSTARTRSCAN);
277         cpoXaAdapter1.recover(XAResource.TMENDRSCAN);
278         cpoXaAdapter1.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN);
279 
280         // This should fail
281         try {
282           cpoXaAdapter1.recover(XAResource.TMSUCCESS);
283           fail("XAException should have been thrown");
284         } catch (XAException xae) {
285           assertTrue(xae.getLocalizedMessage().startsWith(CpoXaError.XAER_INVAL.toString()));
286         }
287       } catch (Exception e) {
288         fail(method + ExceptionHelper.getLocalizedMessage(e));
289       } finally {
290         try {
291           cpoXaAdapter1.close(xid);
292         } catch (Exception ignored) {
293         }
294       }
295     } else {
296       logger.error(cpoAdapter.getDataSourceName() + " does not support XA Transactions");
297     }
298   }
299 
300   @Test
301   public void testSuspendableTx() throws Exception {
302     if (isXaSupported) {
303       String method = "testSuspendableTx:";
304       ValueObject valObj = ValueObjectFactory.createValueObject(IDB + 1);
305       al.add(valObj);
306 
307       Xid xid = new MyXid(100, new byte[] {0x01}, new byte[] {0x02});
308 
309       try {
310         cpoXaAdapter1.start(xid, XAResource.TMNOFLAGS);
311         cpoXaAdapter1.retrieveBean(valObj);
312         cpoXaAdapter1.end(xid, XAResource.TMSUSPEND);
313         cpoXaAdapter1.start(xid, XAResource.TMRESUME);
314         cpoXaAdapter1.retrieveBean(valObj);
315         cpoXaAdapter1.end(xid, XAResource.TMSUCCESS);
316         cpoXaAdapter1.commit(xid, true);
317 
318         cpoXaAdapter1.close(xid);
319 
320         cpoXaAdapter1.start(xid, XAResource.TMNOFLAGS);
321         cpoXaAdapter1.retrieveBean(valObj);
322         cpoXaAdapter1.end(xid, XAResource.TMSUCCESS);
323         cpoXaAdapter1.start(xid, XAResource.TMJOIN);
324         cpoXaAdapter1.retrieveBean(valObj);
325         cpoXaAdapter1.end(xid, XAResource.TMSUCCESS);
326         cpoXaAdapter1.commit(xid, true);
327       } catch (Exception e) {
328         fail(method + ExceptionHelper.getLocalizedMessage(e));
329       } finally {
330         try {
331           cpoXaAdapter1.close(xid);
332         } catch (Exception ignored) {
333         }
334       }
335     } else {
336       logger.error(cpoAdapter.getDataSourceName() + " does not support XA Transactions");
337     }
338   }
339 
340   @Test
341   public void testCommit() {
342     if (isXaSupported) {
343       String method = "testCommit:";
344       ValueObject valObj = ValueObjectFactory.createValueObject(IDB + 1);
345       al.add(valObj);
346       int ret;
347 
348       Xid xid1 = new MyXid(100, new byte[] {0x01}, new byte[] {0x02});
349       try {
350 
351         cpoXaAdapter1.start(xid1, XAResource.TMNOFLAGS);
352         cpoXaAdapter1.insertBean(valObj);
353         cpoXaAdapter1.end(xid1, XAResource.TMSUCCESS);
354 
355         // make sure a cpoAdapter cannot see the insert
356         assertNull(cpoAdapter.retrieveBean(valObj));
357 
358         ret = cpoXaAdapter1.prepare(xid1);
359         if (ret == XAResource.XA_OK) {
360           cpoXaAdapter1.commit(xid1, false);
361         }
362 
363         // make sure a cpoAdapter can now see the insert
364         assertNotNull(cpoAdapter.retrieveBean(valObj));
365 
366       } catch (Exception e) {
367         fail(method + ExceptionHelper.getLocalizedMessage(e));
368       } finally {
369         try {
370           cpoXaAdapter1.close(xid1);
371         } catch (Exception ignored) {
372         }
373       }
374     } else {
375       logger.error(cpoAdapter.getDataSourceName() + " does not support XA Transactions");
376     }
377   }
378 
379   @Test
380   public void testRollback2() {
381     if (isXaSupported) {
382       String method = "testRollback2:";
383       ValueObject valObj = ValueObjectFactory.createValueObject(IDB + 1);
384       al.add(valObj);
385       int ret;
386 
387       Xid xid1 = new MyXid(100, new byte[] {0x01}, new byte[] {0x02});
388       try {
389 
390         cpoXaAdapter1.start(xid1, XAResource.TMNOFLAGS);
391         cpoXaAdapter1.insertBean(valObj);
392         cpoXaAdapter1.end(xid1, XAResource.TMSUCCESS);
393 
394         // make sure a cpoAdapter cannot see the insert
395         assertNull(cpoAdapter.retrieveBean(valObj));
396 
397         ret = cpoXaAdapter1.prepare(xid1);
398         if (ret == XAResource.XA_OK) {
399           cpoXaAdapter1.rollback(xid1);
400         }
401 
402         // make sure a cpoAdapter still cannot see the insert
403         assertNull(cpoAdapter.retrieveBean(valObj));
404       } catch (Exception e) {
405         fail(method + ExceptionHelper.getLocalizedMessage(e));
406       } finally {
407         try {
408           cpoXaAdapter1.close(xid1);
409         } catch (Exception ignored) {
410         }
411       }
412     } else {
413       logger.error(cpoAdapter.getDataSourceName() + " does not support XA Transactions");
414     }
415   }
416 
417   @Test
418   public void testSuspend() {
419     if (isXaSupported) {
420       String method = "testSuspend:";
421       ValueObject valObj1 = ValueObjectFactory.createValueObject(IDB + 1);
422       ValueObject valObj2 = ValueObjectFactory.createValueObject(IDB + 2);
423       ValueObject valObj3 = ValueObjectFactory.createValueObject(IDB + 3);
424       al.add(valObj1);
425       al.add(valObj2);
426       al.add(valObj3);
427       int ret;
428 
429       Xid xid1 = new MyXid(100, new byte[] {0x01}, new byte[] {0x02});
430 
431       try {
432         cpoXaAdapter1.start(xid1, XAResource.TMNOFLAGS);
433         cpoXaAdapter1.insertBean(valObj1);
434         cpoXaAdapter1.end(xid1, XAResource.TMSUSPEND);
435 
436         // This update is done outside of transaction scope, so it
437         // is not affected by the XA rollback.
438         cpoXaAdapter1.insertBean(valObj2);
439 
440         cpoXaAdapter1.start(xid1, XAResource.TMRESUME);
441         cpoXaAdapter1.insertBean(valObj3);
442         cpoXaAdapter1.end(xid1, XAResource.TMSUCCESS);
443 
444         ret = cpoXaAdapter1.prepare(xid1);
445         if (ret == XAResource.XA_OK) {
446           cpoXaAdapter1.rollback(xid1);
447         }
448         // make sure that only valobj2 is in the database
449         ValueObject valObj = ValueObjectFactory.createValueObject();
450         try (Stream<ValueObject> beans =
451             cpoAdapter.retrieveBeans(ValueObject.FG_LIST_NULL, valObj)) {
452           var list =
453               beans
454                   .filter(b -> Math.abs(b.getId()) >= IDB && Math.abs(b.getId()) < IDB + 100000)
455                   .toList();
456           assertEquals(list.size(), 1, "list size is " + list.size());
457           assertEquals(list.getFirst().getId(), valObj2.getId(), "valObj2 is missing");
458         }
459       } catch (Exception e) {
460         fail(method + ExceptionHelper.getLocalizedMessage(e));
461       } finally {
462         try {
463           cpoXaAdapter1.close(xid1);
464         } catch (Exception ignored) {
465         }
466       }
467     } else {
468       logger.error(cpoAdapter.getDataSourceName() + " does not support XA Transactions");
469     }
470   }
471 
472   @Test
473   public void testMultiTrx() {
474     if (isXaSupported) {
475       String method = "testMultiTrx:";
476       ValueObject valObj1 = ValueObjectFactory.createValueObject(IDB + 1);
477       ValueObject valObj2 = ValueObjectFactory.createValueObject(IDB + 2);
478       al.add(valObj1);
479       al.add(valObj2);
480       int ret;
481 
482       Xid xid1 = new MyXid(100, new byte[] {0x01}, new byte[] {0x02});
483       Xid xid2 = new MyXid(100, new byte[] {0x11}, new byte[] {0x22});
484       try {
485 
486         cpoXaAdapter1.start(xid1, XAResource.TMNOFLAGS);
487         cpoXaAdapter1.insertBean(valObj1);
488         cpoXaAdapter1.end(xid1, XAResource.TMSUCCESS);
489 
490         cpoXaAdapter1.start(xid2, XAResource.TMNOFLAGS);
491 
492         // Should allow XA resource to do two-phase commit on
493         // transaction 1 while associated to transaction 2
494         ret = cpoXaAdapter1.prepare(xid1);
495         if (ret == XAResource.XA_OK) {
496           cpoXaAdapter1.commit(xid1, false);
497         }
498 
499         cpoXaAdapter1.insertBean(valObj2);
500         cpoXaAdapter1.end(xid2, XAResource.TMSUCCESS);
501 
502         ret = cpoXaAdapter1.prepare(xid2);
503         if (ret == XAResource.XA_OK) {
504           cpoXaAdapter1.rollback(xid2);
505         }
506         // make sure the xid1 insert can be seen
507         // make sure the xid2 insert cannot be seen
508         ValueObject valObj = ValueObjectFactory.createValueObject();
509         try (Stream<ValueObject> beans =
510             cpoAdapter.retrieveBeans(ValueObject.FG_LIST_NULL, valObj)) {
511           var list =
512               beans
513                   .filter(b -> Math.abs(b.getId()) >= IDB && Math.abs(b.getId()) < IDB + 100000)
514                   .toList();
515           assertEquals(list.size(), 1, "list size is " + list.size());
516           assertEquals(list.getFirst().getId(), valObj1.getId(), "valObj2 is missing");
517         }
518       } catch (Exception e) {
519         fail(method + ExceptionHelper.getLocalizedMessage(e));
520       } finally {
521         try {
522           cpoXaAdapter1.close(xid1);
523         } catch (Exception ignored) {
524         }
525         try {
526           cpoXaAdapter1.close(xid2);
527         } catch (Exception ignored) {
528         }
529       }
530     } else {
531       logger.error(cpoAdapter.getDataSourceName() + " does not support XA Transactions");
532     }
533   }
534 
535   @Test
536   public void testJoin() {
537     if (isXaSupported) {
538       String method = "testJoin:";
539       ValueObject valObj1 = ValueObjectFactory.createValueObject(IDB + 1);
540       ValueObject valObj2 = ValueObjectFactory.createValueObject(IDB + 2);
541       al.add(valObj1);
542       al.add(valObj2);
543       int ret;
544 
545       Xid xid1 = new MyXid(100, new byte[] {0x01}, new byte[] {0x02});
546       try {
547 
548         cpoXaAdapter1.start(xid1, XAResource.TMNOFLAGS);
549         cpoXaAdapter1.insertBean(valObj1);
550         cpoXaAdapter1.end(xid1, XAResource.TMSUCCESS);
551 
552         if (cpoXaAdapter2.isSameRM(cpoXaAdapter1)) {
553           cpoXaAdapter2.start(xid1, XAResource.TMJOIN);
554           cpoXaAdapter2.insertBean(valObj2);
555           cpoXaAdapter2.end(xid1, XAResource.TMSUCCESS);
556         } else {
557           fail("Unable to join XAResources with the same resource manager");
558         }
559 
560         ret = cpoXaAdapter1.prepare(xid1);
561         if (ret == XAResource.XA_OK) {
562           cpoXaAdapter1.commit(xid1, false);
563         }
564 
565         // make sure both records exist
566         ValueObject valObj = ValueObjectFactory.createValueObject();
567         try (Stream<ValueObject> beans =
568             cpoAdapter.retrieveBeans(ValueObject.FG_LIST_NULL, valObj)) {
569           var list =
570               beans
571                   .filter(b -> Math.abs(b.getId()) >= IDB && Math.abs(b.getId()) < IDB + 100000)
572                   .toList();
573           assertEquals(list.size(), 2, "list size is " + list.size());
574         }
575       } catch (Exception e) {
576         fail(method + ExceptionHelper.getLocalizedMessage(e));
577       } finally {
578         try {
579           cpoXaAdapter1.close(xid1);
580         } catch (Exception ignored) {
581         }
582       }
583     } else {
584       logger.error(cpoAdapter.getDataSourceName() + " does not support XA Transactions");
585     }
586   }
587 
588   @Test
589   public void testRecover2() {
590     if (isXaSupported) {
591       String method = "testRecover2:";
592       ValueObject valObj1 = ValueObjectFactory.createValueObject(IDB + 1);
593       ValueObject valObj2 = ValueObjectFactory.createValueObject(IDB + 2);
594       ValueObject valObj3 = ValueObjectFactory.createValueObject(IDB + 3);
595       al.add(valObj1);
596       al.add(valObj2);
597       al.add(valObj3);
598 
599       Xid[] xids;
600 
601       Xid xid1 = new MyXid(100, new byte[] {0x01}, new byte[] {0x02});
602       Xid xid2 = new MyXid(100, new byte[] {0x11}, new byte[] {0x22});
603       Xid xid3 = new MyXid(100, new byte[] {0x13}, new byte[] {0x23});
604 
605       // TODO - need to create some unfinished transactions
606       try {
607 
608         cpoXaAdapter1.start(xid1, XAResource.TMNOFLAGS);
609         cpoXaAdapter1.insertBean(valObj1);
610         cpoXaAdapter1.end(xid1, XAResource.TMSUCCESS);
611         cpoXaAdapter1.prepare(xid1);
612 
613         cpoXaAdapter1.start(xid2, XAResource.TMNOFLAGS);
614         cpoXaAdapter1.insertBean(valObj2);
615         cpoXaAdapter1.end(xid2, XAResource.TMSUSPEND);
616 
617         cpoXaAdapter1.start(xid3, XAResource.TMNOFLAGS);
618         cpoXaAdapter1.insertBean(valObj3);
619         cpoXaAdapter1.end(xid3, XAResource.TMFAIL);
620 
621         xids = cpoXaAdapter1.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN);
622         assertTrue(xids.length > 0, "In progress resources must be created");
623         assertEquals(1, xids.length, "There should be 1 in progress transaction");
624         for (int i = 0; xids != null && i < xids.length; i++) {
625           try {
626             cpoXaAdapter1.rollback(xids[i]);
627           } catch (XAException ex) {
628             try {
629               cpoXaAdapter1.forget(xids[i]);
630             } catch (XAException ex1) {
631               fail("rollback/forget failed: " + ex1.getMessage());
632             }
633           }
634         }
635       } catch (Exception e) {
636         fail(method + ExceptionHelper.getLocalizedMessage(e));
637       } finally {
638         try {
639           cpoXaAdapter1.close(xid1);
640         } catch (Exception ignored) {
641         }
642         try {
643           cpoXaAdapter1.close(xid2);
644         } catch (Exception ignored) {
645         }
646         try {
647           cpoXaAdapter1.close(xid3);
648         } catch (Exception ignored) {
649         }
650       }
651     } else {
652       logger.error(cpoAdapter.getDataSourceName() + " does not support XA Transactions");
653     }
654   }
655 
656   @Test
657   public void testNothing() {}
658 
659   public class MyXid implements Xid {
660     protected int formatId;
661     protected byte[] gtrid;
662     protected byte[] bqual;
663 
664     public MyXid() {}
665 
666     public MyXid(int formatId, byte gtrid[], byte bqual[]) {
667       this.formatId = formatId;
668       this.gtrid = gtrid;
669       this.bqual = bqual;
670     }
671 
672     public int getFormatId() {
673       return formatId;
674     }
675 
676     public byte[] getBranchQualifier() {
677       return bqual;
678     }
679 
680     public byte[] getGlobalTransactionId() {
681       return gtrid;
682     }
683   }
684 }