package pt.ist.fenixframework.util; import org.jgroups.*; import java.nio.charset.Charset; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Behaves as synchronization point between nodes running Fenix Framework * * @author Pedro Ruivo * @since 2.1 */ public class NodeBarrier extends ReceiverAdapter { private final JChannel barrierChannel; private final ConcurrentMap<String, Sync> barriers; public NodeBarrier(String config) throws Exception { barrierChannel = new JChannel(config); barrierChannel.setReceiver(this); barriers = new ConcurrentHashMap<String, Sync>(); } private synchronized void connectIfNeeded() throws Exception { if (!barrierChannel.isConnected()) { barrierChannel.connect("ff-barrier"); } } /** * Blocks the thread execution until {@param expectedMembers} nodes has reached this {@param barrierName} * * @param barrierName the barrier name * @param expectedMembers the number of expected members * @throws Exception if this node has failed to send the barrier synchronization message or it was * interrupted while waiting */ public final void blockUntil(String barrierName, int expectedMembers) throws Exception { connectIfNeeded(); sendBarrierAcquired(barrierName); getOrAdd(barrierName).blockUntil(expectedMembers); } private void sendBarrierAcquired(String barrierName) throws Exception { barrierChannel.send(null, barrierName == null ? new byte[0] : barrierName.getBytes(Charset.forName("UTF-8"))); } /** * shutdowns this barrier */ public final void shutdown() { barrierChannel.close(); barriers.clear(); } private Sync getOrAdd(String barrierName) { Sync sync = barriers.get(barrierName); if (sync == null) { sync = new Sync(); Sync old = barriers.putIfAbsent(barrierName, sync); if (old != null) { sync = old; } } return sync; } @Override public void receive(Message msg) { super.receive(msg); String barrierName = new String(msg.getBuffer()); getOrAdd(barrierName).addMember(msg.getSrc()); } @Override public void viewAccepted(View view) { for (Map.Entry<String, Sync> barrier : barriers.entrySet()) { barrier.getValue().notifyNewView(barrier.getKey(), view); } } private class Sync { private final Set<Address> membersInBarrier; private Sync() { this.membersInBarrier = new HashSet<Address>(); } public synchronized final void addMember(Address from) { if (membersInBarrier.add(from)) { notify(); } } public synchronized final void blockUntil(int expectedMembers) throws InterruptedException { membersInBarrier.add(barrierChannel.getAddress()); while (membersInBarrier.size() < expectedMembers) { wait(); } } //new members list public synchronized void notifyNewView(String barrierName, View view) { if (membersInBarrier.containsAll(view.getMembers())) { //no new node return; } if (membersInBarrier.contains(barrierChannel.getAddress())) { try { sendBarrierAcquired(barrierName); } catch (Exception e) { //ignored } } } } }