4

I want my java application to be able to automatically keep itself up to date, i already made all the code to download the latest jar file and put it in the designated path but since my program has to be open to actually check if there are updates available and then update them it gives me this error:

Exception in thread "main" java.nio.file.FileSystemException: name.jar: The process cannot access the file because it is being used by another process

Now my question is not why i get this error since it is quite obvious why, the question is: how would i go about actually updating the .jar file succesfully since it has to be open to actually download the update? I'd rather not make another .jar to act like a standalone updater if there are other options.

Example of the code i'm using to test:

    URL url = new URL("<working url to the .jar file>");
    InputStream in = url.openStream();
    Files.copy(in, Paths.get("app.jar"), StandardCopyOption.REPLACE_EXISTING);
    in.close();
Robin Withes
  • 61
  • 1
  • 6
  • 2
    This technique is called hot-swapping - there’s a useful rundown of the current solutions on [DZone](https://dzone.com/articles/hot-swap-java-bytecode-on-runtime) – MTCoster Feb 13 '19 at 14:16
  • 1
    This seems to be answered by https://stackoverflow.com/questions/60764/how-should-i-load-jars-dynamically-at-runtime. – togise Feb 13 '19 at 14:18
  • I am not looking to add a jar at runtime though, i am looking for a solution to overwrite the original one with a new one while it is running. – Robin Withes Feb 13 '19 at 14:24

2 Answers2

3

First of all: the other answer is correct, there are ways to silently update/restart JAR files. And downloading new JARs, with a full restart, that is fine.

But the question asks about updating a JAR "in use", and for that I have a distinct non-answer: you are going down the very wrong rabbit hole!

Even if you somehow hack your way into overriding the JAR file on the file system while one (or more!) JVMs that have (or have not) have loaded classes from that JAR are running, the result is not what you would expect it to be: replacing the JAR file content doesn't magically reload all classes into a running JVM. The classes are already loaded! Changing their "load origin" in the file system does not affect "running" classes.

If at all, you enable situations such as:

  • class A is loaded from Jar X (version N)
  • Jar X is updated
  • class B is loaded from Jar X (version N+m)

But snap, that class B expects class A to look differently. And all of a sudden you have a versioning conflict within that JVM instance. Because it loaded some classes from X (version N), and other classes from X (version N+1, or N+5 because that customer skipped downloads for 2 weeks). And it gets worse: the exact error scenario might totally depend on the workload that your JVM has seen so far, and how it is used after the update. One customer might not have any issues, and the other might crash within 5 minutes (crashing at some point is very likely though).

Long story short: don't do this. Instead:

  • either tell your users that they have to restart the application when updates came in
  • or look into the "real" JVM hotswapping mechanism. But honestly: this is more of a debug feature, you do that in a development environment, for example using a tool like JRebel. You do not want to use it in a production environment at your customer. Because as said, runtime versioning issues are bad.
GhostCat
  • 137,827
  • 25
  • 176
  • 248
1

First of all it is close to impossible to replace a running application, without restarting it, but there are techniques to transition fluidly. Here's a popular one (simplistic):

  • You launch application.exe, which checks version and finds that there's a new version.
  • application.exe launches updater.exe and closes itself (or waits, until updated version is downloaded and then closes itself.
  • updater.exe replaces the file and launches application.exe again.

So this part is a staple (replacing the core application), without some sort of hardcode memory hacks, to my knowledge.

If you don't need to update the actual core application and are willing to invest time into developing a dynamic library/asset management in your application, you can essentially unload a library or an asset, update it from application.exe and then reload it when it has finished updating, without needing to restart the application.

This might be the thing you are looking for, because if your application.exe is just a loader, and core logic of the application is 1 of the libraries, you can replace essentially any part of the application. You still have to "restart" that part of the application, but you can save and restore important data before restart and restore it after to make the transition somewhat fast and painless.

It's will most likely be time consuming to learn and implement.

Here's an answer with some insight into the second portion of the answer.

PS: I used ".exe" as a reference everywhere. Just imagine it's about jars, same principles apply.

Zero
  • 1,562
  • 1
  • 13
  • 29
  • Thank you, i had a feeling i would have to do it this way but i just wanted to rule out the other ways first. – Robin Withes Feb 13 '19 at 14:52
  • Yep. Just look at Minecraft, it too has a native launcher to update and boot the Java version. Its best to have software update at startup and not while it is running in any case :) The latter just sounds like a bad user experience waiting to happen. – Gimby Feb 13 '19 at 15:19
  • @Gimby It depends on application, Many MMOs implement in-game downloads or "game streaming" where the core of the game is downloaded (it's tiny in comparison) and assets are being downloaded in the background. Guild Wars 2 does the same thing, but when you try to enter a map that is not downloaded/updated it gives priority to assets of that map and tells you when that map has been downloaded/updated. It's the most user friendly approach to updates I've ever seen, but I'm sure it took a lot of time to design. – Zero Feb 14 '19 at 06:47