public XATransactionContext<K, V> getCurrentContext() { if (currentXid == null) { return null; } return transactionContextFactory.get(new TransactionId(currentXid)); }
@Test public void testCannotRollbackUnknownXidInFlight() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); doThrow(IllegalStateException.class).when(xaTransactionContext).rollback(eq(false)); try { xaResource.rollback(new TestXid(0, 0)); fail("expected XAException"); } catch (XAException xae) { assertThat(xae.errorCode, is(XAException.XAER_NOTA)); } }
@Test public void testCannotCommit1PcPreparedXid() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); doThrow(IllegalStateException.class).when(xaTransactionContext).commitInOnePhase(); try { xaResource.commit(new TestXid(0, 0), true); fail("expected XAException"); } catch (XAException xae) { assertThat(xae.errorCode, is(XAException.XAER_PROTO)); } }
@Test public void testPrepareReadOnly() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); when(xaTransactionContext.prepare()).thenReturn(0); int prepareRc = xaResource.prepare(new TestXid(0, 0)); assertThat(prepareRc, is(XAResource.XA_RDONLY)); verify(xaTransactionContextFactory, times(1)).destroy(eq(new TransactionId(new TestXid(0, 0)))); }
@Test public void testPrepareOk() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); when(xaTransactionContext.prepare()).thenReturn(1); int prepareRc = xaResource.prepare(new TestXid(0, 0)); assertThat(prepareRc, is(XAResource.XA_OK)); verify(xaTransactionContextFactory, times(0)).destroy(eq(new TransactionId(new TestXid(0, 0)))); }
@Test public void testCannotCommitUnknownXidInFlight() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(journal.isInDoubt(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(false); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); doThrow(IllegalArgumentException.class).when(xaTransactionContext).commit(eq(false)); try { xaResource.commit(new TestXid(0, 0), false); fail("expected XAException"); } catch (XAException xae) { assertThat(xae.errorCode, is(XAException.XAER_NOTA)); } }
@Test public void testCommit1Pc() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); xaResource.commit(new TestXid(0, 0), true); verify(xaTransactionContextFactory, times(1)).destroy(eq(new TransactionId(new TestXid(0, 0)))); }
@Test public void testRollback() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); xaResource.rollback(new TestXid(0, 0)); verify(xaTransactionContextFactory, times(1)).destroy(eq(new TransactionId(new TestXid(0, 0)))); }
@Test public void testCannotCommitNonPreparedXid() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); doThrow(IllegalStateException.class).when(xaTransactionContext).commit(anyBoolean()); try { xaResource.commit(new TestXid(0, 0), false); fail("expected XAException"); } catch (XAException xae) { assertThat(xae.errorCode, is(XAException.XAER_PROTO)); } }
@Test public void testCommit() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); xaResource.commit(new TestXid(0, 0), false); verify(xaTransactionContextFactory, times(1)).destroy(eq(new TransactionId(new TestXid(0, 0)))); }
@Test public void testCommit1PcTimeout() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); doThrow(XATransactionContext.TransactionTimeoutException.class).when(xaTransactionContext).commitInOnePhase(); try { xaResource.commit(new TestXid(0, 0), true); fail("expected XAException"); } catch (XAException xae) { assertThat(xae.errorCode, is(XAException.XA_RBTIMEOUT)); } verify(xaTransactionContextFactory, times(1)).destroy(eq(new TransactionId(new TestXid(0, 0)))); }
@Test @SuppressWarnings("unchecked") public void testPrepareTimeout() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); when(xaTransactionContext.prepare()).thenThrow(XATransactionContext.TransactionTimeoutException.class); try { xaResource.prepare(new TestXid(0, 0)); fail("expected XAException"); } catch (XAException xae) { assertThat(xae.errorCode, is(XAException.XA_RBTIMEOUT)); } verify(xaTransactionContextFactory, times(1)).destroy(eq(new TransactionId(new TestXid(0, 0)))); }
@Test public void testTimeoutEndSuccess() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(xaTransactionContextFactory.createTransactionContext(eq(new TransactionId(new TestXid(0, 0))), refEq(underlyingStore), refEq(journal), anyInt())).thenReturn(xaTransactionContext); xaResource.start(new TestXid(0, 0), XAResource.TMNOFLAGS); when(xaTransactionContext.hasTimedOut()).thenReturn(true); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); try { xaResource.end(new TestXid(0, 0), XAResource.TMSUCCESS); fail("expected XAException"); } catch (XAException xae) { assertThat(xae.errorCode, is(XAException.XA_RBTIMEOUT)); } verify(xaTransactionContextFactory, times(1)).destroy(eq(new TransactionId(new TestXid(0, 0)))); }
@Test public void testStartEndWorks() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(xaTransactionContextFactory.createTransactionContext(eq(new TransactionId(new TestXid(0, 0))), refEq(underlyingStore), refEq(journal), anyInt())).thenReturn(xaTransactionContext); xaResource.start(new TestXid(0, 0), XAResource.TMNOFLAGS); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); xaResource.end(new TestXid(0, 0), XAResource.TMSUCCESS); when(xaTransactionContextFactory.createTransactionContext(eq(new TransactionId(new TestXid(0, 1))), refEq(underlyingStore), refEq(journal), anyInt())).thenReturn(xaTransactionContext); xaResource.start(new TestXid(0, 1), XAResource.TMNOFLAGS); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 1))))).thenReturn(xaTransactionContext); xaResource.end(new TestXid(0, 1), XAResource.TMSUCCESS); }
@Test public void testTimeoutEndFail() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(xaTransactionContextFactory.createTransactionContext(eq(new TransactionId(new TestXid(0, 0))), refEq(underlyingStore), refEq(journal), anyInt())).thenReturn(xaTransactionContext); xaResource.start(new TestXid(0, 0), XAResource.TMNOFLAGS); when(xaTransactionContext.hasTimedOut()).thenReturn(true); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); try { xaResource.end(new TestXid(0, 0), XAResource.TMFAIL); fail("expected XAException"); } catch (XAException xae) { assertThat(xae.errorCode, is(XAException.XA_RBTIMEOUT)); } verify(xaTransactionContextFactory, times(1)).destroy(eq(new TransactionId(new TestXid(0, 0)))); }
@Override public int prepare(Xid xid) throws XAException { if (currentXid != null) { throw new EhcacheXAException("Cannot prepare a non-ended start on : " + xid, XAException.XAER_PROTO); } TransactionId transactionId = new TransactionId(xid); XATransactionContext<K, V> transactionContext = transactionContextFactory.get(transactionId); if (transactionContext == null) { throw new EhcacheXAException("Cannot prepare unknown XID : " + xid, XAException.XAER_NOTA); } boolean destroyContext = false; try { destroyContext = transactionContext.prepare() == 0; return destroyContext ? XA_RDONLY : XA_OK; } catch (XATransactionContext.TransactionTimeoutException tte) { destroyContext = true; throw new EhcacheXAException("Transaction timed out", XAException.XA_RBTIMEOUT); } catch (IllegalStateException ise) { throw new EhcacheXAException("Cannot prepare XID : " + xid, XAException.XAER_PROTO, ise); } catch (StoreAccessException cae) { throw new EhcacheXAException("Cannot prepare XID : " + xid, XAException.XAER_RMERR, cae); } finally { if (destroyContext) { transactionContextFactory.destroy(transactionId); } } }
@Override public void end(Xid xid, int flag) throws XAException { if (flag != XAResource.TMSUCCESS && flag != XAResource.TMFAIL) { throw new EhcacheXAException("End flag not supported : " + xaResourceFlagsToString(flag), XAException.XAER_INVAL); } if (currentXid == null) { throw new EhcacheXAException("Not started on : " + xid, XAException.XAER_PROTO); } TransactionId transactionId = new TransactionId(currentXid); XATransactionContext<K, V> transactionContext = transactionContextFactory.get(transactionId); if (transactionContext == null) { throw new EhcacheXAException("Cannot end unknown XID : " + xid, XAException.XAER_NOTA); } boolean destroyContext = false; if (flag == XAResource.TMFAIL) { destroyContext = true; } currentXid = null; try { if (transactionContext.hasTimedOut()) { destroyContext = true; throw new EhcacheXAException("Transaction timeout for XID : " + xid, XAException.XA_RBTIMEOUT); } } finally { if (destroyContext) { transactionContextFactory.destroy(transactionId); } } }
@Test public void testJoinWorks() throws Exception { EhcacheXAResource<Long, String> xaResource = new EhcacheXAResource<>(underlyingStore, journal, xaTransactionContextFactory); when(xaTransactionContextFactory.createTransactionContext(eq(new TransactionId(new TestXid(0, 0))), refEq(underlyingStore), refEq(journal), anyInt())).thenReturn(xaTransactionContext); xaResource.start(new TestXid(0, 0), XAResource.TMNOFLAGS); when(xaTransactionContextFactory.get(eq(new TransactionId(new TestXid(0, 0))))).thenReturn(xaTransactionContext); xaResource.end(new TestXid(0, 0), XAResource.TMSUCCESS); xaResource.start(new TestXid(0, 0), XAResource.TMJOIN); xaResource.end(new TestXid(0, 0), XAResource.TMSUCCESS); }
@Override public void start(Xid xid, int flag) throws XAException { if (flag != XAResource.TMNOFLAGS && flag != XAResource.TMJOIN) { throw new EhcacheXAException("Start flag not supported : " + xaResourceFlagsToString(flag), XAException.XAER_INVAL); } if (currentXid != null) { throw new EhcacheXAException("Already started on : " + xid, XAException.XAER_PROTO); } TransactionId transactionId = new TransactionId(xid); XATransactionContext<K, V> transactionContext = transactionContextFactory.get(transactionId); if (flag == XAResource.TMNOFLAGS) { if (transactionContext == null) { transactionContext = transactionContextFactory.createTransactionContext(transactionId, underlyingStore, journal, transactionTimeoutInSeconds); } else { throw new EhcacheXAException("Cannot start in parallel on two XIDs : starting " + xid, XAException.XAER_RMERR); } } else { if (transactionContext == null) { throw new EhcacheXAException("Cannot join unknown XID : " + xid, XAException.XAER_NOTA); } } if (transactionContext.hasTimedOut()) { transactionContextFactory.destroy(transactionId); throw new EhcacheXAException("Transaction timeout for XID : " + xid, XAException.XA_RBTIMEOUT); } currentXid = xid; }
@Override public void rollback(Xid xid) throws XAException { if (currentXid != null) { throw new EhcacheXAException("Cannot rollback a non-ended start on : " + xid, XAException.XAER_PROTO); } TransactionId transactionId = new TransactionId(xid); XATransactionContext<K, V> transactionContext = transactionContextFactory.get(transactionId); try { XATransactionContext<K, V> rollbackContext = transactionContext; if (rollbackContext == null) { // recovery rollback rollbackContext = new XATransactionContext<>(new TransactionId(new SerializableXid(xid)), underlyingStore, journal, null, 0L); } rollbackContext.rollback(transactionContext == null); } catch (IllegalStateException ise) { throw new EhcacheXAException("Cannot rollback unknown XID : " + xid, XAException.XAER_NOTA); } catch (StoreAccessException cae) { throw new EhcacheXAException("Cannot rollback XID : " + xid, XAException.XAER_RMERR, cae); } finally { if (transactionContext != null) { transactionContextFactory.destroy(transactionId); } } }