How do I set a CLASSPATH variable during runtime while using IKVM?
I've been trying to do it by using:
java.lang.System.setProperty("java.class.path", "whatever");
The class I'm calling requires a configuration file in the classpath to work - and I keep getting errors that seem to indicate that it hasn't gotten its settings.
Is the way I'm trying to add variable incorrect?
If you really can't set the classpath beforehand yourself using the java's -cp or -classpath argument (why not by the way? that's the normal approach), then you can try to use URLClassLoader instead. Here's a kickoff example:
URL url = new URL(whateverPath);
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
ClassLoader urlCL = URLClassLoader.newInstance(new URL[] { url }, contextCL);
Thread.currentThread().setContextClassLoader(urlCL);
// ...
You only need to be lucky if the class you're calling is actually loading its resources through Thread.currentThread().getContextClassLoader().getResource() and thus not through
SomeClass.class.getClassLoader().getResource().
I was trying to do the same thing. I had some jar files compiled to a .Net dll but some of those (3rd party) jar files were trying to load their configuration files from the java classpath.
I solved the problem by specifying the -classloader option for the ikvmc tool. Example:
ikvmc -out:mydotnetapp.dll -classloader:ikvm.runtime.ClassPathAssemblyClassLoader c:/myjavaapp/lib/*.jar
This worked for me!
Source for the solution: http://old.nabble.com/Not-able-to-load-files-from-ClassPath-td31141788.html
Related
I have a quick question about altering the build path as the code is running.
For example, I have a class which downloads a .jar file from the internet and then into the same directory as the code is running from. How, if possible, could I load the jar into the build path to access the classes within the .jar file?
Some suggested amendments / comments:
Remove the jar: prefix and the !/ suffix - these are note required and are probably confusing the matter
Can you verify the jar file exists:
System.out.println(new File(new URL("file://test.jar")).exists());
Amend your class declaration to the following (get the File object to generate the URL for you, to avoid problems):
URL[] classes = new URL[] { new File("test.jar").toURI().toURL() };
This worked for my test example, using commons-codec as the jar, and loading the Base64 class
In a Java project, I am using a third-party library that loads some native library via
System.loadLibrary("libName");
I'd like to be able to influence the search path of this method from within my application, so that the user doesn't need to specify a correct java.library.path value on the command line (this value depends on the current OS and architecture). E.g on Windows I want to set it to "lib/native/windows", on Linux 32bit to "lib/native/linux32" etc.
I tried
System.setProperty("java.library.path", ...)
but this is ignored, apparently because the JVM reads this property only once before my code is run.
I also tried to load the native libray before using the Java library that depends on it with
System.load("fullPath/lib")
This call succeeds, but there will still be an UnsatisfiedLinkError when the native library is loaded again with System.loadLibrary().
The only way I found is the following:
Add interfaces that abstract the whole API of the external library.
Use only these interfaces in the rest of the code.
Add classes that implement the interfaces and delegate to the library.
Write an own ClassLoader, that
overwrites findLibary() so that the native library is found in the correct path
overwrites loadClass() and loads all classes of the external library and the wrapper layer by itself instead of trying to delegate to its parent like the default ClassLoader would do
Ensure that the interfaces are loaded with the normal ClassLoader and the wrapping classes and the external library are loaded with my own ClassLoader.
This works, but I find it very complicated and it is much effort because I need to add all those interfaces. Is there a simpler way?
I needed to change the dll path for my unit tests. I tried the following hack and it worked:
System.setProperty( "java.library.path", "/path/to/libs" );
Field fieldSysPath = ClassLoader.class.getDeclaredField( "sys_paths" );
fieldSysPath.setAccessible( true );
fieldSysPath.set( null, null );
For explanation, see the original link.
There is no approved way to change the library path for a running JVM.
You cannot load a native library more than once ... and you cannot unload a native library so that you can load it again: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4171986
Based on your comments above (particularly, the behavior of the 3rd-party library), I'd say that your best option is to get the library path right when you launch the JVM.
Note that there is a hacky way to change the library path (see https://stackoverflow.com/a/24258955/139985) but it involves nasty reflection, and it reportedly doesn't work for all Java releases. Certainly, it relies on undocumented private implementation details of ClassLoader that could change from one release to the next.
Just recently ran into this issue and using OpenJDK where a NullPointerException is thrown (as #0-0 mentioned in his comment to Samil's answer). The following works in OpenJDK and should work with Oracle JDK as well.
(Option 1) Replace the java.library.path
System.setProperty("java.library.path", newPath);
Field field = ClassLoader.class.getDeclaredField("sys_paths");
field.setAccessible(true);
field.set(ClassLoader.getSystemClassLoader(), new String[]{newPath});
(Option 2) Add to existing java.library.path
String libPath = System.getProperty("java.library.path");
String newPath;
if (libPath == null || libPath.isEmpty()) {
newPath = path;
} else {
newPath = path + File.pathSeparator + libPath;
}
System.setProperty("java.library.path", newPath);
Field field = ClassLoader.class.getDeclaredField("sys_paths");
field.setAccessible(true);
// Create override for sys_paths
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
List<String> newSysPaths = new ArrayList<>();
newSysPaths.add(path);
newSysPaths.addAll(Arrays.asList((String[])field.get(classLoader)));
field.set(classLoader, newSysPaths.toArray(new String[newSysPaths.size()]));
I tried to following to load a native Growl library for a Java application on my Mac where the lib is in the root of the classpath of my application:
System.load(GrowlUtils.class.getResource("/libgrowl.jnilib").getFile().toString());
Is there a simpler way?
Yes, provide batch/script files to start the application. Then you can set the correct path in the batch/shell file or even read the value from an environment variable. Much easier then trying to do it from inside the application.
While technically correct these answers are missleading. Setting the environment variables PATH on Windows, or LD_LIBRARY_PATH on unix will change where the jvm looks for libraries:
What is LD_LIBRARY_PATH and how to use it?
on linux:
export LD_LIBRARY_PATH=/usr/.../
then:
java ....
In which case one would use a URLClassLoader to load a class from a specific jar in a specified path?
E.g.
URL url = new URL("file:///path/to/customClasses.jar");
URLClassLoader pluginLoader = new URLClassLoader(new URL[] { url });
Class<?> cl = pluginLoader.loadClass("apackage.MyCustomClass");
If I wanted to use classes from customClasses.jar, I always thought that placing this jar in a path accesible from CLASSPATH is enough.
Then in my code just use apackage.MyCustomClass.
I think I have something missunderstood or missing here, so could someone please explain and give an example of when the above snippet of loading class this way is useful?
Thanks!
I would say that depending on the type of programming you are doing, the use of the URLClassLoader should be a very rare occurrence.
Typically you will use the class loader for loading in classes at runtime that you couldn't anticipate in advance.
A good example is if you build a tool that can be extended with plugins, and the plugins are loaded at runtime. For example, Eclipse.
If you have the jar available at compile time and are on a command line, add the needed jar file to your compile statement. For example,
javac -cp /path/to/lib/customClasses.jar MyClassThatReferencesCustomClasses
If you're using Eclipse, add the jar to your project, and right click on it and select add to build path.
Regards,
Will
The project that I am currently working on utilizes an old application contained within a .jar file. One of the responsibilities of this application is that it updates the database when changes to the configuration files are made. Every time I try to run this file (which is a simple Ant Task extension), I get an exception thrown early in the process. I decompiled the Java file responsible, and found that the exception being thrown happens here. I do not know what the issue is, as "hibernate.cfg.xml" is contained within the same .jar file as the .class throwing the exception.
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream in = loader.getResourceAsStream("hibernate.cfg.xml");
if (in == null) {
throw new RuntimeException("Couldn't find built in hibernate config");
}
If anyone has any ideas, even pointing me in the right direction, I would be grateful.
Of course, any solution will have to be external, as the client already has this build of the program in production use.
Are you loading it from the root dir and you need "/hibernate.cfg.xml" instead of just hibernate.cfg.xml?
getResourceAsStream() expects the file to be in the same package as the Class that was the origin of the ClassLoader. Is your file in the right package?
Doh. Didn't read the question fully. Please ignore this.
try
InputStream in = YourClass.class..getResourceAsStream("hibernate.cfg.xml");
this will work if the class is in the same jar as the cfg file.
edit:
if you can't rebuild the application, you will need to add the jar to the bootstrap classloader. this is very ugly.
you can do it by running the jvm with (play with the exact arguments, you may need to add rt.jar from your jre to it as well).
-Xbootclasspath your.jar
your problem is that the code is using the classloader that loaded the Thread class, which is most likely the bootstrap classloader. and that you are now in a more complex environment (app server?) that loads your jar using a different classloader. so the bootstrap classloader can't find your resource.
It is possible that the classloader cannot open files contained in the jar file. Try one of two things 1) try extracting the jar file and running it from the file system, or 2) if the jar manifest has a ClassPath entry, try extracting the hibernate.cfg.xml into a directory in the classpath and see if it runs.
Apparently the hibernate.cfg.xml file isn't located in the source root of the JAR file, but it is instead placed inside a package. You'll need to specify the package path in the resource name as well.
Let's say I have a structure
-bin/com/abc/A.class
-src/com/abc/A.java
-config/info.txt
How to address the file info.txt from A class?
Should we use "user.dir" property or "config/info.txt" so that it would work ?
I'll compile this into the jar and after that
the jar will be used from the servlet,
but I don't think that's important
cause this file is written and read from
internal jar's methods only.
Just put it in the runtime classpath and use ClassLoader#getResourceAsStream() to get an InputStream of it. Putting it in the JAR file among the classes, or adding its (JAR-relative) path to the Class-Path entry of the JAR's manifest.mf file is more than sufficient.
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream input = classLoader.getResourceAsStream("config/info.txt");
// Do your thing to read it.
Or if you actually want to get it in flavor of a java.io.File, then make use of ClassLoader#getResource(), URL#toURI() and the File constructor taking an URI:
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL url = classLoader.getResource("config/info.txt");
File file = new File(url.toURI());
// Do your thing with it.
Do not use relative paths in java.io stuff. It would be dependent on the current working directory which you have no control over at any way. It's simply receipt for portability trouble. Just make use of the classpath.
That said, are you aware of the java.util.Properties API? It namely look like you're trying to achieve the same thing which is more easy to be done with propertiesfiles.