org.jgroups.util.Responses Maven / Gradle / Ivy
package org.jgroups.util;
import org.jgroups.Address;
import org.jgroups.annotations.GuardedBy;
import org.jgroups.protocols.PingData;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Manages responses for the discovery protocol. Moved from {@link org.jgroups.protocols.Discovery}
* into this standalone class. Responses are only added but never removed.
* @author Bela Ban
* @since 3.5
*/
public class Responses implements Iterable {
protected PingData[] ping_rsps;
protected int index;
protected final Lock lock=new ReentrantLock();
protected final CondVar cond=new CondVar(lock);
protected final int num_expected_rsps;
protected final boolean break_on_coord_rsp;
@GuardedBy("lock")
protected boolean done=false; // successfully completed, or cancelled
public Responses(boolean break_on_coord_rsp) {
this(0, break_on_coord_rsp);
}
public Responses(int num_expected_rsps, boolean break_on_coord_rsp) {
this(num_expected_rsps, break_on_coord_rsp, 16);
}
public Responses(int num_expected_rsps, boolean break_on_coord_rsp, int initial_capacity) {
this.num_expected_rsps=num_expected_rsps;
this.break_on_coord_rsp=break_on_coord_rsp;
ping_rsps=new PingData[Math.max(5, initial_capacity)];
}
public boolean isDone() {
lock.lock();
try {return done;} finally {lock.unlock();}
}
public Responses done() {
lock.lock();
try {return _done();} finally {lock.unlock();}
}
public Responses clear() {
lock.lock();
try {
index=0;
return _done();
}
finally {
lock.unlock();
}
}
public Responses addResponse(PingData rsp, boolean overwrite) {
if(rsp == null)
return this;
boolean is_coord_rsp=rsp.isCoord(), changed=false;
lock.lock();
try {
// https://jira.jboss.org/jira/browse/JGRP-1179
int ind=find(rsp);
if(ind == -1) { // new addition
add(rsp);
changed=true;
}
else {
PingData existing=ping_rsps[ind]; // cannot be null
if(overwrite || (is_coord_rsp && !existing.isCoord())) {
ping_rsps[ind]=rsp;
changed=true;
}
}
if(changed && ((num_expected_rsps > 0 && index >= num_expected_rsps) || break_on_coord_rsp && is_coord_rsp))
_done();
return this;
}
finally {
lock.unlock();
}
}
public Responses add(Responses rsps, Address local_addr) {
if(rsps != null) {
for(PingData rsp: rsps)
addResponse(rsp, Objects.equals(local_addr, rsp.getAddress()));
}
return this;
}
public boolean containsResponseFrom(Address mbr) {
if(mbr == null) return false;
for(int i=0; i < index; i++) {
if(ping_rsps[i] != null && mbr.equals(ping_rsps[i].getAddress()))
return true;
}
return false;
}
public boolean isCoord(Address addr) {
PingData rsp=findResponseFrom(addr);
return rsp != null && rsp.isCoord() && Objects.equals(rsp.getAddress(), addr);
}
public PingData findResponseFrom(Address mbr) {
if(mbr == null) return null;
for(int i=0; i < index; i++) {
if(ping_rsps[i] != null && mbr.equals(ping_rsps[i].getAddress()))
return ping_rsps[i];
}
return null;
}
public boolean waitFor(long timeout) {
return cond.waitFor(this::isDone, timeout, TimeUnit.MILLISECONDS);
}
public Iterator iterator() {
lock.lock();
try {
return new PingDataIterator(ping_rsps, index);
}
finally {
lock.unlock();
}
}
public int size() {return index;}
public boolean isEmpty() {return size() == 0;}
public String toString() {
int[] num=numResponses();
return String.format("%d rsps (%d coords) [%s]", num[0], num[1], (isDone()? "done" : "pending"));
}
public String print() {
StringBuilder sb=new StringBuilder();
for(PingData data: this)
sb.append(data).append("\n");
return sb.toString();
}
@GuardedBy("lock") protected Responses _done() {
if(!done) {
done=true;
cond.signal(true);
}
return this;
}
protected int[] numResponses() {
lock.lock();
try {
int[] num={0,0};
for(int i=0; i < index; i++) {
PingData data=ping_rsps[i];
num[0]++;
if(data.isCoord())
num[1]++;
}
return num;
}
finally {
lock.unlock();
}
}
@GuardedBy("lock") protected List toList() {
return new ArrayList<>(Arrays.asList(ping_rsps).subList(0, index));
}
protected void resize(int new_size) {
lock.lock();
try {
ping_rsps=Arrays.copyOf(ping_rsps, new_size);
}
finally {
lock.unlock();
}
}
@GuardedBy("lock") protected void add(PingData data) {
if(index >= ping_rsps.length)
resize(newLength(ping_rsps.length));
ping_rsps[index++]=data;
}
@GuardedBy("lock") protected int find(PingData data) {
if(data == null) return -1;
for(int i=0; i < index; i++) {
if(data.equals(ping_rsps[i]))
return i;
}
return -1;
}
protected static int newLength(int length) {
return length > 1000? (int)(length * 1.1) : Math.max(5, length * 2);
}
protected static class PingDataIterator implements Iterator {
protected final PingData[] data;
protected final int end_index;
protected int index;
public PingDataIterator(PingData[] data, int end_index) {
this.data=data;
this.end_index=end_index;
if(data == null)
throw new IllegalArgumentException("data cannot be null");
if(end_index > data.length)
throw new IndexOutOfBoundsException("index is " + end_index + ", but arrays's length is only " + data.length);
}
public boolean hasNext() {
return index < end_index && data[index] != null;
}
public PingData next() {
if(index >= end_index || index >= data.length)
throw new NoSuchElementException("index=" + index);
return data[index++];
}
public void remove() {}
}
}