NoSuchMethodException when Dynamically Added Jar's to Classpath - java

I'm trying to dynamically add Jar's to my programs classpath at run time using THIS method i found since it seems like it worked for a lot of people. When using addPlugin() it throws a NoSuchMethodException (commented in the code below).
Can someone please tell me what I'm missing in order to get this working? I'm not too familiar with this and I've tried looking it up before.
public final class PluginLoader {
private static final Class[] _PARAMS = new Class[] {URL.class};
public static void addPlugin(File plugin) throws PluginException {
URLClassLoader plLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
Class plClass = URLClassLoader.class;
try {
Method m = plClass.getDeclaredMethod("addPlugin", _PARAMS); //ERROR HERE
m.setAccessible(true);
m.invoke(plLoader, new Object[] {plugin.toURI().toURL()});
} catch (Exception ex) {
ex.printStackTrace();
throw new PluginException("ERROR: Could not add plugin '" + plugin.getName() + "' to System ClassLoader");
}
}
}
Usage:
PluginLoader.addPlugin(new File("../path/to/jar.jar"));
Constructor<?> cs = ClassLoader.getSystemClassLoader().loadClass("my.main.class.Main").getConstructor(String.class);

Change:
Method m = plClass.getDeclaredMethod("addPlugin", _PARAMS);
to:
Method m = plClass.getDeclaredMethod("addURL", _PARAMS);

Related

Load resource bundle at runtime

Here is what I would like to achieve. We have an application that is running as a servlet on an IBM Domino server.
The application uses resource bundle to get translated messages and labels according to the browser language.
We want to enable customers to override some of the values.
We cannot modify the bundle_lang.properties files in the .jar at runtime.
So the idea was to provide additional bundleCustom_lang.properties files along with the .jar
This bundle could be loaded at runtime using
private static void addToClassPath(String s) throws Exception {
File file = new File(s);
URLClassLoader cl = (URLClassLoader) ClassLoader.getSystemClassLoader();
java.lang.reflect.Method m = URLClassLoader.class.getDeclaredMethod("addURL", new Class[] { URL.class });
m.setAccessible(true);
m.invoke(cl, new Object[] { file.toURI().toURL() });
}
So far, so good, this works in Eclipse. Here I had the bundleCustom files in a directory outside the workspace ( /volumes/DATA/Temp/ )
Once the addition ResourceBundle is available, We check this bundle for the key first. If it returns a value than this value is being used for the translation. If no value is returned, or the file does not exist, the value from the bundle inside the .jar is used.
My full code is here
public class BundleTest2 {
static final String CUSTOM_BUNDLE_PATH = "/volumes/DATA/Temp/";
static final String CUSTOM_BUNDLE_MODIFIER = "Custom";
public static void main(String[] args) {
try {
addToClassPath(CUSTOM_BUNDLE_PATH);
System.out.println(_getTranslation("LabelBundle", "OutlineUsersAllVIP"));
} catch (Exception e) {
}
}
private static String _getTranslation(String bundle, String translation) {
return _getTranslation0(bundle, new Locale("de"), translation);
}
private static String _getTranslation0(String bundle, Locale locale, String key) {
String s = null;
try {
try {
ResourceBundle custom = ResourceBundle.getBundle(bundle + CUSTOM_BUNDLE_MODIFIER, locale);
if (custom.containsKey(key)) {
s = custom.getString(key);
}
} catch (MissingResourceException re) {
System.out.println("CANNOT FIND CUSTOM RESOURCE BUNDLE: " + bundle + CUSTOM_BUNDLE_MODIFIER);
}
if (null == s || "".equals(s)) {
s = ResourceBundle.getBundle(bundle, locale).getString(key);
}
} catch (Exception e) {
}
return s;
}
private static void addToClassPath(String s) throws Exception {
File file = new File(s);
URLClassLoader cl = (URLClassLoader) ClassLoader.getSystemClassLoader();
java.lang.reflect.Method m = URLClassLoader.class.getDeclaredMethod("addURL", new Class[] { URL.class });
m.setAccessible(true);
m.invoke(cl, new Object[] { file.toURI().toURL() });
}
}
When I try the same from inside the servlet, I get a MissingResourceException.
I also tried to put the .properties files into a customization.jar and provide the full path ( incl. the .jar ) when invoking addToClassPath().
Apparently, the customization.jar is loaded ( it is locked in the file system ), but I still get the MissingResourceException.
We already use the same code in addToClassPath to load a Db2 driver and this is working as expected.
What am I missing?
Why don't you use Database to store the overriden translations? Persisting something crated by client in the local deployment of application is generally not a good idea, what will happen if you redeploy the app, will these resources be deleted? What if you have to run another node of your app, how will you replicate the custom properties file?

fail using a variable to call a method in java-android

I've tried using a variable to invoke a java method, using method.invoke(), as suggested in this example. But it seems there should be an object or something as a parameter in method.invoke(). I've tried using null, but the method didn't get invoked. My code is as follows:
String ACTION = "cart";
Method method = SolverService.class.getDeclaredMethod("Method" + ACTION);
method.invoke(null);
I've got a method as:
public void Methodcart(){
Toast.makeText(this,"Method called",Toast.LENGTH_LONG).show();
}
PS: I HAVE TO make this method.invoke() work. Otherwise, I need to write a very long list of switch-case statements.I've gone through the documentation but couldn't understand much about the object instance i might need to use here as I'm new to android app developing.
You can try something similar to the code shown below (Java Reflection) -
Suppose I have a class ClassWithMethods.java with the methods I want to invoke in some other class as shown below -
public class ClassWithMethods {
private int counter;
public void printIt(){
System.out.println("printIt() no param");
}
public void printItString(String temp){
System.out.println("printIt() with param String : " + temp);
}
}
Now I also have another class TestApp.java which will invoke methods of the ClassWithMethods class at runtime using Java Reflection -
public class TestApp {
public static void main(String[] args) {
//no paramater
Class noparams[] = {};
//String parameter
Class[] paramString = new Class[1];
paramString[0] = String.class;
//int parameter
Class[] paramInt = new Class[1];
paramInt[0] = Integer.TYPE;
try{
//load the ClassWithMethods at runtime
Class cls = Class.forName("com.myapps.ClassWithMethods");
Object obj = cls.newInstance();
//call the printIt method
Method method = cls.getDeclaredMethod("printIt", noparams);
method.invoke(obj, null);
//call the printItString method, pass a String param
method = cls.getDeclaredMethod("printItString", paramString);
method.invoke(obj, new String("someString"));
}catch(Exception ex){
ex.printStackTrace();
}
}
I am using Java Reflection in my current project (since you mentioned you are using Android Studio) to get Battery Capacity of device from PowerProfile class which is internal to the Android OS.
public double getBatteryCapacity() {
Object mPowerProfile = null;
try {
mPowerProfile = Class.forName("com.android.internal.os.PowerProfile")
.getConstructor(Context.class)
.newInstance(getContext());
} catch (Exception e) {
e.printStackTrace();
}
try {
// get access to method named "getAveragePower()" in the class "PowerProfile"
Method getAveragePower = Class.forName("com.android.internal.os.PowerProfile").getMethod("getAveragePower", String.class);
//Get total battery capacity in mAh.
double batteryCapacity = (Double) getAveragePower.invoke(mPowerProfile, "battery.capacity");
return batteryCapacity;
} catch (Exception e) {
e.printStackTrace();
}
return 0.0;
}
Here is a screenshot of how the actual method structure looks like in the PowerProfile class -

Intercepting ClassLoader.getResource(String) calls, with a custom ClassLoader

We're trying to debug an unreproducible issue with WebStart, where access to resources inside Jars will "randomly" fail. Maybe one every 1000 application run will end with this error, which can happen anywhere where resources are read from a jar.
Searching in Google and the Java Bug database brought nothing similar (or at least, nothing helpful).
We are trying to get more info into what happens on the client by "instrumenting" the application so we track all calls to ClassLoader.getResource(String) (including indirectly over ClassLoader.getResourceAsStream(String)). Without changing the app code, I have created a "launcher" that would run the whole app with a custom classloader.
Unfortunately, it seems my ClassLoader is somehow bypassed. I do not see any of the expected System.out output. Here is what I tried:
private static final class MyClassLoader extends ClassLoader {
private MyClassLoader() {
super(TheClassThatMainIsIn.class.getClassLoader());
}
#Override
public URL getResource(String name) {
System.out.println("getResource("+name+")");
// Snip
return super.getResource(name);
}
#Override
public InputStream getResourceAsStream(String name) {
System.out.println("getResourceAsStream("+name+")");
final URL url = getResource(name);
try {
return url != null ? url.openStream() : null;
} catch (final IOException e) {
return null;
}
}
}
public static void main(String[] args) {
System.out.println("Starting MyRealApp Launcher ...");
final MyClassLoader loader = new MyClassLoader();
try {
Class<?> realAppClasss = loader.loadClass("MyRealAppClass");
Method main = realAppClasss.getMethod("main", String[].class);
main.invoke(null, (Object) args);
} catch (final RuntimeException e) {
throw e;
} catch (final Error e) {
throw e;
} catch (final InvocationTargetException e) {
final Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new UndeclaredThrowableException(cause);
} catch (final Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
What am I doing wrong here?
Yes. This works, in principal.
However, you've to account how the resource loading code get's to the class loader. Since the class don't show up, it looks like they use the parents class loader.
You've to account different scenarios:
Code using context class loader, like:
Thread.currentThread().getContextClassLoader().getResource("via-context");
This is easy to achieve, by setting it before calling into main:
Thread.currentThread().setContextClassLoader(loader);
Method main = realAppClasss.getMethod("main", String[].class);
main.invoke(null, (Object) args);
Next thing you've to account is code which 'takes' class loader from current class, and load it that. When you're class is loaded via the parent class loader, it will also use that class loader to get the resource. Like:
MyRealAppClass.class.getResource("via-class");
MyRealAppClass.class.getClassLoader().getResource("via-class");
objectInfApp.getClass().getClassLoader().getResource("via-class");
To avoid that you've to ensure that the apps classes are actually loaded with your class loader, not the parent. For a simple main, you can extend from the URL class loader, skip any parent and user the original class path for the URL's. Like:
// URL class loader to lookup in jars etc
private static class MyClassLoader extends URLClassLoader
{
public MyClassLoader(URL[] urls) {
// Use the given URLs and skip any parent class loader, directly go to the system loader
super(urls,null);
}
// ...
// Then setup the class path
String[] classPath = System.getProperty("java.class.path").split(";");
URL[] classPathUrls = new URL[classPath.length];
for (int i = 0; i < classPath.length; i++) {
classPathUrls[i] = new File(classPath[i]).toURL();
}
MyClassLoader loader = new MyClassLoader(classPathUrls);
This should cover the most basic cases. When you're actual application itself has more class loader trickery, there might more you need to setup.

Custom Class Loader in java different behaviour

I am working on a project which requires dynamic Loading of a class from a file system, I googled and found out that I need to use custom ClassLoader. I have implemented my own class loader which is working fine when I run it on console, the problem I when I try to deploy the application on the server it results in ClassNotFoundException.
The problem is the class which I am trying to load contain some references to another class which is already loaded by the application but it is trying to load the reference from the same location.
public class HandlerClassLoader extends ClassLoader {
static Logger log = Logger.getLogger(HandlerClassLoader.class.getName());
URLClassLoader ucl;
private ProcessDefinition processDefinition = null;
boolean flag = false;
public URL[] loadJars() throws MalformedURLException {
PropertiesFiles propFiles = new PropertiesFiles();
File f = new File(propFiles.getValue("jarslocation"));
log.debug("Loading JARS files from " + f.getAbsolutePath());
List<URL> urls = new ArrayList<URL>();
String filesName[] = f.list();
for (String jars : filesName)
if (jars.endsWith("jar")) {
urls.add(new URL("file:///"
+ propFiles.getValue("jarslocation") + "/" + jars));
}
URL[] array = urls.toArray(new URL[urls.size()]);
return array;
}
public HandlerClassLoader() {
super(Thread.currentThread().getContextClassLoader());
log.debug("Called to the " + this.getClass().getName()
+ " No Parameter Constructor");
try {
ucl = new URLClassLoader(loadJars());
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
public HandlerClassLoader(ClassLoader parent,
ProcessDefinition processDefinition) {
super(parent);
log.debug("Called to the " + this.getClass().getName()
+ " Parameterized Constructor");
try {
ucl = new URLClassLoader(loadJars());
} catch (MalformedURLException e) {
e.printStackTrace();
}
this.processDefinition = processDefinition;
}
public Class<?> findClass(String className) throws ClassNotFoundException {
log.debug("findClass method of " + this.getClass().getName()
+ " is called with className : " + className);
return ucl.loadClass(className);
}
I think the delegation principle is not working or maybe that server must have a different implementation of classloader.
Most likely, you do not delegate to the correct parent. I assume that picking up Thread.currentThread().getContextClassLoader() results in using the wrong class loader on the application server while this is simply the system class loader (class path) when running from a console app.
Therefore, you need to make sure that the parent that you are handing to your custom class loader is capable of seeing the classes you are intending to load by it.
On a final note, implementing your own class loader is a tricky business. For example, you have not accounted for locating ressources or for defining packages. Both might be required by third-party libraries that you are using. If you really only need to load files from disk, consider using a URLClassLoader.

Load a class from a jar

I´m trying to load a class from a jar, I´m using a classLoader.
I have this parts of code for prepare the classLoader:
private void loadClass(){
try{
JarFile jarFile = new JarFile( Path);
Enumeration e = jarFile.entries();
URL[] urls = { new URL("jar:file:" + Path +"!/") };
classLoader = URLClassLoader.newInstance(urls);
} catch (MalformedURLException ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Now I load a class, and I try to get a new instance
....
loadClass();
Class device = classLoader.loadClass( "org.myPackage.MyClass");
MyMotherClass Device = ( MyMotherClass) device.newInstance();
...
MyClass extends of MyMotherClass, and when I do classLoader.loadClass( "org.myPackage.MyClass"), the MyMotherClass it is in the classLoader.
At the moment, all right.
now, in device.newInstance(), I get a exception. The problem is the other classes that are used by MyClass, are not in the classpath.
What can i do?
I have a another method that load all the needed classes in the classLoader, but does not work when I get the new instance.
I can not change MyClass and the others
Here's some code I use to load a jar dynamically at run-time. I exploit reflection to circumvent the fact that you ain't really supposed to do this (that is, modify the class path after the JVM has started). Just change my.proprietary.exception to something sensible.
/*
* Adds the supplied library to java.class.path.
* This is benign if the library is already loaded.
*/
public static synchronized void loadLibrary(java.io.File jar) throws my.proprietary.exception
{
try {
/*We are using reflection here to circumvent encapsulation; addURL is not public*/
java.net.URLClassLoader loader = (java.net.URLClassLoader)ClassLoader.getSystemClassLoader();
java.net.URL url = jar.toURI().toURL();
/*Disallow if already loaded*/
for (java.net.URL it : java.util.Arrays.asList(loader.getURLs())){
if (it.equals(url)){
return;
}
}
java.lang.reflect.Method method = java.net.URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{java.net.URL.class});
method.setAccessible(true); /*promote the method to public access*/
method.invoke(loader, new Object[]{url});
} catch (final NoSuchMethodException |
java.lang.IllegalAccessException |
java.net.MalformedURLException |
java.lang.reflect.InvocationTargetException e){
throw new my.proprietary.exception(e.getMessage());
}
}

Categories

Resources