JDK-8279366 : CDS should allow alternative locations for JAR files in classpath
  • Type: Enhancement
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 17
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_10
  • CPU: x86_64
  • Submitted: 2022-01-01
  • Updated: 2022-11-14
  • Resolved: 2022-10-25
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 20
20 b21Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Description
A DESCRIPTION OF THE PROBLEM :
I was experimenting with "JEP 310: Application Class-Data Sharing" and "JEP 350: Dynamic CDS Archives" to improve startup performance of my multi-platform Java Swing Desktop application and command-line tool.

It worked on my development machine. It worked on my build machine. Nothing works after deployment. To my surprise, the AppCDS *.jsa (as opposed to the JDK *.jsa generated via java -Xshare:dump) hardcodes the machine-specific absolute file paths (in this case, my build machine) into my *.jsa file, making the *.jsa file not portable (e.g. my app jar might be located at X:/build/app.jar during build and AppCDS *.jsa generation, but will be located at C:/Program Files/MyApp/app.jar after installation).

This severely the limits use cases of AppCDS, effectively to docker where you just ship a pre-built machine, or generating the AppCDS *.jsa after deployment, which is not always desirable or possible.


e.g. java usage after application installation:

"C:\Program Files\MyApp\jre\bin\java.exe" -Xshare:on -XX:SharedArchiveFile="C:\Program Files\MyApp\app.jsa" -jar "C:\Program Files\MyApp\app.jar"


Problem:
If the app.jsa file was pre-generated during build (e.g. with -jar "X:\build\app.jsa") then it won't work after deployment, because the JVM will complain about a classpath mismatch (even though it's the same jar, relative to the same application folder / embedded jre folder).


Proposed Solution:
Please make it so that the expected hardcoded classpath is relative to the given -jar app.jar and make it possible to produce *.jsa files that are not machine-specific, 
and don't require the $PWD to be the application program files folder.


Workaround:

During build, cd into the app.jar folder and then call java -XX:ArchiveClassesAtExit -jar app.jar (just the file name, i.e. use relative file path as classpath) to generate the *.jsa to make it portable. However, the caveat here is that it now only works if java is executed with a specific $PWD which is not feasible for command-line tools.
Comments
Changeset: 427f5062 Author: Calvin Cheung <ccheung@openjdk.org> Date: 2022-10-25 17:52:18 +0000 URL: https://git.openjdk.org/jdk/commit/427f50624f9f60bb3502227fd04a04991986329c
25-10-2022

If the program MUST use an absolute path in -classpath, one possible solution is to compute the LCP (longest common prefix) of the classpaths and the paths of the archive(s). E.g., Dump: -cp /a/foo.jar:/a/lib/bar.jar -XX:ArchiveClassesAtExit=/a/cache/app.jsa LCP = /a/ Run: -cp /b/foo.jar:/b/lib/bar.jar -XX:SharedArchiveFile=/b/cache/app.jsa LCP = /b/ At runtime, we first do the existing classpath check. If that fails, we remove the LCP from both the dump time and run time classpaths, and check the remaining parts. E.g., after removal, we have dump (remove "/a/"): -cp foo.jar:lib/bar.jar run (remove "/b/"): -cp foo.jar:lib/bar.jar
10-10-2022

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/10615 Date: 2022-10-07 22:59:09 +0000
09-10-2022

JDK 17 already supports what the user requested. The keys are: - You must specify relative paths in -classpath (see [1] and [2] below) - When deploying the app to a different directory, you must make sure that the timestamp of the JAR files in your classpath are unchanged (see the usage of "cp -a" in [2]) - When deploying the JDK to a different directory, you must make sure that the timestamp of all the files in the JDK are unchanged. (see [3]) - If the timestamps are changed, the JVM will refuse to load the CDS archive (see [4]). # [1] Create HelloWorld.jsa inside ~/tmp/8279366 # ~/tmp/8279366$ ls -l HelloWorld.jar -rw-r--r-- 1 iklam dba 1408 Apr 4 13:19 HelloWorld.jar ~/tmp/8279366$ java -cp HelloWorld.jar -XX:ArchiveClassesAtExit=HelloWorld.jsa HelloWorld Hello World ~/tmp/8279366$ java -cp HelloWorld.jar --show-version -XX:SharedArchiveFile=HelloWorld.jsa HelloWorld java 17 2021-09-14 LTS Java(TM) SE Runtime Environment (build 17+35-LTS-2724) Java HotSpot(TM) 64-Bit Server VM (build 17+35-LTS-2724, mixed mode, sharing) Hello World # [2] Copy the JAR/JSA file into ~/tmp/8279366 (timestamps preserved), HelloWorld.jsa still works # ~/tmp/8279366$ mkdir moved ~/tmp/8279366$ cp -a HelloWorld.jar HelloWorld.jsa moved/ ~/tmp/8279366$ cd moved ~/tmp/8279366/moved$ java -cp HelloWorld.jar --show-version -XX:SharedArchiveFile=HelloWorld.jsa HelloWorld java 17 2021-09-14 LTS Java(TM) SE Runtime Environment (build 17+35-LTS-2724) Java HotSpot(TM) 64-Bit Server VM (build 17+35-LTS-2724, mixed mode, sharing) Hello World ~/tmp/8279366/moved$ java -Xlog:class+path -cp HelloWorld.jar --show-version -XX:SharedArchiveFile=HelloWorld.jsa HelloWorld [0.002s][info][class,path] bootstrap loader class path=/jdk/official/jdk17/lib/modules [0.027s][info][class,path] Expecting BOOT path=/jdk/official/jdk17/lib/modules [0.027s][info][class,path] Expecting -Djava.class.path= [0.027s][info][class,path] checking shared classpath entry: /jdk/official/jdk17/lib/modules [0.027s][info][class,path] ok [0.027s][info][class,path] Expecting BOOT path=/jdk/official/jdk17/lib/modules [0.027s][info][class,path] Expecting -Djava.class.path=HelloWorld.jar [0.027s][info][class,path] checking shared classpath entry: /jdk/official/jdk17/lib/modules [0.027s][info][class,path] ok [0.027s][info][class,path] checking shared classpath entry: HelloWorld.jar [0.027s][info][class,path] ok java 17 2021-09-14 LTS Java(TM) SE Runtime Environment (build 17+35-LTS-2724) Java HotSpot(TM) 64-Bit Server VM (build 17+35-LTS-2724, mixed mode, sharing) Hello World ~/tmp/8279366/moved$ java -Xlog:class+load -cp HelloWorld.jar --show-version -XX:SharedArchiveFile=HelloWorld.jsa HelloWorld | grep HelloWorld [0.072s][info][class,load] HelloWorld source: shared objects file (top) # [3] Copy the JDK to /jdk2/tmp/8279366/moved/copied_jdk (timestamps preserved), HelloWorld.jsa still works: # ~/tmp/8279366/moved$ cp -a /jdk/official/jdk17 copied_jdk ~/tmp/8279366/moved$ ./copied_jdk/bin/java -Xlog:class+path -cp HelloWorld.jar --show-version -XX:SharedArchiveFile=HelloWorld.jsa HelloWorld [0.003s][info][class,path] bootstrap loader class path=/jdk2/tmp/8279366/moved/copied_jdk/lib/modules [0.028s][info][class,path] Expecting BOOT path=/jdk2/tmp/8279366/moved/copied_jdk/lib/modules [0.028s][info][class,path] Expecting -Djava.class.path= [0.028s][info][class,path] checking shared classpath entry: /jdk2/tmp/8279366/moved/copied_jdk/lib/modules [0.028s][info][class,path] ok [0.028s][info][class,path] Expecting BOOT path=/jdk2/tmp/8279366/moved/copied_jdk/lib/modules [0.028s][info][class,path] Expecting -Djava.class.path=HelloWorld.jar [0.028s][info][class,path] checking shared classpath entry: /jdk2/tmp/8279366/moved/copied_jdk/lib/modules [0.028s][info][class,path] ok [0.028s][info][class,path] checking shared classpath entry: HelloWorld.jar [0.028s][info][class,path] ok java 17 2021-09-14 LTS Java(TM) SE Runtime Environment (build 17+35-LTS-2724) Java HotSpot(TM) 64-Bit Server VM (build 17+35-LTS-2724, mixed mode, sharing) Hello World ~/tmp/8279366/moved$ ./copied_jdk/bin/java -Xlog:class+load -cp HelloWorld.jar --show-version -XX:SharedArchiveFile=HelloWorld.jsa HelloWorld | grep HelloWorld [0.070s][info][class,load] HelloWorld source: shared objects file (top) # [4] Copy HelloWorld.jar HelloWorld.jsa (timestamps changed) # ~/tmp/8279366$ mkdir copied ~/tmp/8279366$ cd copied/ ~/tmp/8279366/copied$ cp ../HelloWorld.jar ../HelloWorld.jsa . ~/tmp/8279366/copied$ ls -l ../HelloWorld.jar HelloWorld.jar -rw-r--r-- 1 iklam dba 1408 Apr 4 13:19 ../HelloWorld.jar -rw-r--r-- 1 iklam dba 1408 Apr 4 13:29 HelloWorld.jar ~/tmp/8279366/copied$ java -cp HelloWorld.jar --show-version -XX:SharedArchiveFile=HelloWorld.jsa HelloWorld [0.028s][warning][cds,dynamic] Unable to use shared archive. The top archive failed to load: HelloWorld.jsa java 17 2021-09-14 LTS Java(TM) SE Runtime Environment (build 17+35-LTS-2724) Java HotSpot(TM) 64-Bit Server VM (build 17+35-LTS-2724, mixed mode, sharing) Hello World ~/tmp/8279366/copied$ java -Xlog:class+path -cp HelloWorld.jar --show-version -XX:SharedArchiveFile=HelloWorld.jsa HelloWorld [0.002s][info][class,path] bootstrap loader class path=/jdk/official/jdk17/lib/modules [0.032s][info][class,path] Expecting BOOT path=/jdk/official/jdk17/lib/modules [0.032s][info][class,path] Expecting -Djava.class.path= [0.032s][info][class,path] checking shared classpath entry: /jdk/official/jdk17/lib/modules [0.032s][info][class,path] ok [0.032s][info][class,path] Expecting BOOT path=/jdk/official/jdk17/lib/modules [0.032s][info][class,path] Expecting -Djava.class.path=HelloWorld.jar [0.032s][info][class,path] checking shared classpath entry: /jdk/official/jdk17/lib/modules [0.032s][info][class,path] ok [0.032s][info][class,path] checking shared classpath entry: HelloWorld.jar [0.032s][warning][cds,dynamic] Unable to use shared archive. The top archive failed to load: HelloWorld.jsa java 17 2021-09-14 LTS Java(TM) SE Runtime Environment (build 17+35-LTS-2724) Java HotSpot(TM) 64-Bit Server VM (build 17+35-LTS-2724, mixed mode, sharing) Hello World
04-04-2022

This is an enhancement request, moving it to JDK for further analysis.
03-01-2022