Working with libpath with java reflection
I'm dynamically loading a class and calling a method on it. This class does JNI. When I call the class, java attempts to load the library. This causes an error because the library is not on the libpath. I'm calling from instead a jar so I can't easily change the libpath (especially since the library is not in the same directory or a sub directory of the jar). I do know the path of the library, but how can I load it before I load the class.
Current code:
public Class<?> loadClass(String name) throws ClassNotFoundException {
if(!CLASS_NAME.equals(name))
return super.loadClass(name);
try {
URL myUrl = new URL(classFileUrl);
URLConnection connection = myUrl.openConnection();
InputStream input = connection.getInputStream();
byte[] classData = readConnectionToArray(input);
return defineClass(CLASS_NAME,
classData, 0, classData.length);
} catch (MalformedURLException e) {
throw new UndeclaredThrowableException(e);
} catch (IOException e) {
throw new UndeclaredThrowableException(e);
}
}
Exception:
Can't find library libvcommon.so java.lang.UnsatisfiedLinkError: vcommon (A file or directory in the path name does not exist.) at java.lang.ClassLoader.loadLibraryWithPath(ClassLoader.java:998) at java.lang.ClassLoader.loadLibraryWithClassLoader(ClassLoader.java:962) at java.lang.System.loadLibrary(System.java:465) at vcommon.(vcommon.java:103) at java.lang.J9VMInternals.initializeImpl(Native Method) at java.lang.J9VMInternals.initialize(J9VMInternals.java:200) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37) at java.lang.reflect.Method.invoke(Method.java:599) at com.fortune500.fin.v.vunit.reflection.ReflectionvProcessor.calculateV(ReflectionvProcessor.java:36) at com.fortune500.fin.v.vunit.UTLTestCase.execute(UTLTestCase.java:42) at com.fortune500.fin.v.vunit.TestSuite.execute(TestS开发者_高级运维uite.java:15) at com.fortune500.fin.v.vunit.batch.Testvendor.execute(Testvendor.java:101) at com.fortune500.fin.v.vunit.batch.Testvendor.main(Testvendor.java:58)
Edit: I'm having a 64bit vs 32bit issue right now. I'll come back to this when I've sorted that out.
Related: Dynamic loading a class in java with a different package name
If you know the path to the library, you could add the path to the java.library.path
environment variable in your custom class loader. A simpler approach is to compute the path and use it in your call to Runtime.loadLibrary
.
The code below outlines two approaches, using loadLibrary and setting the java.library.path system property.
if(CLASS_NAME.equals(name)) {
// two ways of doing this - either load the library explicitly from the full path
if (useFullPath) {
Runtime.getRuntime().loadLibrary("/full/path/to/mylibrary");
}
else { // or tweaking the library path
System.setProperty("java.library.path",
System.getProperty("java.library.path")
+ System.getProperty("file.separator")
+ "/path/to/lib");
}
}
return super.loadClass(name);
You mention your code is being called from a jar - using a custom class loader is going to be difficult if the ClassLoader is part of the jar as well. Have you verified that your class loader is indeed being used?
A simpler approach is to change the current call to loadLibrary in your native class to use the full path. E.g. fetch from system properties, or compute it, if you know in advance where to find it. This is only an option of course if you have the source to the native class. If you can't modify the native class, then use the loadLibrary call in the classloader.
It's my understanding that calls to load library with the same library name (regardless of path) load the same library. (At least, that's the behaviour on Windows - I haven't verified on Linux.) So, even though the classloader loads the library using the full path, and the native class loads the library using it's simple name, both should resolve to the same library.
(Just for completeness, resolving equivalent libraries happens in the kernel, again, speaking from Win32 experience. Each library internally has a name, and windows only loads one instance of the library with the same internal name per process.)
精彩评论