Is it possible to Stop loading library files at runtime in java? - java

Is there a way to stop loading a java library (.jar file) at runtime, if it is on the classpath?

Although I agreee with #Joachim Suer that your question sounds like an XY Problem I's try to assume that you want to load some classes dynamically. To do this you probably want to create your own class loader. You do not even have to implement it. You can use URLClassLoader that is a part of your JDK. Its instance can load classes from locations that are not mentioned in static class path of your jvm (using -cp or -classpath command line option).
In this case you have full control on your class loader. You can start and stop using it at any moment.
But again, all written above is just an assumption. Try to explain what your real problem is.

Related

Java Find the absolute path of dependent jar

I have Jar file which dependency on another project jar. Both are thin jars and are at same location. 1st jar has manifest file which list second jar in its class-path property.
In 1st jar I am launching second jar as a process using ProcesBuilder class in java. To do so I need absolute path of second jar. In 1st jar i have class XClient
If I do XClient.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
i am getting absolute path of 1st jar. Then I can split and add the name of second jar(hard-coded) to build the absolute path
In second jar I have class XServer
If I do
XServer .class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
Its throws exception
I am not sure if I am doing the right approach but my goal is very clear I wanted to get the absolute path to the dependent jar.
Please help
I tried to use the same approach (but used File file=new File(this.getClass().getProtectionDomain().getCodeSource().toUri()) instead of getPath()) but this can fail in different ways:
when the class is inside a jar the File object points to the jar instead the folder the jar is in - so an if(file.isFile()) file=file.getParentFile(); is needed to get the directory instead of the jar file
when the jar file is loaded by something other than the usual URLClassLoader (last time I tried was back in 1.8 - and I only know that since Jigsaw the main classloader can't be cast to an URLClassLoader anymore) this may will return some unspecified result, if at all, so actual behaviour depends on the very system setup - wich can make it difficult to debug when used on a remote system not under your control
UNC paths (Windows shares) are error prone by themselfs - adding another layer on top of it (java) just add a lot of potential other pitfalls you all have to test and debug - wich often ends up you tell the client what to use and how to setup instead of design your code to follow the java principle: "write once, compile once, run everywhere" (btw: this also applies even if you "mount" a network share so you can address it by a local drive letter instead of a remote network path - but this even causes problems when you try to link two machines where one is a clone of the other)
as already mentioned as comment: "it doesn'T work" is not a usefull or meaningfull description - if you get an error message (in this case as you mentioned an exception stacktrace) post it along with the code wich produced it (if accessible)
How I solved my problem? I just ask the user for the directory / file by a swing JFileChooser. Yes, this isn't fool proof and maybe not the best way - but it works as swing still ships with SE JVM (instead of FX).
If you want to find a path use Class.getResource() and let java do the work, pretty much like crypto: don'T do your own.
Aside from all that: Your mentioned "usecase" doesn'T require what you try to do. You said that the server is already in the classpath - so it gets loaded on startup and you can access the XServer class. The easiest way instead of forking another process is to just run it in another thread. If you know wich class has the main (the manifest of the server.jar will tell you) and you can access it in classpath just do something like this:
Thread serverThread=new Thread(new Runnable()
{
public void run()
{
String[] args=Arrays.asList("required", "parameters");
XServer.main(args);
}
});
serverThread.start();
If no paramters required you can just pass an empty String array. As main() should not throw Exceptions (at least no checked ones) no exception should be needed.
Before all those comments are thrown at me: Yes, I am very well aware of possible issues with such approach like classpath issues (same classname in same packagename but different versions) and such it may be more feasible than try to figure out the absolute path and launch a fork / sub process.
Also: Starting another process may require to interact with its streams (provide required input into child process inputstream and read the child process outputstream and errorstream - otherwise the forked process may can "hang" as it waits for the pipelines to get cleared. It's a pain in the but to debug that kind of issue if it's not your own code and you can have a profiler and debugger attached to it to figure out why all just suddenly stopped to work.
If you really want to (I don't think there's any requirement forcing a "you need to") launch your server along with the client do it with a launch script outside of java but with os level stuff.

JVM can't find custom ClassLoader

I have created a custom Java ClassLoader but I can't get the JVM to load it. I'm getting a ClassNotFoundException when I launch the application. The JAR that contains my code is added to the CLASSPATH as an argument to the JVM. Now I am wondering if I have the JAR in the correct place...do ClassLoaders get added here? I know that application code gets added to the CLASSPATH, but one could argue if a custom ClassLoader is application code or internal to the JVM. So, any ideas?
EDIT:
Ok, after thinking about this more, I think I may asking the JVM to do something circular. The ClassLoader I have created is a system class loader. These class loaders are the ones that search CLASSPATH for classes to load...but I have put the class loader itself there! So, if I'm correct, where do custom class loaders go?

Class loading by bootstrapclassloader

Here is what I was asked in interview:
If a new method is added to String class and compiled and put in
rt.jar, then will bootstrap class loader load it?
I answered that it will not get loaded but could not tell why.
Please help me with correct answer and explanation of that.
It will be loaded.
And not just that: it need not even be in rt.jar, if you prepend a jar file to the boot-classpath (see -Xbootclasspath/p:path), then you can even load java.lang.* classes from other jar files.
This of course is a way to violate the security of the JVM, but you need pretty deep access (either write-access to rt.jar or access to the command line parameters of the JVM) and when you have those, you can do a lot more than just replace String.toString().

Is the Java classpath final after JVM startup?

I have read a lot about the Java class loading process lately. Often I came across texts that claimed that it is not possible to add classes to the classpath during runtime and load them without class loader hackery (URLClassLoaders etc.)
As far as I know classes are loaded dynamically. That means their bytecode representation is only loaded and transformed to a java.lang.Class object when needed.
So shouldn't it be possible to add a JAR or *.class file to the classpath after the JVM started and load those classes, provided they haven't been loaded yet? (To be clear: In this case the classpath is simply folder on the filesystem. "Adding a JAR or *.class file" simply means dropping them in this folder.)
And if not, does that mean that the classpath is searched on JVM startup and all fully qualified names of the found classes are cached in an internal "list"?
It would be nice of you if you could point me to some sources in your answers. Preferably the offical SUN documentation: Sun JVM Spec. I have read the spec but could not find anything about the classpath and if it's finalized on JVM startup.
P.s.
This is a theoretical question. I just want to know if it is possible. There is nothing practical I want to achieve. There is just my thirst for knowledge :)
There are two concepts here that are being intermixed: The classpath and the class files in the classpath.
If you point the classpath to a directory, you will generally have no issue adding a file to the directory and having it picked up as part of the classpath. Due to the potential size of all classes in the classpath it isn't really feasible for a modern JVM to load them all at startup. However this is of limited value as it will not include Jar files.
However, changing the classpath itself (which directories, jars, etc. are searched) on a running JVM will depend very much on the implementation. As far as I know, on standard Sun JVMs there is no documented (as in guaranteed to work) method of accomplishing this.
In general, if this is something you need to do (have a dynamic classpath that changes at runtime) then you want to be implementing a ClassLoader, if for no other reason than to be able to throw it away and make a new one that doesn't reference those classes anymore if they need to be unloaded.
However, for a small amount of dynamic loading there are better ways. In Java 1.6 you can specify all the jar files in a directory (*.jar) so you can tell users to put additional libraries in a specified location (although they have to be there at startup).
You also have the option of including a jar file or other location in the classpath even though you don't need it, as a placeholder for someone to put an optional jar or resource file there (such as a log configuration file).
But if you need serious dynamic class loading and especially unloading while the application is running, a Classloader implementation is required.
Since nobody could give my a definite answer nor a link to a corresponding part of the documentation I provide a answer myself. Nevertheless I would like to thank everybody that tried to answer the question.
Short answer:
The classpath is not final upon JVM start.
You actually can put classes in the classpath after the JVM started and they will be loaded.
Long answer:
To answer this question I went after user unknowns suggestion and wrote a little test program.
The basic idea is to have two classes. One is the main class which instantiates the second class. On startup the second class is not on the classpath. After the cli program started it'll prompt you to press enter. Before you press enter you copy the second class on the classpath. After you press enter the second class is instantiated. If the classpath would be final on JVM startup this would throw an Exception. But it doesn't. So I assume the classpath is not final on JVM startup.
Here are the source codes:
JVMTest.java
package jvmtest;
import java.io.Console;
import jvmtest.MyClass;
public class JVMTest {
public static void main(String[] args) {
System.out.println("JVMTest started ...");
Console c = System.console();
String enter = c.readLine("Press Enter to proceed");
MyClass myClass = new MyClass();
System.out.println("Bye Bye");
}
}
MyClass.java
package jvmtest;
public class MyClass {
public MyClass() {
System.out.println("MyClass v2");
}
}
The folder structure looks like this:
jvmtest/
JVMTest.class
MyClass.class
I started the cli program with this command:
> java -cp /tmp/ jvmtest.JVMTest
As you can see I had my jvmtest folder in /tmp/jvmtest. You obviously have to change this according to where you put the classes.
So here are the steps I performed:
Make sure only JVMTest.class is in jvmtest.
Start the program with the command from above.
Just to be sure press enter. You should see an Exception telling you that no class could be found.
Now start the program again.
After the program started and you are prompted to press enter copy the MyClass file into the jvmtest folder.
Press enter. You should see "MyClass v1".
Additional notes:
This also worked when I packed the MyClass class in a jar and run the test above.
I ran this on my Macbook Pro running Mac OS X 10.6.3
> Java -version
results in:
java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02-279-10M3065)
Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01-279, mixed mode)
#Jen I don't think your experiment can prove your theory, because it is more about object instantiation: your printline happens when an object of this class is instantiated, but not necessarily telling that JVM knows your code, the class, just when it is instantiating.
My opinion is that all Java classes are loaded when JVM is up, and it is possible to plug in more classes into JVM while it is running: this technique is called: Hot deployment.
Bottom line: it is possible to add entries to the system classpath at runtime, and is shown how. This, however, has irreversible side-effects and relies on Sun JVM implementation details.
Class path is final, in the most literal sense:
The system class loader (the one that loads from the main class path) is sun.misc.Launcher$AppClassLoader in rt.jar.
rt.jar:sun/misc/Launcher.class (sources are generated with Java Decompiler):
public class Launcher
{
<...>
static class AppClassLoader
extends URLClassLoader
{
final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
<...>
rt.jar:sun/misc/URLClassLoader.class:
protected Class<?> findClass(final String paramString)
throws ClassNotFoundException
{
<...>
String str = paramString.replace('.', '/').concat(".class");
Resource localResource = URLClassLoader.this.ucp.getResource(str, false);
<...>
But, even if the field is final, this doesn't mean we can't mutate the object itself if we somehow get access to it. The field is without an access modifier - which means, we can access it if only we make the call from the same package. (the following is IPython with JPype; the commands are readable enough to easily derive their Java counterparts)
#jpype doesn't automatically add top-level packages except `java' and `javax'
In [28]: jpype.sun=jpype._jpackage.JPackage("sun")
In [32]: jpype.sun.misc.Launcher
Out[32]: jpype._jclass.sun.misc.Launcher
In [35]: jpype.sun.misc.Launcher.getLauncher().getClassLoader()
Out[35]: <jpype._jclass.sun.misc.Launcher$AppClassLoader at 0x19e23b0>
In [36]: acl=_
In [37]: acl.ucp
Out[37]: <jpype._jclass.sun.misc.URLClassPath at 0x19e2c90>
In [48]: [u.toString() for u in acl.ucp.getURLs()]
Out[48]: [u'file:/C:/Documents%20and%20Settings/User/']
Now, URLClassPath has a public addURL method. Let's try it out and see what happens:
#normally, this is done with Launcher.getFileURL but we can't call it directly
#public static URLClassPath.pathToURLs also does the same, but it returns an array
In [72]: jpype.sun.net.www.ParseUtil.fileToEncodedURL(
jpype.java.io.File(r"c:\Ivan\downloads\dom4j-2.0.0-RC1.jar")
.getCanonicalFile())
Out[72]: <jpype._jclass.java.net.URL at 0x1a04b50>
In [73]: _.toString()
Out[73]: u'file:/C:/Ivan/downloads/dom4j-2.0.0-RC1.jar'
In [74]: acl.ucp.addURL(_72)
In [75]: [u.toString() for u in acl.ucp.getURLs()]
Out[75]:
[u'file:/C:/Documents%20and%20Settings/User/',
u'file:/C:/Ivan/downloads/dom4j-2.0.0-RC1.jar']
Now, let's try to load some class from the .jar:
In [78]: jpype.org=jpype._jpackage.JPackage("org")
In [79]: jpype.org.dom4j.Entity
Out[79]: jpype._jclass.org.dom4j.Entity
Success!
This will probably fail from a sandbox or such where there are custom class loaders or security settings in the way (AppClassLoader.loadClass does security checks before calling super).
Further code inspection shows that addURL also disables the URLClassPath's lookup cache (implemented in a few native methods), and this is irreversible. Initially, the lookupCacheEnabled flag is set to the value of the sun.cds.enableSharedLookupCache system property.
The interface provides no way to edit the entries. URLs are added to URLClassPath's private ArrayList path and Stack urls. urls is accessible, but it turns out, it only holds entries temporarily, before it's attempted to load from it, at which point the information moves to HashMap lmap and ArrayList loaders. getURLs() returns a copy of path. So, it's theoretically possible to edit it by hacking the accessible fields, but it's nowhere near reliable and won't affect getURLs result.
I can only comment from what I remember of my own experience of hacking a non-sun JVM ten years ago, but it did scan the entire classpath on startup, as an efficiency measure. The names of all classes found were added to an internal hashtable along with their locations (directory or zip/jar file). However, that was ten years ago and I can't help but wonder whether this would still be a reasonable thing to do in most settings considering how disk and memory architectures have evolved.
I believe that the classpath is taken to be static and the result of changing the files is undefined.
If you really want to be able to add and remove classes at runtime, consider doing so in your own classloader. This is what web containers do.
So shouldn't it be possible to add a
JAR or *.class file to the classpath
after the JVM started ...
You add jars and directories to the classpath, not classes. The classes are in either the directory, or the jar.
And if not, does that mean that the
classpath is searched on JVM startup
and all fully qualified names of the
found classes are cached in an
internal "list"?
That could be easily tested: Set the classpath, start your program, move a new class into the CP, call 'Class.forName ("NewClass") from your program. Does it find the new class?
I think you could read documentation of TomCat server. This server implements java classpapth by its own. So, when this server is started, you can deploy new webApp just drag and drop jar in appropriatefolder in hot, without restart server, and it will upload your app.

java.lang.UnsatisfiedLinkError no *****.dll in java.library.path

How can I load a custom dll file in my web application? I've tried the following:
Copied all required dlls in system32 folder and tried to load one of them in Servlet constructor System.loadLibrary
Copied required dlls into tomcat_home/shared/lib and tomcat_home/common/lib
All these dlls are in WEB-INF/lib of the web-application
In order for System.loadLibrary() to work, the library (on Windows, a DLL) must be in a directory somewhere on your PATH or on a path listed in the java.library.path system property (so you can launch Java like java -Djava.library.path=/path/to/dir).
Additionally, for loadLibrary(), you specify the base name of the library, without the .dll at the end. So, for /path/to/something.dll, you would just use System.loadLibrary("something").
You also need to look at the exact UnsatisfiedLinkError that you are getting. If it says something like:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no foo in java.library.path
then it can't find the foo library (foo.dll) in your PATH or java.library.path. If it says something like:
Exception in thread "main" java.lang.UnsatisfiedLinkError: com.example.program.ClassName.foo()V
then something is wrong with the library itself in the sense that Java is not able to map a native Java function in your application to its actual native counterpart.
To start with, I would put some logging around your System.loadLibrary() call to see if that executes properly. If it throws an exception or is not in a code path that is actually executed, then you will always get the latter type of UnsatisfiedLinkError explained above.
As a sidenote, most people put their loadLibrary() calls into a static initializer block in the class with the native methods, to ensure that it is always executed exactly once:
class Foo {
static {
System.loadLibrary('foo');
}
public Foo() {
}
}
Changing 'java.library.path' variable at runtime is not enough because it is read only once by JVM. You have to reset it like:
System.setProperty("java.library.path", path);
//set sys_paths to null
final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
sysPathsField.setAccessible(true);
sysPathsField.set(null, null);
Please, take a loot at: Changing Java Library Path at Runtime.
The original answer by Adam Batkin will lead you to a solution, but if you redeploy your webapp (without restarting your web container), you should run into the following error:
java.lang.UnsatisfiedLinkError: Native Library "foo" already loaded in another classloader
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1715)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1646)
at java.lang.Runtime.load0(Runtime.java:787)
at java.lang.System.load(System.java:1022)
This happens because the ClassLoader that originally loaded your DLL still references this DLL. However, your webapp is now running with a new ClassLoader, and because the same JVM is running and a JVM won't allow 2 references to the same DLL, you can't reload it. Thus, your webapp can't access the existing DLL and can't load a new one. So.... you're stuck.
Tomcat's ClassLoader documentation outlines why your reloaded webapp runs in a new isolated ClassLoader and how you can work around this limitation (at a very high level).
The solution is to extend Adam Batkin's solution a little:
package awesome;
public class Foo {
static {
System.loadLibrary('foo');
}
// required to work with JDK 6 and JDK 7
public static void main(String[] args) {
}
}
Then placing a jar containing JUST this compiled class into the TOMCAT_HOME/lib folder.
Now, within your webapp, you just have to force Tomcat to reference this class, which can be done as simply as this:
Class.forName("awesome.Foo");
Now your DLL should be loaded in the common classloader, and can be referenced from your webapp even after being redeployed.
Make sense?
A working reference copy can be found on google code, static-dll-bootstrapper .
You can use System.load() to provide an absolute path which is what you want, rather than a file in the standard library folder for the respective OS.
If you want native applications that already exist, use System.loadLibrary(String filename). If you want to provide your own you're probably better with load().
You should also be able to use loadLibrary with the java.library.path set correctly. See ClassLoader.java for implementation source showing both paths being checked (OpenJDK)
In the case where the problem is that System.loadLibrary cannot find the DLL in question, one common misconception (reinforced by Java's error message) is that the system property java.library.path is the answer. If you set the system property java.library.path to the directory where your DLL is located, then System.loadLibrary will indeed find your DLL. However, if your DLL in turn depends on other DLLs, as is often the case, then java.library.path cannot help, because the loading of the dependent DLLs is managed entirely by the operating system, which knows nothing of java.library.path. Thus, it is almost always better to bypass java.library.path and simply add your DLL's directory to LD_LIBRARY_PATH (Linux), DYLD_LIBRARY_PATH (MacOS), or Path (Windows) prior to starting the JVM.
(Note: I am using the term "DLL" in the generic sense of DLL or shared library.)
If you need to load a file that's relative to some directory where you already are (like in the current directory), here's an easy solution:
File f;
if (System.getProperty("sun.arch.data.model").equals("32")) {
// 32-bit JVM
f = new File("mylibfile32.so");
} else {
// 64-bit JVM
f = new File("mylibfile64.so");
}
System.load(f.getAbsolutePath());
For those who are looking for java.lang.UnsatisfiedLinkError: no pdf_java in java.library.path
I was facing same exception; I tried everything and important things to make it work are:
Correct version of pdf lib.jar ( In my case it was wrong version jar kept in server runtime )
Make a folder and keep the pdflib jar in it and add the folder in your PATH variable
It worked with tomcat 6.
If you believe that you added a path of native lib to %PATH%, try testing with:
System.out.println(System.getProperty("java.library.path"))
It should show you actually if your dll is on %PATH%
Restart the IDE Idea, which appeared to work for me after I setup the env variable by adding it to the %PATH%
The issue for me was naming:
The library name should begin with "lib..." such as libnative.dll.
So you might think you need to load "libnative": System.loadLibrary("libnative")
But you actually need to load "native": System.loadLibrary("native")
Poor me ! spent a whole day behind this.Writing it down here if any body replicates this issue.
I was trying to load as Adam suggested but then got caught with AMD64 vs IA 32 exception.If in any case after working as per Adam's(no doubt the best pick) walkthrough,try to have a 64 bit version of latest jre.Make sure your JRE AND JDK are 64 bit and you have correctly added it to your classpath.
My working example goes here:unstatisfied link error
I'm using Mac OS X Yosemite and Netbeans 8.02, I got the same error and the simple solution I have found is like above, this is useful when you need to include native library in the project. So do the next for Netbeans:
1.- Right click on the Project
2.- Properties
3.- Click on RUN
4.- VM Options: java -Djava.library.path="your_path"
5.- for example in my case: java -Djava.library.path=</Users/Lexynux/NetBeansProjects/NAO/libs>
6.- Ok
I hope it could be useful for someone.
The link where I found the solution is here:
java.library.path – What is it and how to use
It is simple just write java -XshowSettings:properties on your command line in windows and then paste all the files in the path shown by the java.library.path.
I had the same problem and the error was due to a rename of the dll.
It could happen that the library name is also written somewhere inside the dll.
When I put back its original name I was able to load using System.loadLibrary
First, you'll want to ensure the directory to your native library is on the java.library.path. See how to do that here. Then, you can call System.loadLibrary(nativeLibraryNameWithoutExtension) - making sure to not include the file extension in the name of your library.

Categories

Resources