/* * Copyright 2008 The Topaz Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * Contributions: */ package org.mulgara.resolver.distributed; // Java 2 standard packages import java.io.PrintWriter; import java.io.StringWriter; import java.util.Arrays; import java.util.Collection; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; // Third party packages import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.apache.log4j.Logger; // Locally written packages import org.mulgara.resolver.spi.DummyXAResource; import org.mulgara.resolver.spi.Resolver; import org.mulgara.resolver.spi.ResolverFactory; import org.mulgara.resolver.spi.ResolverSession; /** * Testing MultiXAResource. * * @created 2008-10-21 * @author Ronald Tschalär * @copyright ©2008 <a href="http://www.topazproject.org/">Topaz Project Foundation</a> * @licence Apache License v2.0 */ public class MultiXAResourceUnitTest extends TestCase { /** Logger. */ private static final Logger logger = Logger.getLogger(MultiXAResourceUnitTest.class); // XA error codes in shorter form private static final int UN = 0; private static final int RB = XAException.XA_RBOTHER; private static final int HR = XAException.XA_HEURHAZ; private static final int ER = XAException.XAER_RMERR; private static final int FL = XAException.XAER_RMFAIL; private static final int NT = XAException.XAER_NOTA; public MultiXAResourceUnitTest(String name) { super(name); } public static Test suite() { TestSuite suite = new TestSuite(); suite.addTest(new MultiXAResourceUnitTest("testCommit")); suite.addTest(new MultiXAResourceUnitTest("testRollback")); suite.addTest(new MultiXAResourceUnitTest("testSuspend")); suite.addTest(new MultiXAResourceUnitTest("testStartFailure")); suite.addTest(new MultiXAResourceUnitTest("testSuspendFailure")); suite.addTest(new MultiXAResourceUnitTest("testResumeFailure")); suite.addTest(new MultiXAResourceUnitTest("testEndFailure")); suite.addTest(new MultiXAResourceUnitTest("testPrepareFailure")); suite.addTest(new MultiXAResourceUnitTest("testCommitFailure")); suite.addTest(new MultiXAResourceUnitTest("testRollbackFailure")); suite.addTest(new MultiXAResourceUnitTest("testMultiFailure")); return suite; } // // Test cases // /** * Test simple sequence ending in commit. */ public void testCommit() { logger.info("Testing commit"); testCommit(new MockXAResource[] { new MockXAResource() }); testCommit(new MockXAResource[] { new MockXAResource(), new MockXAResource() }); testCommit(new MockXAResource[] { new MockXAResource(), new MockXAResource(), new MockXAResource() }); } private void testCommit(MockXAResource[] mocks) { testCommit(mocks, new int[] { }); testCommit(mocks, new int[] { 0 }); if (mocks.length < 2) return; testCommit(mocks, new int[] { 1 }); testCommit(mocks, new int[] { 0, 1 }); if (mocks.length < 3) return; testCommit(mocks, new int[] { 2 }); testCommit(mocks, new int[] { 0, 2 }); testCommit(mocks, new int[] { 1, 2 }); testCommit(mocks, new int[] { 0, 1, 2 }); } private void testCommit(MockXAResource[] mocks, int[] readOnlys) { try { TestMultiXAResource xares = new TestMultiXAResource(15, new DummyResolverFactory()); // one-phase commit for (MockXAResource mock : mocks) mock.reset(); for (int idx : readOnlys) mocks[idx].prepareStatus = XAResource.XA_RDONLY; Xid xid = new TestXid(1); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); for (MockXAResource mock : mocks) assertEquals(MockXAResource.State.ACTIVE, mock.state); xares.end(xid, XAResource.TMSUCCESS); for (MockXAResource mock : mocks) assertEquals(MockXAResource.State.ENDED, mock.state); xares.commit(xid, true); for (int idx = 0; idx < mocks.length; idx++) { if (Arrays.binarySearch(readOnlys, idx) >= 0) { assertEquals(MockXAResource.State.ROLLEDBACK, mocks[idx].state); } else { assertEquals(MockXAResource.State.COMMITTED, mocks[idx].state); } } assertEquals(0, xares.getTxns().size()); // two-phase commit for (MockXAResource mock : mocks) mock.reset(); for (int idx : readOnlys) mocks[idx].prepareStatus = XAResource.XA_RDONLY; xid = new TestXid(2); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); for (MockXAResource mock : mocks) assertEquals(MockXAResource.State.ACTIVE, mock.state); xares.end(xid, XAResource.TMSUCCESS); for (MockXAResource mock : mocks) assertEquals(MockXAResource.State.ENDED, mock.state); xares.prepare(xid); for (int idx = 0; idx < mocks.length; idx++) { if (Arrays.binarySearch(readOnlys, idx) >= 0) { assertEquals(MockXAResource.State.ROLLEDBACK, mocks[idx].state); } else { assertEquals(MockXAResource.State.PREPARED, mocks[idx].state); } } xares.commit(xid, false); for (int idx = 0; idx < mocks.length; idx++) { if (Arrays.binarySearch(readOnlys, idx) >= 0) { assertEquals(MockXAResource.State.ROLLEDBACK, mocks[idx].state); } else { assertEquals(MockXAResource.State.COMMITTED, mocks[idx].state); } } assertEquals(0, xares.getTxns().size()); } catch (Exception e) { fail(e); } } /** * Test simple sequence ending in rollback. */ public void testRollback() { logger.info("Testing rollback"); testRollback(new MockXAResource[] { new MockXAResource() }); testRollback(new MockXAResource[] { new MockXAResource(), new MockXAResource() }); testRollback(new MockXAResource[] { new MockXAResource(), new MockXAResource(), new MockXAResource() }); } private void testRollback(MockXAResource[] mocks) { testRollback(mocks, new int[] { }); testRollback(mocks, new int[] { 0 }); if (mocks.length < 2) return; testRollback(mocks, new int[] { 1 }); testRollback(mocks, new int[] { 0, 1 }); if (mocks.length < 3) return; testRollback(mocks, new int[] { 2 }); testRollback(mocks, new int[] { 0, 2 }); testRollback(mocks, new int[] { 1, 2 }); testRollback(mocks, new int[] { 0, 1, 2 }); } private void testRollback(MockXAResource[] mocks, int[] readOnlys) { try { TestMultiXAResource xares = new TestMultiXAResource(15, new DummyResolverFactory()); // rollback w/o prepare for (MockXAResource mock : mocks) mock.reset(); for (int idx : readOnlys) mocks[idx].prepareStatus = XAResource.XA_RDONLY; Xid xid = new TestXid(1); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); for (MockXAResource mock : mocks) assertEquals(MockXAResource.State.ACTIVE, mock.state); xares.end(xid, XAResource.TMSUCCESS); for (MockXAResource mock : mocks) assertEquals(MockXAResource.State.ENDED, mock.state); xares.rollback(xid); for (MockXAResource mock : mocks) assertEquals(MockXAResource.State.ROLLEDBACK, mock.state); assertEquals(0, xares.getTxns().size()); // rollback after prepare for (MockXAResource mock : mocks) mock.reset(); for (int idx : readOnlys) mocks[idx].prepareStatus = XAResource.XA_RDONLY; xid = new TestXid(2); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); for (MockXAResource mock : mocks) assertEquals(MockXAResource.State.ACTIVE, mock.state); xares.end(xid, XAResource.TMSUCCESS); for (MockXAResource mock : mocks) assertEquals(MockXAResource.State.ENDED, mock.state); xares.prepare(xid); for (int idx = 0; idx < mocks.length; idx++) { if (Arrays.binarySearch(readOnlys, idx) >= 0) { assertEquals(MockXAResource.State.ROLLEDBACK, mocks[idx].state); } else { assertEquals(MockXAResource.State.PREPARED, mocks[idx].state); } } xares.rollback(xid); for (MockXAResource mock : mocks) assertEquals(MockXAResource.State.ROLLEDBACK, mock.state); assertEquals(0, xares.getTxns().size()); } catch (Exception e) { fail(e); } } /** * Test simple suspend/resume. */ public void testSuspend() { logger.info("Testing suspend"); testSuspend(new MockXAResource[] { new MockXAResource() }); testSuspend(new MockXAResource[] { new MockXAResource(), new MockXAResource() }); testSuspend(new MockXAResource[] { new MockXAResource(), new MockXAResource(), new MockXAResource() }); } private void testSuspend(MockXAResource[] mocks) { try { TestMultiXAResource xares = new TestMultiXAResource(15, new DummyResolverFactory()); // simple suspend/resume Xid xid = new TestXid(1); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); for (MockXAResource mock : mocks) assertEquals(MockXAResource.State.ACTIVE, mock.state); xares.end(xid, XAResource.TMSUSPEND); for (MockXAResource mock : mocks) assertEquals(MockXAResource.State.SUSPENDED, mock.state); xares.start(xid, XAResource.TMRESUME); for (MockXAResource mock : mocks) assertEquals(MockXAResource.State.ACTIVE, mock.state); xares.end(xid, XAResource.TMSUCCESS); for (MockXAResource mock : mocks) assertEquals(MockXAResource.State.ENDED, mock.state); assertEquals(1, xares.getTxns().size()); // start/suspend/resume first, then enlist second, suspend and resume if (mocks.length < 2) return; for (MockXAResource mock : mocks) mock.reset(); xid = new TestXid(2); xares.start(xid, XAResource.TMNOFLAGS); xares.enlistResource(mocks[0]); assertEquals(MockXAResource.State.ACTIVE, mocks[0].state); assertEquals(MockXAResource.State.IDLE, mocks[1].state); if (mocks.length > 2) assertEquals(MockXAResource.State.IDLE, mocks[2].state); xares.end(xid, XAResource.TMSUSPEND); assertEquals(MockXAResource.State.SUSPENDED, mocks[0].state); assertEquals(MockXAResource.State.IDLE, mocks[1].state); if (mocks.length > 2) assertEquals(MockXAResource.State.IDLE, mocks[2].state); xares.start(xid, XAResource.TMRESUME); assertEquals(MockXAResource.State.ACTIVE, mocks[0].state); assertEquals(MockXAResource.State.IDLE, mocks[1].state); if (mocks.length > 2) assertEquals(MockXAResource.State.IDLE, mocks[2].state); xares.enlistResource(mocks[1]); assertEquals(MockXAResource.State.ACTIVE, mocks[0].state); assertEquals(MockXAResource.State.ACTIVE, mocks[1].state); if (mocks.length > 2) assertEquals(MockXAResource.State.IDLE, mocks[2].state); xares.end(xid, XAResource.TMSUCCESS); assertEquals(MockXAResource.State.ENDED, mocks[0].state); assertEquals(MockXAResource.State.ENDED, mocks[1].state); if (mocks.length > 2) assertEquals(MockXAResource.State.IDLE, mocks[2].state); assertEquals(2, xares.getTxns().size()); } catch (Exception e) { fail(e); } } /** * Test start failures. */ public void testStartFailure() { logger.info("Testing start failure"); testStartFailure(new MockFailingXAResource[] { new MockFailingXAResource() }); testStartFailure(new MockFailingXAResource[] { new MockFailingXAResource(), new MockFailingXAResource() }); testStartFailure(new MockFailingXAResource[] { new MockFailingXAResource(), new MockFailingXAResource(), new MockFailingXAResource() }); } private void testStartFailure(MockFailingXAResource[] mocks) { testStartFailure(mocks, UN, 0); testStartFailure(mocks, RB, 0); testStartFailure(mocks, FL, 0); testStartFailure(mocks, NT, 0); if (mocks.length < 2) return; testStartFailure(mocks, UN, 1); testStartFailure(mocks, RB, 1); testStartFailure(mocks, FL, 1); testStartFailure(mocks, NT, 1); if (mocks.length < 3) return; testStartFailure(mocks, UN, 2); testStartFailure(mocks, RB, 2); testStartFailure(mocks, FL, 2); testStartFailure(mocks, NT, 2); } private void testStartFailure(MockFailingXAResource[] mocks, int errorCode, int failer) { try { TestMultiXAResource xares = new TestMultiXAResource(15, new DummyResolverFactory()); for (MockXAResource mock : mocks) mock.reset(); setFailMode(mocks[failer], errorCode, 1, -1, -1, -1, false, false, false); Xid xid = new TestXid(1); xares.start(xid, XAResource.TMNOFLAGS); for (int idx = 0; idx < failer; idx++) xares.enlistResource(mocks[idx]); try { xares.enlistResource(mocks[failer]); fail("should have thrown exception"); } catch (XAException xae) { if (errorCode == FL) assertTrue(FL != xae.errorCode); else if (errorCode == NT) assertTrue(NT != xae.errorCode); else assertEquals(errorCode, xae.errorCode); assertStates(mocks, failer, MockXAResource.State.ACTIVE, (errorCode == FL || errorCode == NT) ? MockXAResource.State.ROLLEDBACK : MockXAResource.State.RB_ONLY, MockXAResource.State.IDLE); } xares.end(xid, XAResource.TMFAIL); assertStates(mocks, failer, MockXAResource.State.ENDED, (errorCode == FL || errorCode == NT) ? MockXAResource.State.ROLLEDBACK : MockXAResource.State.RB_ONLY, MockXAResource.State.IDLE); xares.rollback(xid); assertStates(mocks, failer, MockXAResource.State.ROLLEDBACK, MockXAResource.State.ROLLEDBACK, MockXAResource.State.IDLE); assertEquals(0, xares.getTxns().size()); } catch (Exception e) { fail(e); } } /** * Test suspend failures. */ public void testSuspendFailure() { logger.info("Testing suspend failure"); testSuspendFailure(new MockFailingXAResource[] { new MockFailingXAResource() }); testSuspendFailure(new MockFailingXAResource[] { new MockFailingXAResource(), new MockFailingXAResource() }); testSuspendFailure(new MockFailingXAResource[] { new MockFailingXAResource(), new MockFailingXAResource(), new MockFailingXAResource() }); } private void testSuspendFailure(MockFailingXAResource[] mocks) { testSuspendFailure(mocks, UN, 0); testSuspendFailure(mocks, RB, 0); testSuspendFailure(mocks, FL, 0); testSuspendFailure(mocks, NT, 0); if (mocks.length < 2) return; testSuspendFailure(mocks, UN, 1); testSuspendFailure(mocks, RB, 1); testSuspendFailure(mocks, FL, 1); testSuspendFailure(mocks, NT, 1); if (mocks.length < 3) return; testSuspendFailure(mocks, UN, 2); testSuspendFailure(mocks, RB, 2); testSuspendFailure(mocks, FL, 2); testSuspendFailure(mocks, NT, 2); } private void testSuspendFailure(MockFailingXAResource[] mocks, int errorCode, int failer) { try { TestMultiXAResource xares = new TestMultiXAResource(15, new DummyResolverFactory()); for (MockXAResource mock : mocks) mock.reset(); setFailMode(mocks[failer], errorCode, -1, 1, -1, -1, false, false, false); Xid xid = new TestXid(1); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); assertStates(mocks, failer, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE); try { xares.end(xid, XAResource.TMSUSPEND); fail("should have thrown exception"); } catch (XAException xae) { if (errorCode == FL) assertTrue(FL != xae.errorCode); else if (errorCode == NT) assertTrue(NT != xae.errorCode); else assertEquals(errorCode, xae.errorCode); assertStates(mocks, failer, MockXAResource.State.ENDED, (errorCode == FL || errorCode == NT) ? MockXAResource.State.ROLLEDBACK : MockXAResource.State.RB_ONLY, MockXAResource.State.ENDED); } xares.rollback(xid); assertStates(mocks, failer, MockXAResource.State.ROLLEDBACK, MockXAResource.State.ROLLEDBACK, MockXAResource.State.ROLLEDBACK); assertEquals(0, xares.getTxns().size()); } catch (Exception e) { fail(e); } } /** * Test resume failures. */ public void testResumeFailure() { logger.info("Testing resume failure"); testResumeFailure(new MockFailingXAResource[] { new MockFailingXAResource() }); testResumeFailure(new MockFailingXAResource[] { new MockFailingXAResource(), new MockFailingXAResource() }); testResumeFailure(new MockFailingXAResource[] { new MockFailingXAResource(), new MockFailingXAResource(), new MockFailingXAResource() }); } private void testResumeFailure(MockFailingXAResource[] mocks) { testResumeFailure(mocks, UN, 0); testResumeFailure(mocks, RB, 0); testResumeFailure(mocks, FL, 0); testResumeFailure(mocks, NT, 0); if (mocks.length < 2) return; testResumeFailure(mocks, UN, 1); testResumeFailure(mocks, RB, 1); testResumeFailure(mocks, FL, 1); testResumeFailure(mocks, NT, 1); if (mocks.length < 3) return; testResumeFailure(mocks, UN, 2); testResumeFailure(mocks, RB, 2); testResumeFailure(mocks, FL, 2); testResumeFailure(mocks, NT, 2); } private void testResumeFailure(MockFailingXAResource[] mocks, int errorCode, int failer) { try { TestMultiXAResource xares = new TestMultiXAResource(15, new DummyResolverFactory()); for (MockXAResource mock : mocks) mock.reset(); setFailMode(mocks[failer], errorCode, -1, -1, 1, -1, false, false, false); Xid xid = new TestXid(1); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); assertStates(mocks, failer, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE); xares.end(xid, XAResource.TMSUSPEND); assertStates(mocks, failer, MockXAResource.State.SUSPENDED, MockXAResource.State.SUSPENDED, MockXAResource.State.SUSPENDED); try { xares.start(xid, XAResource.TMRESUME); fail("should have thrown exception"); } catch (XAException xae) { if (errorCode == FL) assertTrue(FL != xae.errorCode); else if (errorCode == NT) assertTrue(NT != xae.errorCode); else assertEquals(errorCode, xae.errorCode); assertStates(mocks, failer, MockXAResource.State.ENDED, (errorCode == FL || errorCode == NT) ? MockXAResource.State.ROLLEDBACK : MockXAResource.State.RB_ONLY, MockXAResource.State.ENDED); } xares.rollback(xid); assertStates(mocks, failer, MockXAResource.State.ROLLEDBACK, MockXAResource.State.ROLLEDBACK, MockXAResource.State.ROLLEDBACK); assertEquals(0, xares.getTxns().size()); } catch (Exception e) { fail(e); } } /** * Test end failures. */ public void testEndFailure() { logger.info("Testing end failure"); testEndFailure(new MockFailingXAResource[] { new MockFailingXAResource() }); testEndFailure(new MockFailingXAResource[] { new MockFailingXAResource(), new MockFailingXAResource() }); testEndFailure(new MockFailingXAResource[] { new MockFailingXAResource(), new MockFailingXAResource(), new MockFailingXAResource() }); } private void testEndFailure(MockFailingXAResource[] mocks) { testEndFailure(mocks, UN, 0); testEndFailure(mocks, RB, 0); testEndFailure(mocks, FL, 0); testEndFailure(mocks, NT, 0); if (mocks.length < 2) return; testEndFailure(mocks, UN, 1); testEndFailure(mocks, RB, 1); testEndFailure(mocks, FL, 1); testEndFailure(mocks, NT, 1); if (mocks.length < 3) return; testEndFailure(mocks, UN, 2); testEndFailure(mocks, RB, 2); testEndFailure(mocks, FL, 2); testEndFailure(mocks, NT, 2); } private void testEndFailure(MockFailingXAResource[] mocks, int errorCode, int failer) { try { TestMultiXAResource xares = new TestMultiXAResource(15, new DummyResolverFactory()); // straight start-end for (MockXAResource mock : mocks) mock.reset(); setFailMode(mocks[failer], errorCode, -1, -1, -1, 1, false, false, false); Xid xid = new TestXid(1); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); assertStates(mocks, failer, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE); try { xares.end(xid, XAResource.TMSUCCESS); fail("should have thrown exception"); } catch (XAException xae) { if (errorCode == FL) assertTrue(FL != xae.errorCode); else if (errorCode == NT) assertTrue(NT != xae.errorCode); else assertEquals(errorCode, xae.errorCode); assertStates(mocks, failer, MockXAResource.State.ENDED, (errorCode == FL || errorCode == NT) ? MockXAResource.State.ROLLEDBACK : MockXAResource.State.RB_ONLY, MockXAResource.State.ENDED); } xares.rollback(xid); assertStates(mocks, failer, MockXAResource.State.ROLLEDBACK, MockXAResource.State.ROLLEDBACK, MockXAResource.State.ROLLEDBACK); assertEquals(0, xares.getTxns().size()); // start-suspend-resume-end for (MockXAResource mock : mocks) mock.reset(); setFailMode(mocks[failer], errorCode, -1, -1, -1, 1, false, false, false); xid = new TestXid(2); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); assertStates(mocks, failer, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE); xares.end(xid, XAResource.TMSUSPEND); assertStates(mocks, failer, MockXAResource.State.SUSPENDED, MockXAResource.State.SUSPENDED, MockXAResource.State.SUSPENDED); xares.start(xid, XAResource.TMRESUME); assertStates(mocks, failer, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE); try { xares.end(xid, XAResource.TMSUCCESS); fail("should have thrown exception"); } catch (XAException xae) { if (errorCode == FL) assertTrue(FL != xae.errorCode); else if (errorCode == NT) assertTrue(NT != xae.errorCode); else assertEquals(errorCode, xae.errorCode); assertStates(mocks, failer, MockXAResource.State.ENDED, (errorCode == FL || errorCode == NT) ? MockXAResource.State.ROLLEDBACK : MockXAResource.State.RB_ONLY, MockXAResource.State.ENDED); } xares.rollback(xid); assertStates(mocks, failer, MockXAResource.State.ROLLEDBACK, MockXAResource.State.ROLLEDBACK, MockXAResource.State.ROLLEDBACK); assertEquals(0, xares.getTxns().size()); } catch (Exception e) { fail(e); } } /** * Test prepare failures. */ public void testPrepareFailure() { logger.info("Testing prepare failure"); testPrepareFailure(new MockFailingXAResource[] { new MockFailingXAResource() }); testPrepareFailure(new MockFailingXAResource[] { new MockFailingXAResource(), new MockFailingXAResource() }); testPrepareFailure(new MockFailingXAResource[] { new MockFailingXAResource(), new MockFailingXAResource(), new MockFailingXAResource() }); } private void testPrepareFailure(MockFailingXAResource[] mocks) { testPrepareFailure(mocks, UN, 0); testPrepareFailure(mocks, RB, 0); testPrepareFailure(mocks, FL, 0); testPrepareFailure(mocks, NT, 0); if (mocks.length < 2) return; testPrepareFailure(mocks, UN, 1); testPrepareFailure(mocks, RB, 1); testPrepareFailure(mocks, FL, 1); testPrepareFailure(mocks, NT, 1); if (mocks.length < 3) return; testPrepareFailure(mocks, UN, 2); testPrepareFailure(mocks, RB, 2); testPrepareFailure(mocks, FL, 2); testPrepareFailure(mocks, NT, 2); } private void testPrepareFailure(MockFailingXAResource[] mocks, int errorCode, int failer) { try { TestMultiXAResource xares = new TestMultiXAResource(15, new DummyResolverFactory()); // implied prepare (one phase commit) for (MockXAResource mock : mocks) mock.reset(); setFailMode(mocks[failer], errorCode, -1, -1, -1, -1, true, false, false); Xid xid = new TestXid(1); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); assertStates(mocks, failer, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE); xares.end(xid, XAResource.TMSUCCESS); assertStates(mocks, failer, MockXAResource.State.ENDED, MockXAResource.State.ENDED, MockXAResource.State.ENDED); try { xares.commit(xid, true); fail("should have thrown exception"); } catch (XAException xae) { assertTrue("not a rollback: " + xae.errorCode, DummyXAResource.isRollback(xae.errorCode)); assertStates(mocks, failer, MockXAResource.State.ROLLEDBACK, MockXAResource.State.ROLLEDBACK, MockXAResource.State.ROLLEDBACK); } assertEquals(0, xares.getTxns().size()); // explicit prepare (two phase commit) for (MockXAResource mock : mocks) mock.reset(); setFailMode(mocks[failer], errorCode, -1, -1, -1, -1, true, false, false); xid = new TestXid(2); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); assertStates(mocks, failer, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE); xares.end(xid, XAResource.TMSUCCESS); assertStates(mocks, failer, MockXAResource.State.ENDED, MockXAResource.State.ENDED, MockXAResource.State.ENDED); try { xares.prepare(xid); fail("should have thrown exception"); } catch (XAException xae) { boolean isEndCode = errorCode == FL || errorCode == NT || DummyXAResource.isRollback(errorCode); if (isEndCode) assertEquals(ER, xae.errorCode); else assertEquals(errorCode, xae.errorCode); assertStates(mocks, failer, MockXAResource.State.PREPARED, (isEndCode) ? MockXAResource.State.ROLLEDBACK : MockXAResource.State.RB_ONLY, MockXAResource.State.PREPARED); } xares.rollback(xid); assertStates(mocks, failer, MockXAResource.State.ROLLEDBACK, MockXAResource.State.ROLLEDBACK, MockXAResource.State.ROLLEDBACK); assertEquals(0, xares.getTxns().size()); } catch (Exception e) { fail(e); } } /** * Test commit failures. */ public void testCommitFailure() { logger.info("Testing commit failure"); testCommitFailure(new MockFailingXAResource[] { new MockFailingXAResource() }); testCommitFailure(new MockFailingXAResource[] { new MockFailingXAResource(), new MockFailingXAResource() }); testCommitFailure(new MockFailingXAResource[] { new MockFailingXAResource(), new MockFailingXAResource(), new MockFailingXAResource() }); } private void testCommitFailure(MockFailingXAResource[] mocks) { testCommitFailure(mocks, UN, 0); testCommitFailure(mocks, RB, 0); testCommitFailure(mocks, HR, 0); testCommitFailure(mocks, FL, 0); if (mocks.length < 2) return; testCommitFailure(mocks, UN, 1); testCommitFailure(mocks, RB, 1); testCommitFailure(mocks, HR, 1); testCommitFailure(mocks, FL, 1); if (mocks.length < 3) return; testCommitFailure(mocks, UN, 2); testCommitFailure(mocks, RB, 2); testCommitFailure(mocks, HR, 2); testCommitFailure(mocks, FL, 2); } private void testCommitFailure(MockFailingXAResource[] mocks, int errorCode, int failer) { try { TestMultiXAResource xares = new TestMultiXAResource(15, new DummyResolverFactory()); // one-phase for (MockXAResource mock : mocks) mock.reset(); setFailMode(mocks[failer], errorCode, -1, -1, -1, -1, false, true, false); Xid xid = new TestXid(1); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); assertStates(mocks, failer, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE); xares.end(xid, XAResource.TMSUCCESS); assertStates(mocks, failer, MockXAResource.State.ENDED, MockXAResource.State.ENDED, MockXAResource.State.ENDED); try { xares.commit(xid, true); fail("should have thrown exception"); } catch (XAException xae) { assertTrue("expected heuristic code: " + xae.errorCode, DummyXAResource.isHeuristic(xae.errorCode)); assertStates(mocks, failer, MockXAResource.State.COMMITTED, (errorCode == UN) ? MockXAResource.State.COMMITTED : (errorCode == HR) ? MockXAResource.State.HEUR : MockXAResource.State.ROLLEDBACK, (failer == 0 && errorCode == RB) ? MockXAResource.State.ROLLEDBACK : MockXAResource.State.COMMITTED); } xares.forget(xid); assertStates(mocks, failer, MockXAResource.State.COMMITTED, (errorCode == UN) ? MockXAResource.State.COMMITTED : (errorCode == HR) ? MockXAResource.State.HEUR_DONE : MockXAResource.State.ROLLEDBACK, (failer == 0 && errorCode == RB) ? MockXAResource.State.ROLLEDBACK : MockXAResource.State.COMMITTED); assertEquals(0, xares.getTxns().size()); // two-phase for (MockXAResource mock : mocks) mock.reset(); setFailMode(mocks[failer], errorCode, -1, -1, -1, -1, false, true, false); xid = new TestXid(2); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); assertStates(mocks, failer, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE); xares.end(xid, XAResource.TMSUCCESS); assertStates(mocks, failer, MockXAResource.State.ENDED, MockXAResource.State.ENDED, MockXAResource.State.ENDED); xares.prepare(xid); assertStates(mocks, failer, MockXAResource.State.PREPARED, MockXAResource.State.PREPARED, MockXAResource.State.PREPARED); try { xares.commit(xid, false); fail("should have thrown exception"); } catch (XAException xae) { assertTrue("expected heuristic code: " + xae.errorCode, DummyXAResource.isHeuristic(xae.errorCode)); assertStates(mocks, failer, MockXAResource.State.COMMITTED, (errorCode == UN) ? MockXAResource.State.COMMITTED : (errorCode == HR) ? MockXAResource.State.HEUR : MockXAResource.State.ROLLEDBACK, (failer == 0 && errorCode == RB) ? MockXAResource.State.ROLLEDBACK : MockXAResource.State.COMMITTED); } xares.forget(xid); assertStates(mocks, failer, MockXAResource.State.COMMITTED, (errorCode == UN) ? MockXAResource.State.COMMITTED : (errorCode == HR) ? MockXAResource.State.HEUR_DONE : MockXAResource.State.ROLLEDBACK, (failer == 0 && errorCode == RB) ? MockXAResource.State.ROLLEDBACK : MockXAResource.State.COMMITTED); assertEquals(0, xares.getTxns().size()); } catch (Exception e) { fail(e); } } /** * Test rollback failures. */ public void testRollbackFailure() { logger.info("Testing rollback failure"); testRollbackFailure(new MockFailingXAResource[] { new MockFailingXAResource() }); testRollbackFailure(new MockFailingXAResource[] { new MockFailingXAResource(), new MockFailingXAResource() }); testRollbackFailure(new MockFailingXAResource[] { new MockFailingXAResource(), new MockFailingXAResource(), new MockFailingXAResource() }); } private void testRollbackFailure(MockFailingXAResource[] mocks) { testRollbackFailure(mocks, UN, 0); testRollbackFailure(mocks, RB, 0); testRollbackFailure(mocks, HR, 0); testRollbackFailure(mocks, FL, 0); if (mocks.length < 2) return; testRollbackFailure(mocks, UN, 1); testRollbackFailure(mocks, RB, 1); testRollbackFailure(mocks, HR, 1); testRollbackFailure(mocks, FL, 1); if (mocks.length < 3) return; testRollbackFailure(mocks, UN, 2); testRollbackFailure(mocks, RB, 2); testRollbackFailure(mocks, HR, 2); testRollbackFailure(mocks, FL, 2); } private void testRollbackFailure(MockFailingXAResource[] mocks, int errorCode, int failer) { try { TestMultiXAResource xares = new TestMultiXAResource(15, new DummyResolverFactory()); // after end for (MockXAResource mock : mocks) mock.reset(); setFailMode(mocks[failer], errorCode, -1, -1, -1, -1, false, false, true); Xid xid = new TestXid(1); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); assertStates(mocks, failer, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE); xares.end(xid, XAResource.TMSUCCESS); assertStates(mocks, failer, MockXAResource.State.ENDED, MockXAResource.State.ENDED, MockXAResource.State.ENDED); try { xares.rollback(xid); fail("should have thrown exception"); } catch (XAException xae) { assertTrue("expected heuristic code: " + xae.errorCode, DummyXAResource.isHeuristic(xae.errorCode)); assertStates(mocks, failer, MockXAResource.State.ROLLEDBACK, (errorCode == HR) ? MockXAResource.State.HEUR : MockXAResource.State.ROLLEDBACK, MockXAResource.State.ROLLEDBACK); } xares.forget(xid); assertStates(mocks, failer, MockXAResource.State.ROLLEDBACK, (errorCode == HR) ? MockXAResource.State.HEUR_DONE : MockXAResource.State.ROLLEDBACK, MockXAResource.State.ROLLEDBACK); assertEquals(0, xares.getTxns().size()); // after prepare for (MockXAResource mock : mocks) mock.reset(); setFailMode(mocks[failer], errorCode, -1, -1, -1, -1, false, false, true); xid = new TestXid(2); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); assertStates(mocks, failer, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE, MockXAResource.State.ACTIVE); xares.end(xid, XAResource.TMSUCCESS); assertStates(mocks, failer, MockXAResource.State.ENDED, MockXAResource.State.ENDED, MockXAResource.State.ENDED); xares.prepare(xid); assertStates(mocks, failer, MockXAResource.State.PREPARED, MockXAResource.State.PREPARED, MockXAResource.State.PREPARED); try { xares.rollback(xid); fail("should have thrown exception"); } catch (XAException xae) { assertTrue("expected heuristic code: " + xae.errorCode, DummyXAResource.isHeuristic(xae.errorCode)); assertStates(mocks, failer, MockXAResource.State.ROLLEDBACK, (errorCode == HR) ? MockXAResource.State.HEUR : MockXAResource.State.ROLLEDBACK, MockXAResource.State.ROLLEDBACK); } xares.forget(xid); assertStates(mocks, failer, MockXAResource.State.ROLLEDBACK, (errorCode == HR) ? MockXAResource.State.HEUR_DONE : MockXAResource.State.ROLLEDBACK, MockXAResource.State.ROLLEDBACK); assertEquals(0, xares.getTxns().size()); } catch (Exception e) { fail(e); } } public void testMultiFailure() { logger.info("Testing multiple failures"); try { TestMultiXAResource xares = new TestMultiXAResource(15, new DummyResolverFactory()); MockFailingXAResource[] mocks = new MockFailingXAResource[] { new MockFailingXAResource(), new MockFailingXAResource() }; // two in end for (MockXAResource mock : mocks) mock.reset(); setFailMode(mocks[0], FL, -1, -1, -1, 1, false, false, false); setFailMode(mocks[1], RB, -1, -1, -1, 1, false, false, false); Xid xid = new TestXid(1); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); assertEquals(MockXAResource.State.ACTIVE, mocks[0].state); assertEquals(MockXAResource.State.ACTIVE, mocks[1].state); try { xares.end(xid, XAResource.TMSUCCESS); fail("should have thrown exception"); } catch (XAException xae) { assertTrue(xae.errorCode != FL); assertEquals(MockXAResource.State.ROLLEDBACK, mocks[0].state); assertEquals(MockXAResource.State.RB_ONLY, mocks[1].state); } xares.rollback(xid); assertEquals(MockXAResource.State.ROLLEDBACK, mocks[0].state); assertEquals(MockXAResource.State.ROLLEDBACK, mocks[1].state); assertEquals(0, xares.getTxns().size()); // two RMFAIL in end for (MockXAResource mock : mocks) mock.reset(); setFailMode(mocks[0], FL, -1, -1, -1, 1, false, false, false); setFailMode(mocks[1], FL, -1, -1, -1, 1, false, false, false); xid = new TestXid(2); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); assertEquals(MockXAResource.State.ACTIVE, mocks[0].state); assertEquals(MockXAResource.State.ACTIVE, mocks[1].state); try { xares.end(xid, XAResource.TMSUCCESS); fail("should have thrown exception"); } catch (XAException xae) { assertTrue(xae.errorCode != FL); assertEquals(MockXAResource.State.ROLLEDBACK, mocks[0].state); assertEquals(MockXAResource.State.ROLLEDBACK, mocks[1].state); } xares.rollback(xid); assertEquals(MockXAResource.State.ROLLEDBACK, mocks[0].state); assertEquals(MockXAResource.State.ROLLEDBACK, mocks[1].state); assertEquals(0, xares.getTxns().size()); // one in prepare, one in rollback for (MockXAResource mock : mocks) mock.reset(); setFailMode(mocks[0], UN, -1, -1, -1, -1, true, false, false); setFailMode(mocks[1], HR, -1, -1, -1, -1, false, false, true); xid = new TestXid(3); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); assertEquals(MockXAResource.State.ACTIVE, mocks[0].state); assertEquals(MockXAResource.State.ACTIVE, mocks[1].state); xares.end(xid, XAResource.TMSUCCESS); assertEquals(MockXAResource.State.ENDED, mocks[0].state); assertEquals(MockXAResource.State.ENDED, mocks[1].state); try { xares.prepare(xid); fail("should have thrown exception"); } catch (XAException xae) { assertTrue(xae.errorCode != FL); assertEquals(MockXAResource.State.RB_ONLY, mocks[0].state); assertEquals(MockXAResource.State.PREPARED, mocks[1].state); } try { xares.rollback(xid); } catch (XAException xae) { assertTrue(xae.errorCode != FL); assertEquals(MockXAResource.State.ROLLEDBACK, mocks[0].state); assertEquals(MockXAResource.State.HEUR, mocks[1].state); } xares.forget(xid); assertEquals(MockXAResource.State.ROLLEDBACK, mocks[0].state); assertEquals(MockXAResource.State.HEUR_DONE, mocks[1].state); assertEquals(0, xares.getTxns().size()); // two in commit for (MockXAResource mock : mocks) mock.reset(); setFailMode(mocks[0], UN, -1, -1, -1, -1, false, true, false); setFailMode(mocks[1], FL, -1, -1, -1, -1, false, true, false); xid = new TestXid(4); xares.start(xid, XAResource.TMNOFLAGS); for (MockXAResource mock : mocks) xares.enlistResource(mock); assertEquals(MockXAResource.State.ACTIVE, mocks[0].state); assertEquals(MockXAResource.State.ACTIVE, mocks[1].state); xares.end(xid, XAResource.TMSUCCESS); assertEquals(MockXAResource.State.ENDED, mocks[0].state); assertEquals(MockXAResource.State.ENDED, mocks[1].state); try { xares.commit(xid, true); fail("should have thrown exception"); } catch (XAException xae) { assertTrue("expected heuristic code: " + xae.errorCode, DummyXAResource.isHeuristic(xae.errorCode)); assertEquals(MockXAResource.State.COMMITTED, mocks[0].state); assertEquals(MockXAResource.State.ROLLEDBACK, mocks[1].state); } xares.forget(xid); assertEquals(MockXAResource.State.COMMITTED, mocks[0].state); assertEquals(MockXAResource.State.ROLLEDBACK, mocks[1].state); assertEquals(0, xares.getTxns().size()); } catch (Exception e) { fail(e); } } // // Internal Test Helpers // private static void assertStates(MockFailingXAResource[] mocks, int failer, MockXAResource.State preFail, MockXAResource.State fail, MockXAResource.State postFail) { for (int idx = 0; idx < failer; idx++) assertEquals(preFail, mocks[idx].state); assertEquals(fail, mocks[failer].state); for (int idx = failer + 1; idx < mocks.length; idx++) assertEquals(postFail, mocks[idx].state); } private static void setFailMode(MockFailingXAResource mock, int errorCode, int failStartAfter, int failSuspendAfter, int failResumeAfter, int failEndAfter, boolean failPrepare, boolean failCommit, boolean failRollback) { mock.errorCode = errorCode; mock.failStartAfter = failStartAfter >= 0 ? failStartAfter : Integer.MAX_VALUE; mock.failSuspendAfter = failSuspendAfter >= 0 ? failSuspendAfter : Integer.MAX_VALUE; mock.failResumeAfter = failResumeAfter >= 0 ? failResumeAfter : Integer.MAX_VALUE; mock.failEndAfter = failEndAfter >= 0 ? failEndAfter : Integer.MAX_VALUE; mock.failPrepare = failPrepare; mock.failCommit = failCommit; mock.failRollback = failRollback; } /** * A simple extension to MultiXAResource so we can get at the list of active transactions. */ private static class TestMultiXAResource extends MultiXAResource { public TestMultiXAResource(int transactionTimeout, ResolverFactory resolverFactory) { super(transactionTimeout, resolverFactory); } public Collection<MultiXAResource.MultiTxInfo> getTxns() { return resourceManager.transactions.values(); } } /** * A stub/mock XAResource. This verifies state transitions to make sure they follow the rules as * layed out in the X/Open and JTA specs. The prepare-status can be modified to return RDONLY on * prepare. */ private static class MockXAResource extends DummyXAResource { protected final ThreadLocal<Xid> currTxn = new ThreadLocal<Xid>(); public static enum State { IDLE, ACTIVE, SUSPENDED, ENDED, RB_ONLY, PREPARED, COMMITTED, ROLLEDBACK, HEUR, HEUR_DONE }; public State state = State.IDLE; public int startCnt = 0; public int resumeCnt = 0; public int suspendCnt = 0; public int endCnt = 0; public int prepareCnt = 0; public int commitCnt = 0; public int rollbackCnt = 0; public int prepareStatus = XA_OK; public void start(Xid xid, int flags) throws XAException { super.start(xid, flags); if (currTxn.get() != null) { throw new XAException("transaction already active: " + currTxn.get()); } currTxn.set(xid); if (flags == XAResource.TMNOFLAGS && state == State.ACTIVE) { throw new XAException("resource already active: " + state); } if (flags == XAResource.TMRESUME && state != State.SUSPENDED) { throw new XAException("resource not suspended: " + state); } state = State.ACTIVE; if (flags == XAResource.TMNOFLAGS) startCnt++; if (flags == XAResource.TMRESUME) resumeCnt++; } public void end(Xid xid, int flags) throws XAException { super.end(xid, flags); if (!(state == State.SUSPENDED && (flags == XAResource.TMSUCCESS || flags == XAResource.TMFAIL))) { if (!xid.equals(currTxn.get())) { throw new XAException("mismatched transaction end"); } currTxn.set(null); if (state != State.ACTIVE) { throw new XAException("resource not active: " + state); } } state = (flags == XAResource.TMSUSPEND) ? State.SUSPENDED : State.ENDED; if (flags == XAResource.TMSUSPEND) suspendCnt++; if (flags != XAResource.TMSUSPEND) endCnt++; } public int prepare(Xid xid) throws XAException { super.prepare(xid); if (currTxn.get() != null) { throw new XAException("transaction still active: " + currTxn.get()); } if (state != State.ENDED) { throw new XAException("resource not ended: " + state); } state = (prepareStatus == XA_OK) ? State.PREPARED : State.ROLLEDBACK; prepareCnt++; return prepareStatus; } public void commit(Xid xid, boolean onePhase) throws XAException { super.commit(xid, onePhase); if (currTxn.get() != null) { throw new XAException("transaction still active: " + currTxn.get()); } if (state != State.HEUR) { if (onePhase && state != State.ENDED) { throw new XAException("resource not ended: " + state); } if (!onePhase && state != State.PREPARED) { throw new XAException("resource not prepared: " + state); } state = State.COMMITTED; } commitCnt++; } public void rollback(Xid xid) throws XAException { super.rollback(xid); if (currTxn.get() != null) throw new XAException("transaction still active: " + currTxn.get()); if (state != State.HEUR) { if (state != State.ENDED && state != State.RB_ONLY && state != State.PREPARED) { throw new XAException("resource not ended or prepared: " + state); } state = State.ROLLEDBACK; } rollbackCnt++; } public void forget(Xid xid) throws XAException { super.forget(xid); if (state != State.HEUR) throw new XAException("transaction not heuristically completed: " + state); state = State.HEUR_DONE; } public void reset() { state = State.IDLE; startCnt = 0; resumeCnt = 0; suspendCnt = 0; endCnt = 0; prepareCnt = 0; commitCnt = 0; rollbackCnt = 0; prepareStatus = XA_OK; currTxn.set(null); } } /** * This extends MockXAResource to be able to force failures at various points. The fail* and * errorCode fields can be set to control the behaviour. */ private static class MockFailingXAResource extends MockXAResource { public int failStartAfter = Integer.MAX_VALUE; public int failSuspendAfter = Integer.MAX_VALUE; public int failResumeAfter = Integer.MAX_VALUE; public int failEndAfter = Integer.MAX_VALUE; public int errorCode = 0; public boolean failPrepare = false; public boolean failCommit = false; public boolean failRollback = false; public void start(Xid xid, int flags) throws XAException { super.start(xid, flags); if (startCnt >= failStartAfter || resumeCnt >= failResumeAfter) { currTxn.set(null); state = State.RB_ONLY; fail("start"); } } public void end(Xid xid, int flags) throws XAException { super.end(xid, flags); if (endCnt >= failEndAfter || suspendCnt >= failSuspendAfter) { state = State.RB_ONLY; fail("end"); } } public int prepare(Xid xid) throws XAException { int sts = super.prepare(xid); if (failPrepare) { if (isRollback(errorCode)) state = State.ROLLEDBACK; else state = State.RB_ONLY; fail("prepare"); } return sts; } public void commit(Xid xid, boolean onePhase) throws XAException { super.commit(xid, onePhase); if (failCommit) { if (isHeuristic(errorCode)) state = State.HEUR; if (isRollback(errorCode)) state = State.ROLLEDBACK; fail("commit"); } } public void rollback(Xid xid) throws XAException { super.rollback(xid); if (failRollback) { if (isHeuristic(errorCode)) state = State.HEUR; fail("rollback"); } } private void fail(String op) throws XAException { if (errorCode == XAException.XAER_RMFAIL || errorCode == XAException.XAER_NOTA) { state = State.ROLLEDBACK; } throw (errorCode != 0) ? new XAException(errorCode) : new XAException("Testing " + op + " failure"); } public void reset() { super.reset(); failStartAfter = Integer.MAX_VALUE; failSuspendAfter = Integer.MAX_VALUE; failResumeAfter = Integer.MAX_VALUE; failEndAfter = Integer.MAX_VALUE; errorCode = 0; failPrepare = false; failCommit = false; failRollback = false; } } /** * Basic Xid implementation. */ private static class TestXid implements Xid { private int xid; public TestXid(int xid) { this.xid = xid; } public int getFormatId() { return 'X'; } public byte[] getBranchQualifier() { return new byte[] { (byte)(xid >> 0x00), (byte)(xid >> 0x08) }; } public byte[] getGlobalTransactionId() { return new byte[] { (byte)(xid >> 0x10), (byte)(xid >> 0x18) }; } } /** * Just a dummy - nothing is ever called. */ private static class DummyResolverFactory implements ResolverFactory { public void close() { } public void delete() { } public Graph[] getDefaultGraphs() { return null; } public boolean supportsExport() { return true; } public Resolver newResolver(boolean canWrite, ResolverSession resolverSession, Resolver systemResolver) { return null; } } /** * Fail with an unexpected exception */ private void fail(Throwable throwable) { StringWriter stringWriter = new StringWriter(); throwable.printStackTrace(new PrintWriter(stringWriter)); fail(stringWriter.toString()); } }