/*
* 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.accumulo.fate.zookeeper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import org.junit.Assert;
import org.junit.Test;
public class TransactionWatcherTest {
static class SimpleArbitrator implements TransactionWatcher.Arbitrator {
Map<String,List<Long>> started = new HashMap<>();
Map<String,List<Long>> cleanedUp = new HashMap<>();
public synchronized void start(String txType, Long txid) throws Exception {
List<Long> txids = started.get(txType);
if (txids == null)
txids = new ArrayList<>();
if (txids.contains(txid))
throw new Exception("transaction already started");
txids.add(txid);
started.put(txType, txids);
txids = cleanedUp.get(txType);
if (txids == null)
txids = new ArrayList<>();
if (txids.contains(txid))
throw new IllegalStateException("transaction was started but not cleaned up");
txids.add(txid);
cleanedUp.put(txType, txids);
}
public synchronized void stop(String txType, Long txid) throws Exception {
List<Long> txids = started.get(txType);
if (txids != null && txids.contains(txid)) {
txids.remove(txids.indexOf(txid));
return;
}
throw new Exception("transaction does not exist");
}
public synchronized void cleanup(String txType, Long txid) throws Exception {
List<Long> txids = cleanedUp.get(txType);
if (txids != null && txids.contains(txid)) {
txids.remove(txids.indexOf(txid));
return;
}
throw new Exception("transaction does not exist");
}
@Override
synchronized public boolean transactionAlive(String txType, long tid) throws Exception {
List<Long> txids = started.get(txType);
if (txids == null)
return false;
return txids.contains(tid);
}
@Override
public boolean transactionComplete(String txType, long tid) throws Exception {
List<Long> txids = cleanedUp.get(txType);
if (txids == null)
return true;
return !txids.contains(tid);
}
}
@Test
public void testTransactionWatcher() throws Exception {
final String txType = "someName";
final long txid = 7;
final SimpleArbitrator sa = new SimpleArbitrator();
final TransactionWatcher txw = new TransactionWatcher(sa);
sa.start(txType, txid);
try {
sa.start(txType, txid);
Assert.fail("simple arbitrator did not throw an exception");
} catch (Exception ex) {
// expected
}
txw.isActive(txid);
Assert.assertFalse(txw.isActive(txid));
txw.run(txType, txid, new Callable<Object>() {
@Override
public Object call() throws Exception {
Assert.assertTrue(txw.isActive(txid));
return null;
}
});
Assert.assertFalse(txw.isActive(txid));
Assert.assertFalse(sa.transactionComplete(txType, txid));
sa.stop(txType, txid);
Assert.assertFalse(sa.transactionAlive(txType, txid));
Assert.assertFalse(sa.transactionComplete(txType, txid));
sa.cleanup(txType, txid);
Assert.assertTrue(sa.transactionComplete(txType, txid));
try {
txw.run(txType, txid, new Callable<Object>() {
@Override
public Object call() throws Exception {
Assert.fail("Should not be able to start a new work on a discontinued transaction");
return null;
}
});
Assert.fail("work against stopped transaction should fail");
} catch (Exception ex) {
}
final long txid2 = 9;
sa.start(txType, txid2);
txw.run(txType, txid2, new Callable<Object>() {
@Override
public Object call() throws Exception {
Assert.assertTrue(txw.isActive(txid2));
sa.stop(txType, txid2);
try {
txw.run(txType, txid2, new Callable<Object>() {
@Override
public Object call() throws Exception {
Assert.fail("Should not be able to start a new work on a discontinued transaction");
return null;
}
});
Assert.fail("work against a stopped transaction should fail");
} catch (Exception ex) {
// expected
}
Assert.assertTrue(txw.isActive(txid2));
return null;
}
});
}
}