org.jgroups.util.Util Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
package org.jgroups.util;
import org.jgroups.*;
import org.jgroups.annotations.Component;
import org.jgroups.annotations.ManagedAttribute;
import org.jgroups.annotations.Property;
import org.jgroups.blocks.cs.Connection;
import org.jgroups.conf.ClassConfigurator;
import org.jgroups.jmx.JmxConfigurator;
import org.jgroups.logging.Log;
import org.jgroups.protocols.*;
import org.jgroups.protocols.pbcast.GMS;
import org.jgroups.protocols.pbcast.NAKACK2;
import org.jgroups.protocols.pbcast.STABLE;
import org.jgroups.protocols.relay.SiteMaster;
import org.jgroups.protocols.relay.SiteUUID;
import org.jgroups.stack.*;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.management.*;
import java.lang.reflect.*;
import java.math.BigInteger;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static java.lang.System.nanoTime;
import static org.jgroups.protocols.TP.LIST;
import static org.jgroups.protocols.TP.MULTICAST;
/**
* Collection of various utility routines that can not be assigned to other classes.
* @author Bela Ban
*/
public class Util {
private static final Map,Byte> TYPES=new IdentityHashMap<>(30);
private static final IntHashMap> CLASS_TYPES=new IntHashMap<>(20);
private static final byte TYPE_NULL = 0;
private static final byte TYPE_BOOLEAN = 1; // boolean
private static final byte TYPE_BOOLEAN_OBJ = 2; // Boolean
private static final byte TYPE_BYTE = 3; // byte
private static final byte TYPE_BYTE_OBJ = 4; // Byte
private static final byte TYPE_CHAR = 5; // char
private static final byte TYPE_CHAR_OBJ = 6; // Character
private static final byte TYPE_DOUBLE = 7; // double
private static final byte TYPE_DOUBLE_OBJ = 8; // Double
private static final byte TYPE_FLOAT = 9; // float
private static final byte TYPE_FLOAT_OBJ = 10; // Float
private static final byte TYPE_INT = 11; // int
private static final byte TYPE_INT_OBJ = 12; // Integer
private static final byte TYPE_LONG = 13; // long
private static final byte TYPE_LONG_OBJ = 14; // Long
private static final byte TYPE_SHORT = 15; // short
private static final byte TYPE_SHORT_OBJ = 16; // Short
private static final byte TYPE_STRING = 17; // ascii
private static final byte TYPE_BYTEARRAY = 18;
private static final byte TYPE_CLASS = 19; // a class
private static final byte TYPE_STREAMABLE = 50;
private static final byte TYPE_SERIALIZABLE = 51;
public static final int MAX_PORT=65535; // highest port allocatable
private static final Pattern METHOD_NAME_TO_ATTR_NAME_PATTERN=Pattern.compile("[A-Z]+");
private static final Pattern ATTR_NAME_TO_METHOD_NAME_PATTERN=Pattern.compile("_.");
protected static int CCHM_INITIAL_CAPACITY=16;
protected static float CCHM_LOAD_FACTOR=0.75f;
protected static int CCHM_CONCURRENCY_LEVEL=16;
public static final int DEFAULT_HEADERS;
public static final String JAVA_VERSION;
/**
* The max size of an address list, used in View or Digest when toString() is called. Limiting this
* reduces the amount of log data
*/
public static int MAX_LIST_PRINT_SIZE=20;
private static final byte[] TYPE_NULL_ARRAY={0};
private static final byte[] TYPE_BOOLEAN_TRUE={TYPE_BOOLEAN, 1};
private static final byte[] TYPE_BOOLEAN_FALSE={TYPE_BOOLEAN, 0};
public static final Class extends Protocol>[] getUnicastProtocols() {return new Class[]{UNICAST3.class};}
public enum AddressScope {GLOBAL,SITE_LOCAL,LINK_LOCAL,LOOPBACK,NON_LOOPBACK}
private static boolean ipv4_stack_available=false, ipv6_stack_available=false;
private static final StackType ip_stack_type;
private static volatile List CACHED_INTERFACES=null;
private static volatile Collection CACHED_ADDRESSES=null;
public static final boolean can_bind_to_mcast_addr;
protected static ResourceBundle resource_bundle;
static DateTimeFormatter UTF_FORMAT=DateTimeFormatter.ofPattern("E MMM d H:m:s 'UTC' y");
static {
try {
CACHED_INTERFACES=getAllAvailableInterfaces();
CACHED_ADDRESSES=getAllAvailableAddresses();
}
catch(SocketException e) {
throw new RuntimeException(e);
}
ip_stack_type=_getIpStackType();
String tmp;
resource_bundle=ResourceBundle.getBundle("jg-messages",Locale.getDefault(),Util.class.getClassLoader());
add(TYPE_NULL, Void.class);
add(TYPE_BOOLEAN, boolean.class);
add(TYPE_BOOLEAN_OBJ, Boolean.class);
add(TYPE_BYTE, byte.class);
add(TYPE_BYTE_OBJ, Byte.class);
add(TYPE_CHAR, char.class);
add(TYPE_CHAR_OBJ, Character.class);
add(TYPE_DOUBLE, double.class);
add(TYPE_DOUBLE_OBJ, Double.class);
add(TYPE_FLOAT, float.class);
add(TYPE_FLOAT_OBJ, Float.class);
add(TYPE_INT, int.class);
add(TYPE_INT_OBJ, Integer.class);
add(TYPE_LONG, long.class);
add(TYPE_LONG_OBJ, Long.class);
add(TYPE_SHORT, short.class);
add(TYPE_SHORT_OBJ, Short.class);
add(TYPE_STRING, String.class);
add(TYPE_BYTEARRAY, byte[].class);
add(TYPE_CLASS, Class.class);
can_bind_to_mcast_addr=(Util.checkForLinux() && !Util.checkForAndroid())
|| Util.checkForSolaris() || Util.checkForHp() || Util.checkForMac();
try {
String cchm_initial_capacity=SecurityActions.getProperty(Global.CCHM_INITIAL_CAPACITY);
if(cchm_initial_capacity != null)
CCHM_INITIAL_CAPACITY=Integer.parseInt(cchm_initial_capacity);
}
catch(SecurityException ignored) {
}
try {
String cchm_load_factor=SecurityActions.getProperty(Global.CCHM_LOAD_FACTOR);
if(cchm_load_factor != null)
CCHM_LOAD_FACTOR=Float.parseFloat(cchm_load_factor);
}
catch(SecurityException ignored) {
}
try {
String cchm_concurrency_level=SecurityActions.getProperty(Global.CCHM_CONCURRENCY_LEVEL);
if(cchm_concurrency_level != null)
CCHM_CONCURRENCY_LEVEL=Integer.parseInt(cchm_concurrency_level);
}
catch(SecurityException ignored) {
}
try {
tmp=SecurityActions.getProperty(Global.MAX_LIST_PRINT_SIZE);
if(tmp != null)
MAX_LIST_PRINT_SIZE=Integer.parseInt(tmp);
}
catch(SecurityException ignored) {
}
try {
tmp=SecurityActions.getProperty(Global.DEFAULT_HEADERS);
DEFAULT_HEADERS=tmp != null? Integer.parseInt(tmp) : 4;
}
catch(Throwable t) {
throw new IllegalArgumentException(String.format("property %s has an incorrect value", Global.DEFAULT_HEADERS), t);
}
JAVA_VERSION=System.getProperty("java.vm.version", "");
}
@Deprecated
public static boolean fibersAvailable() {
return ThreadCreator.hasVirtualThreads();
}
public static boolean virtualThreadsAvailable() {
return ThreadCreator.hasVirtualThreads();
}
public static int getNextHigherPowerOfTwo(int num) {
if(num <= 0) return 1;
int highestBit=Integer.highestOneBit(num);
return num <= highestBit? highestBit : highestBit << 1;
}
public static String bold(String msg) {
StringBuilder sb=new StringBuilder("\033[1m");
sb.append(msg).append("\033[0m");
return sb.toString();
}
public static String getMessage(String key) {
return key != null? resource_bundle.getString(key) : null;
}
/**
* Returns a default stack for testing with transport = SHARED_LOOPBACK
* @param additional_protocols Any number of protocols to add to the top of the returned protocol list
* @return
*/
public static Protocol[] getTestStack(Protocol... additional_protocols) {
Protocol[] protocols={
new SHARED_LOOPBACK(),
new SHARED_LOOPBACK_PING(),
new NAKACK2(),
new UNICAST3(),
new STABLE(),
new GMS().setJoinTimeout(1000),
new FRAG2().setFragSize(8000)
};
if(additional_protocols == null)
return protocols;
Protocol[] tmp=Arrays.copyOf(protocols,protocols.length + additional_protocols.length);
System.arraycopy(additional_protocols, 0, tmp, protocols.length, additional_protocols.length);
return tmp;
}
/**
* Blocks until all channels have the same view
* @param timeout How long to wait (max in ms)
* @param interval Check every interval ms
* @param channels The channels which should form the view. The expected view size is channels.length.
* Must be non-null
*/
public static void waitUntilAllChannelsHaveSameView(long timeout, long interval, JChannel... channels) throws TimeoutException {
if(interval >= timeout || timeout <= 0)
throw new IllegalArgumentException("interval needs to be smaller than timeout or timeout needs to be > 0");
if(channels == null || channels.length == 0)
return;
long target_time=System.currentTimeMillis() + timeout;
while(System.currentTimeMillis() <= target_time) {
boolean all_channels_have_correct_view=true;
View first=channels[0].getView();
for(JChannel ch: channels) {
View view=ch.getView();
if(first == null || !Objects.equals(view, first) || view.size() != channels.length) {
all_channels_have_correct_view=false;
break;
}
}
if(all_channels_have_correct_view)
return;
Util.sleep(interval);
}
View[] views=new View[channels.length];
StringBuilder sb=new StringBuilder();
for(int i=0; i < channels.length; i++) {
views[i]=channels[i].getView();
sb.append(channels[i].getName()).append(": ").append(views[i]).append("\n");
}
View first=channels[0].getView();
for(View view : views)
if(view == null || !Objects.equals(view, first) || view.size() != channels.length)
throw new TimeoutException("Timeout " + timeout + " kicked in, views are:\n" + sb);
}
public static void waitUntilAllChannelsHaveSameView(long timeout, long interval,
Collection channels) throws TimeoutException {
JChannel[] tmp=new JChannel[channels != null? channels.size() : 0];
if(tmp == null)
return;
int index=0;
for(Iterator it=channels.iterator(); it.hasNext();)
tmp[index++]=it.next();
waitUntilAllChannelsHaveSameView(timeout, interval, tmp);
}
public static void waitUntil(long timeout, long interval, BooleanSupplier condition) throws TimeoutException {
waitUntil(timeout, interval, condition, null);
}
public static void waitUntil(long timeout, long interval, BooleanSupplier condition, Supplier msg) throws TimeoutException {
long target_time=System.currentTimeMillis() + timeout;
while(System.currentTimeMillis() <= target_time) {
if(condition.getAsBoolean())
return;
Util.sleep(interval);
}
String error_msg=String.format("Timeout %d kicked in%s", timeout, msg != null? ": " + msg.get() : "");
throw new TimeoutException(error_msg);
}
public static boolean waitUntilNoX(long timeout, long interval, BooleanSupplier condition) {
long target_time=System.currentTimeMillis() + timeout;
while(System.currentTimeMillis() <= target_time) {
if(condition.getAsBoolean())
return true;
Util.sleep(interval);
}
return condition.getAsBoolean();
}
public static boolean waitUntilTrue(long timeout, long interval, BooleanSupplier condition) {
long target_time=System.currentTimeMillis() + timeout;
while(System.currentTimeMillis() <= target_time) {
if(condition.getAsBoolean())
return true;
Util.sleep(interval);
}
return false;
}
public static boolean allChannelsHaveSameView(JChannel... channels) {
View first=channels[0].getView();
for(JChannel ch : channels) {
View view=ch.getView();
if(!Objects.equals(view, first) || view.size() != channels.length)
return false;
}
return true;
}
public static void assertAllChannelsHaveSameView(JChannel ... channels) {
assert allChannelsHaveSameView(channels) : String.format("channels have different views:\n%s\n", printViews(channels));
}
public static String printViews(JChannel ... channels) {
StringBuilder sb=new StringBuilder();
for(JChannel ch: channels)
sb.append(String.format("%s: %s\n", ch.name(), ch.getView()));
return sb.toString();
}
/**
* Waits until a list has the expected number of elements. Throws an exception if not met
* @param list The list
* @param expected_size The expected size
* @param timeout The time to wait (in ms)
* @param interval The interval at which to get the size of the list (in ms)
* @param The type of the list
*/
public static void waitUntilListHasSize(List list,int expected_size,long timeout,long interval) {
if(list == null)
throw new IllegalStateException("list is null");
long target_time=System.currentTimeMillis() + timeout;
while(System.currentTimeMillis() < target_time) {
if(list.size() == expected_size)
break;
Util.sleep(interval);
}
assert list.size() == expected_size : "list doesn't have the expected (" + expected_size + ") elements: " + list;
}
public static void removeFromViews(Address mbr, JChannel ... channels) {
if(mbr == null || channels == null)
return;
for(JChannel ch: channels) {
if(ch == null)
continue;
GMS gms=ch.getProtocolStack().findProtocol(GMS.class);
if(gms == null) continue;
View v=gms.view();
if(v != null && v.containsMember(mbr)) {
List mbrs=new ArrayList<>(v.getMembers());
mbrs.remove(mbr);
long id=v.getViewId().getId() + 1;
View next=View.create(mbrs.get(0), id, mbrs);
gms.installView(next);
}
}
}
/** Creates an enum from a string */
public static > T createEnum(String name, Type type) {
return Enum.valueOf((Class) type, name);
}
public static byte[] createAuthenticationDigest(String passcode,long t1,double q1) throws IOException,
NoSuchAlgorithmException {
ByteArrayOutputStream baos=new ByteArrayOutputStream(512);
DataOutputStream out=new DataOutputStream(baos);
byte[] digest=createDigest(passcode,t1,q1);
out.writeLong(t1);
out.writeDouble(q1);
out.writeInt(digest.length);
out.write(digest);
out.flush();
return baos.toByteArray();
}
public static byte[] createDigest(String passcode,long t1,double q1)
throws IOException, NoSuchAlgorithmException {
MessageDigest md=MessageDigest.getInstance("SHA");
md.update(passcode.getBytes());
ByteBuffer bb=ByteBuffer.allocate(16); //8 bytes for long and double each
bb.putLong(t1);
bb.putDouble(q1);
md.update(bb);
return md.digest();
}
/**
* Utility method. If the dest address is IPv6, convert scoped link-local addrs into unscoped ones
* @param sock
* @param dest
* @param sock_conn_timeout
* @throws IOException
*/
public static void connect(Socket sock,SocketAddress dest,int sock_conn_timeout) throws IOException {
if(dest instanceof InetSocketAddress) {
InetAddress addr=((InetSocketAddress)dest).getAddress();
if(addr instanceof Inet6Address) {
Inet6Address tmp=(Inet6Address)addr;
if(tmp.getScopeId() != 0) {
dest=new InetSocketAddress(InetAddress.getByAddress(tmp.getAddress()),((InetSocketAddress)dest).getPort());
}
}
}
sock.connect(dest, sock_conn_timeout);
}
public static boolean connect(SocketChannel ch, SocketAddress dest) throws IOException {
if(dest instanceof InetSocketAddress) {
InetAddress addr=((InetSocketAddress)dest).getAddress();
if(addr instanceof Inet6Address) {
Inet6Address tmp=(Inet6Address)addr;
if(tmp.getScopeId() != 0) {
dest=new InetSocketAddress(InetAddress.getByAddress(tmp.getAddress()),((InetSocketAddress)dest).getPort());
}
}
}
return ch.connect(dest);
}
public static void close(Closeable closeable) {
if(closeable != null) {
try {
closeable.close();
}
catch(Throwable ignored) {
}
}
}
public static void close(Closeable... closeables) {
if(closeables != null) {
for(Closeable closeable: closeables)
Util.close(closeable);
}
}
public static void closeReverse(Closeable... closeables) {
if(closeables != null) {
for(int i=closeables.length-1; i >= 0; i--)
Util.close(closeables[i]);
}
}
/** Closes all non-coordinators first, in parallel, then closes the coord. This should produce just 2 views */
public static void closeFast(JChannel ... channels) {
if(channels != null) {
// close all non-coordinator channels first (in parallel)
Stream.of(channels).parallel().filter(ch -> !isCoordinator(ch)).forEach(JChannel::close);
try {
Util.waitUntil(5000, 100,
() -> Stream.of(channels).filter(ch -> !isCoordinator(ch)).allMatch(JChannel::isClosed));
}
catch(TimeoutException e) {
}
// then close the cordinator's channel
Stream.of(channels).filter(Util::isCoordinator).forEach(JChannel::close);
}
}
/**
* Drops messages to/from other members and then closes the channel. Note that this member won't get excluded from
* the view until failure detection has kicked in and the new coord installed the new view
*/
public static void shutdown(JChannel ch) throws Exception {
DISCARD discard=new DISCARD();
discard.setAddress(ch.getAddress());
discard.discardAll(true);
ProtocolStack stack=ch.getProtocolStack();
TP transport=stack.getTransport();
stack.insertProtocol(discard,ProtocolStack.Position.ABOVE,transport.getClass());
//abruptly shutdown FD_SOCK just as in real life when member gets killed non gracefully
FD_SOCK fd=ch.getProtocolStack().findProtocol(FD_SOCK.class);
if(fd != null)
fd.stopServerSocket(false);
View view=ch.getView();
if(view != null) {
ViewId vid=view.getViewId();
List members=Collections.singletonList(ch.getAddress());
ViewId new_vid=new ViewId(ch.getAddress(),vid.getId() + 1);
View new_view=new View(new_vid,members);
// inject view in which the shut-down member is the only element
GMS gms=stack.findProtocol(GMS.class);
gms.installView(new_view);
}
Util.close(ch);
}
public static byte setFlag(byte bits,byte flag) {
return bits|=flag;
}
public static boolean isFlagSet(byte bits,byte flag) {
return (bits & flag) == flag;
}
public static byte clearFlags(byte bits,byte flag) {
return bits&=~flag;
}
public static String flagsToString(short flags) {
StringBuilder sb=new StringBuilder();
boolean first=true;
Message.Flag[] all_flags=Message.Flag.values();
for(Message.Flag flag: all_flags) {
if(isFlagSet(flags, flag)) {
if(first)
first=false;
else
sb.append("|");
sb.append(flag);
}
}
return sb.toString();
}
public static String transientFlagsToString(short flags) {
StringBuilder sb=new StringBuilder();
boolean first=true;
Message.TransientFlag[] all_flags=Message.TransientFlag.values();
for(Message.TransientFlag flag: all_flags) {
if(isTransientFlagSet(flags, flag)) {
if(first)
first=false;
else
sb.append("|");
sb.append(flag);
}
}
return sb.toString();
}
public static boolean isFlagSet(short flags, Message.Flag flag) {
return flag != null && ((flags & flag.value()) == flag.value());
}
public static boolean isTransientFlagSet(short flags, Message.TransientFlag flag) {
return flag != null && (flags & flag.value()) == flag.value();
}
/**
* Copies a message. Copies only headers with IDs >= starting_id or IDs which are in the copy_only_ids list
* @param copy_buffer
* @param starting_id
* @param copy_only_ids
* @return
*/
public static Message copy(Message msg, boolean copy_buffer, short starting_id, short... copy_only_ids) {
Message retval=msg.copy(copy_buffer, false);
for(Map.Entry entry: msg.getHeaders().entrySet()) {
short id=entry.getKey();
if(id >= starting_id || Util.containsId(id, copy_only_ids))
retval.putHeader(id, entry.getValue());
}
return retval;
}
/**
* Creates an object from a byte buffer
*/
public static T objectFromByteBuffer(byte[] buffer) throws IOException, ClassNotFoundException {
if(buffer == null) return null;
return objectFromByteBuffer(buffer,0,buffer.length);
}
public static T objectFromByteBuffer(byte[] buffer,int offset,int length) throws IOException, ClassNotFoundException {
return objectFromByteBuffer(buffer, offset, length, null);
}
public static T objectFromBuffer(ByteArray b, ClassLoader loader) throws IOException, ClassNotFoundException {
return objectFromByteBuffer(b.getArray(), b.getOffset(), b.getLength(), loader);
}
public static T objectFromByteBuffer(byte[] buffer,int offset,int length, ClassLoader loader) throws IOException, ClassNotFoundException {
if(buffer == null) return null;
byte type=buffer[offset++];
length--;
switch(type) {
case TYPE_NULL: return null;
case TYPE_STREAMABLE:
DataInput in=new ByteArrayDataInputStream(buffer,offset,length);
return readGenericStreamable(in, loader);
case TYPE_SERIALIZABLE: // the object is Externalizable or Serializable
InputStream in_stream=new ByteArrayInputStream(buffer,offset,length);
try(ObjectInputStream oin=new ObjectInputStreamWithClassloader(in_stream, loader)) {
return (T)oin.readObject();
}
case TYPE_BOOLEAN: case TYPE_BOOLEAN_OBJ: return (T)(Boolean)(buffer[offset] == 1);
case TYPE_BYTE: case TYPE_BYTE_OBJ: return (T)(Byte)buffer[offset];
case TYPE_CHAR: case TYPE_CHAR_OBJ: return (T)(Character)Bits.readChar(buffer, offset);
case TYPE_DOUBLE: case TYPE_DOUBLE_OBJ: return (T)(Double)Bits.readDouble(buffer, offset);
case TYPE_FLOAT: case TYPE_FLOAT_OBJ: return (T)(Float)Bits.readFloat(buffer, offset);
case TYPE_INT: case TYPE_INT_OBJ: return (T)(Integer)Bits.readIntCompressed(buffer, offset);
case TYPE_LONG: case TYPE_LONG_OBJ: return (T)(Long)Bits.readLongCompressed(buffer, offset);
case TYPE_SHORT: case TYPE_SHORT_OBJ: return (T)(Short)Bits.readShort(buffer, offset);
case TYPE_STRING:
in=new ByteArrayDataInputStream(buffer, offset, length);
return (T)readString(in);
case TYPE_BYTEARRAY:
byte[] tmp=new byte[length];
System.arraycopy(buffer,offset,tmp,0,length);
return (T)tmp;
case TYPE_CLASS:
return (T)CLASS_TYPES.get(buffer[offset]);
default:
throw new IllegalArgumentException("type " + type + " is invalid");
}
}
/**
* Parses an object from a {@link ByteBuffer}. Note that this changes the position of the buffer, so it this is
* not desired, use {@link ByteBuffer#duplicate()} to create a copy and pass the copy to this method.
*/
public static T objectFromByteBuffer(ByteBuffer buffer, ClassLoader loader) throws Exception {
if(buffer == null) return null;
byte type=buffer.get();
switch(type) {
case TYPE_NULL:
return null;
case TYPE_STREAMABLE:
DataInput in=new ByteBufferInputStream(buffer);
return readGenericStreamable(in, loader);
case TYPE_SERIALIZABLE: // the object is Externalizable or Serializable
InputStream in_stream=new ByteBufferInputStream(buffer);
try(ObjectInputStream oin=new ObjectInputStreamWithClassloader(in_stream, loader)) {
return (T)oin.readObject();
}
case TYPE_BOOLEAN: case TYPE_BOOLEAN_OBJ: return (T)(Boolean)(buffer.get() == 1);
case TYPE_BYTE: case TYPE_BYTE_OBJ: return (T)(Byte)buffer.get();
case TYPE_CHAR: case TYPE_CHAR_OBJ: return (T)(Character)Bits.readChar(buffer);
case TYPE_DOUBLE: case TYPE_DOUBLE_OBJ: return (T)(Double)Bits.readDouble(buffer);
case TYPE_FLOAT: case TYPE_FLOAT_OBJ: return (T)(Float)Bits.readFloat(buffer);
case TYPE_INT: case TYPE_INT_OBJ: return (T)(Integer)Bits.readIntCompressed(buffer);
case TYPE_LONG: case TYPE_LONG_OBJ: return (T)(Long)Bits.readLongCompressed(buffer);
case TYPE_SHORT: case TYPE_SHORT_OBJ: return (T)(Short)Bits.readShort(buffer);
case TYPE_BYTEARRAY:
byte[] tmp=new byte[buffer.remaining()];
buffer.get(tmp);
return (T)tmp;
case TYPE_STRING:
in=new ByteBufferInputStream(buffer);
return (T)readString(in);
case TYPE_CLASS:
return (T)CLASS_TYPES.get(buffer.get());
default:
throw new IllegalArgumentException("type " + type + " is invalid");
}
}
public static byte[] objectToByteBuffer(Object obj) throws IOException {
return objectToBuffer(obj).getBytes();
}
/**
* Serializes/Streams an object into a byte buffer.
* The object has to implement interface Serializable or Externalizable or Streamable.
*/
public static ByteArray objectToBuffer(Object obj) throws IOException {
if(obj == null)
return new ByteArray(TYPE_NULL_ARRAY);
if(obj instanceof Streamable)
return writeStreamable((Streamable)obj);
Byte type=obj instanceof Class>? TYPES.get(obj) : TYPES.get(obj.getClass());
return type == null? writeSerializable(obj) : new ByteArray(marshalPrimitiveType(type, obj));
}
protected static ByteArray writeStreamable(Streamable obj) throws IOException {
int expected_size=obj instanceof SizeStreamable? ((SizeStreamable)obj).serializedSize() : 512;
final ByteArrayDataOutputStream out=new ByteArrayDataOutputStream(expected_size, true);
out.write(TYPE_STREAMABLE);
writeGenericStreamable(obj, out);
return out.getBuffer();
}
protected static ByteArray writeSerializable(Object obj) throws IOException {
final ByteArrayDataOutputStream out_stream=new ByteArrayDataOutputStream(512, true);
out_stream.write(TYPE_SERIALIZABLE);
try(ObjectOutputStream out=new ObjectOutputStream(new OutputStreamAdapter(out_stream))) {
out.writeObject(obj);
out.flush();
return out_stream.getBuffer();
}
}
protected static byte[] marshalPrimitiveType(Byte type, Object obj) {
if(obj instanceof Class>)
return new byte[]{TYPE_CLASS, type};
switch(type) {
case TYPE_BOOLEAN: case TYPE_BOOLEAN_OBJ:
return ((Boolean)obj)? TYPE_BOOLEAN_TRUE : TYPE_BOOLEAN_FALSE;
case TYPE_BYTE: case TYPE_BYTE_OBJ:
return new byte[]{type, (byte)obj};
case TYPE_CHAR: case TYPE_CHAR_OBJ:
byte[] buf=new byte[Global.BYTE_SIZE *3];
buf[0]=type;
Bits.writeChar((char)obj, buf, 1);
return buf;
case TYPE_DOUBLE: case TYPE_DOUBLE_OBJ:
buf=new byte[Global.BYTE_SIZE + Global.DOUBLE_SIZE];
buf[0]=type;
Bits.writeDouble((double)obj, buf, 1);
return buf;
case TYPE_FLOAT: case TYPE_FLOAT_OBJ:
buf=new byte[Global.BYTE_SIZE + Global.FLOAT_SIZE];
buf[0]=type;
Bits.writeFloat((float)obj, buf, 1);
return buf;
case TYPE_INT: case TYPE_INT_OBJ:
int i=(int)obj;
buf=new byte[Global.BYTE_SIZE + Bits.size(i)];
buf[0]=type;
Bits.writeIntCompressed(i, buf, 1);
return buf;
case TYPE_LONG: case TYPE_LONG_OBJ:
long l=(long)obj;
buf=new byte[Global.BYTE_SIZE + Bits.size(l)];
buf[0]=type;
Bits.writeLongCompressed((long)obj, buf, 1);
return buf;
case TYPE_SHORT: case TYPE_SHORT_OBJ:
buf=new byte[Global.BYTE_SIZE + Global.SHORT_SIZE];
buf[0]=type;
Bits.writeShort((short)obj, buf, 1);
return buf;
case TYPE_STRING:
String str=(String)obj;
ByteArrayDataOutputStream out=new ByteArrayDataOutputStream(str.length()*2 +3);
try {
out.writeByte(TYPE_STRING);
writeString(str, out);
byte[] ret=new byte[out.position()];
System.arraycopy(out.buffer(), 0, ret, 0, ret.length);
return ret;
}
catch(IOException ex) {
throw new RuntimeException(ex);
}
case TYPE_BYTEARRAY:
buf=(byte[])obj;
byte[] buffer=new byte[Global.BYTE_SIZE + buf.length];
buffer[0]=TYPE_BYTEARRAY;
System.arraycopy(buf, 0, buffer, 1, buf.length);
return buffer;
default:
throw new IllegalArgumentException("type " + type + " is invalid");
}
}
public static void writeString(String str, DataOutput out) throws IOException {
boolean is_ascii=isAsciiString(str);
out.writeBoolean(is_ascii);
if(is_ascii) {
int len=str.length();
out.writeInt(len);
for(int i=0; i < len; i++)
out.write((byte)str.charAt(i));
}
else
out.writeUTF(str);
}
public static String readString(DataInput in) throws IOException {
if(in.readBoolean() == false)
return in.readUTF();
byte[] tmp=new byte[in.readInt()];
in.readFully(tmp);
return new String(tmp);
}
public static int size(Object obj) {
if(obj == null)
return Global.BYTE_SIZE;
if(obj instanceof SizeStreamable)
return Global.BYTE_SIZE + Util.size((SizeStreamable)obj);
return sizePrimitive(obj);
}
public static int size(SizeStreamable s) {
int retval=Global.BYTE_SIZE;
if(s == null)
return retval;
retval+=Global.SHORT_SIZE; // magic number
short magic_number=ClassConfigurator.getMagicNumber(s.getClass());
if(magic_number == -1)
retval+=Bits.sizeUTF(s.getClass().getName());
return retval + s.serializedSize();
}
public static int sizePrimitive(Object obj) {
if(obj == null)
return 1; // TYPE_NULL
Byte type=obj instanceof Class>? TYPES.get(obj) : TYPES.get(obj.getClass());
if(obj instanceof Class>) {
if(type != null)
return Global.BYTE_SIZE *2;
}
int retval=Global.BYTE_SIZE;
switch(type) {
case TYPE_BOOLEAN: case TYPE_BOOLEAN_OBJ:
case TYPE_BYTE: case TYPE_BYTE_OBJ:
return retval+Global.BYTE_SIZE;
case TYPE_SHORT: case TYPE_SHORT_OBJ:
case TYPE_CHAR: case TYPE_CHAR_OBJ:
return retval+Character.BYTES;
case TYPE_LONG: case TYPE_LONG_OBJ:
case TYPE_DOUBLE: case TYPE_DOUBLE_OBJ:
return retval+Double.BYTES;
case TYPE_INT: case TYPE_INT_OBJ:
case TYPE_FLOAT: case TYPE_FLOAT_OBJ:
return retval+Float.BYTES;
case TYPE_STRING:
String s=(String)obj;
retval+=Global.BYTE_SIZE; // is_ascii
if(isAsciiString(s))
return retval+Global.INT_SIZE + s.length();
else
return retval+Bits.sizeUTF(s);
case TYPE_BYTEARRAY:
byte[] buf=(byte[])obj;
return retval+Global.INT_SIZE + buf.length;
default:
throw new IllegalArgumentException("type " + type + " is invalid");
}
}
public static void writeTypeStreamable(Streamable obj, DataOutput out) throws IOException {
out.writeByte(TYPE_STREAMABLE);
Util.writeGenericStreamable(obj, out);
}
public static void primitiveToStream(Object obj, DataOutput out) throws IOException {
if(obj == null) {
out.write(TYPE_NULL);
return;
}
Byte type=obj instanceof Class>? TYPES.get(obj) : TYPES.get(obj.getClass());
if(obj instanceof Class>) {
out.writeByte(TYPE_CLASS);
out.writeByte(type);
return;
}
out.writeByte(type);
switch(type) {
case TYPE_BOOLEAN: case TYPE_BOOLEAN_OBJ:
out.writeBoolean((Boolean)obj);
break;
case TYPE_BYTE: case TYPE_BYTE_OBJ:
out.writeByte((Byte)obj);
break;
case TYPE_CHAR: case TYPE_CHAR_OBJ:
out.writeChar((Character)obj);
break;
case TYPE_DOUBLE: case TYPE_DOUBLE_OBJ:
out.writeDouble((Double)obj);
break;
case TYPE_FLOAT: case TYPE_FLOAT_OBJ:
out.writeFloat((Float)obj);
break;
case TYPE_INT: case TYPE_INT_OBJ:
out.writeInt((Integer)obj);
break;
case TYPE_LONG: case TYPE_LONG_OBJ:
out.writeLong((Long)obj);
break;
case TYPE_SHORT: case TYPE_SHORT_OBJ:
out.writeShort((Short)obj);
break;
case TYPE_STRING:
writeString((String)obj, out);
break;
case TYPE_BYTEARRAY:
byte[] buf=(byte[])obj;
out.writeInt(buf.length);
out.write(buf, 0, buf.length);
break;
default:
throw new IllegalArgumentException("type " + type + " is invalid");
}
}
public static T primitiveFromStream(DataInput in) throws IOException {
if(in == null) return null;
byte b=in.readByte();
return primitiveFromStream(in, b);
}
public static T primitiveFromStream(DataInput in, byte type) throws IOException {
if(in == null) return null;
switch(type) {
case TYPE_NULL:
return null;
case TYPE_BOOLEAN: case TYPE_BOOLEAN_OBJ: return (T)(Boolean)in.readBoolean();
case TYPE_BYTE: case TYPE_BYTE_OBJ: return (T)(Byte)in.readByte();
case TYPE_CHAR: case TYPE_CHAR_OBJ: return (T)(Character)in.readChar();
case TYPE_DOUBLE: case TYPE_DOUBLE_OBJ: return (T)(Double)in.readDouble();
case TYPE_FLOAT: case TYPE_FLOAT_OBJ: return (T)(Float)in.readFloat();
case TYPE_INT: case TYPE_INT_OBJ: return (T)(Integer)in.readInt();
case TYPE_LONG: case TYPE_LONG_OBJ: return (T)(Long)in.readLong();
case TYPE_SHORT: case TYPE_SHORT_OBJ: return (T)(Short)in.readShort();
case TYPE_STRING: return (T)readString(in);
case TYPE_BYTEARRAY:
byte[] tmpbuf=new byte[in.readInt()];
in.readFully(tmpbuf);
return (T)tmpbuf;
case TYPE_CLASS:
return (T)CLASS_TYPES.get(in.readByte());
default:
throw new IllegalArgumentException("type " + type + " is invalid");
}
}
public static void objectToStream(Object obj, DataOutput out) throws IOException {
if(obj == null) {
out.write(TYPE_NULL);
return;
}
if(obj instanceof Streamable) {
writeTypeStreamable((Streamable) obj, out);
return;
}
Byte type=obj instanceof Class>? TYPES.get(obj) : TYPES.get(obj.getClass());
if(type == null) {
out.write(TYPE_SERIALIZABLE);
try(ObjectOutputStream tmp=new ObjectOutputStream(out instanceof ByteArrayDataOutputStream?
new OutputStreamAdapter((ByteArrayDataOutputStream)out) :
(OutputStream)out)) {
tmp.writeObject(obj);
return;
}
}
primitiveToStream(obj, out);
}
public static T objectFromStream(DataInput in) throws IOException, ClassNotFoundException {
return objectFromStream(in, null);
}
public static T objectFromStream(DataInput in, ClassLoader loader) throws IOException, ClassNotFoundException {
if(in == null) return null;
byte b=in.readByte();
switch(b) {
case TYPE_STREAMABLE:
return readGenericStreamable(in, loader);
case TYPE_SERIALIZABLE: // the object is Externalizable or Serializable
InputStream is=in instanceof ByteArrayDataInputStream?
new org.jgroups.util.InputStreamAdapter((ByteArrayDataInputStream)in) : (InputStream)in;
try(ObjectInputStream tmp=new ObjectInputStreamWithClassloader(is, loader)) {
return (T)tmp.readObject();
}
}
return primitiveFromStream(in, b);
}
public static T streamableFromByteBuffer(Class extends Streamable> cl,byte[] buffer)
throws Exception {
return streamableFromByteBuffer(cl,buffer,0,buffer.length);
}
public static T streamableFromByteBuffer(Supplier factory, byte[] buffer) throws Exception {
return streamableFromByteBuffer(factory, buffer, 0, buffer.length);
}
/**
* Poor man's serialization of an exception. Serializes only the message, stack trace and cause (not suppressed exceptions)
*/
public static void exceptionToStream(Throwable t, DataOutput out) throws IOException {
Set causes=new HashSet<>();
exceptionToStream(causes, t, out);
}
protected static void exceptionToStream(Set causes, Throwable t, DataOutput out) throws IOException {
// 1. null check
if( t == null) {
out.writeBoolean(true);
return;
}
out.writeBoolean(false);
// 2. InvocationTargetException
boolean invocation_target_ex=t instanceof InvocationTargetException;
out.writeBoolean(invocation_target_ex);
if(invocation_target_ex) {
writeException(causes, t.getCause(), out);
return;
}
writeException(causes, t, out);
}
public static ByteArray exceptionToBuffer(Throwable t) throws IOException {
ByteArrayDataOutputStream out=new ByteArrayDataOutputStream(512, true);
exceptionToStream(t, out);
return out.getBuffer();
}
public static Throwable exceptionFromStream(DataInput in) throws IOException, ClassNotFoundException {
return exceptionFromStream(in, 0);
}
protected static Throwable exceptionFromStream(DataInput in, int recursion_count) throws IOException, ClassNotFoundException {
// 1. null check
if(in.readBoolean())
return null;
// 2. InvocationTargetException
if(in.readBoolean())
return new InvocationTargetException(readException(in, recursion_count));
return readException(in, recursion_count);
}
protected static void add(byte type, Class> cl) {
if(TYPES.putIfAbsent(cl, type) != null)
throw new IllegalStateException(String.format("type %d (class=%s) is already present in types map", type, cl));
CLASS_TYPES.put(type, cl);
}
protected static void writeException(Set causes, Throwable t, DataOutput out) throws IOException {
// 3. classname
Bits.writeString(t.getClass().getName(), out);
// 4. message
Bits.writeString(t.getMessage(), out);
// 5. stack trace
StackTraceElement[] stack_trace=t.getStackTrace();
int depth=stack_trace == null? 0 : stack_trace.length;
out.writeShort(depth);
if(depth > 0) {
for(int i=0; i < stack_trace.length; i++) {
StackTraceElement trace=stack_trace[i];
Bits.writeString(trace.getClassName(), out);
Bits.writeString(trace.getMethodName(), out);
Bits.writeString(trace.getFileName(), out);
Bits.writeIntCompressed(trace.getLineNumber(), out);
}
}
// 6. cause
Throwable cause=t.getCause();
boolean serialize_cause=cause != null && causes.add(cause);
out.writeBoolean(serialize_cause);
if(serialize_cause)
exceptionToStream(causes, cause, out);
}
protected static Throwable readException(DataInput in, int recursion_count) throws IOException, ClassNotFoundException {
// 3. classname
String classname=Bits.readString(in);
Class extends Throwable> clazz=(Class extends Throwable>)Util.loadClass(classname, (ClassLoader)null);
// 4. message
String message=Bits.readString(in);
Throwable retval=null;
Constructor extends Throwable> ctor=null;
try {
ctor=clazz.getDeclaredConstructor(String.class);
if(ctor != null) {
if(!ctor.isAccessible())
ctor.setAccessible(true);
retval=ctor.newInstance(message);
}
}
catch(Throwable ignored) {
}
if(retval == null) {
try {
retval=clazz.getDeclaredConstructor().newInstance();
} catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
// 5. stack trace
int depth=in.readShort();
if(depth > 0) {
StackTraceElement[] stack_trace=new StackTraceElement[depth];
for(int i=0; i < depth; i++) {
String class_name=Bits.readString(in);
String method_name=Bits.readString(in);
String filename=Bits.readString(in);
int line_number=Bits.readIntCompressed(in);
StackTraceElement trace=new StackTraceElement(class_name, method_name, filename, line_number);
stack_trace[i]=trace;
}
retval.setStackTrace(stack_trace);
}
// 6. cause
if(in.readBoolean()) {
// if malicious code constructs an exception whose causes have circles, then that would blow up the stack, so
// we make sure here that we stop after a certain number of recursive calls
if(++recursion_count > 20)
throw new IllegalStateException(String.format("failed deserializing exception: recursion count=%d",
recursion_count));
Throwable cause=exceptionFromStream(in, recursion_count);
try {
retval.initCause(cause); // will throw an exception if cause is already set
}
catch(Throwable t) {
return retval;
}
}
return retval;
}
public static Throwable exceptionFromBuffer(byte[] buf, int offset, int length) throws IOException, ClassNotFoundException {
ByteArrayDataInputStream in=new ByteArrayDataInputStream(buf, offset,length);
return exceptionFromStream(in);
}
/** Returns a copy of the byte array between position and limit; requires a non-null buffer */
public static byte[] bufferToArray(final ByteBuffer buf) {
if(buf == null)
return null;
ByteBuffer tmp=buf.duplicate();
int length=tmp.remaining();
byte[] retval=new byte[length];
tmp.get(retval, 0, retval.length);
return retval;
}
public static void bufferToArray(final Address sender, final ByteBuffer buf, org.jgroups.blocks.cs.Receiver target) {
if(buf == null)
return;
int offset=buf.hasArray()? buf.arrayOffset() + buf.position() : buf.position(),
len=buf.remaining();
if(!buf.isDirect())
target.receive(sender, buf.array(), offset, len);
else { // by default use a copy; but of course implementers of Receiver can override this
byte[] tmp=new byte[len];
buf.get(tmp, 0, len);
target.receive(sender, tmp, 0, len);
}
}
public static T streamableFromByteBuffer(Class extends Streamable> cl,byte[] buffer,int offset,int length) throws Exception {
if(buffer == null) return null;
DataInput in=new ByteArrayDataInputStream(buffer,offset,length);
T retval=(T)cl.getDeclaredConstructor().newInstance();
retval.readFrom(in);
return retval;
}
public static T streamableFromByteBuffer(Class extends Streamable> cl, ByteBuffer buffer) throws Exception {
if(buffer == null) return null;
DataInput in=new ByteBufferInputStream(buffer);
T retval=(T)cl.newInstance();
retval.readFrom(in);
return retval;
}
public static T streamableFromByteBuffer(Supplier factory, byte[] buffer, int offset, int length) throws Exception {
if(buffer == null) return null;
DataInput in=new ByteArrayDataInputStream(buffer,offset,length);
T retval=factory.get();
retval.readFrom(in);
return retval;
}
public static T streamableFromBuffer(Supplier factory, byte[] buf, int off, int len)
throws IOException, ClassNotFoundException {
DataInput in=new ByteArrayDataInputStream(buf,off,len);
return Util.readStreamable(factory, in);
}
public static byte[] streamableToByteBuffer(Streamable obj) throws IOException {
int expected_size=obj instanceof SizeStreamable? ((SizeStreamable)obj).serializedSize() : 512;
final ByteArrayDataOutputStream out=new ByteArrayDataOutputStream(expected_size);
obj.writeTo(out);
return Arrays.copyOf(out.buffer(), out.position());
}
public static ByteArray streamableToBuffer(Streamable obj) throws Exception {
int expected_size=obj instanceof SizeStreamable? ((SizeStreamable)obj).serializedSize() +1 : 512;
final ByteArrayDataOutputStream out=new ByteArrayDataOutputStream(expected_size);
Util.writeStreamable(obj,out);
return out.getBuffer();
}
public static ByteArray messageToBuffer(Message msg) throws Exception {
int expected_size=msg.size() + Global.SHORT_SIZE; // type
final ByteArrayDataOutputStream out=new ByteArrayDataOutputStream(expected_size);
out.writeShort(msg.getType());
msg.writeTo(out);
return out.getBuffer();
}
public static Message messageFromBuffer(byte[] buf, int offset, int length, MessageFactory mf) throws Exception {
ByteArrayDataInputStream in=new ByteArrayDataInputStream(buf, offset, length);
short type=in.readShort();
Message msg=mf.create(type);
msg.readFrom(in);
return msg;
}
public static ByteArray messageToByteBuffer(Message msg) throws Exception {
ByteArrayDataOutputStream out=new ByteArrayDataOutputStream(msg.size() +2);
out.writeBoolean(msg != null);
if(msg != null) {
out.writeShort(msg.getType());
msg.writeTo(out);
}
return out.getBuffer();
}
public static Message messageFromByteBuffer(byte[] buffer, int offset, int length, MessageFactory mf) throws Exception {
DataInput in=new ByteArrayDataInputStream(buffer,offset,length);
if(!in.readBoolean())
return null;
short type=in.readShort();
Message msg=mf.create(type);
msg.readFrom(in);
return msg;
}
/** Tries to return a legible representation of a message's payload */
public static String getObject(Message msg) {
if(msg == null)
return "null";
if(!msg.hasPayload())
return msg.printHeaders();
try {
Object obj=msg.getObject();
return obj != null? obj.toString() : "null";
}
catch(Throwable t) {
return String.format("<%d bytes>", msg.getLength());
}
}
public static String getObjects(Iterable it) {
if(it == null)
return "0";
return StreamSupport.stream(it.spliterator(), false)
.map(Message::getObject)
.map(Object::toString).collect(Collectors.joining(", "));
}
public static ByteBuffer wrapDirect(byte[] array) {
return wrapDirect(array, 0, array.length);
}
public static ByteBuffer wrapDirect(byte[] array, int offset, int length) {
ByteBuffer b=ByteBuffer.allocateDirect(length).put(array, offset, length);
b.flip();
return b;
}
public static byte[] collectionToByteBuffer(Collection c) throws IOException {
final ByteArrayDataOutputStream out=new ByteArrayDataOutputStream((int)Util.size(c));
Util.writeAddresses(c,out);
return Arrays.copyOf(out.buffer(), out.position());
}
public static byte[] stringToBytes(String str) {
if(str == null) return null;
byte[] retval=new byte[str.length()];
for(int i=0; i < retval.length; i++)
retval[i]=(byte)str.charAt(i);
return retval;
}
public static String bytesToString(byte[] bytes) {
return bytes != null? new String(bytes) : null;
}
public static String byteArrayToHexString(byte[] b) {
if(b == null)
return "null";
return byteArrayToHexString(b, 0, b.length);
}
public static String byteArrayToHexString(byte[] b, int offset, int length) {
if(b == null)
return "null";
StringBuilder sb = new StringBuilder(length * 2);
for (int i = 0; i < length; i++){
int v = b[i+offset] & 0xff;
if (v < 16) { sb.append('0'); }
sb.append(Integer.toHexString(v));
}
return sb.toString().toUpperCase();
}
public static boolean isAsciiString(String str) {
if(str == null) return false;
for(int i=0; i < str.length(); i++) {
int ch=str.charAt(i);
if(ch >= 128)
return false;
}
return true;
}
public static String hexToBin(String s) {
return new BigInteger(s, 16).toString(2);
}
/** Compares 2 byte arrays, elements are treated as unigned */
public static int compare(byte[] left,byte[] right) {
for(int i=0, j=0; i < left.length && j < right.length; i++,j++) {
int a=(left[i] & 0xff);
int b=(right[j] & 0xff);
if(a != b) {
return a - b;
}
}
return left.length - right.length;
}
public static Object convert(String arg, Class> type) {
if(type == String.class) return arg;
if(type == boolean.class || type == Boolean.class) return Boolean.valueOf(arg);
if(type == byte.class || type == Byte.class) return Byte.valueOf(arg);
if(type == short.class || type == Short.class) return Short.valueOf(arg);
if(type == int.class || type == Integer.class) return Integer.valueOf(arg);
if(type == long.class || type == Long.class) return Long.valueOf(arg);
if(type == float.class || type == Float.class) return Float.valueOf(arg);
if(type == double.class || type == Double.class) return Double.valueOf(arg);
if(arg == null || arg.equals("null"))
return null;
if(type.isEnum()) {
return Util.createEnum(arg, type);
}
return arg;
}
public static void writeMessage(Message msg, DataOutput dos, boolean multicast) throws IOException {
byte flags=0;
dos.writeShort(Version.version); // write the version
if(multicast)
flags+=MULTICAST;
dos.writeByte(flags);
dos.writeShort(msg.getType());
msg.writeTo(dos);
}
public static Message readMessage(DataInput in, MessageFactory mf) throws IOException, ClassNotFoundException {
short type=in.readShort();
Message msg=mf.create(type);
msg.readFrom(in);
return msg;
}
/**
* Write a list of messages with the *same* destination and src addresses. The message list is
* marshalled as follows (see doc/design/MarshallingFormat.txt for details):
*
* List: * | version | flags | dest | src | cluster-name | [Message*] |
*
* Message: | presence | leading | flags | [src] | length | [buffer] | size | [Headers*] |
*
*
*/
public static void writeMessageList(Address dest, Address src, byte[] cluster_name, List msgs,
DataOutput dos, boolean multicast, short transport_id) throws IOException {
writeMessageListHeader(dest, src, cluster_name, msgs != null ? msgs.size() : 0, dos, multicast);
if(msgs != null)
for(Message msg: msgs) {
dos.writeShort(msg.getType());
msg.writeToNoAddrs(src, dos, transport_id); // exclude the transport header
}
}
public static void writeMessageList(Address dest, Address src, byte[] cluster_name, FastArray msgs,
DataOutput dos, boolean multicast, short transport_id) throws IOException {
writeMessageListHeader(dest, src, cluster_name, msgs != null ? msgs.size() : 0, dos, multicast);
if(msgs != null)
for(Message msg: msgs) {
dos.writeShort(msg.getType());
msg.writeToNoAddrs(src, dos, transport_id); // exclude the transport header
}
}
public static void writeMessageList(Address dest, Address src, byte[] cluster_name,
Message[] msgs, int offset, int length, DataOutput dos, boolean multicast,
short transport_id) throws IOException {
writeMessageListHeader(dest, src, cluster_name, length, dos, multicast);
if(msgs != null)
for(int i=0; i < length; i++) {
Message msg=msgs[offset+i];
dos.writeShort(msg.getType());
msg.writeToNoAddrs(src, dos, transport_id); // exclude the transport header
}
}
public static void writeMessageListHeader(Address dest, Address src, byte[] cluster_name, int numMsgs, DataOutput dos, boolean multicast) throws IOException {
dos.writeShort(Version.version);
byte flags=LIST;
if(multicast)
flags+=MULTICAST;
dos.writeByte(flags);
Util.writeAddress(dest, dos);
Util.writeAddress(src, dos);
dos.writeShort(cluster_name != null? cluster_name.length : -1);
if(cluster_name != null)
dos.write(cluster_name);
dos.writeInt(numMsgs);
}
public static List readMessageList(DataInput in, short transport_id, MessageFactory mf)
throws IOException, ClassNotFoundException {
List list=new LinkedList<>();
Address dest=Util.readAddress(in);
Address src=Util.readAddress(in);
// AsciiString cluster_name=Bits.readAsciiString(in); // not used here
short length=in.readShort();
byte[] cluster_name=length >= 0? new byte[length] : null;
if(cluster_name != null)
in.readFully(cluster_name, 0, cluster_name.length);
int len=in.readInt();
for(int i=0; i < len; i++) {
short type=in.readShort(); // skip the
Message msg=mf.create(type);
msg.readFrom(in);
msg.setDest(dest);
if(msg.getSrc() == null)
msg.setSrc(src);
// Now add a TpHeader back on, was not marshalled. Every message references the *same* TpHeader, saving memory !
msg.putHeader(transport_id, new TpHeader(cluster_name));
list.add(msg);
}
return list;
}
/**
* Reads a list of messages into 2 MessageBatches:
*
* - regular
* - OOB
*
* @return an array of 2 MessageBatches in the order above, the first batch is at index 0
*/
public static MessageBatch[] readMessageBatch(DataInput in, boolean multicast, MessageFactory factory)
throws IOException, ClassNotFoundException {
MessageBatch[] batches=new MessageBatch[2]; // [0]: reg, [1]: OOB
Address dest=Util.readAddress(in);
Address src=Util.readAddress(in);
short length=in.readShort();
byte[] cluster_name=length >= 0? new byte[length] : null;
if(cluster_name != null)
in.readFully(cluster_name, 0, cluster_name.length);
int len=in.readInt();
for(int i=0; i < len; i++) {
short type=in.readShort();
Message msg=factory.create(type).setDest(dest).setSrc(src);
msg.readFrom(in);
boolean oob=msg.isFlagSet(Message.Flag.OOB);
int index=0;
MessageBatch.Mode mode=MessageBatch.Mode.REG;
if(oob) {
mode=MessageBatch.Mode.OOB;
index=1;
}
if(batches[index] == null)
batches[index]=new MessageBatch(dest, src, cluster_name != null? new AsciiString(cluster_name) : null, multicast, mode, len);
batches[index].add(msg);
}
return batches;
}
public static void parse(byte[] buf, int offset, int length, BiConsumer msg_consumer,
BiConsumer batch_consumer, Consumer gossip_consumer,
boolean tcp, boolean gossip) {
parse(new ByteArrayInputStream(buf, offset, length), msg_consumer, batch_consumer, gossip_consumer, tcp, gossip);
}
public static void parse(InputStream input, BiConsumer msg_consumer,
BiConsumer batch_consumer, Consumer gossip_consumer,
boolean tcp, boolean gossip) {
if(msg_consumer == null && batch_consumer == null)
return;
byte[] tmp=new byte[Global.INT_SIZE];
MessageFactory mf=new DefaultMessageFactory();
try(DataInputStream dis=new DataInputStream(input)) {
for(;;) {
// for TCP, we send the length first; this needs to be skipped as it is not part of the JGroups payload
if(tcp) { // TCP / TCP_NIO2
dis.readFully(tmp);
if(Arrays.equals(Connection.cookie, tmp)) {
// connection establishment; parse version (short) and IpAddress
dis.readShort(); // version
dis.readShort(); // address length (only needed by TCP_NIO2)
IpAddress peer=new IpAddress();
peer.readFrom(dis);
continue;
}
else {
// do nothing - the 4 bytes were the length
// int len=Bits.readInt(tmp, 0);
}
}
if(gossip) { // messages to or from a GossipRouter
GossipData g=new GossipData();
g.readFrom(dis, true, false);
if(g.getType() != GossipType.MESSAGE) {
if(gossip_consumer != null)
gossip_consumer.accept(g);
continue;
}
}
short version=dis.readShort();
byte flags=dis.readByte();
boolean is_message_list=(flags & LIST) == LIST;
boolean multicast=(flags & MULTICAST) == MULTICAST;
if(is_message_list) { // used if message bundling is enabled
final MessageBatch[] batches=Util.readMessageBatch(dis,multicast, mf);
for(MessageBatch batch: batches) {
if(batch == null)
continue;
if(batch_consumer != null)
batch_consumer.accept(version, batch);
else {
for(Message msg: batch)
msg_consumer.accept(version, msg);
}
}
}
else {
Message msg=Util.readMessage(dis, mf);
if(msg_consumer != null)
msg_consumer.accept(version, msg);
}
}
}
catch(EOFException ignored) {
}
catch(Throwable t) {
t.printStackTrace();
}
}
public static void writeView(View view,DataOutput out) throws IOException {
if(view == null) {
out.writeBoolean(false);
return;
}
out.writeBoolean(true);
out.writeBoolean(view instanceof MergeView);
view.writeTo(out);
}
public static View readView(DataInput in) throws IOException, ClassNotFoundException {
if(!in.readBoolean())
return null;
boolean isMergeView=in.readBoolean();
View view;
if(isMergeView)
view=new MergeView();
else
view=new View();
view.readFrom(in);
return view;
}
public static void writeViewId(ViewId vid,DataOutput out) throws IOException {
if(vid == null) {
out.writeBoolean(false);
return;
}
out.writeBoolean(true);
vid.writeTo(out);
}
public static ViewId readViewId(DataInput in) throws IOException, ClassNotFoundException {
if(!in.readBoolean())
return null;
ViewId retval=new ViewId();
retval.readFrom(in);
return retval;
}
public static void writeAddress(Address addr,DataOutput out) throws IOException {
byte flags=0;
boolean streamable_addr=true;
if(addr == null) {
flags=Util.setFlag(flags,Address.NULL);
out.writeByte(flags);
return;
}
if(addr instanceof UUID) {
Class extends Address> clazz=addr.getClass();
if(clazz.equals(UUID.class))
flags=Util.setFlag(flags,Address.UUID_ADDR);
else if(clazz.equals(SiteUUID.class))
flags=Util.setFlag(flags,Address.SITE_UUID);
else if(clazz.equals(SiteMaster.class))
flags=Util.setFlag(flags,Address.SITE_MASTER);
else
streamable_addr=false;
}
else if(addr.getClass().equals(IpAddress.class))
flags=Util.setFlag(flags,Address.IP_ADDR);
else
streamable_addr=false;
out.writeByte(flags);
if(streamable_addr)
addr.writeTo(out);
else
writeOtherAddress(addr,out);
}
public static Address readAddress(DataInput in) throws IOException, ClassNotFoundException {
byte flags=in.readByte();
if(Util.isFlagSet(flags,Address.NULL))
return null;
Address addr;
if(Util.isFlagSet(flags,Address.UUID_ADDR)) {
addr=new UUID();
addr.readFrom(in);
}
else if(Util.isFlagSet(flags,Address.SITE_UUID)) {
addr=new SiteUUID();
addr.readFrom(in);
}
else if(Util.isFlagSet(flags,Address.SITE_MASTER)) {
addr=new SiteMaster();
addr.readFrom(in);
}
else if(Util.isFlagSet(flags,Address.IP_ADDR)) {
addr=new IpAddress();
addr.readFrom(in);
}
else
addr=readOtherAddress(in);
return addr;
}
public static int size(Address addr) {
int retval=Global.BYTE_SIZE; // flags
if(addr == null)
return retval;
if(addr instanceof UUID) {
Class extends Address> clazz=addr.getClass();
if(clazz.equals(UUID.class) || clazz.equals(SiteUUID.class) || clazz.equals(SiteMaster.class))
return retval + addr.serializedSize();
}
if(addr instanceof IpAddress)
return retval + addr.serializedSize();
retval+=Global.SHORT_SIZE; // magic number
retval+=addr.serializedSize();
return retval;
}
public static int size(View view) {
int retval=Global.BYTE_SIZE; // presence
if(view != null)
retval+=view.serializedSize() + Global.BYTE_SIZE; // merge view or regular view
return retval;
}
public static int size(ViewId vid) {
int retval=Global.BYTE_SIZE; // presence
if(vid != null)
retval+=vid.serializedSize();
return retval;
}
public static int size(String s) {
int retval=Global.BYTE_SIZE;
if(s != null)
retval+=s.length() + 2;
return retval;
}
public static int size(byte[] buf) {
return buf == null? Global.BYTE_SIZE : Global.BYTE_SIZE + Global.INT_SIZE + buf.length;
}
private static Address readOtherAddress(DataInput in) throws IOException, ClassNotFoundException {
short magic_number=in.readShort();
Address addr=ClassConfigurator.create(magic_number);
addr.readFrom(in);
return addr;
}
private static void writeOtherAddress(Address addr,DataOutput out) throws IOException {
short magic_number=ClassConfigurator.getMagicNumber(addr.getClass());
// write the class info
if(magic_number == -1)
throw new RuntimeException("magic number " + magic_number + " not found");
out.writeShort(magic_number);
addr.writeTo(out);
}
/**
* Writes a list of Addresses. Can contain 65K addresses at most
* @param v A Collection
* @param out
* @throws Exception
*/
public static void writeAddresses(Collection extends Address> v,DataOutput out) throws IOException {
if(v == null) {
out.writeShort(-1);
return;
}
out.writeShort(v.size());
for(Address addr : v) {
Util.writeAddress(addr,out);
}
}
public static void writeAddresses(final Address[] addrs,DataOutput out) throws IOException {
if(addrs == null) {
out.writeShort(-1);
return;
}
out.writeShort(addrs.length);
for(Address addr : addrs)
Util.writeAddress(addr,out);
}
public static > T readAddresses(DataInput in, IntFunction factory)
throws IOException, ClassNotFoundException {
short length=in.readShort();
if(length < 0) return null;
T retval = factory.apply(length);
Address addr;
for(int i=0; i < length; i++) {
addr=Util.readAddress(in);
retval.add(addr);
}
return retval;
}
public static Address[] readAddresses(DataInput in) throws IOException, ClassNotFoundException {
short length=in.readShort();
if(length < 0) return null;
Address[] retval=new Address[length];
for(int i=0; i < length; i++) {
Address addr=Util.readAddress(in);
retval[i]=addr;
}
return retval;
}
/**
* Returns the marshalled size of a Collection of Addresses.
* Assumes elements are of the same type !
* @param addrs Collection
* @return long size
*/
public static long size(Collection extends Address> addrs) {
int retval=Global.SHORT_SIZE; // number of elements
if(addrs != null && !addrs.isEmpty()) {
Address addr=addrs.iterator().next();
retval+=size(addr) * addrs.size();
}
return retval;
}
public static long size(Address[] addrs) {
int retval=Global.SHORT_SIZE; // number of elements
if(addrs != null)
for(Address addr : addrs)
retval+=Util.size(addr);
return retval;
}
public static void writeStreamable(Streamable obj,DataOutput out) throws IOException {
if(obj == null) {
out.writeBoolean(false);
return;
}
out.writeBoolean(true);
obj.writeTo(out);
}
public static T readStreamable(Supplier factory, DataInput in) throws IOException, ClassNotFoundException {
T retval=null;
if(!in.readBoolean())
return null;
retval=factory.get();
retval.readFrom(in);
return retval;
}
public static void writeGenericStreamable(Streamable obj, DataOutput out) throws IOException {
if(obj == null) {
out.write(0);
return;
}
out.write(1);
short magic_number=ClassConfigurator.getMagicNumber(obj.getClass());
out.writeShort(magic_number);
if(magic_number == -1) {
String classname=obj.getClass().getName();
out.writeUTF(classname);
}
obj.writeTo(out); // write the contents
}
public static T readGenericStreamable(DataInput in) throws IOException, ClassNotFoundException {
return readGenericStreamable(in, null);
}
public static T readGenericStreamable(DataInput in, ClassLoader loader) throws IOException, ClassNotFoundException {
T retval=null;
int b=in.readByte();
if(b == 0)
return null;
short magic_number=in.readShort();
Class> clazz;
if(magic_number != -1) {
retval=ClassConfigurator.create(magic_number);
}
else {
String classname=in.readUTF();
clazz=ClassConfigurator.get(classname, loader);
try {
retval=(T)clazz.getDeclaredConstructor().newInstance();
}
catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
retval.readFrom(in);
return retval;
}
public static SizeStreamable readSizeStreamable(DataInput in, ClassLoader loader) throws IOException, ClassNotFoundException {
int b=in.readByte();
if(b == 0)
return null;
SizeStreamable retval=null;
short magic_number=in.readShort();
if(magic_number != -1) {
retval=ClassConfigurator.create(magic_number);
}
else {
String classname=in.readUTF();
Class clazz=(Class)ClassConfigurator.get(classname, loader);
try {
retval=clazz.getDeclaredConstructor().newInstance();
}
catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
retval.readFrom(in);
return retval;
}
public static void write(T[] array, DataOutput out) throws IOException {
Bits.writeIntCompressed(array != null? array.length : 0, out);
if(array == null)
return;
for(T el: array)
el.writeTo(out);
}
public static T[] read(Class clazz, DataInput in) throws Exception {
int size=Bits.readIntCompressed(in);
if(size == 0)
return null;
T[] retval=(T[])Array.newInstance(clazz, size);
for(int i=0; i < retval.length; i++) {
retval[i]=clazz.getDeclaredConstructor().newInstance();
retval[i].readFrom(in);
}
return retval;
}
public static String readFile(String filename) throws FileNotFoundException {
FileInputStream in=new FileInputStream(filename);
try {
return readContents(in);
}
finally {
Util.close(in);
}
}
public static String readContents(InputStream input) {
StringBuilder sb=new StringBuilder();
int ch;
while(true) {
try {
ch=input.read();
if(ch != -1)
sb.append((char)ch);
else
break;
}
catch(IOException e) {
break;
}
}
return sb.toString();
}
public static byte[] readFileContents(InputStream input) throws IOException {
byte[] contents=new byte[10000];
byte[] buf=new byte[1024];
InputStream in=new BufferedInputStream(input);
int bytes_read=0;
for(; ; ) {
int tmp=in.read(buf,0,buf.length);
if(tmp == -1)
break;
System.arraycopy(buf,0,contents,bytes_read,tmp);
bytes_read+=tmp;
}
byte[] retval=new byte[bytes_read];
System.arraycopy(contents,0,retval,0,bytes_read);
return retval;
}
/** Returns whitespace-separated strings from the input stream, or null if the end of the stream has been reached */
public static String readToken(InputStream in) {
StringBuilder sb=new StringBuilder();
boolean first=true;
int ch;
while(true) {
try {
ch=in.read();
if(ch == -1)
return sb.length() > 0? sb.toString() : null;
if(Character.isWhitespace(ch)) {
if(first)
continue;
break;
}
sb.append((char)ch);
first=false;
}
catch(IOException e) {
break;
}
}
return sb.length() > 0? sb.toString() : null;
}
/** Returns all characters read from the current position until the next occurrence of 'c' has been read
* (including 'c'), or eof, whichever comes first */
public static String readTillMatchingCharacter(InputStream in, char c) throws IOException {
StringBuilder sb=new StringBuilder();
boolean eof=false;
for(;;) {
int ch=in.read();
if(ch == -1) {
eof=true;
break;
}
sb.append((char)ch);
if(ch == c)
break;
}
String retval=sb.toString().trim();
return eof && retval.isEmpty()? null : retval;
}
public static String readStringFromStdin(String message) throws Exception {
System.out.print(message);
System.out.flush();
System.in.skip(available(System.in));
BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
String line=reader.readLine();
return line != null? line.trim() : null;
}
public static long readLongFromStdin(String message) throws Exception {
String tmp=readStringFromStdin(message);
return Long.parseLong(tmp);
}
public static double readDoubleFromStdin(String message) throws Exception {
String tmp=readStringFromStdin(message);
return Double.parseDouble(tmp);
}
public static int readIntFromStdin(String message) throws Exception {
String tmp=readStringFromStdin(message);
return Integer.parseInt(tmp);
}
public static void writeByteBuffer(byte[] buf,DataOutput out) throws IOException {
writeByteBuffer(buf,0,buf.length,out);
}
public static void writeByteBuffer(byte[] buf, int offset, int length, DataOutput out) throws IOException {
if(buf != null) {
out.write(1);
out.writeInt(length);
out.write(buf,offset,length);
}
else
out.write(0);
}
public static byte[] readByteBuffer(DataInput in) throws IOException {
int b=in.readByte();
if(b == 1) {
b=in.readInt();
byte[] buf=new byte[b];
in.readFully(buf, 0, buf.length);
return buf;
}
return null;
}
public static boolean match(T obj1,T obj2) {
if(obj1 == null && obj2 == null)
return true;
if(obj1 != null)
return obj1.equals(obj2);
else
return obj2.equals(obj1);
}
public static boolean patternMatch(String pattern,String str) {
Pattern pat=Pattern.compile(pattern);
Matcher matcher=pat.matcher(str);
return matcher.matches();
}
public static boolean productGreaterThan(long n1, long n2, long val) {
long div=(long)Math.floor(val/(double)n1);
return div < n2;
}
public static boolean different(T one,T two) {
return !match(one,two);
}
/** Sleep for timeout msecs. Returns when timeout has elapsed or thread was interrupted */
public static void sleep(long timeout) {
try {
Thread.sleep(timeout);
}
catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public static void sleep(long timeout,int nanos) {
//the Thread.sleep method is not precise at all regarding nanos
if(timeout > 0 || nanos > 900000) {
try {
Thread.sleep(timeout + (nanos / 1000000),(nanos % 1000000));
}
catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
}
else {
//this isn't a superb metronome either, but allows a granularity
//with a reasonable precision in the order of 50ths of millisecond
long initialTime=nanoTime() - 200;
while(nanoTime() < initialTime + nanos) ;
}
}
public static int keyPress(String msg) {
System.out.println(msg);
try {
int ret=System.in.read();
System.in.skip(available(System.in));
return ret;
}
catch(IOException e) {
return -1;
}
}
public static int available(InputStream in) {
try {
return in.available();
}
catch(Exception ex) {
return 0;
}
}
public static long micros() {
return nanoTime() / 1000;
}
public static int factorial(int n) {
if(n == 1) return n;
return n * factorial(n-1);
}
/**
* Inefficient permutation of a generic list; uses too much copying among other things. PR welcome! :-)
* @param list The list
* @param permutations All permutations will be added to this list (as copies)
* @param the type of the list elements
*/
public static void permute(List list, List> permutations) {
if(list.size() == 1) {
permutations.add(list);
return;
}
for(int i=0; i < list.size(); i++) {
permute(null, list, i, permutations);
}
}
protected static void permute(List prefix, List list, int index, final List> permutations) {
if(list.size() <= 1)
permutations.add(combine(prefix, list));
else {
List swapped=swap(list, index);
List first=car(swapped);
List rest=cdr(swapped);
for(int i=0; i < rest.size(); i++)
permute(combine(prefix, first), rest, i, permutations);
}
}
public static List car(List l) {
return l.isEmpty()? Collections.emptyList() : Collections.singletonList(l.get(0));
}
public static List cdr(List l) {
return l.isEmpty()? Collections.emptyList() : l.subList(1, l.size());
}
public static List combine(List l1, List l2) {
ArrayList retval=new ArrayList<>();
if(l1 != null)
retval.addAll(l1);
if(l2 != null)
retval.addAll(l2);
return retval;
}
@SafeVarargs
public static E[] combine(E[] ... arrays) {
if(arrays == null)
return null;
int size=(int)Stream.of(arrays).flatMap(Stream::of).map(Objects::nonNull).count();
E[] retval=(E[])Array.newInstance(arrays[0].getClass().getComponentType(), size);
AtomicInteger index=new AtomicInteger();
Stream.of(arrays).flatMap(Stream::of).forEach(el -> retval[index.getAndIncrement()]=el);
return retval;
}
// moves el at index to the front, returns a copy
protected static List swap(List l, int index) {
List swapped=new ArrayList<>();
E el=l.get(index);
swapped.add(el);
for(int i=0; i < l.size(); i++) {
el=l.get(i);
if(i != index)
swapped.add(el);
}
return swapped;
}
/**
* Performs an ordered permutation of the elements of a and b such that the order of elements in list a and b is
* preserved. Example: {A1,A2} and {B1,B2} -> {A1,B1,A2,B2} but not {A1,B2,B1,A2}
*/
public static Collection> orderedPermutation(List a, List b) {
Collection> perms=new LinkedHashSet<>();
for(int i=0; i <= a.size(); i++) {
for(int j=0; j <= b.size(); j++) {
if(i == 0 && j == 0)
continue;
List l1=new ArrayList<>(a), l2=new ArrayList<>(b);
List perm=permute(l1, l2, i, j);
perms.add(perm);
}
}
for(int i=0; i <= b.size(); i++) {
for(int j=0; j <= a.size(); j++) {
if(i == 0 && j == 0)
continue;
List l1=new ArrayList<>(b), l2=new ArrayList<>(a);
List perm=permute(l1, l2, i, j);
perms.add(perm);
}
}
return perms;
}
protected static List permute(List l1, List l2, int remove_from_l1, int remove_from_l2) {
List retval=new ArrayList<>();
while(!(l1.isEmpty() || l2.isEmpty())) {
int a=remove_from_l1, b=remove_from_l2;
while(a-- > 0 && !l1.isEmpty())
retval.add(l1.remove(0));
while(b-- > 0 && !l2.isEmpty())
retval.add(l2.remove(0));
}
retval.addAll(l1);
retval.addAll(l2);
return retval;
}
@SafeVarargs
public static boolean checkOrder(Collection perm, List ... lists) {
for(List list: lists) {
if(!perm.containsAll(list))
return false;
int pos=0;
for(E el: list) {
int index=index(perm, el);
if(index < pos)
return false;
pos=index;
}
}
return true;
}
protected static int index(Collection list, E el) {
int index=0;
for(E element: list) {
if(element.equals(el))
return index;
index++;
}
return -1;
}
/**
* Reorders elements of an array in-place. No bounds checking is performed. Null elements are shuffled, too
* @param array the array to be shuffled; the array will be modified
* @param from the start index inclusive
* @param to the end index (exclusive), must be >= from (not checked)
* @param the type of the array's elements
*/
public static void shuffle(T[] array, int from, int to) {
if(array == null)
return;
for(int i=from; i < to; i++) {
int random=(int)random(to);
int other=random -1 + from;
// int other=(int)(random(to)-1 + from);
if(i != other) {
T tmp=array[i];
array[i]=array[other];
array[other]=tmp;
}
}
}
public static Enumeration enumerate(final T[] array, int offset, final int length) {
return new Enumeration<>() {
final int end_pos=offset + length;
int pos=offset;
public boolean hasMoreElements() {
return pos < end_pos;
}
public T nextElement() {
if(pos < end_pos)
return array[pos++];
throw new NoSuchElementException(String.format("pos=%d, end_pos=%d", pos, end_pos));
}
};
}
public static Enumeration enumerate(final T[] array, int offset, final int length, Function converter) {
return new Enumeration<>() {
final int end_pos=offset + length;
int pos=offset;
public boolean hasMoreElements() {
return pos < end_pos;
}
public R nextElement() {
if(pos < end_pos)
return converter.apply(array[pos++]);
throw new NoSuchElementException(String.format("pos=%d, end_pos=%d", pos, end_pos));
}
};
}
public static T nonNegativeValue(T num) {
if(num.longValue() < 0)
throw new IllegalArgumentException(String.format("%s must not be negative", num));
return num;
}
/** Returns a random value in the range [1 - range]. If range is 0, 1 will be returned. If range is negative, an
* exception will be thrown */
public static long random(long range) {
return range == 0? 1 : ThreadLocalRandom.current().nextLong(range) + 1;
}
/** Sleeps between floor and ceiling milliseconds, chosen randomly */
public static void sleepRandom(long floor,long ceiling) {
if(ceiling - floor <= 0) {
return;
}
long diff=ceiling - floor;
long r=(int)((Math.random() * 100000) % diff) + floor;
sleep(r);
}
/**
* Tosses a coin weighted with probability and returns true or false. Example: if probability=0.8,
* chances are that in 80% of all cases, true will be returned and false in 20%.
*/
public static boolean tossWeightedCoin(double probability) {
if(probability >= 1)
return true;
if(probability <= 0)
return false;
long r=random(100);
long cutoff=(long)(probability * 100);
return r < cutoff;
}
public static String dumpThreads() {
ThreadMXBean bean=ManagementFactory.getThreadMXBean();
ThreadInfo[] threads=bean.dumpAllThreads(true, true);
return Stream.of(threads).map(Util::dumpThreadInfo).collect(Collectors.joining("\n"));
}
private static String dumpThreadInfo(ThreadInfo thread) { // copied from Infinispan
StringBuilder sb=new StringBuilder(String.format("\"%s\" #%s prio=0 tid=0x%x nid=NA %s%n", thread.getThreadName(), thread.getThreadId(),
thread.getThreadId(), thread.getThreadState().toString().toLowerCase()));
sb.append(String.format(" java.lang.Thread.State: %s%n", thread.getThreadState()));
LockInfo blockedLock = thread.getLockInfo();
StackTraceElement[] s = thread.getStackTrace();
MonitorInfo[] monitors = thread.getLockedMonitors();
for (int i = 0; i < s.length; i++) {
StackTraceElement ste = s[i];
sb.append(String.format("\tat %s\n", ste));
if (i == 0 && blockedLock != null) {
boolean parking = ste.isNativeMethod() && ste.getMethodName().equals("park");
sb.append(String.format("\t- %s <0x%x> (a %s)%n", blockedState(thread, blockedLock, parking),
blockedLock.getIdentityHashCode(), blockedLock.getClassName()));
}
if (monitors != null) {
for (MonitorInfo monitor : monitors) {
if (monitor.getLockedStackDepth() == i) {
sb.append(String.format("\t- locked <0x%x> (a %s)%n", monitor.getIdentityHashCode(), monitor.getClassName()));
}
}
}
}
sb.append('\n');
LockInfo[] synchronizers = thread.getLockedSynchronizers();
if (synchronizers != null && synchronizers.length > 0) {
sb.append("\n Locked ownable synchronizers:\n");
for (LockInfo synchronizer : synchronizers) {
sb.append(String.format("\t- <0x%x> (a %s)%n", synchronizer.getIdentityHashCode(), synchronizer.getClassName()));
}
sb.append('\n');
}
return sb.toString();
}
private static String blockedState(ThreadInfo thread, LockInfo blockedLock, boolean parking) {
String state;
if (blockedLock != null) {
if (thread.getThreadState() == Thread.State.BLOCKED) {
state = "waiting to lock";
} else if (parking) {
state = "parking to wait for";
} else {
state = "waiting on";
}
} else {
state = null;
}
return state;
}
public static boolean interruptAndWaitToDie(Thread t) {
return interruptAndWaitToDie(t,Global.THREAD_SHUTDOWN_WAIT_TIME);
}
public static boolean interruptAndWaitToDie(Thread t,long timeout) {
if(t == null)
throw new IllegalArgumentException("Thread can not be null");
t.interrupt(); // interrupts the sleep()
try {
t.join(timeout);
}
catch(InterruptedException e) {
Thread.currentThread().interrupt(); // set interrupt flag again
}
return t.isAlive();
}
public static String printTime(double time, TimeUnit unit) {
switch(unit) {
case NANOSECONDS:
if(time < 1000) return print(time, unit);
return printTime(time / 1000.0, TimeUnit.MICROSECONDS);
case MICROSECONDS:
if(time < 1000) return print(time, unit);
return printTime(time / 1000.0, TimeUnit.MILLISECONDS);
case MILLISECONDS:
if(time < 1000) return print(time, unit);
return printTime(time / 1000.0, TimeUnit.SECONDS);
case SECONDS:
if(time < 60) return print(time, unit);
return printTime(time / 60.0, TimeUnit.MINUTES);
case MINUTES:
if(time < 60) return print(time, unit);
return printTime(time / 60.0, TimeUnit.HOURS);
case HOURS:
if(time < 24) return print(time, unit);
return printTime(time / 24.0, TimeUnit.DAYS);
default: return print(time, unit);
}
}
public static String suffix(TimeUnit u) {
switch(u) {
case NANOSECONDS: return "ns";
case MICROSECONDS: return "us";
case MILLISECONDS: return "ms";
case SECONDS: return "s";
case MINUTES: return "m";
case HOURS: return "h";
case DAYS: return "d";
default: return u.toString();
}
}
public static String print(double time, TimeUnit unit) {
return format(time, suffix(unit));
}
public static long readBytesLong(String input) {
Tuple tuple=readBytes(input);
double num=Double.parseDouble(tuple.getVal1());
return (long)(num * tuple.getVal2());
}
public static int readBytesInteger(String input) {
Tuple tuple=readBytes(input);
double num=Double.parseDouble(tuple.getVal1());
return (int)(num * tuple.getVal2());
}
public static double readBytesDouble(String input) {
Tuple tuple=readBytes(input);
double num=Double.parseDouble(tuple.getVal1());
return num * tuple.getVal2();
}
private static Tuple readBytes(String input) {
input=input.trim().toLowerCase();
int index=-1;
long factor=1;
if((index=input.indexOf('k')) != -1)
factor=1000;
else if((index=input.indexOf("kb")) != -1)
factor=1000;
else if((index=input.indexOf('m')) != -1)
factor=1_000_000;
else if((index=input.indexOf("mb")) != -1)
factor=1_000_000;
else if((index=input.indexOf('g')) != -1)
factor=1_000_000_000;
else if((index=input.indexOf("gb")) != -1)
factor=1_000_000_000;
String str=index != -1? input.substring(0,index) : input;
return new Tuple<>(str,factor);
}
/**
* MB nowadays doesn't mean 1024 * 1024 bytes, but 1 million bytes, see http://en.wikipedia.org/wiki/Megabyte
* @param bytes
*/
public static String printBytes(double bytes) {
double tmp;
if(bytes < 1000)
return format(bytes, "b");
if(bytes < 1_000_000) {
tmp=bytes / 1000.0;
return format(tmp, "KB");
}
if(bytes < 1_000_000_000) {
tmp=bytes / 1000_000.0;
return format(tmp, "MB");
}
else {
tmp=bytes / 1_000_000_000.0;
return format(tmp, "GB");
}
}
public static String format(double val, String suffix) {
int trailing=Math.floor(val) == val? 0 : 2;
String fmt=String.format("%%,.%df%s", trailing, suffix);
return String.format(fmt, val);
}
public static String[] components(String path,String separator) {
if(path == null || path.isEmpty())
return null;
String[] tmp=path.split(separator + "+"); // multiple separators could be present
if(tmp == null)
return null;
if(tmp.length == 0)
return null;
if(tmp[0].isEmpty())
tmp[0]=separator;
return tmp;
}
/**
* Fragments a byte buffer into smaller fragments of (max.) frag_size.
* Example: a byte buffer of 1024 bytes and a frag_size of 248 gives 4 fragments
* of 248 bytes each and 1 fragment of 32 bytes.
* @return An array of byte buffers ({@code byte[]}).
*/
public static byte[][] fragmentBuffer(byte[] buf,int frag_size,final int length) {
byte[][] retval;
byte[] fragment;
int accumulated_size=0, tmp_size=0, num_frags, index=0;
num_frags=length % frag_size == 0? length / frag_size : length / frag_size + 1;
retval=new byte[num_frags][];
while(accumulated_size < length) {
if(accumulated_size + frag_size <= length)
tmp_size=frag_size;
else
tmp_size=length - accumulated_size;
fragment=new byte[tmp_size];
System.arraycopy(buf,accumulated_size,fragment,0,tmp_size);
retval[index++]=fragment;
accumulated_size+=tmp_size;
}
return retval;
}
/**
* Given a buffer and a fragmentation size, compute a list of fragmentation offset/length pairs, and
* return them in a list. Example:
* Buffer is 10 bytes, frag_size is 4 bytes. Return value will be ({0,4}, {4,4}, {8,2}).
* This is a total of 3 fragments: the first fragment starts at 0, and has a length of 4 bytes, the second fragment
* starts at offset 4 and has a length of 4 bytes, and the last fragment starts at offset 8 and has a length
* of 2 bytes.
* @param frag_size
* @return List. A List of offset/length pairs
*/
public static List computeFragOffsets(int offset,int length,int frag_size) {
int num_frags=(int)Math.ceil(length / (double)frag_size);
List retval=new ArrayList<>(num_frags);
long total_size=(long)length + offset;
int index=offset;
int tmp_size=0;
while(index < total_size) {
if(index + frag_size <= total_size)
tmp_size=frag_size;
else
tmp_size=(int)(total_size - index);
Range r=new Range(index,tmp_size);
retval.add(r);
index+=tmp_size;
}
return retval;
}
public static List computeFragOffsets(byte[] buf,int frag_size) {
return computeFragOffsets(0,buf.length,frag_size);
}
/**
* Concatenates smaller fragments into entire buffers.
* @param fragments An array of byte buffers ({@code byte[]})
* @return A byte buffer
*/
public static byte[] defragmentBuffer(byte[][] fragments) {
int total_length=0;
byte[] ret;
int index=0;
if(fragments == null) return null;
for(int i=0; i < fragments.length; i++) {
if(fragments[i] == null) continue;
total_length+=fragments[i].length;
}
ret=new byte[total_length];
for(int i=0; i < fragments.length; i++) {
if(fragments[i] == null) continue;
System.arraycopy(fragments[i],0,ret,index,fragments[i].length);
index+=fragments[i].length;
}
return ret;
}
/** Returns true if all elements in the collection are the same */
public static boolean allEqual(Collection elements) {
if(elements.isEmpty())
return false;
Iterator it=elements.iterator();
T first=it.next();
while(it.hasNext()) {
T el=it.next();
if(!el.equals(first))
return false;
}
return true;
}
public static String printList(Collection l) {
return printListWithDelimiter(l, ",");
}
public static String printListWithDelimiter(Collection list,String delimiter) {
return printListWithDelimiter(list,delimiter,MAX_LIST_PRINT_SIZE,true);
}
public static String printListWithDelimiter(Collection list,String delimiter,int limit) {
return printListWithDelimiter(list,delimiter,limit,true);
}
public static String printListWithDelimiter(Collection list,String delimiter,int limit,boolean print_size) {
boolean first=true;
int count=0, size=print_size? list.size() : 0;
StringBuilder sb=new StringBuilder(print_size? "(" + size + ") " : "");
for(T el : list) {
if(first)
first=false;
else
sb.append(delimiter);
sb.append(el);
if(limit > 0 && ++count >= limit) {
if(size > count)
sb.append(" ..."); // .append(list.size()).append("...");
break;
}
}
return sb.toString();
}
public static String printListWithDelimiter(T[] list,String delimiter,int limit) {
boolean first=true;
StringBuilder sb=new StringBuilder();
int count=0, size=list.length;
for(T el : list) {
if(first)
first=false;
else
sb.append(delimiter);
sb.append(el);
if(limit > 0 && ++count >= limit) {
if(size > count)
sb.append(" ..."); // .append(list.length).append("...");
break;
}
}
return sb.toString();
}
public static String printMapWithDelimiter(Map map,String delimiter) {
boolean first=true;
StringBuilder sb=new StringBuilder();
for(Map.Entry entry : map.entrySet()) {
if(first)
first=false;
else
sb.append(delimiter);
sb.append(entry.getKey()).append("=").append(entry.getValue());
}
return sb.toString();
}
public static List leftMembers(Collection old_list,Collection new_list) {
if(old_list == null || new_list == null)
return null;
List retval=new ArrayList<>(old_list);
retval.removeAll(new_list);
return retval;
}
public static List newMembers(List old_list,List new_list) {
if(old_list == null || new_list == null)
return null;
List retval=new ArrayList<>(new_list);
retval.removeAll(old_list);
return retval;
}
public static List newElements(List old_list,List new_list) {
if(new_list == null)
return new ArrayList<>();
List retval=new ArrayList<>(new_list);
if(old_list != null)
retval.removeAll(old_list);
return retval;
}
public static boolean contains(T key,T[] list) {
if(list == null) return false;
for(T tmp: list)
if(tmp == key || tmp.equals(key))
return true;
return false;
}
public static boolean containsViewId(Collection views,ViewId vid) {
for(View view : views) {
ViewId tmp=view.getViewId();
if(tmp.equals(vid))
return true;
}
return false;
}
public static boolean containsId(short id,short[] ids) {
if(ids == null)
return false;
for(short tmp : ids)
if(tmp == id)
return true;
return false;
}
/**
* Determines the members which take part in a merge. The resulting list consists of all merge coordinators
* plus members outside a merge partition, e.g. for views A={B,A,C}, B={B,C} and C={B,C}, the merge coordinator
* is B, but the merge participants are B and A.
* @param map
* @return
*/
public static Collection determineMergeParticipants(Map map) {
Set coords=new HashSet<>();
Set all_addrs=new HashSet<>();
if(map == null)
return Collections.emptyList();
for(View view : map.values())
all_addrs.addAll(view.getMembers());
for(View view : map.values()) {
Address coord=view.getCreator();
if(coord != null)
coords.add(coord);
}
for(Address coord : coords) {
View view=map.get(coord);
Collection mbrs=view != null? view.getMembers() : null;
if(mbrs != null)
all_addrs.removeAll(mbrs);
}
coords.addAll(all_addrs);
return coords;
}
/**
* This is the same or a subset of {@link #determineMergeParticipants(java.util.Map)} and contains only members
* which are currently sub-partition coordinators.
* @param map
* @return
*/
public static Collection determineMergeCoords(Map map) {
Set retval=new HashSet<>();
if(map != null) {
for(View view : map.values()) {
Address coord=view.getCreator();
if(coord != null)
retval.add(coord);
}
}
return retval;
}
/**
* Similar to {@link #determineMergeCoords(java.util.Map)} but only actual coordinators are counted: an actual
* coord is when the sender of a view is the first member of that view
*/
public static Collection determineActualMergeCoords(Map map) {
Set retval=new HashSet<>();
if(map != null) {
for(Map.Entry entry : map.entrySet()) {
Address sender=entry.getKey();
List members=entry.getValue().getMembers();
Address actual_coord=members.isEmpty()? null : members.get(0);
if(sender.equals(actual_coord))
retval.add(sender);
}
}
return retval;
}
/**
* Returns the rank of a member in a given view
* @param view The view
* @param addr The address of a member
* @return A value between 1 and view.size(). The first member has rank 1, the second 2 and so on. If the
* member is not found, 0 is returned
*/
public static int getRank(View view,Address addr) {
if(view == null || addr == null)
return 0;
List members=view.getMembers();
for(int i=0; i < members.size(); i++) {
Address mbr=members.get(i);
if(mbr.equals(addr))
return i + 1;
}
return 0;
}
public static int getRank(Collection members,Address addr) {
if(members == null || addr == null)
return -1;
int index=0;
for(Iterator it=members.iterator(); it.hasNext(); ) {
Address mbr=it.next();
if(mbr.equals(addr))
return index + 1;
index++;
}
return -1;
}
public static T pickRandomElement(List list) {
if(list == null || list.isEmpty()) return null;
int size=list.size();
int index=(int)Util.random(size)-1;
return list.get(index);
}
public static T pickRandomElement(Collection set) {
if(set == null || set.isEmpty()) return null;
int size=set.size();
int random=(int)Util.random(size)-1;
for(Iterator it=set.iterator(); it.hasNext();) {
T el=it.next();
if(random-- <= 0)
return el;
}
return null;
}
public static T pickRandomElement(T[] array) {
if(array == null) return null;
int size=array.length;
int index=(int)Util.random(size)-1;
return array[index];
}
public static T pickNext(List list,T obj) {
if(list == null || obj == null)
return null;
int size=list.size();
for(int i=0; i < size; i++) {
T tmp=list.get(i);
if(Objects.equals(tmp, obj))
return list.get((i + 1) % size);
}
return null;
}
/** Returns the next min(N,list.size()) elements after obj */
public static List pickNext(List list,T obj,int num) {
List retval=new ArrayList<>();
if(list == null || list.size() < 2)
return retval;
int index=list.indexOf(obj);
if(index != -1) {
for(int i=1; i <= num && i < list.size(); i++) {
T tmp=list.get((index + i) % list.size());
if(!retval.contains(tmp))
retval.add(tmp);
}
}
return retval;
}
/** Returns the element before el. If el is the first element, returns the last element. Returns null
* if array.length < 2 */
public static T pickPrevious(T[] array, T el) {
if(array == null || el == null || array.length < 2)
return null;
for(int i=0; i < array.length; i++) {
if(Objects.equals(el, array[i])) {
int prev_index=i-1;
if(prev_index < 0)
prev_index=array.length-1;
return array[prev_index];
}
}
return null;
}
public static T pickPrevious(List list, T el) {
if(list == null || el == null || list.size() < 2)
return null;
for(int i=0; i < list.size(); i++) {
T tmp=list.get(i);
if(Objects.equals(el, tmp)) {
int prev_index=i-1;
if(prev_index < 0)
prev_index=list.size()-1;
return list.get(prev_index);
}
}
return null;
}
public static byte[] generateArray(int length) {
byte[] array=new byte[length];
int index=0, num=1;
while(index + Global.INT_SIZE <= array.length) {
Bits.writeInt(num, array, index);
index+=Global.INT_SIZE;
num++;
}
return array;
}
public static boolean verifyArray(byte[] array) {
int index=0, expected_num=1;
while(index + Global.INT_SIZE <= array.length) {
int actual_num=Bits.readInt(array, index);
assert expected_num == actual_num : String.format("expected %d, but got %d", expected_num, actual_num);
index+=Global.INT_SIZE;
expected_num++;
}
return true;
}
public static boolean verifyByteBuffer(ByteBuffer buf) {
int index=buf.position(), expected_num=1;
while(index + Global.INT_SIZE <= buf.limit()) {
int actual_num=buf.getInt();
assert expected_num == actual_num : String.format("expected %d, but got %d", expected_num, actual_num);
index+=Global.INT_SIZE;
expected_num++;
}
return true;
}
public static Address createRandomAddress() {
return createRandomAddress(generateLocalName());
}
/** Returns an array of num random addresses, named A, B, C etc */
public static Address[] createRandomAddresses(int num) {
return createRandomAddresses(num, false);
}
public static Address[] createRandomAddresses(int num,boolean use_numbers) {
Address[] addresses=new Address[num];
int number=1;
char c='A';
for(int i=0; i < addresses.length; i++)
addresses[i]=Util.createRandomAddress(use_numbers? String.valueOf(number++) : String.valueOf(c++));
return addresses;
}
public static Address createRandomAddress(String name) {
UUID retval=UUID.randomUUID();
NameCache.add(retval,name);
return retval;
}
public static Object[][] createTimer() {
return new Object[][]{
{new TimeScheduler3()},
};
}
/**
* Returns all members that left between 2 views. All members that are element of old_mbrs but not element of
* new_mbrs are returned.
*/
public static List determineLeftMembers(List old_mbrs,List new_mbrs) {
List retval=new ArrayList<>();
if(old_mbrs == null || new_mbrs == null)
return retval;
for(int i=0; i < old_mbrs.size(); i++) {
Address mbr=old_mbrs.get(i);
if(!new_mbrs.contains(mbr))
retval.add(mbr);
}
return retval;
}
public static String print(Collection objs) {
return objs == null? "null" : objs.stream().map(Objects::toString).collect(Collectors.joining(", "));
}
public static String print(Object[] objs) {
if(objs == null || objs.length == 0)
return "";
return objs.length == 1? (objs[0] == null? "" : objs[0].toString()) : Arrays.toString(objs);
}
public static String print(Map map) {
StringBuilder sb=new StringBuilder();
boolean first=true;
for(Map.Entry entry : map.entrySet()) {
if(first)
first=false;
else
sb.append(", ");
sb.append(entry.getKey()).append("=").append(entry.getValue());
}
return sb.toString();
}
/**
* Tries to load the class from the current thread's context class loader. If
* not successful, tries to load the class from the current instance.
* @param classname Desired class.
* @param clazz Class object used to obtain a class loader
* if no context class loader is available.
* @return Class, or null on failure.
*/
public static Class> loadClass(String classname, Class> clazz) throws ClassNotFoundException {
return loadClass(classname, clazz != null? clazz.getClassLoader() : null);
}
/**
* Tries to load the class from the preferred loader. If not successful, tries to
* load the class from the current thread's context class loader or system class loader.
* @param classname Desired class name.
* @param preferredLoader The preferred class loader
* @return the loaded class.
* @throws ClassNotFoundException if the class could not be loaded by any loader
*/
public static Class> loadClass(String classname, ClassLoader preferredLoader) throws ClassNotFoundException {
ClassNotFoundException exception = null;
List list=preferredLoader != null?
Arrays.asList(preferredLoader, Thread.currentThread().getContextClassLoader(), ClassLoader.getSystemClassLoader()) :
Arrays.asList(Thread.currentThread().getContextClassLoader(), ClassLoader.getSystemClassLoader());
for(ClassLoader loader: list) {
try {
return loader.loadClass(classname);
}
catch (ClassNotFoundException e) {
if(exception == null)
exception=e;
}
}
throw exception;
}
public static Class extends Protocol> loadProtocolClass(String protocol_name, Class> cl) throws Exception {
String defaultProtocolName=Global.PREFIX + protocol_name;
try {
return (Class extends Protocol>) Util.loadClass(defaultProtocolName, cl);
}
catch(ClassNotFoundException ignored1) {
try {
return (Class extends Protocol>)Util.loadClass(protocol_name, cl);
} catch (ClassNotFoundException ignored2) {
throw new Exception(String.format(Util.getMessage("ProtocolLoadError"), protocol_name, defaultProtocolName));
}
}
}
@SafeVarargs
public static Field[] getAllDeclaredFieldsWithAnnotations(final Class> clazz, Class extends Annotation>... annotations) {
List list=new ArrayList<>(30);
for(Class> curr=clazz; curr != null; curr=curr.getSuperclass()) {
Field[] fields=curr.getDeclaredFields();
if(fields != null) {
for(Field field: fields) {
if(annotations != null && annotations.length > 0) {
for(Class extends Annotation> annotation : annotations) {
if(field.isAnnotationPresent(annotation))
list.add(field);
}
}
else
list.add(field);
}
}
}
Field[] retval=new Field[list.size()];
for(int i=0; i < list.size(); i++)
retval[i]=list.get(i);
return retval;
}
/**
* Applies a function against all fields and methods of a given target object which satisfy a given predicate.
* @param obj The target object
* @param filter The filter. Needs to be able to handle Fields and Methods (superclass: {@link AccessibleObject}).
* If null, all fields/methods will be selected
* @param field_func The function to be applied to all found fields. No-op if null.
* @param method_func The function to be applied to all found methods. No-op if null.
*/
public static void forAllFieldsAndMethods(Object obj, Predicate super AccessibleObject> filter,
BiConsumer field_func, BiConsumer method_func) {
Objects.requireNonNull(obj, "target object cannot be null");
if(field_func != null) {
Stream.of(Util.getAllDeclaredFieldsWithAnnotations(obj.getClass()))
.filter(f -> filter != null && filter.test(f)).forEach(f -> field_func.accept(f, obj));
}
if(method_func != null) {
Stream.of(Util.getAllDeclaredMethodsWithAnnotations(obj.getClass()))
.filter(m -> filter != null && filter.test(m)).peek(m -> m.setAccessible(true))
.forEach(m -> method_func.accept(m, obj));
}
}
public static String getNameFromAnnotation(AccessibleObject obj) {
ManagedAttribute attr_annotation=obj.getAnnotation(ManagedAttribute.class);
Property prop=obj.getAnnotation(Property.class);
String attr_name=attr_annotation != null? attr_annotation.name() : prop != null? prop.name() : null;
if(attr_name != null && !attr_name.trim().isEmpty())
return attr_name.trim();
else
return ((Member)obj).getName();
}
@SafeVarargs
public static Method[] getAllDeclaredMethodsWithAnnotations(final Class> clazz, Class extends Annotation>... annotations) {
List list=new ArrayList<>(30);
for(Class> curr=clazz; curr != null; curr=curr.getSuperclass()) {
Method[] methods=curr.getDeclaredMethods();
if(methods != null) {
for(Method method : methods) {
if(annotations != null && annotations.length > 0) {
for(Class extends Annotation> annotation : annotations) {
if(method.isAnnotationPresent(annotation))
list.add(method);
}
}
else
list.add(method);
}
}
}
Method[] retval=new Method[list.size()];
for(int i=0; i < list.size(); i++)
retval[i]=list.get(i);
return retval;
}
public static A getAnnotation(Class> clazz, Class annotationClass) {
for(Class> curr=clazz; curr != null; curr=curr.getSuperclass()) {
A ann=curr.getAnnotation(annotationClass);
if(ann != null)
return ann;
}
return null;
}
public static Field getField(final Class> clazz, String field_name) {
try {
return getField(clazz, field_name, false);
}
catch(NoSuchFieldException e) {
return null;
}
}
public static Field getField(final Class> clazz, String field_name, boolean throw_exception) throws NoSuchFieldException {
if(clazz == null || field_name == null)
return null;
Field field=null;
for(Class> curr=clazz; curr != null; curr=curr.getSuperclass()) {
try {
return curr.getDeclaredField(field_name);
}
catch(NoSuchFieldException ignored) {
}
}
if(field == null && throw_exception)
throw new NoSuchFieldException(String.format("%s not found in %s or superclasses", field_name, clazz.getName()));
return field;
}
public static void setField(Field field,Object target,Object value) {
if(!Modifier.isPublic(field.getModifiers())) {
field.setAccessible(true);
}
try {
field.set(target,value);
}
catch(IllegalAccessException iae) {
throw new IllegalArgumentException("Could not set field " + field,iae);
}
}
public static Object getField(Field field,Object target) {
if(!Modifier.isPublic(field.getModifiers())) {
field.setAccessible(true);
}
try {
return field.get(target);
}
catch(IllegalAccessException iae) {
throw new IllegalArgumentException("Could not get field " + field,iae);
}
}
public static Field findField(Object target,List possible_names) {
if(target == null)
return null;
for(Class> clazz=target.getClass(); clazz != null; clazz=clazz.getSuperclass()) {
for(String name : possible_names) {
try {
return clazz.getDeclaredField(name);
}
catch(Exception ignored) {
}
}
}
return null;
}
/** Called by the ProbeHandler impl. All args are strings. Needs to find a method where all parameter
* types are primitive types, so the strings can be converted */
public static Method findMethod(Class> target_class, String method_name, Object[] args) throws Exception {
int len=args != null? args.length : 0;
Method retval=null;
Method[] methods=getAllMethods(target_class);
for(int i=0; i < methods.length; i++) {
Method m=methods[i];
if(m.getName().equals(method_name)) {
Class>[] parameter_types=m.getParameterTypes();
if(parameter_types.length == len) {
retval=m;
// now check if all parameter types are primitive types:
boolean all_primitive=true;
for(Class> parameter_type: parameter_types) {
if(!isPrimitiveType(parameter_type)) {
all_primitive=false;
break;
}
}
if(all_primitive)
return m;
}
}
}
return retval;
}
/**
* The method walks up the class hierarchy and returns all methods of this class
* and those inherited from superclasses and superinterfaces.
*/
public static Method[] getAllMethods(Class> target) {
Class> superclass = target;
Set methods = new HashSet<>();
while(superclass != null) {
try {
Method[] m = superclass.getDeclaredMethods();
Collections.addAll(methods, m);
// find the default methods of all interfaces (https://issues.redhat.com/browse/JGRP-2247)
Class>[] interfaces=superclass.getInterfaces();
if(interfaces != null) {
for(Class> cl: interfaces) {
Method[] tmp=getAllMethods(cl);
if(tmp != null) {
for(Method mm: tmp)
if(mm.isDefault())
methods.add(mm);
}
}
}
superclass = superclass.getSuperclass();
}
catch(SecurityException e) {
// if it runs in an applet context, it won't be able to retrieve methods from superclasses that belong
// to the java VM and it will raise a security exception, so we catch it here.
superclass=null;
}
}
Method[] result = new Method[methods.size()];
int index = 0;
for(Method m: methods)
result[index++]=m;
return result;
}
/**
* Returns the non-null values of all fields of target annotated with @Component
*/
public static List