Open mauromol opened 7 years ago
Yes, documentation is lacking and sometimes even outdated. I am sorry for that. I really need to work on that.
There exists a plugin de.gliderpilot.jnlp-war. This is not really documented yet. It contains a new implementation of the demo webstart servlet and supports pack200 and even jardiff (although jardiff support is not tested yet).
The easiest way to go is to use the jnlp-war plugin. Your project structure is OK.
In your ria subproject:
apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'de.gliderpilot.jnlp'
[...]
In your war subproject
apply plugin: 'de.gliderpilot.jnlp-war'
repositories {
// needed for the jnlp-servlet
jcenter()
}
jnlpWar {
from project(':ria')
}
What this does is as follows:
createWebstartZip
(because it applies the application plugin and the jnlp plugin automatically does some setup) and a configuration webstartZip
from project(':ria')
does all the heavy lifting (removes the lines <property name="jnlp.versionEnabled"/>
and jnlp.packEnabled
from the jnlp again and sets codbase and href to $$href
and $$name
-- because all that is handled by the servlet). And de.gliderpilot.gradle.jnlp:jnlp-servlet:$jnlpVersion
is automatically added.The final layout of the war file is
META-INF/
META-INF/MANIFEST.MF
WEB-INF/
WEB-INF/lib/
WEB-INF/lib/jnlp-servlet-1.2.2.jar
WEB-INF/lib/javax.servlet-api-3.1.0.jar
WEB-INF/lib/javax.websocket-api-1.0.jar
launch.jnlp
lib/
lib/ria__V1.0.0.jar.pack.gz
The jnlp-war plugin also allows jardiff download, but that is not production ready yet, might not work and needs some further setup:
ria/build.gradle:
apply plugin: 'maven-publish'
publishing {
repositories {
maven {
url "..."
}
}
publications {
mavenJava(MavenPublication) {
artifact webstartDistZip {
classifier "webstart"
}
}
}
}
war/build.gradle:
repositories {
maven {
url "..."
}
}
jnlpWar {
from rootProject
versions {
"1.0.0" "$group:ria:1.0.0:webstart@zip"
}
launchers {
"$version" {
jardiff {
from "1.0.0"
}
}
}
}
The above will also put jardiff files to upgrade from v1.0 to e.g. v1.1 of your application with minimizing the download by using the jardiff protocol. The resulting war has the following content:
META-INF/
META-INF/MANIFEST.MF
WEB-INF/
WEB-INF/lib/
WEB-INF/lib/jnlp-servlet-1.2.2.jar
WEB-INF/lib/javax.servlet-api-3.1.0.jar
WEB-INF/lib/javax.websocket-api-1.0.jar
launch.jnlp
lib/
lib/ria__V1.1.0.jar.pack.gz
lib/ria__V1.0.0__V1.1.0.diff.jar.pack.gz
The webstart client does support the jardiff protocoll. If version 1.0.0 has previously been used on the client, it would ask for an upgrade from v1.0.0 to v1.1.0. The servlet would in that case deliver the jardiff. The code is even intelligent enough to only use jardiff, if the jardiff is smaller than the new jar file. It also uses pack200, if the ria project uses pack200. It even checks if the diff.pack200 file is smaller than the diff file without pack200.
But again. The jardiff part of the jnlp-war plugin is not production ready yet -- the rest though is tested heavily by my employer). But I would be happy to have any beta testers for the jardiff part.
And again. Sorry for the lack of documentation. I really need to work on that.
Hi Tobias, thank you very much. I think that some quick documentation like this would be very useful to be included here on GitHub in README files or such.
Just some more questions:
- In war from project(':ria') does all the heavy lifting (removes the lines
and jnlp.packEnabled from the jnlp again and sets codbase and href to $$href and $$name -- because all that is handled by the servlet). And de.gliderpilot.gradle.jnlp:jnlp-servlet:$jnlpVersion is automatically added.
I think you mean $$href
and $$codebase
. Anyway, I was wondering why you remove jnlp.versionEnabled
and jnlp.packEnabled
: may it be because your own servlet just requires the use of versions and pack200 in any case?
from rootProject
Shouldn't the above line in the second example be again the following?
from project(':ria')
And what do you think about my thoughts on the following?
IMHO, the whole block at https://github.com/tschulte/gradle-jnlp-plugin/blob/develop/gradle-jnlp-plugin/src/main/groovy/de/gliderpilot/gradle/jnlp/GradleJnlpPlugin.groovy#L48 should be applied only if the project does not also apply the war plugin.
Yes, I meant $$href
and $$codebase
. And from project(':ria')
. I copied that from our project, and we use rootProject
.
jnlp.versionEnabled
and jnlp.packEnabled
are removed, because they are handled by the servlet instead of the JNLP client. The Feature of letting the client handle them was added in Java 6 or 7, I think. When the versionsEnabled
property is set, the client will automatically request __Vx.y.z.jar
files. When the packEnabled
property is set, the client will in addition automatically request .jar.pack.gz
files. This feature is meant to be used with static web sites.
The jnlp plugin is designed to support hosting the jnlp files on a static site. Which could be a war file without jnlp-servlet.
To also allow jardiff, you have to disable this client-side features again and use the jnlp-servlet. The servlet also allows replacement of href and codebase.
The sample project applying the jnlp plugin and the war plugin was a mistake. I don't think it is wise to do that. The problem is, that if you apply the war plugin, the java plugin is automatically applied as well. I don't know of a way to define if plugin java and not plugin war
. The current line project.plugins.withId('java') {... }
is a way to lazily do somethink if a plugin is applied. The code is run as soon as the plugin is applied. I could check inside with if (plugins.hasPlugin('war') {}
, but that would not work if the project does
apply plugin: 'java'
apply plugin: 'war'
only if the first line was removed. The only reliable way would be to introduce a property in the jnlp extension to disable that. But again, I think it is best to not mix jnlp and war in the same project. The jnlp plugin is best applied to the same project as the application, not the war.
I will try to improve the documentation.
jnlp.versionEnabled
andjnlp.packEnabled
are removed, because they are handled by the servlet instead of the JNLP client. The Feature of letting the client handle them was added in Java 6 or 7, I think. When the versionsEnabled property is set, the client will automatically request __Vx.y.z.jar files. When the packEnabled property is set, the client will in addition automatically request .jar.pack.gz files. This feature is meant to be used with static web sites.
Then I didn't understand this right. I thought those two properties were a mutual convention between the servlet and the client. Sorry for bothering you, but I'd like to be sure I've understood it thoroughly. What is working in my current setup is this:
JnlpDownloadServlet
in usejnlp.versionEnabled
and jnlp.packEnabled
both present in the final JNLP file and set to true
jar
elements in the JNLP file hence containing href
attributes with no __Vx.y.z.jar[.pack.gz]
suffixes BUT containing the version
attributesThis works (I tested with a couple of Java 8 Web Start clients).
Instead, if I set jnlp.versionEnabled
and jnlp.packEnabled
to false
(so they are not present in the final JNLP file) and hence the generated JNLP contains jar
elements with href
values containing __Vx.y.z.jar
suffixes but no version
attributes, the client seems to produce plain requests with URLs like http://baseURL/ria/myjar__V1.0.0.jar and the JnlpDownloadServlet
produces 404 errors. This puzzles me a bit.
Probably I'm just mixing concepts: I must use the JnlpDownloadServlet
XOR use jnlp.versionEnabled
and jnlp.packEnabled
to enable versioning and packing for static websites.
Things however are a bit complicated and mixed up by the fact that to produce the right result for using versioning and Pack200, I do must set jnlp.versionEnabled
and jnlp.packEnabled
to true
in the jnlp
block of the ria project (otherwise JARs are not packed with Pack200 and version
attributes are not produced) BUT then use the jnlp-war
plugin in the war project to strip out the corresponding elements from the generated JNLP file.
Am I understanding it right, now?
The java 7 version of the documentation I linked in https://github.com/tschulte/gradle-jnlp-plugin/issues/43#issuecomment-320340796 is even more clear for this: http://docs.oracle.com/javase/7/docs/technotes/guides/jweb/tools/pack200.html.
I think the JnlpDownloadServlet does just have an issue with files ending in __Vsomething.jar.
I strongly suggest giving the jnlp-war plugin and it's servlet a try. Or are there any features the servlet is missing?
Hi Tobias, is there a way to tell the jnlp-war
plugin to put everything into a subdirectory of the WAR? By default, the JNLP is put on the root of the WAR and the signed JARs in the lib
subfolder of the WAR root. I would like to move both into the /ria
subfolder (so that the JNLP is available at http://host:port/context/ria/launch.jnlp
).
I tried with:
jnlpWar {
from project(':ria')
launchesSpec.into('/ria')
}
but what I see is that instead of putting things into /ria
, they are put into ria-webstart-1.0
(which resembles the name of the webstart distribution ZIP of the ria
project).
No, that is not possible. But why do you need to create one big jar? You can create one ria.war
to have http://host:port/ria/launch.jnlp
and a second war for the rest.
No, that is not possible. But why do you need to create one big jar? You can create one ria.war to have http://host:port/ria/launch.jnlp and a second war for the rest.
I suppose you're thinking of a JEE server scenario (with an EAR and two WARs), but we work with Tomcat, hence we produce just a single WAR.
No, I was not necessarily thinking of an JEE scenario with EARs containing two WARs. It should be possible to have multiple WARs deployed to Tomcat (e.g. https://stackoverflow.com/questions/28908895/deploy-multiple-wars-on-tomcat).
But nonetheless. You might want to create an issue and pull request for such a feature. It should be relatively easy to do so by introducing a new intermediate childSpec of the war task to be used instead of the war task directly. And it must be checked if the defaults for codebase
and href
are still correct.
And it must be checked if the defaults for codebase and href are still correct.
I opened #47. I don't think anything in codebase
and href
defaults should be changed, as all the paths are relative to codebase
and if you let it use $$codebase
the JnlpDownloadServlet
will take care of adding the subfolder name. With my own solution (which does not use the jnlp-war
plugin) I'm currently embedding the JNLP and signed JARs in a subfolder with no issue.
Yes, you are right. The defaults for codebase and href work OK. I just checked.
Is this issue the only thing hindering you to use the jnlp-war plugin and it's servlet?
Right now it may be the only showstopper. However I could not do a complete test yet.
The other problem may be the difficulty to use the jnlp-war
plugin from the plugins.gradle.org (which is my preferred choice, if available).
In my own Gradle multiproject, I ended up with this project structure:
The ria project applies the
application
andjnlp
plugins, defines all the configuration needed to generate the JNLP file and the signed JARs. The war project applies thewar
project and simply decorates the war task:(please note, in my actual project I also add a similar decoration to
eclipseWtpComponent
to include the packed ria application in Eclipse WTP project files, too, but it's not relevant to this reporting).However, in your versionBasedAndPack200EnabledMinimalWebstart example you suggest you may apply the JNLP plugin directly to the war project. Correct me if I'm wrong, but I understand you may specify the JNLP dependencies through the newly defined
jnlp
configuration (while in a project that applies theapplication
plugin dependencies are taken directly from theruntime
configuration). However, since thejnlp
plugin automatically makes thejnlp
configuration extendruntime
, you have to include this:This path would allow to pack a RIA which is not defined in the same Gradle multiproject (the dependency added to the
jnlp
configuration may indeed be a standard external dependency) and move the problem to generate the JNLP and signed JARs to the war project (enabling different packaging of the same RIA application on different web applications). However, I encountered another problem. Event if you redefine thejnlp
configuration to extend from "nothing" ([]
), which in any case sounds like a not-so-elegant requirement, looking at the source code I also see that the whole project thejnlp
plugin is applied to is added to thejnlp
dependencies (see: https://github.com/tschulte/gradle-jnlp-plugin/blob/develop/gradle-jnlp-plugin/src/main/groovy/de/gliderpilot/gradle/jnlp/GradleJnlpPlugin.groovy#L52). This means that the whole war project classpath is added to thejnlp
configuration (through transitive dependencies), which does not sound correct to me and makes me wonder whether making thejnlp
configuration extend theruntime
configuration is indeed needed or not.IMHO, the whole block at https://github.com/tschulte/gradle-jnlp-plugin/blob/develop/gradle-jnlp-plugin/src/main/groovy/de/gliderpilot/gradle/jnlp/GradleJnlpPlugin.groovy#L48 should be applied only if the project does not also apply the war plugin.
What do you think?