I have a class with methods that return a class only if that class exists, calling the method would generate a NoClassDefFoundError caused by ClassNotFoundException
exception if the classpath is not loaded (rightfully so, because it's not mandatory).
How can I still get the methods from Class#getMethods() when some of the methods (or fields) may not be loaded in the classpath? Is it possible to ignore the methods/fields when retrieving them?
Note that using try-catch will prevent the entire process from working at all, so it is NOT the solution!
Here is how I am loading the class:
try {
String name = StringUtil.format("Nifty{0}", (isBungee ? "Bungee" : "Bukkit"));
Reflection main = new Reflection(name, StringUtil.format("net.netcoding.{0}", name.toLowerCase())); // net.netcoding.niftybukkit.NiftyBukkit
Object mainObj = main.invokeMethod("getPlugin", null); // See below
logger = (Logger)main.invokeMethod("getLogger", mainObj);
} catch (Exception ex) { }
Here is the two bits that try to locate the method in Reflection:
public class Reflection {
private static final transient ConcurrentHashMap<Class<?>, Class<?>> CORRESPONDING_TYPES = new ConcurrentHashMap<>();
private final String className;
private final String subPackage;
private final String packagePath;
static {
CORRESPONDING_TYPES.put(Byte.class, byte.class);
CORRESPONDING_TYPES.put(Short.class, short.class);
CORRESPONDING_TYPES.put(Integer.class, int.class);
CORRESPONDING_TYPES.put(Long.class, long.class);
CORRESPONDING_TYPES.put(Character.class, char.class);
CORRESPONDING_TYPES.put(Float.class, float.class);
CORRESPONDING_TYPES.put(Double.class, double.class);
CORRESPONDING_TYPES.put(Boolean.class, boolean.class);
}
public Reflection(String className, String packagePath) {
this(className, "", packagePath);
}
public Reflection(String className, String subPackage, String packagePath) {
this.className = className;
this.subPackage = StringUtil.stripNull(subPackage).replaceAll("\\.$", "").replaceAll("^\\.", "");
this.packagePath = packagePath;
}
public String getClassName() {
return this.className;
}
public String getClassPath() {
return this.getPackagePath() + (StringUtil.notEmpty(this.subPackage) ? "." + this.subPackage : "") + "." + this.getClassName();
}
public Class<?> getClazz() throws Exception {
return Class.forName(this.getClassPath());
}
public Object invokeMethod(String name, Object obj, Object... args) throws Exception {
return this.getMethod(name, toPrimitiveTypeArray(args)).invoke(obj, args);
}
private static Class<?> getPrimitiveType(Class<?> clazz) {
return clazz != null ? CORRESPONDING_TYPES.containsKey(clazz) ? CORRESPONDING_TYPES.get(clazz) : clazz : null;
}
public Method getMethod(String name, Class<?>... paramTypes) throws Exception {
Class<?>[] types = toPrimitiveTypeArray(paramTypes);
// In this example, this.getClazz() simply returns
// a Class of net.netcoding.niftybukkit.NiftyBukkit
// this.getClazz().getMethods() throws the ClassNotFOundException
// I want to still access the methods, even if one of them is not available
for (Method method : this.getClazz().getMethods()) {
Class<?>[] methodTypes = toPrimitiveTypeArray(method.getParameterTypes());
if (method.getName().equals(name) && isEqualsTypeArray(methodTypes, types)) {
method.setAccessible(true);
return method;
}
}
System.out.println(StringUtil.format("The method {0} was not found with parameters {1}!", name, Arrays.asList(types)));
return null;
}