package org.yamcs.utils; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yamcs.ConfigurationException; import org.yamcs.YConfiguration; import org.yamcs.YamcsServer; public class YObjectLoader<T> { static Logger log=LoggerFactory.getLogger(YamcsServer.class); /** * Loads classes defined in the yamcs server or client configuration properties * @param className * @param args * @return an object of the given class instantiated with the given parameters * @throws ConfigurationException * @throws IOException */ @SuppressWarnings({ "rawtypes", "unchecked" }) static public <T> T loadObject(String className, Object... args) throws ConfigurationException, IOException { try { Class ic = Class.forName(className); Constructor<T> constructor = null; Constructor[] constructors = ic.getConstructors(); for(Constructor c:constructors) { Class<?>[] params = c.getParameterTypes(); if(params.length!=args.length) { continue; } boolean ok=true; for(int i=0;i<params.length;i++) { if(!params[i].isAssignableFrom(args[i].getClass())) { ok=false; break; } } if(ok) { constructor=c; break; } } if(constructor==null){ StringBuilder sb=new StringBuilder(); sb.append("Cannot find a constructor for class '"+className+"' and arguments ("); boolean first=true; for(Object o: args) { if(!first) { sb.append(", "); } else first = false; sb.append(o.getClass().getName()); } sb.append(")"); throw new ConfigurationException(sb.toString()); } else { checkDeprecated(ic); return constructor.newInstance(args); } } catch (InvocationTargetException e) { Throwable t=e.getCause(); if(t instanceof ConfigurationException) { throw (ConfigurationException)t; } else if (t instanceof IOException) { throw (IOException)t; } else if (t instanceof ExceptionInInitializerError) { throw new ConfigurationException("Cannot instantiate object from class " +className+": "+t.getCause(), t.getCause()); } else { throw new ConfigurationException("Cannot instantiate object from class " +className+": "+t, t); } } catch (ConfigurationException e) { throw e; } catch (Exception e) { throw new ConfigurationException("Cannot instantiate object from class "+className+": "+e, e); } } /** * loads an object defined like this: * class: org.yamcs.... * args: * key1: value1 * key2: value2 * * "args" can also be called "config" or can be missing. * The value of args can also be a list or a scalar type. * args can also be called config or spec. * * If args is present, then a constructor with the given type is invoked otherwise the constructor without any argument is invoked. * * * @param conf * @return a new object * @throws IOException * @throws ConfigurationException */ static public <T> T loadObject(Map<String, Object> conf) throws ConfigurationException, IOException { String className = YConfiguration.getString(conf, "class"); Object args = getArgs(conf); if(args!=null) { return loadObject(className, args); } else { return loadObject(className); } } /** * same as the method above but loads a constructor with the firstArg as the first argument * @param conf * @param firstArg * @return a newly created object * @throws ConfigurationException * @throws IOException */ static public <T> T loadObject(Map<String, Object> conf, Object firstArg) throws ConfigurationException, IOException { String className = YConfiguration.getString(conf, "class"); Object args = getArgs(conf); if(args!=null) { return loadObject(className, firstArg, args); } else { return loadObject(className, firstArg); } } /** * same as the method above but loads a constructor with firstArg and secondArg as the first two arguments * @param conf * @param firstArg * @param secondArg * @return a newly created object * @throws ConfigurationException * @throws IOException */ static public <T> T loadObject(Map<String, Object> conf, Object firstArg, Object secondArg) throws ConfigurationException, IOException { String className = YConfiguration.getString(conf, "class"); Object args = getArgs(conf); if(args!=null) { return loadObject(className, firstArg, secondArg, args); } else { return loadObject(className, firstArg, secondArg); } } static private Object getArgs(Map<String, Object> conf) { if(conf.containsKey("config")) { return conf.get("config"); } else if(conf.containsKey("args")) { return conf.get("args"); } else if(conf.containsKey("spec")) { return conf.get("spec"); } else { return null; } } @SuppressWarnings({"rawtypes" }) static private void checkDeprecated(Class objclass) { checkAndPrintDeprecatedWarning("The class "+objclass.getName()+" is deprecated", objclass); Class c = objclass.getSuperclass(); while(c!=null) { checkAndPrintDeprecatedWarning("The class "+c.getName()+" extended by "+objclass.getName()+" is deprecated", c); c = c.getSuperclass(); } for(Class i: objclass.getInterfaces()) { checkAndPrintDeprecatedWarning("The class "+objclass.getName()+" implements interface "+i.getName()+" which is deprecated", i); } } @SuppressWarnings({ "unchecked", "rawtypes" }) static void checkAndPrintDeprecatedWarning(String prefix, Class objclass) { DeprecationInfo di = (DeprecationInfo) objclass.getAnnotation(DeprecationInfo.class); if(di!=null) { log.warn("{}: {}", prefix, di.info()); } else { Annotation a = objclass.getAnnotation(Deprecated.class); if(a!=null) { log.warn("{}. Please check the javadoc for alternatives.", prefix); } } } }