2

I'm building an application with Spring Boot. I packaged it as an executable jar. I'd like to deploy this app using Java Web Start. I wrote a JNLP file, but when I'm trying to run it, I get he following Exception :

java.lang.IllegalStateException: Unable to determine code source archive from \\localhost\printpoc.jar
at org.springframework.boot.loader.Launcher.createArchive(Launcher.java:126)
at org.springframework.boot.loader.ExecutableArchiveLauncher.<init>(ExecutableArchiveLauncher.java:38)
at org.springframework.boot.loader.JarLauncher.<init>(JarLauncher.java:35)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.sun.javaws.Launcher.executeApplication(Unknown Source)
at com.sun.javaws.Launcher.executeMainClass(Unknown Source)
at com.sun.javaws.Launcher.doLaunchApp(Unknown Source)
at com.sun.javaws.Launcher.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

Here is my JNLP file :

<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0+" codebase="http://localhost/" href="printpoc.jnlp">
<information>
    <vendor>Me</vendor>
    <homepage href="http://localhost/" />
    <description>PrintPoc</description>
</information>
<security>
    <all-permissions/>
</security>
<resources>
    <j2se version="1.8+" />
    <jar href="printpoc.jar" />
</resources>
<application-desc main-class="org.springframework.boot.loader.JarLauncher" />

I wanted to try that, but the layout MODULE doesn't existe anymore. I tried with ZIP, NONE etc, still no success. Here is my pom.xml :

        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <layout>JAR</layout>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

Coud you please help me ?

11OClock
  • 65
  • 6

1 Answers1

1

This looks to me like a Spring bug. From the source:

ProtectionDomain protectionDomain = getClass().getProtectionDomain();
CodeSource codeSource = protectionDomain.getCodeSource();
URI location = (codeSource != null) ? codeSource.getLocation().toURI() : null;
String path = (location != null) ? location.getSchemeSpecificPart() : null;

if (path == null) {
    throw new IllegalStateException("Unable to determine code source archive");
}

File root = new File(path);
if (!root.exists()) {
    throw new IllegalStateException(
            "Unable to determine code source archive from " + root);
}

The problem is this line:

String path = (location != null) ? location.getSchemeSpecificPart() : null;

From the URI documentation:

At the highest level a URI reference (hereinafter simply "URI") in string form has the syntax

[scheme:]scheme-specific-part[#fragment]

So, in the URI http://localhost/printpoc.jnlp, the scheme-specific part is //localhost/printpoc.jnlp. Spring then tries to treat that as a file name, which of course does not exist, which is why you are seeing the exception you’re seeing.

The Spring code is incorrect. It wrongly assumes you can make a file name out of any URI. Only file: URIs can be safely converted to file names, and that is correctly done with new File(location) or Paths.get(location).

I’m not sure what the solution is. It appears the Spring Boot launcher is assuming the .jar is always a local file. I doubt it can work with a .jar file obtained via HTTP.

VGR
  • 40,506
  • 4
  • 48
  • 63
  • So I guess reproducing the bahavior of the MODULE layout with a custom LayoutFactory wouldn't work either. What about creating a Jar which contains my Spring boot jar ? The first one would be downloaded and extracted via Java Web Start, then the second one (the spring boot one) would be launched. Does it sound like a doable thing ? – 11OClock Sep 13 '18 at 15:10
  • That might work. Your Web Start program would probably want to do something like `Path jar = Files.createTempFile(null, ".jar"); try (InputStream embeddedJar = getClass().getResourceAsStream("springboot.jar")) { Files.copy(embeddedJar, jar, REPLACE_EXISTING); }`. – VGR Sep 13 '18 at 15:24