edvin / fxlauncher

Auto updating launcher for JavaFX Applications
Apache License 2.0
713 stars 110 forks source link

Support for UNC paths #143

Closed TurekBot closed 5 years ago

TurekBot commented 5 years ago

Hi! Thanks so much for creating this lovely way to deploy an application.

I've successfully gotten FXLauncher to pull from a regular old file://C:/some/lovely/path.

What I want to do is get FXLauncher to pull from a Windows UNC path (ex. //server/share/).

Java seems to handle UNC paths a little differently than I expected.

First I tried configuring the applicationUrl with \\myServer\myShare\, but URI.create() doesn't like that one bit and says that the \ character is not allowed.

I did a little searching and found that you can use the forward slash in combination with the file protocol when working with UNC paths; something like file:////myServer/myShare/.

So I tried that and it got further, but still no cigar. At some point along the way the path of the URI is losing a slash going from //myServer/myShare/ to /myServer/myShare. I'm betting this is happening during getFXAppURI() or sometime before when uri.resolve() is called.

Here's the output when I try to run the natively installed app from the command line:

C:\Program Files (x86)\FXLauncherTest\app>java -jar fxlauncher.jar
logging to C:\Users\BRADTU~1\AppData\Local\Temp\\fxlauncher.log
Dec 19, 2018 12:06:41 AM fxlauncher.AbstractLauncher syncManifest
WARNING: Unable to update manifest from file:/atlas/Sundance_Apps/FXLauncherTest/app.xml
javax.xml.bind.DataBindingException: javax.xml.bind.UnmarshalException
 - with linked exception:
[java.io.FileNotFoundException: C:\atlas\Sundance_Apps\FXLauncherTest\app.xml (The system cannot find the path specified)]
        at javax.xml.bind.JAXB.unmarshal(Unknown Source)
        at fxlauncher.FXManifest.load(FXManifest.java:190)
        at fxlauncher.AbstractLauncher.syncManifest(AbstractLauncher.java:223)
        at fxlauncher.AbstractLauncher.updateManifest(AbstractLauncher.java:92)
        at fxlauncher.Launcher.lambda$start$0(Launcher.java:140)
        at java.lang.Thread.run(Unknown Source)
Caused by: javax.xml.bind.UnmarshalException
 - with linked exception:
[java.io.FileNotFoundException: C:\atlas\Sundance_Apps\FXLauncherTest\app.xml (The system cannot find the path specified)]
        at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(Unknown Source)
        at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(Unknown Source)
        at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(Unknown Source)
        ... 6 more
Caused by: java.io.FileNotFoundException: C:\atlas\Sundance_Apps\FXLauncherTest\app.xml (The system cannot find the path specified)
        at java.io.FileInputStream.open0(Native Method)
        at java.io.FileInputStream.open(Unknown Source)
        at java.io.FileInputStream.<init>(Unknown Source)
        at java.io.FileInputStream.<init>(Unknown Source)
        at sun.net.www.protocol.file.FileURLConnection.connect(Unknown Source)
        at sun.net.www.protocol.file.FileURLConnection.getInputStream(Unknown Source)
        at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(Unknown Source)
        at com.sun.org.apache.xerces.internal.impl.XMLVersionDetector.determineDocVersion(Unknown Source)
        at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
        at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
        at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown Source)
        at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(Unknown Source)
        at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
        ... 9 more

Dec 19, 2018 12:06:41 AM fxlauncher.AbstractLauncher syncFiles
INFO: Using cache dir C:\Users\BradTurek\AppData\Local\FXLauncherTest
Dec 19, 2018 12:06:41 AM fxlauncher.Launcher lambda$start$0
WARNING: Error during File Synchronization phase
java.nio.file.NoSuchFileException: \atlas\Sundance_Apps\FXLauncherTest\commons-validator.jar
        at sun.nio.fs.WindowsException.translateToIOException(Unknown Source)
        at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
        at sun.nio.fs.WindowsException.rethrowAsIOException(Unknown Source)
        at sun.nio.fs.WindowsFileSystemProvider.newByteChannel(Unknown Source)
        at java.nio.file.Files.newByteChannel(Unknown Source)
        at java.nio.file.Files.newByteChannel(Unknown Source)
        at java.nio.file.spi.FileSystemProvider.newInputStream(Unknown Source)
        at java.nio.file.Files.newInputStream(Unknown Source)
        at fxlauncher.AbstractLauncher.openDownloadStream(AbstractLauncher.java:147)
        at fxlauncher.AbstractLauncher.syncFiles(AbstractLauncher.java:130)
        at fxlauncher.Launcher.lambda$start$0(Launcher.java:142)
        at java.lang.Thread.run(Unknown Source)

Dec 19, 2018 12:06:41 AM fxlauncher.Launcher$1 reportError
WARNING: Error during Create Application phase
java.lang.ClassNotFoundException: pkg.Main
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at fxlauncher.AbstractLauncher.createApplicationEnvironment(AbstractLauncher.java:170)
        at fxlauncher.Launcher.lambda$start$0(Launcher.java:152)
        at java.lang.Thread.run(Unknown Source)

and here's my fxlauncher.log:

Dec 19, 2018 12:06:41 AM fxlauncher.AbstractLauncher syncManifest
WARNING: Unable to update manifest from file:/atlas/Sundance_Apps/FXLauncherTest/app.xml
javax.xml.bind.DataBindingException: javax.xml.bind.UnmarshalException
 - with linked exception:
[java.io.FileNotFoundException: C:\atlas\Sundance_Apps\FXLauncherTest\app.xml (The system cannot find the path specified)]
    at javax.xml.bind.JAXB.unmarshal(Unknown Source)
    at fxlauncher.FXManifest.load(FXManifest.java:190)
    at fxlauncher.AbstractLauncher.syncManifest(AbstractLauncher.java:223)
    at fxlauncher.AbstractLauncher.updateManifest(AbstractLauncher.java:92)
    at fxlauncher.Launcher.lambda$start$0(Launcher.java:140)
    at java.lang.Thread.run(Unknown Source)
Caused by: javax.xml.bind.UnmarshalException
 - with linked exception:
[java.io.FileNotFoundException: C:\atlas\Sundance_Apps\FXLauncherTest\app.xml (The system cannot find the path specified)]
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(Unknown Source)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(Unknown Source)
    ... 6 more
Caused by: java.io.FileNotFoundException: C:\atlas\Sundance_Apps\FXLauncherTest\app.xml (The system cannot find the path specified)
    at java.io.FileInputStream.open0(Native Method)
    at java.io.FileInputStream.open(Unknown Source)
    at java.io.FileInputStream.<init>(Unknown Source)
    at java.io.FileInputStream.<init>(Unknown Source)
    at sun.net.www.protocol.file.FileURLConnection.connect(Unknown Source)
    at sun.net.www.protocol.file.FileURLConnection.getInputStream(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(Unknown Source)
    at com.sun.org.apache.xerces.internal.impl.XMLVersionDetector.determineDocVersion(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
    ... 9 more

Dec 19, 2018 12:06:41 AM fxlauncher.AbstractLauncher syncFiles
INFO: Using cache dir C:\Users\BradTurek\AppData\Local\FXLauncherTest

I'm using the gradle plugin, version 1.0.20.

Here's the relevant configuration:

fxlauncher {
    applicationVendor 'My Company'
    // Base URL where you will host the application artifacts
    applicationUrl 'file:////atlas/Sundance_Apps/FXLauncherTest'
    applicationMainClass 'pkg.Main'
    acceptDowngrade false
    // Optional scp target for application artifacts hosted at the above url
    deployTarget '//atlas/Sundance_Apps/FXLauncherTest'
    cacheDir "USERLIB/FXLauncherTest"
}
TurekBot commented 5 years ago

Some testing is proving insightful:

URI uri = URI.create("file:////atlas/Sundance_Apps/MyApp/");

results in a URI that looks like this on the inside

uri = {URI@2693} "file:////atlas/Sundance_Apps/MyApp/"
 scheme = "file"
 fragment = null
 authority = null
 userInfo = null
 host = null
 port = -1
 path = "//atlas/Sundance_Apps/MyApp/"
 query = null
 schemeSpecificPart = "////atlas/Sundance_Apps/MyApp/"
 hash = 0
 decodedUserInfo = null
 decodedAuthority = null
 decodedPath = null
 decodedQuery = null
 decodedFragment = null
 decodedSchemeSpecificPart = null
 string = "file:////atlas/Sundance_Apps/MyApp/"

But when you try to use uri.resolve(String)—like is done in various parts of FXLauncher—it breaks UNC paths.

URI resolvedURI = uri.resolve("app.xml");

results in a broken UNC URI

resolvedURI = {URI@2714} "file:/atlas/Sundance_Apps/MyApp/app.xml"
 scheme = "file"
 fragment = null
 authority = null
 userInfo = null
 host = null
 port = -1
 path = "/atlas/Sundance_Apps/MyApp/app.xml"
 query = null
 schemeSpecificPart = null
 hash = 0
 decodedUserInfo = null
 decodedAuthority = null
 decodedPath = null
 decodedQuery = null
 decodedFragment = null
 decodedSchemeSpecificPart = null
 string = "file:/atlas/Sundance_Apps/MyApp/app.xml"
TurekBot commented 5 years ago

Digging into java.net.URI#resolve(java.net.URI) we're told that

this method constructs a new hierarchical URI in a manner consistent with RFC 2396, section 5.2 the new URI's path is computed by resolving the path of the given URI against the path of this URI. This is done by concatenating all but the last segment of this URI's path, if any, with the given URI's path and then normalizing the result as if by invoking the normalize method.

That's when I found the bug: JDK-4723726: URI.normalize() ruins URI built from UNC File

and in the discussion it's mentioned that

Normalization is removing empty path segments and is not aware of the Windows specific convention for UNCs.

They're not going to fix the normalize method; it would break too much existing code. So what I propose is, when it comes to the App URI, we avoid using the resolve method (and hence the normalize method).

Instead, can we not just change the path of the URI to include what we would otherwise resolve?

edvin commented 5 years ago

Sounds good. Will you make a PR for the change?

TurekBot commented 5 years ago

Thanks for all your time in reviewing and merging.

When can we expect the new artifact and what version will it be?

edvin commented 5 years ago

Version 1.0.21 was just released now :)

ohkio commented 5 years ago

I'm still having issues getting UNC to work and noticed that the fxlauncher-gradle-plugin is still on version 1.0.20, would this affect things?

https://search.maven.org/artifact/no.tornado/fxlauncher-gradle-plugin/1.0.20/jar

Any help appreciated.

ohkio commented 5 years ago

I'm having such a hard time. I have a build file that looks like it matches all the requirements. I seem to be running fxlauncher version 1.0.21, and yet I get the same exact error as is listed above.

ohkio commented 5 years ago

I think I found it and made a pull request in the fxlauncher-gradle-plugin project.

https://github.com/edvin/fxlauncher-gradle-plugin/pull/26

edvin commented 5 years ago

Thanks! I will do a new release tonight.