blob: ecfe76ee357b801cdc6fd677a68e7d8713d4a1e8 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.geronimo.transaction.manager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import junit.framework.TestCase;
/**
* This is just a unit test for recovery, depending on proper behavior of the log(s) it uses.
*
* @version $Rev$ $Date$
*
* */
public class RecoveryTest extends TestCase {
XidFactory xidFactory = new XidFactoryImpl();
protected final RetryScheduler retryScheduler = new ExponentialtIntervalRetryScheduler();
private final String RM1 = "rm1";
private final String RM2 = "rm2";
private final String RM3 = "rm3";
public void testCommittedRMToBeRecovered() throws Exception {
MockLog mockLog = new MockLog();
Xid[] xids = getXidArray(1);
// specifies an empty Xid array such that this XAResource has nothing
// to recover. This means that the presumed abort protocol has failed
// right after the commit of the RM and before the force-write of the
// committed log record.
MockXAResource xares1 = new MockXAResource(RM1, new Xid[0]);
MockTransactionInfo[] txInfos = makeTxInfos(xids);
addBranch(txInfos, xares1);
prepareLog(mockLog, txInfos);
Recovery recovery = new RecoveryImpl(mockLog, xidFactory, retryScheduler);
recovery.recoverLog();
assertTrue(!recovery.hasRecoveryErrors());
assertTrue(recovery.getExternalXids().isEmpty());
assertTrue(!recovery.localRecoveryComplete());
recovery.recoverResourceManager(xares1);
assertEquals(0, xares1.committed.size());
assertTrue(recovery.localRecoveryComplete());
}
public void test2ResOnlineAfterRecoveryStart() throws Exception {
MockLog mockLog = new MockLog();
Xid[] xids = getXidArray(3);
MockXAResource xares1 = new MockXAResource(RM1, xids);
MockXAResource xares2 = new MockXAResource(RM2, xids);
MockTransactionInfo[] txInfos = makeTxInfos(xids);
addBranch(txInfos, xares1);
addBranch(txInfos, xares2);
prepareLog(mockLog, txInfos);
Recovery recovery = new RecoveryImpl(mockLog, xidFactory, retryScheduler);
recovery.recoverLog();
assertTrue(!recovery.hasRecoveryErrors());
assertTrue(recovery.getExternalXids().isEmpty());
assertTrue(!recovery.localRecoveryComplete());
recovery.recoverResourceManager(xares1);
assertTrue(!recovery.localRecoveryComplete());
assertEquals(3, xares1.committed.size());
recovery.recoverResourceManager(xares2);
assertEquals(3, xares2.committed.size());
assertTrue(recovery.localRecoveryComplete());
}
private void addBranch(MockTransactionInfo[] txInfos, MockXAResource xaRes) {
for (int i = 0; i < txInfos.length; i++) {
MockTransactionInfo txInfo = txInfos[i];
txInfo.branches.add(new MockTransactionBranchInfo(xaRes.getName()));
}
}
private MockTransactionInfo[] makeTxInfos(Xid[] xids) {
MockTransactionInfo[] txInfos = new MockTransactionInfo[xids.length];
for (int i = 0; i < xids.length; i++) {
Xid xid = xids[i];
txInfos[i] = new MockTransactionInfo(xid, new ArrayList());
}
return txInfos;
}
public void test3ResOnlineAfterRecoveryStart() throws Exception {
MockLog mockLog = new MockLog();
Xid[] xids12 = getXidArray(3);
List xids12List = Arrays.asList(xids12);
Xid[] xids13 = getXidArray(3);
List xids13List = Arrays.asList(xids13);
Xid[] xids23 = getXidArray(3);
List xids23List = Arrays.asList(xids23);
ArrayList tmp = new ArrayList();
tmp.addAll(xids12List);
tmp.addAll(xids13List);
Xid[] xids1 = (Xid[]) tmp.toArray(new Xid[6]);
tmp.clear();
tmp.addAll(xids12List);
tmp.addAll(xids23List);
Xid[] xids2 = (Xid[]) tmp.toArray(new Xid[6]);
tmp.clear();
tmp.addAll(xids13List);
tmp.addAll(xids23List);
Xid[] xids3 = (Xid[]) tmp.toArray(new Xid[6]);
MockXAResource xares1 = new MockXAResource(RM1, xids1);
MockXAResource xares2 = new MockXAResource(RM2, xids2);
MockXAResource xares3 = new MockXAResource(RM3, xids3);
MockTransactionInfo[] txInfos12 = makeTxInfos(xids12);
addBranch(txInfos12, xares1);
addBranch(txInfos12, xares2);
prepareLog(mockLog, txInfos12);
MockTransactionInfo[] txInfos13 = makeTxInfos(xids13);
addBranch(txInfos13, xares1);
addBranch(txInfos13, xares3);
prepareLog(mockLog, txInfos13);
MockTransactionInfo[] txInfos23 = makeTxInfos(xids23);
addBranch(txInfos23, xares2);
addBranch(txInfos23, xares3);
prepareLog(mockLog, txInfos23);
Recovery recovery = new RecoveryImpl(mockLog, xidFactory, retryScheduler);
recovery.recoverLog();
assertTrue(!recovery.hasRecoveryErrors());
assertTrue(recovery.getExternalXids().isEmpty());
assertEquals(9, recovery.localUnrecoveredCount());
recovery.recoverResourceManager(xares1);
assertEquals(9, recovery.localUnrecoveredCount());
assertEquals(6, xares1.committed.size());
recovery.recoverResourceManager(xares2);
assertEquals(6, recovery.localUnrecoveredCount());
assertEquals(6, xares2.committed.size());
recovery.recoverResourceManager(xares3);
assertEquals(0, recovery.localUnrecoveredCount());
assertEquals(6, xares3.committed.size());
}
private void prepareLog(TransactionLog txLog, MockTransactionInfo[] txInfos) throws LogException {
for (int i = 0; i < txInfos.length; i++) {
MockTransactionInfo txInfo = txInfos[i];
txLog.prepare(txInfo.globalXid, txInfo.branches);
}
}
private Xid[] getXidArray(int i) {
Xid[] xids = new Xid[i];
for (int j = 0; j < xids.length; j++) {
xids[j] = xidFactory.createXid();
}
return xids;
}
private static class MockXAResource implements NamedXAResource {
private final String name;
private final Xid[] xids;
private final List committed = new ArrayList();
private final List rolledBack = new ArrayList();
public MockXAResource(String name, Xid[] xids) {
this.name = name;
this.xids = xids;
}
public String getName() {
return name;
}
public void commit(Xid xid, boolean onePhase) throws XAException {
committed.add(xid);
}
public void end(Xid xid, int flags) throws XAException {
}
public void forget(Xid xid) throws XAException {
}
public int getTransactionTimeout() throws XAException {
return 0;
}
public boolean isSameRM(XAResource xaResource) throws XAException {
return false;
}
public int prepare(Xid xid) throws XAException {
return 0;
}
public Xid[] recover(int flag) throws XAException {
return xids;
}
public void rollback(Xid xid) throws XAException {
rolledBack.add(xid);
}
public boolean setTransactionTimeout(int seconds) throws XAException {
return false;
}
public void start(Xid xid, int flags) throws XAException {
}
public List getCommitted() {
return committed;
}
public List getRolledBack() {
return rolledBack;
}
}
private static class MockTransactionInfo {
private Xid globalXid;
private List branches;
public MockTransactionInfo(Xid globalXid, List branches) {
this.globalXid = globalXid;
this.branches = branches;
}
}
private static class MockTransactionBranchInfo implements TransactionBranchInfo {
private String name;
public MockTransactionBranchInfo(String name) {
this.name = name;
}
public String getResourceName() {
return name;
}
public Xid getBranchXid() {
return null;
}
}
}