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());
}
}
Related
I have been getting my hands dirty with Java reflection and have come across a bit of a hurdle. If I try to call the following method via reflection I get a java.lang.NoSuchMethodException: org.demonking.CrossHandler.HandleRequest(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) thrown.
Here is the code for the method:
public void HandleRequest(HttpServletRequest req,HttpServletResponse resp)
{
try{
System.out.println("i am here");
//RequestDispatcher view = req.getRequestDispatcher("CrossFile.jsp");
// don't add your web-app name to the path
// view.forward(req, resp);
}catch(Exception ex)
{
System.out.println("in exception");
}
}
and here is the reflection code:
try {
Method m=cls.getMethod("HandleRequest", HttpServletRequest.class,HttpServletResponse.class); // error on this line
//Object obj=cls.newInstance();
//m.invoke(obj,new Object[]{req,resp});
} catch (Exception ex) {
ex.printStackTrace();
System.out.println(ex.getCause());
}
Note that other methods called via reflection works just fine.
here is how i am loading the class via classloader.Also note that the reflection code is placed in a servlet class's doGet method of a web app where as the class loaded and method being called reside in a different non-web application.Also the same class's other method are working just fine via reflection only this method which has httpservletrequest and response parameters is not working.
File file = new File("C:\\Users\\Demonking\\Documents\\eclipseprojects\\ADemoOne\\bin\\");
URL url = file.toURI().toURL();
URL[] urls = new URL[] { url};
ClassLoader cl = new URLClassLoader(urls);
Class<?> cls = cl.loadClass("org.demonking.CrossHandler");
try {
Method m=cls.getMethod("HandleRequest", HttpServletRequest.class,HttpServletResponse.class);
Object obj=cls.newInstance();
m.invoke(obj,new Object[]{req,resp});
} catch (Exception ex)
{
ex.printStackTrace();
System.out.println(ex.getCause());
}
Actually i am having a spring main class as follows.
ClassLoader loader = null;
try {
loader = URLClassLoader.newInstance(new URL[]{new
File(plugins + "/" + pluginName + "/" + pluginName +
".jar").toURI().toURL()}, getClass().getClassLoader());
} catch (MalformedURLException e) {
e.printStackTrace();
}
Class<?> clazz = null;
try {
clazz = Class.forName("com.sample.Specific", true, loader);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method method = null;
try {
method = clazz.getMethod("run",new Class[]{});
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
method.invoke(clazz.newinstance,new Object[]{});
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Specific Class is follow :
package com.sample
#Service
public class Specific {
#Autowired
private FD fd;
public void run(){
fd.init();
}
}
#Autowired FD comes to be null. Can anyone give me some solution as i also know new operator will not work for #autowired. As i am loading class with new instance then only it becomes null. Can anyone guide me in this thing
Spring has its own way to provide you new objects. As long as you're consistent using #Autowired and #Component/#Service/#Repository/#Controller there should be no problem
And since all "business" object instantiation is handled by Spring you should never use new. If you have no other way of getting an instance (something I realy doubt about it) you can use ApplicationContext.getBean() but as I said, in most cases this is not required (and this is also a bad practice)
If you need several instances of a class instead of injecting them (by using #Autowired) you can inject a Provider<T>
UPDATE
Since the class is known at runtime you need to inject an ApplicationContext and use it to get the bean:
public class TheClassWhereYouAreCreatingTheObject {
#Autowired
private ApplicationContext context; // You definitely need this
public void theMethodWhereYouAreCreatingTheObject() {
Class<?> clazz = ... // getting the object class
Object instance = context.getBean(clazz); // getting and instance trough Spring
// If you know that kind of object you will get cast it at call its methods
((Specific) instance).run();
// If you know anything about the class you will have to use reflection
Method method = clazz.getMethod("run", new Class[]{});
method.invoke(instance, new Object[]{});
}
}
Add Specific Service bean inside your main class. As long as the service is inside one your component scan packages then you shall be fine. Do not use new operator.
#Autowired
private Specific specific;
If you want to take advantage of autowiring then I think we have to think from spring terms.
you can use Beanutils to create a new instance and play with reflections supporting spring features.
Please go through below methods:
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html
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.
To clarify: I am writing a plugin framework for my application. Both my application and the plugin are jar files. Currently, I use a script to merge the plugin files into the application jar, and the plugins end up in the package: com.crimson.server.plugins. I need to be able to access the plugin files, but I cant do hardcoded packages. For instance: com.crimson.server.plugins.nst.Plugin. The only other thing I can think of is to leave the plugins as jars and load them somehow. How could this be done?
You can use the reflection API. It is used to describe code which is able to inspect other code in the same system (or itself).
Here is an example how to create an instance of a class from a string.
import com.stakoverflow.plugin.MyPluginClass;
public class Main {
public static void main(String[] args) {
String myclass = "com.stakoverflow.plugin.MyPluginClass";
try {
Class clazz = Class.forName(myclass);
MyPluginClass myPlugin = (MyPluginClass) clazz.newInstance();
myPlugin.helloWorld();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
here is the definition of MyPluginClass:
package com.stakoverflow.plugin;
public class MyPluginClass {
public void helloWorld() {
System.out.println("Hello world!");
}
}
Of course in this example your class must be in the class path. Otherwise the classloader won't be able to find it. But it's possible to load dynamicaly a jar.
As #JEY mentioned you should use reflection API or existing module framework, such as OSGi.
In case of reflection, the classes you are interested in are ClassLoader and its descendants (URLClassLoader, which can load classes from jars). After you have a class loader you can use Class.forName(name, true, classLoader) to load classes and have access to them.
In case of OSGi you do so more declaratively, with Spring OSGi or use API directly.
I'm using a util class (http://pastie.org/private/mxvpdrs3y2xutbjdo68a#11,14,17) to iterate over all classes inside a package.
The problem is that getResources("my/package") returns an empty Enumeration even though getResource("my/package/MyClass.class") gives the correct URL for all containing classes ("jar:file:/C:/Users/---/Desktop/Server/plugins/Test.jar!/my/package/MyClass.class").
Usage (in my.test.Reader):
List<Class<?>> classes = new ArrayList<Class<?>>();
try {
classes = new ClassScanner("my.package").scan();
} catch (ClassNotFoundException | IOException ex) {
ex.printStackTrace();
}
Debugging (in my.scan.ClassScanner): http://pastie.org/private/dhahvghtitqeiv6jex15q
Regards,
squibs