package org.robotninjas.barge; import static com.google.common.base.Preconditions.checkArgument; import org.robotninjas.barge.utils.Files; import org.robotninjas.barge.utils.Prober; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.Callable; import javax.annotation.Nonnull; public class SimpleCounterMachine implements StateMachine { private final int id; private final List<NettyReplica> replicas; private final GroupOfCounters groupOfCounters; private int counter; private File logDirectory; private NettyRaftService service; public SimpleCounterMachine(int id, List<NettyReplica> replicas, GroupOfCounters groupOfCounters) { checkArgument((id >= 0) && (id < replicas.size()), "replica id " + id + " should be between 0 and " + replicas.size()); this.groupOfCounters = groupOfCounters; this.id = id; this.replicas = replicas; } @Override public Object applyOperation(@Nonnull ByteBuffer entry) { return this.counter++; } public void startRaft() { int clusterSize = replicas.size(); NettyReplica[] configuration = new NettyReplica[clusterSize - 1]; for (int i = 0; i < (clusterSize - 1); i++) { configuration[i] = replicas.get((id + i + 1) % clusterSize); } NettyClusterConfig config1 = NettyClusterConfig.from(replicas.get(id % clusterSize), configuration); NettyRaftService service1 = NettyRaftService.newBuilder(config1) .logDir(logDirectory) .timeout(500) .transitionListener(groupOfCounters) .build(this); service1.startAsync().awaitRunning(); this.service = service1; } public File makeLogDirectory(File parentDirectory) throws IOException { this.logDirectory = new File(parentDirectory, "log" + id); if (logDirectory.exists()) { Files.delete(logDirectory); } if (!logDirectory.exists() && !logDirectory.mkdirs()) { throw new IllegalStateException("cannot create log directory " + logDirectory); } return logDirectory; } public void commit(byte[] bytes) throws InterruptedException, RaftException { service.commit(bytes); } public void stop() { service.stopAsync().awaitTerminated(); } public void deleteLogDirectory() { Files.delete(logDirectory); } /** * Wait at most {@code timeout} for this counter to reach value {@code increments}. * * @param increments value expected for counter. * @param timeout timeout in ms. * @throws IllegalStateException if {@code expected} is not reached at end of timeout. */ public void waitForValue(final int increments, long timeout) { new Prober(new Callable<Boolean>() { @Override public Boolean call() throws Exception { return increments == counter; } }).probe(timeout); } public boolean isLeader() { return service.isLeader(); } }