Swapping a running jar at runtime - java

I am building an update system in which I need to be able to replace a referenced jar of a running application jar at runtime. However, I am running into file locking issues on Windows when trying to perform file utility functions on the jar such as 'setLastModified'.
After some googling I found this snippet...
What I found in my research is that
the standard ClassLoader
implementation never closes a jar
file once it has been opened. It also
only loads resources from the jar file
as needed. So at any particular time,
there may be Classes in the jar file
that have not been loaded into memory.
Obviously, if you delete the jar file
out from under the ClassLoader, and it
tries to find a resource in the
missing file, you're going to get an
IOException at the very least.
Does anyone have any references to information on doing this, or working around this issue?
Thanks.

There is a lot of solutions for classes/lib hot-swapping :
Hotswap for ant
JRebel
OSGi
It depends on what you want to do.

The closest thing is OSGi
Please have a look at the 'See also' section of the mentioned artice for concrete products.

I don't know of any good way to deal with this. Creating a custom classloader might be an option, although I don't like that idea..
We worked around it by running an updater from a separate jar before launching the main application. This works well enough, but obviously, you can only update the jar when the application is launched.

Related

Class-Path setting in executable jar doesn't seem to work for plugins

I've looked here and the wider web to find a solution to this. There's related material, but I've been unable to find anything useful about my specific question.
I'm working on some Java software that needs to accept plugins. I don't want to use a fancy framework like OSGi, and ServiceLoader seems to offer the right level of support. I've basically got it working but am having a problem with classpaths. My directory structure is as follows:
progfolder
|___________ plugintest.jar
|___________/plugins
|________ plugin1.jar
|________ plugin2.jar
If I run plugintest.jar with java -jar plugintest.jar then it doesn't find the plugins even if I add ./plugins/ (or variations of this) to the Class-Path: in the manifest. Reading suggests that this only works for classes, not jars, so I've tried putting the classes from the two plugins inside plugins both directly and within their full path of directories, but with no success.
I'm not allowed to add -cp plugins/* to add the plugins folder to the classpath if I'm using the -jar option. To get round this, I can run using java -cp plugintest.jar;plugins/* com.plugin.test.Main and this works as expected - the two plugins are detected and accessible via code, but the command line is a bit clunky, although I could live with it, if it's the best option.
I found another solution where I create a classloader for jars found in plugins, which works in this simple case, but reading suggests I might run into security issues in a more complex application.
Is there a way to fix things so I can simply run with java -jar plugintest.jar without having to do my own class loading or is this just the way it is?
Ok, so at least a partial answer, following more experimentation. Putting the class files in the plugins directory does work, after all, but you have to remember to include the META-INF directory and META-INF/services. The file in the services directory has to include references to all the plugins.
It would be nice if there was a solution that allowed the plugin jar files to be used directly, but creating a class loader seems to be the only way to do this (that I've found, at least), and this may cause security issues, as previously noted.
last time I faced with similar problem [1]. I found answer in java documentation [2]:
Note: The Class-Path header points to classes or JAR files on the local network, not JAR files within the JAR file or classes accessible over Internet protocols. To load classes in JAR files within a JAR file into the class path, you must write custom code to load those classes. For example, if MyJar.jar contains another JAR file called MyUtils.jar, you cannot use the Class-Path header in MyJar.jar's manifest to load classes in MyUtils.jar into the class path.
[1] https://github.com/narvi-blog/01-exec-jar#dependency-jar-files-within-an-executable-jar-are-not-so-easy
[2] https://docs.oracle.com/javase/tutorial/deployment/jar/downman.html

Loading a jar library that loads libraries

Well, maybe it's convoluted, anyway...
I'm not very experienced with java, though I've dabbled in creating some Minecraft mods and Android apps.
My question is: I have a .jar that contains code that I don't have a lot of control on (I don't have the source code though I do have some infos about the classes in it), and when this jar is run it itself load some code from other external libraries and classes, and consolidate everything.
My question is then, how do I, without touching/modifying the jar, make a java program that runs the jar, let it do its consolidating from other jars and external classes, then get the results (a few objects) of that consolidating into my own java program ? Is that even permitted in the java security model ?
I've heard of URLclassloader that I think is to load load classes from a jar. It can't seem to make it work, and I'm not even sure that would work ? I know roughly which classes are entry point in the jar in order to run it and make it load the external libraries. But I always run into exceptions left and right.
First you should make sure all 3rd partyd dependencies (e.g. libraries that your jar needs) are ~visible~ to your application (e.g. reside on classpath and/or -Djava.library.path). Next, you should just instantiate classes/call methods from your jar file normally and operate with returned objects as its been locally created by your Java application. jar file is just an external library that becomes the part of your application upon loading.

How to access Jar file located within source folders?

in my Java project I am using an H2 in-memory database, for which I have to load the JDBC driver when I initialize my application. I want/need to load the H2 .jar file dynamically, so I do the following:
String classname = "org.h2.Driver";
URL u = new URL("jar:file:libs/h2.jar!/");
URLClassLoader ucl = new URLClassLoader(new URL[] { u });
Driver d = (Driver) Class.forName(classname, true, ucl).newInstance();
DriverManager.registerDriver(new DriverShim(d));
When I put the H2 .jar file into a "libs" folder outside my Java source code folder (that is, in Eclipse, this "libs" directory is on the same level as the "src" folder), then this approach works fine. However, unfortunately I have to put this H2 .jar file into a folder within the source code folder tree, but below the main class folder.
For example, my Java package structure looks like this in Eclipse:
<project>/src/my/app/MyApp.java // main class of my application
<project>/src/my/app/sub/package/h2.jar // how to access this?
<project>/libs/h2.jar // loading from here works
I know this is stupid, but unfortunately I have to work with this strange setup. But what I don't know: how can I edit my Java code (listed above) in order to work with this setup?
EDIT: This has to work outside Eclipse as well, so adding the JAR file to the Java Build Path in Eclipse is no option for me.
EDIT2: I already tried to load "jar:file:my/app/sub/package/h2.jar!/", but that did not work for me.
Thanks in advance for all helpful ideas!
Kind regards, Matthias
In some frameworks referring to files inside JARs can be done using the classpath: prefix. I doubt URLClassLoader supports it natively, but it's worth a try (e.g. classpath:/my/app/sub/package/h2.jar). But since that doesn't work with URLClassLoader, here are other ways:
One way to do it would be to write your own ClassLoader which reads the JAR file from classpath (using getResourceAsStream), uncompresses it (using ZipInputStream) to memory (e.g. a map of byte arrays) and loads the classes from there.
Another, slightly easier way, is to read the JAR file from classpath and write it into a temporary file. Then you can use the plain URLClassLoader to load classes from it. This has the disadvantage that the file must be written to a file and the file probably cannot be removed until the JVM exits (unless using Java 7 or higher).
I'm using the second approach (copying to a temp file) in one project, though I'm using it to launch an external process. I would be curious to hear why you have such a requirement. If it's just a matter of having the whole application in one JAR, there are numerous simpler methods for achieving that (Maven Assembly Plugin, Maven Shade Plugin, Jar Jar Links, One-JAR to name a few).
No it's not a homework, but an online build system that uses my classes under my/app/* and several other classes (not from me) to automatically build the whole solution. Anyway, I can't give you more details on the internals of this system, as I don't know them. As said, I simply have to live with it, and that is why I am asking here...
Sounds like you are working in a WTF environment (does it have a name?), so here are some ways to start hacking around it:
Find out more about your environment, especially absolute file paths of the following: directory where the source files are saved, directory where the generated .class files are saved, and the current working directory when the program is run.
If you can get any kind of output of what your program prints during runtime, you can put into your application some debug code where you use File.listFiles() to crawl the machine's directory trees. If you can get output only from what happens when compiling, it might be possible to execute your own code during compile by creating your own annotation processor (apt is part of javac since Java 6), though I'm not sure whether the annotation processor must be compiled first separately.
The working directory can be read from the user.dir system property and the location of class files can be probably gotten from the java.class.path system property (unless custom class loaders are used). There is no guarantee that a JAR file in the source directory would be copied to the classpath, so you might need to do some looking around.
Then when you know the file path of the JAR file, then you can get an URL to it using new File("path/to/h2.jar").toURI().toURL() which you can then pass to URLClassLoader.
If nothing else works, upload the source code of the libraries and compile them together with your project.
In the long run, try to replace the WTF build environment with one that uses a standard build tool (such as Maven) and a common CI server (such as Jenkins). It's normal for projects to have lots of library dependencies, so you shouldn't need to hack around a build environment to use them.

What method/class etc. uses this .jar?

I have a big application as an enterprise scale, there are lots of developers working on it; it is a Java Spring app, we use Eclipse as development environment. We have lots of libs, which means jar files in the lib directory. I can't touch to these jar files so easily, because whenever I change some jars, application may get failed/unstable because of little changes of the jars' new versions. On the other hand, as this application is a big and old project, I can't know what jar is really needed and which line, what function or class uses/needs this jar exactly.
So I have a problem now: As an example naming a-lib-1.0.jar is old jar and a-lib-2.0.jar is new version; I need to use a-lib-2.0.jar but a-lib-1.0.jar is already in lib directory. How can I know that a-lib-1.0.jar is needed or not without testing by deleting it?
And how can I find the line/method which uses this jar?
Please check Tattletale from JBoss.
What you need is a Dependency Walker.
Try this:
http://www.dependency-analyzer.org/
http://depfind.sourceforge.net/

How to load a resource from compound JAR?

I have the following problem:
I am writing an application that uses some of the JARs from the Netbeans Platform. To be exact, I am only using the Netbeans Visual Library for creating some graphs. This can be done without using the Netbeans Platform by extracting 3 JARs from the platform. This is all working well, except for 1 problem.
Some Background
I am using the Java Simple Plugin Framework (JSPF) to handle my plugin management. So I have an application that basically consists of a skeleton framework, and then depending on which plugin JARs it finds, it can do various things, one of which is drawing graphs. The JAR plugin for this functionality has all it's dependant libraries inside. This is done by exporting the JAR as an artifact in IntelliJ, which will unJAR all the dependant libraries and reJAR them inside yours (so everything you need is there).
The Problem
What seems to be happening though, is that when it tries to start use the classes from the embedded libraries, it works fine, but when it needs resources (.png specifically in my case), it complains that it cannot find it.
My Thoughts
The only thing I can think of why it is not working, is that it could be since the plugin JAR is not in a classpath. Could this be it?
Is there anyway to specify a classpath directory in the MANIFEST maybe? Otherwise must I create my own ClassLoader and manually load all the JARs in the plugins directory?
Thank you!
UPDATE:
I have subsequently pinpointed that it is indeed a problem with the classpath. If I place my compound library on the classpath, everything works perfectly. The problem I experience now though is:
If I copy the library to /Library/Java/Home/lib/ext/ it works fine. If I execute the application with java -cp "/path/to/plugins/myLib.jar" -jar Application.jar it does not work.
How can I load all the jars in the plugins directory into my application so the resources inside them can be used?
Thanks again!
So I have finally figured out what was happening. When creating a executable jar, the MANIFEST.MF file overrides any classpath you specify in the command-line, which basically renders it useless if you want to specify external jars. This seems to be a general problem that has been logged since Java 1.3 already.
My simple solution is to simply not create a executable jar, and then launch the application with a script:
java -cp App.jar:plugins/* my.package.structure.App
which works perfectly.
The default classloader's do not load classes in nested jars. You'll need to write your own classloader to get the classes in the nested jars.
You can check out this jspf article...
"I forgot: Adding dependencies as JARs inside JARs is not possible, because it would not work in all scenarios (e.g., applets); IIRC also tools like Eclipse would have problems if you used classes with unresolved (read: runtime-resolved-dependencies). To my knowledge there is no established way yet to gracefully handle nested JARs in all circumstances."
http://code.google.com/p/jspf/wiki/UsageGuide

Categories

Resources