All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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).

There is a newer version: 34.0.0.Final
Show newest version

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[] 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 supplier=(Supplier)() -> { try { return clazz.getDeclaredConstructor().newInstance(); } catch(Throwable throwable) { return null; } }; magicMap[m]=supplier; } Object inst=magicMap[m].get(); if(inst == null) continue; // test to confirm that the Constructable impl returns an instance of the correct type if(!inst.getClass().equals(clazz)) throw new IllegalStateException(String.format("%s.create() returned the wrong class: %s\n", clazz.getSimpleName(), inst.getClass().getSimpleName())); // check that the IDs are the same if(inst instanceof Header) checkSameId((Header)inst, m); classMap.put(clazz, m); } mapping=readMappings(protocol_id_file); // Read jg-protocol-ids.xml for(Triple tuple: mapping) { short m=tuple.getVal1(); boolean external=tuple.getVal3(); if(external) { if(protocol_names.containsKey(m)) alreadyInProtocolsMap(m, tuple.getVal2()); continue; } Class clazz=Util.loadClass(tuple.getVal2(), ClassConfigurator.class); if(protocol_ids.containsKey(clazz)) alreadyInProtocolsMap(m, clazz.getName()); protocol_ids.put(clazz, m); protocol_names.put(m, clazz); } } protected static void checkSameId(Header hdr, short magic) { short tmp_id=hdr.getMagicId(); if(tmp_id != magic) throw new IllegalStateException(String.format("mismatch between %s.getId() (%d) and the defined ID (%d)", hdr.getClass().getSimpleName(), magic, tmp_id)); } protected static void alreadyInMagicMap(short magic, String classname) { throw new IllegalArgumentException("key " + magic + " (" + classname + ')' + " is already in magic map; make sure that all keys are unique"); } protected static void alreadyInProtocolsMap(short prot_id, String classname) { throw new IllegalArgumentException("ID " + prot_id + " (" + classname + ')' + " is already in protocol-ids map; make sure that all protocol IDs are unique"); } /** * try to read the magic number configuration file as a Resource form the classpath using getResourceAsStream * if this fails this method tries to read the configuration file from mMagicNumberFile using a FileInputStream (not in classpath but somewhere else in the disk) * * @return an array of ClassMap objects that where parsed from the file (if found) or an empty array if file not found or had en exception */ protected static List> readMappings(String name) throws Exception { InputStream stream=Util.getResourceAsStream(name, ClassConfigurator.class); // try to load the map from file even if it is not a Resource in the class path if(stream == null) stream=new FileInputStream(name); return parse(stream); } protected static List> parse(InputStream in) throws Exception { List lines=parseLines(in); List> retval=new ArrayList<>(); for(String line: lines) { short id; String name; boolean external=false; int index=line.indexOf(ID); if(index == -1) notFound(ID, line); index+=ID.length()+1; id=Short.parseShort(parseNextString(line, index)); index=line.indexOf(NAME); if(index == -1) notFound(NAME, line); index+=NAME.length()+1; name=parseNextString(line, index); index=line.indexOf(EXTERNAL); if(index >= 0) { index+=EXTERNAL.length()+1; external=Boolean.parseBoolean(parseNextString(line, index)); } Triple t=new Triple<>(id, name, external); retval.add(t); } return retval; } protected static void notFound(String id, String line) { throw new IllegalStateException(String.format("%s not found in line %s", id, line)); } protected static String parseNextString(String line, int index) { int start=line.indexOf("\"", index); if(index == -1) notFound("\"", line.substring(index)); int end=line.indexOf("\"", start+1); if(index == -1) notFound("\"", line.substring(index+1)); return line.substring(start+1, end); } protected static List parseLines(InputStream in) throws IOException { List lines=new LinkedList<>(); for(;;) { String token=Util.readToken(in); if(token == null) break; token=token.trim(); if(token.startsWith(CLASS)) { String line=token + " " + readTillMatchingParens(in); lines.add(line); } } return lines; } protected static String readTillMatchingParens(InputStream in) throws IOException { StringBuilder sb=new StringBuilder(); for(;;) { int ch=in.read(); if(ch == -1) break; sb.append((char)ch); if(ch == '>') break; } return sb.toString(); } }