package org.jgroups.util; import org.jgroups.Address; import org.jgroups.annotations.GuardedBy; import java.util.*; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.Condition; import java.util.concurrent.TimeUnit; /** Similar to AckCollector, but collects responses, not just acks. Null is not a valid key. * @author Bela Ban */ public class ResponseCollector<T> { @GuardedBy("lock") private final Map<Address,T> responses; private final Lock lock=new ReentrantLock(false); private final Condition cond=lock.newCondition(); /** * * @param members List of members from which we expect responses */ public ResponseCollector(Collection<Address> members) { responses=members != null? new HashMap<Address,T>(members.size()) : new HashMap<Address,T>(); reset(members); } public ResponseCollector(Address ... members) { responses=members != null? new HashMap<Address,T>(members.length) : new HashMap<Address,T>(); reset(members); } public ResponseCollector() { responses=new HashMap<Address,T>(); } public void add(Address member, T data) { if(member == null) return; lock.lock(); try { if(responses.containsKey(member)) { responses.put(member, data); cond.signalAll(); } } finally { lock.unlock(); } } public void remove(Address member) { if(member == null) return; lock.lock(); try { responses.remove(member); cond.signalAll(); } finally { lock.unlock(); } } public void remove(List<Address> members) { if(members == null || members.isEmpty()) return; lock.lock(); try { for(Address member: members) responses.remove(member); cond.signalAll(); } finally { lock.unlock(); } } public void retainAll(List<Address> members) { if(members == null || members.isEmpty()) return; lock.lock(); try { if(responses.keySet().retainAll(members)) cond.signalAll(); } finally { lock.unlock(); } } public void suspect(Address member) { if(member == null) return; lock.lock(); try { if(responses.remove(member) != null) cond.signalAll(); } finally { lock.unlock(); } } public boolean hasAllResponses() { lock.lock(); try { if(responses.isEmpty()) return true; for(Map.Entry<Address,T> entry: responses.entrySet()) { if(entry.getValue() == null) return false; } return true; } finally { lock.unlock(); } } public int numberOfValidResponses() { int retval=0; lock.lock(); try { for(Map.Entry<Address,T> entry: responses.entrySet()) { if(entry.getValue() != null) retval++; } return retval; } finally { lock.unlock(); } } /** Returns a list of members which didn't send a valid response */ public List<Address> getMissing() { List<Address> retval=new ArrayList<Address>(); for(Map.Entry<Address,T> entry: responses.entrySet()) { if(entry.getValue() == null) retval.add(entry.getKey()); } return retval; } public List<Address> getValidResults() { List<Address> retval=new ArrayList<Address>(); for(Map.Entry<Address,T> entry: responses.entrySet()) { if(entry.getValue() != null) retval.add(entry.getKey()); } return retval; } public Map<Address,T> getResults() { return responses; } public int size() { lock.lock(); try { return responses.size(); } finally { lock.unlock(); } } /** * Waits until all responses have been received, or until a timeout has elapsed. * @param timeout Number of milliseconds to wait max. This value needs to be greater than 0, or else * it will be adjusted to 2000 * @return boolean True if all responses have been received within timeout ms, else false (e.g. if interrupted) */ public boolean waitForAllResponses(long timeout) { if(timeout <= 0) timeout=2000L; lock.lock(); try { final long end_time=System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeout, TimeUnit.MILLISECONDS); while(!hasAllResponses()) { long wait_time=end_time - System.nanoTime(); if(wait_time <= 0) return false; try { cond.await(wait_time, TimeUnit.NANOSECONDS); } catch(InterruptedException e) { Thread.currentThread().interrupt(); // set interrupt flag again return false; } } return true; } finally { lock.unlock(); } } public void reset() { reset((Collection<Address>)null); } public void reset(Collection<Address> members) { lock.lock(); try { responses.clear(); if(members != null) { for(Address mbr: members) responses.put(mbr, null); } cond.signalAll(); } finally { lock.unlock(); } } public void reset(Address ... members) { lock.lock(); try { responses.clear(); if(members != null) { for(Address mbr: members) responses.put(mbr, null); } cond.signalAll(); } finally { lock.unlock(); } } public String toString() { StringBuilder sb=new StringBuilder(); sb.append(responses).append(", complete=").append(hasAllResponses()); return sb.toString(); } }