use of javax.transaction.xa.Xid in project graphdb by neo4j-attic.
the class TestJtaCompliance method testRollback1.
/**
* o Tests that multiple enlistments receive rollback calls properly.
*/
@Test
public void testRollback1() throws Exception {
tm.begin();
FakeXAResource res1 = new FakeXAResource("XAResource1");
FakeXAResource res2 = new FakeXAResource("XAResource2");
// enlist two different resources and verify that the start method
// is invoked with correct flags
// res1
tm.getTransaction().enlistResource(res1);
MethodCall[] calls1 = res1.getAndRemoveMethodCalls();
assertEquals(1, calls1.length);
assertEquals("start", calls1[0].getMethodName());
// res2
tm.getTransaction().enlistResource(res2);
MethodCall[] calls2 = res2.getAndRemoveMethodCalls();
assertEquals(1, calls2.length);
assertEquals("start", calls2[0].getMethodName());
// verify Xid
Object[] args = calls1[0].getArgs();
Xid xid1 = (Xid) args[0];
assertEquals(XAResource.TMNOFLAGS, ((Integer) args[1]).intValue());
args = calls2[0].getArgs();
Xid xid2 = (Xid) args[0];
assertEquals(XAResource.TMNOFLAGS, ((Integer) args[1]).intValue());
// should have same global transaction id
byte[] globalTxId1 = xid1.getGlobalTransactionId();
byte[] globalTxId2 = xid2.getGlobalTransactionId();
assertTrue(globalTxId1.length == globalTxId2.length);
for (int i = 0; i < globalTxId1.length; i++) {
assertEquals(globalTxId1[i], globalTxId2[i]);
}
byte[] branch1 = xid1.getBranchQualifier();
byte[] branch2 = xid2.getBranchQualifier();
// make sure a different branch was created
if (branch1.length == branch2.length) {
boolean same = true;
for (int i = 0; i < branch1.length; i++) {
if (branch1[i] != branch2[i]) {
same = false;
break;
}
}
assertTrue(!same);
}
// verify delist of resource
tm.getTransaction().delistResource(res2, XAResource.TMSUCCESS);
calls2 = res2.getAndRemoveMethodCalls();
assertEquals(1, calls2.length);
tm.getTransaction().delistResource(res1, XAResource.TMSUCCESS);
calls1 = res1.getAndRemoveMethodCalls();
// res1
assertEquals(1, calls1.length);
assertEquals("end", calls1[0].getMethodName());
args = calls1[0].getArgs();
assertTrue(((Xid) args[0]).equals(xid1));
assertEquals(XAResource.TMSUCCESS, ((Integer) args[1]).intValue());
// res2
assertEquals(1, calls2.length);
assertEquals("end", calls2[0].getMethodName());
args = calls2[0].getArgs();
assertTrue(((Xid) args[0]).equals(xid2));
assertEquals(XAResource.TMSUCCESS, ((Integer) args[1]).intValue());
// verify proper rollback
tm.rollback();
calls1 = res1.getAndRemoveMethodCalls();
calls2 = res2.getAndRemoveMethodCalls();
// res1
assertEquals(1, calls1.length);
assertEquals("rollback", calls1[0].getMethodName());
args = calls1[0].getArgs();
assertTrue(((Xid) args[0]).equals(xid1));
// res2
assertEquals(1, calls2.length);
assertEquals("rollback", calls2[0].getMethodName());
args = calls2[0].getArgs();
assertTrue(((Xid) args[0]).equals(xid2));
}
use of javax.transaction.xa.Xid in project graphdb by neo4j-attic.
the class TestJtaCompliance method test2PhaseCommits2.
/**
* o Tests that two enlistments of same resource (according to the
* isSameRM() method) only receive one set of prepare/commit calls.
*/
@Test
public void test2PhaseCommits2() throws Exception {
tm.begin();
FakeXAResource res1 = new FakeXAResource("XAResource1");
FakeXAResource res2 = new FakeXAResource("XAResource1");
// enlist two (same) resources and verify that the start method
// is invoked with correct flags
// res1
tm.getTransaction().enlistResource(res1);
MethodCall[] calls1 = res1.getAndRemoveMethodCalls();
assertEquals(1, calls1.length);
assertEquals("start", calls1[0].getMethodName());
// res2
tm.getTransaction().enlistResource(res2);
MethodCall[] calls2 = res2.getAndRemoveMethodCalls();
assertEquals(1, calls2.length);
assertEquals("start", calls2[0].getMethodName());
// make sure we get a two-phase commit
FakeXAResource res3 = new FakeXAResource("XAResource2");
tm.getTransaction().enlistResource(res3);
// verify Xid and flags
Object[] args = calls1[0].getArgs();
Xid xid1 = (Xid) args[0];
assertEquals(XAResource.TMNOFLAGS, ((Integer) args[1]).intValue());
args = calls2[0].getArgs();
Xid xid2 = (Xid) args[0];
assertEquals(XAResource.TMJOIN, ((Integer) args[1]).intValue());
assertTrue(xid1.equals(xid2));
assertTrue(xid2.equals(xid1));
// verify delist of resource
tm.getTransaction().delistResource(res3, XAResource.TMSUCCESS);
tm.getTransaction().delistResource(res2, XAResource.TMSUCCESS);
calls2 = res2.getAndRemoveMethodCalls();
assertEquals(1, calls2.length);
tm.getTransaction().delistResource(res1, XAResource.TMSUCCESS);
calls1 = res1.getAndRemoveMethodCalls();
// res1
assertEquals(1, calls1.length);
assertEquals("end", calls1[0].getMethodName());
args = calls1[0].getArgs();
assertTrue(((Xid) args[0]).equals(xid1));
assertEquals(XAResource.TMSUCCESS, ((Integer) args[1]).intValue());
// res2
assertEquals(1, calls2.length);
assertEquals("end", calls2[0].getMethodName());
args = calls2[0].getArgs();
assertTrue(((Xid) args[0]).equals(xid2));
assertEquals(XAResource.TMSUCCESS, ((Integer) args[1]).intValue());
// verify proper prepare/commit
tm.commit();
calls1 = res1.getAndRemoveMethodCalls();
calls2 = res2.getAndRemoveMethodCalls();
// res1
assertEquals(2, calls1.length);
assertEquals("prepare", calls1[0].getMethodName());
args = calls1[0].getArgs();
assertTrue(((Xid) args[0]).equals(xid1));
assertEquals("commit", calls1[1].getMethodName());
args = calls1[1].getArgs();
assertTrue(((Xid) args[0]).equals(xid1));
assertEquals(false, ((Boolean) args[1]).booleanValue());
// res2
assertEquals(0, calls2.length);
}
use of javax.transaction.xa.Xid in project graphdb by neo4j-attic.
the class TxLog method getDanglingRecords.
/**
* Returns an array of lists, each list contains dangling records
* (transactions that han't been completed yet) grouped after global by
* transaction id.
*/
public synchronized Iterator<List<Record>> getDanglingRecords() throws IOException {
fileChannel.position(0);
buffer.clear();
fileChannel.read(buffer);
buffer.flip();
// next record position
long nextPosition = 0;
// holds possible dangling records
int seqNr = 0;
Map<Xid, List<Record>> recordMap = new HashMap<Xid, List<Record>>();
while (buffer.hasRemaining()) {
byte recordType = buffer.get();
if (recordType == TX_START) {
if (!buffer.hasRemaining()) {
break;
}
byte[] globalId = new byte[buffer.get()];
if (buffer.limit() - buffer.position() < globalId.length) {
break;
}
buffer.get(globalId);
Xid xid = new XidImpl(globalId, new byte[0]);
if (recordMap.containsKey(xid)) {
throw new IOException("Tx start for same xid[" + xid + "] found twice");
}
List<Record> recordList = new LinkedList<Record>();
recordList.add(new Record(recordType, globalId, null, seqNr++));
recordMap.put(xid, recordList);
nextPosition += 2 + globalId.length;
} else if (recordType == BRANCH_ADD) {
if (buffer.limit() - buffer.position() < 2) {
break;
}
byte[] globalId = new byte[buffer.get()];
byte[] branchId = new byte[buffer.get()];
if (buffer.limit() - buffer.position() < globalId.length + branchId.length) {
break;
}
buffer.get(globalId);
buffer.get(branchId);
Xid xid = new XidImpl(globalId, new byte[0]);
if (!recordMap.containsKey(xid)) {
throw new IOException("Branch[" + UTF8.decode(branchId) + "] found for [" + xid + "] but no record list found in map");
}
recordMap.get(xid).add(new Record(recordType, globalId, branchId, seqNr++));
nextPosition += 3 + globalId.length + branchId.length;
} else if (recordType == MARK_COMMIT) {
if (!buffer.hasRemaining()) {
break;
}
byte[] globalId = new byte[buffer.get()];
if (buffer.limit() - buffer.position() < globalId.length) {
break;
}
buffer.get(globalId);
Xid xid = new XidImpl(globalId, new byte[0]);
if (!recordMap.containsKey(xid)) {
throw new IOException("Commiting xid[" + xid + "] mark found but no record list found in map");
}
List<Record> recordList = recordMap.get(xid);
recordList.add(new Record(recordType, globalId, null, seqNr++));
recordMap.put(xid, recordList);
nextPosition += 2 + globalId.length;
} else if (recordType == TX_DONE) {
if (!buffer.hasRemaining()) {
break;
}
byte[] globalId = new byte[buffer.get()];
if (buffer.limit() - buffer.position() < globalId.length) {
break;
}
buffer.get(globalId);
Xid xid = new XidImpl(globalId, new byte[0]);
if (!recordMap.containsKey(xid)) {
throw new IOException("Commiting xid[" + xid + "] mark found but no record list found in map");
}
recordMap.remove(xid);
nextPosition += 2 + globalId.length;
} else if (recordType == 0) {
continue;
} else {
throw new IOException("Unknown type: " + recordType);
}
if ((buffer.limit() - buffer.position()) < (3 + Xid.MAXGTRIDSIZE + Xid.MAXBQUALSIZE)) {
// make sure we don't try to read non full entry
buffer.clear();
fileChannel.position(nextPosition);
fileChannel.read(buffer);
buffer.flip();
}
}
return recordMap.values().iterator();
}
use of javax.transaction.xa.Xid in project graphdb by neo4j-attic.
the class TxManager method buildRecoveryInfo.
private void buildRecoveryInfo(List<NonCompletedTransaction> commitList, List<Xid> rollbackList, Map<Resource, XAResource> resourceMap, Iterator<List<TxLog.Record>> danglingRecordList) {
while (danglingRecordList.hasNext()) {
Iterator<TxLog.Record> dListItr = danglingRecordList.next().iterator();
TxLog.Record startRecord = dListItr.next();
if (startRecord.getType() != TxLog.TX_START) {
throw new TransactionFailureException("First record not a start record, type=" + startRecord.getType());
}
// get branches & commit status
HashSet<Resource> branchSet = new HashSet<Resource>();
int markedCommit = -1;
while (dListItr.hasNext()) {
TxLog.Record record = dListItr.next();
if (record.getType() == TxLog.BRANCH_ADD) {
if (markedCommit != -1) {
throw new TransactionFailureException("Already marked commit " + startRecord);
}
branchSet.add(new Resource(record.getBranchId()));
} else if (record.getType() == TxLog.MARK_COMMIT) {
if (markedCommit != -1) {
throw new TransactionFailureException("Already marked commit " + startRecord);
}
markedCommit = record.getSequenceNumber();
} else {
throw new TransactionFailureException("Illegal record type[" + record.getType() + "]");
}
}
Iterator<Resource> resourceItr = branchSet.iterator();
List<Xid> xids = new LinkedList<Xid>();
while (resourceItr.hasNext()) {
Resource resource = resourceItr.next();
if (!resourceMap.containsKey(resource)) {
resourceMap.put(resource, getXaResource(resource.getResourceId()));
}
xids.add(new XidImpl(startRecord.getGlobalId(), resource.getResourceId()));
}
if (// this xid needs to be committed
markedCommit != -1) {
commitList.add(new NonCompletedTransaction(markedCommit, xids));
} else {
rollbackList.addAll(xids);
}
}
}
use of javax.transaction.xa.Xid in project graphdb by neo4j-attic.
the class TxManager method recover.
private void recover(Iterator<List<TxLog.Record>> danglingRecordList) {
msgLog.logMessage("TM non resolved transactions found in " + txLog.getName(), true);
try {
// contains NonCompletedTransaction that needs to be committed
List<NonCompletedTransaction> commitList = new ArrayList<NonCompletedTransaction>();
// contains Xids that should be rolledback
List<Xid> rollbackList = new LinkedList<Xid>();
// key = Resource(branchId) value = XAResource
Map<Resource, XAResource> resourceMap = new HashMap<Resource, XAResource>();
buildRecoveryInfo(commitList, rollbackList, resourceMap, danglingRecordList);
// invoke recover on all xa resources found
Iterator<Resource> resourceItr = resourceMap.keySet().iterator();
List<Xid> recoveredXidsList = new LinkedList<Xid>();
while (resourceItr.hasNext()) {
XAResource xaRes = resourceMap.get(resourceItr.next());
Xid[] xids = xaRes.recover(XAResource.TMNOFLAGS);
for (int i = 0; i < xids.length; i++) {
if (XidImpl.isThisTm(xids[i].getGlobalTransactionId())) {
// linear search
if (rollbackList.contains(xids[i])) {
log.fine("Found pre commit " + xids[i] + " rolling back ... ");
msgLog.logMessage("TM: Found pre commit " + xids[i] + " rolling back ... ", true);
rollbackList.remove(xids[i]);
xaRes.rollback(xids[i]);
} else {
recoveredXidsList.add(xids[i]);
}
} else {
log.warning("Unknown xid: " + xids[i]);
}
}
}
// sort the commit list after sequence number
Collections.sort(commitList, new Comparator<NonCompletedTransaction>() {
public int compare(NonCompletedTransaction r1, NonCompletedTransaction r2) {
return r1.getSequenceNumber() - r2.getSequenceNumber();
}
});
// go through and commit
Iterator<NonCompletedTransaction> commitItr = commitList.iterator();
while (commitItr.hasNext()) {
NonCompletedTransaction nct = commitItr.next();
int seq = nct.getSequenceNumber();
Xid[] xids = nct.getXids();
log.fine("Marked as commit tx-seq[" + seq + "] branch length: " + xids.length);
for (int i = 0; i < xids.length; i++) {
if (!recoveredXidsList.contains(xids[i])) {
log.fine("Tx-seq[" + seq + "][" + xids[i] + "] not found in recovered xid list, " + "assuming already committed");
continue;
}
recoveredXidsList.remove(xids[i]);
Resource resource = new Resource(xids[i].getBranchQualifier());
if (!resourceMap.containsKey(resource)) {
throw new TransactionFailureException("Couldn't find XAResource for " + xids[i]);
}
log.fine("Commiting tx seq[" + seq + "][" + xids[i] + "] ... ");
msgLog.logMessage("TM: Committing tx " + xids[i], true);
resourceMap.get(resource).commit(xids[i], false);
}
}
// rollback the rest
Iterator<Xid> rollbackItr = recoveredXidsList.iterator();
while (rollbackItr.hasNext()) {
Xid xid = rollbackItr.next();
Resource resource = new Resource(xid.getBranchQualifier());
if (!resourceMap.containsKey(resource)) {
throw new TransactionFailureException("Couldn't find XAResource for " + xid);
}
log.fine("Rollback " + xid + " ... ");
msgLog.logMessage("TM: no match found for " + xid + " removing", true);
resourceMap.get(resource).rollback(xid);
}
if (rollbackList.size() > 0) {
log.fine("TxLog contained unresolved " + "xids that needed rollback. They couldn't be matched to " + "any of the XAResources recover list. " + "Assuming " + rollbackList.size() + " transactions already rolled back.");
msgLog.logMessage("TM: no match found for in total " + rollbackList.size() + " transaction that should have been rolled back", true);
}
} catch (XAException e) {
throw new TransactionFailureException("Recovery failed." + e);
}
}
Aggregations