org.jgroups.conf.ClassConfigurator 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.conf;
import org.jgroups.Constructable;
import org.jgroups.Global;
import org.jgroups.Header;
import org.jgroups.util.IntHashMap;
import org.jgroups.util.Triple;
import org.jgroups.util.Util;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.function.Supplier;
/**
* Maintains a mapping between magic IDs and classes (defined in jg-magic-map.xml), and between protocol IDs and
* protocol classes (defined in jg-protocol-ids.xml). The first mapping is used to for fast serialization, whereas
* the second is used to assign protocol IDs to protocols at startup time.
*
* @author Filip Hanik
* @author Bela Ban
*/
public class ClassConfigurator {
public static final String MAGIC_NUMBER_FILE = "jg-magic-map.xml";
public static final String PROTOCOL_ID_FILE = "jg-protocol-ids.xml";
protected static final String CLASS = ",Short> classMap=new IdentityHashMap<>(MAX_MAGIC_VALUE);
// Magic map for all values defined in jg-magic-map.xml; elements are supplier functions which create instances
private static final Supplier extends Object>[] magicMap=new Supplier[MAX_MAGIC_VALUE];
// Magic map for user-defined IDs / classes or suppliers
private static final Map magicMapUser=new HashMap<>(); // key=magic number, value=Class or Supplier
/** Contains data read from jg-protocol-ids.xml */
private static final Map,Short> protocol_ids=new IdentityHashMap<>(MAX_PROT_ID_VALUE);
private static final IntHashMap> protocol_names=new IntHashMap<>(MAX_PROT_ID_VALUE);
static {
try {
init();
}
catch(Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public ClassConfigurator() {
}
public static void addIfAbsent(short magic, Class> clazz) {
if(!magicMapUser.containsKey(magic) && !classMap.containsKey(clazz))
add(magic, clazz);
}
/**
* Method to register a user-defined header with jg-magic-map at runtime
* @param magic The magic number. Needs to be > 1024
* @param clazz The class. Usually a subclass of Header
* @throws IllegalArgumentException If the magic number is already taken, or the magic number is <= 1024
*/
public static void add(short magic, Class> clazz) {
if(magic < MIN_CUSTOM_MAGIC_NUMBER)
throw new IllegalArgumentException("magic ID (" + magic + ") must be >= " + MIN_CUSTOM_MAGIC_NUMBER);
if(magicMapUser.containsKey(magic) || classMap.containsKey(clazz))
alreadyInMagicMap(magic, clazz.getName());
Object inst=null;
try {
inst=clazz.getDeclaredConstructor().newInstance();
}
catch(Exception e) {
throw new IllegalStateException("failed creating instance " + clazz, e);
}
Object val=clazz;
if(Header.class.isAssignableFrom(clazz)) { // class is a header
checkSameId((Header)inst, magic);
val=((Header)inst).create();
}
if(Constructable.class.isAssignableFrom(clazz)) {
val=((Constructable>)inst).create();
inst=((Supplier>)val).get();
if(!inst.getClass().equals(clazz))
throw new IllegalStateException(String.format("%s.create() returned the wrong class: %s\n",
clazz.getSimpleName(), inst.getClass().getSimpleName()));
}
magicMapUser.put(magic, val);
classMap.put(clazz, magic);
}
public static void addProtocol(short id, Class> protocol) {
if(id < MIN_CUSTOM_PROTOCOL_ID)
throw new IllegalArgumentException("protocol ID (" + id + ") needs to be greater than or equal to " + MIN_CUSTOM_PROTOCOL_ID);
if(protocol_ids.containsKey(protocol))
alreadyInProtocolsMap(id, protocol.getName());
protocol_ids.put(protocol, id);
protocol_names.put(id, protocol);
}
public static T create(short id) throws ClassNotFoundException {
if(id >= MIN_CUSTOM_MAGIC_NUMBER) {
Object val=magicMapUser.get(id);
if(val == null)
throw new ClassNotFoundException("Class for magic number " + id + " cannot be found, map: "+ magicMapUser);
if (val instanceof Supplier) {
return ((Supplier) val).get();
}
try {
return ((Class) val).getDeclaredConstructor().newInstance();
}
catch (ReflectiveOperationException e) {
throw new IllegalStateException(e);
}
}
if(id < 0 || id > MAX_MAGIC_VALUE)
throw new IllegalArgumentException(String.format("invalid magic number %d; needs to be in range [0..%d]",
id, MAX_MAGIC_VALUE));
Supplier> supplier=magicMap[id];
if(supplier == null)
throw new ClassNotFoundException("Class for magic number " + id + " cannot be found");
return (T)supplier.get();
}
/**
* Loads and returns the class from the class name
*
* @param clazzname a fully classified class name to be loaded
* @return a Class object that represents a class that implements java.io.Externalizable
*/
public static Class> get(String clazzname, ClassLoader loader) throws ClassNotFoundException {
return Util.loadClass(clazzname, loader != null? loader : ClassConfigurator.class.getClassLoader());
}
public static Class> get(String clazzname) throws ClassNotFoundException {
return Util.loadClass(clazzname, ClassConfigurator.class);
}
/**
* Returns the magic number for the class.
*
* @param clazz a class object that we want the magic number for
* @return the magic number for a class, -1 if no mapping is available
*/
public static short getMagicNumber(Class> clazz) {
Short i=classMap.get(clazz);
if(i == null)
return -1;
else
return i;
}
public static short getProtocolId(Class> protocol) {
Short retval=protocol_ids.get(protocol);
return retval != null? retval : 0;
}
public static Class> getProtocol(short id) {
return protocol_names.get(id);
}
public static String printClassMap() {
StringBuilder sb=new StringBuilder();
for(Iterator,Short>> it=classMap.entrySet().iterator(); it.hasNext();) {
Map.Entry,Short> entry=it.next();
sb.append(entry.getKey()).append(": ").append(entry.getValue()).append('\n');
}
return sb.toString();
}
protected static void init() throws Exception {
String magic_number_file=null, protocol_id_file=null;
try { // PropertyPermission not granted if running in an untrusted environment with JNLP
magic_number_file=Util.getProperty(new String[]{Global.MAGIC_NUMBER_FILE, "org.jgroups.conf.magicNumberFile"},
null, null, MAGIC_NUMBER_FILE);
protocol_id_file=Util.getProperty(new String[]{Global.PROTOCOL_ID_FILE, "org.jgroups.conf.protocolIDFile"},
null, null, PROTOCOL_ID_FILE);
}
catch (SecurityException ignored) {
}
// Read jg-magic-map.xml
List> mapping=readMappings(magic_number_file);
for(Triple tuple: mapping) {
short m=tuple.getVal1();
if(m >= MAX_MAGIC_VALUE)
throw new IllegalArgumentException("ID " + m + " is bigger than MAX_MAGIC_VALUE (" +
MAX_MAGIC_VALUE + "); increase MAX_MAGIC_VALUE");
boolean external=tuple.getVal3();
if(external) {
if(magicMap[m] != null)
alreadyInMagicMap(m, tuple.getVal2());
continue;
}
Class> clazz=Util.loadClass(tuple.getVal2(), ClassConfigurator.class);
if(magicMap[m] != null)
alreadyInMagicMap(m, clazz.getName());
if(Constructable.class.isAssignableFrom(clazz)) {
Constructable> obj=(Constructable>)clazz.getDeclaredConstructor().newInstance();
magicMap[m]=obj.create();
}
else {
Supplier extends Object> supplier=(Supplier