JNLP Applet with custom DownloadServiceListener & Versioning
I encountered a problem, when using a JNLP applet with custom DownloadServiceListener as well as versioned jar files at the same time.
The project structure is as follows:
/
+ WEB-INF/
| + lib/
| | + jnlp-servlet.jar
| + web.xml
+ lib/
| + progress-0.1.jar
| + MyApplet-1.0.jar
| + applet.jnlp
| + version.xml
+ index.jsp
The contents of my web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!-- JNLP download servlet-->
<servlet>
<servlet-name>JnlpDownloadServlet</servlet-name>
<servlet-class>jnlp.sample.servlet.JnlpDownloadServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JnlpDownloadServlet</servlet-name>
<url-pattern>/lib/*</url-pattern>
</servlet-mapping>
</web-app>
My applet.jnlp file looks as follows:
<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="6.0+" codebase="$$codebase" href="$$name">
<information>
<title>MyApplet</title>
<vendor>My AG</vendor>
<homepage href="http://www.nowhere.com"/>
<description>XYZ</description>
</information>
<update check="always" policy="always"/>
<resources>
<j2se href="http://java.sun.com/products/autodl/j2se" version="1.6+" initial-heap-size="128m" max-heap-size="128m"/>
<property name="jnlp.versionEnabled" value="true"/>
<jar href="progress.jar" version="0.1" download="progress"/>
<jar href="MyApplet.jar" version="1.0" main="true"/>
</resources>
<applet-desc name="MyApplet"
main-class="test.MyApplet"
width="800" height="600"
progress-class="progress.CustomProgress">
<param name="bgcolor" value="28,28,28"/>
<param name="textcolor" value="255,222,123"/>
</applet-desc>
</jnlp>
To display the applet, I use the following JSP-File (index.jsp):
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Simple jsp page</title></head>
<body style="margin: 0;">
<object width="100%" vspace="0" hspace="0" height="100%" align="top" name="MyApplet"
classid="clsid:CAFEEFAC-0016-0000-0026-ABCDEFFEDCBA" onerror="alert('Invalid Java Version..')">
<param value="MyApplet" name="name">
<param value="application/x-java-applet;jpi-version=1.6.0_26" name="type">
<param value="test.MyApplet.class" name="code">
<param value="lib/MyApplet-1.0.jar" name="archive">
<param value="true" name="mayscript">
<param value="false" name="scriptable">
<param value="lib/applet.jnlp" name="jnlp_href">
<param value="true" name="progressbar">
<param value="238,244,248" name="boxbgcolor">
<param value="255,255,255" name="boxfgcolor">
<param value="255,255,227" name="progresscolor">
<param value="Loading MyApplet - please wait" name="boxmessage">
<param value="-Djnlp.versionEnabled=true -Xms256m -Xmx256m" name="java_arguments">
<param value="false" name="classloader_cache">
<param value="true" name="separate_jvm">
<comment>
<embed width="100%" vspace="0" hspace="0" height="100%" align="top" separate_jvm="true"
classloader_cache="false" java_arguments="-Djnlp.packEnabled -Xms256m -Xmx256m"
boxmessage="Loading MyApplet - please wait" boxfgcolor="255,255,255"
boxbgcolor="238,244,248" progressbar="true"
pluginspage="http://java.sun.com/products/plugin/index.html#download"
jnlp_href="lib/applet.jnlp" scriptable="false" mayscript="true"
archive="lib/MyApplet-1.0.jar" code="test.MyApplet.class"
开发者_JS百科 type="application/x-java-applet;jpi-version=1.6.0_26"
name="MyApplet" onerror="alert('Invalid Java version')">
</comment>
</object>
</body>
</html>
And the file for the version mapping (version.xml):
<?xml version="1.0"?>
<jnlp-versions>
<resource>
<pattern>
<name>MyApplet.jar</name>
<version-id>1.0</version-id>
</pattern>
<file>MyApplet-1.0.jar</file>
</resource>
<resource>
<pattern>
<name>progress.jar</name>
<version-id>0.1</version-id>
</pattern>
<file>progress-0.1.jar</file>
</resource>
</jnlp-versions>
The implementation of the DownloadServiceListener (which is +/- an exact copy of the Sun (or Oracle) example (except that no image is used):
package progress;
import javax.jnlp.DownloadServiceListener;
import java.awt.Container;
import java.awt.Color;
import java.awt.BorderLayout;
import javax.swing.*;
import java.net.URL;
import java.applet.AppletStub;
public class CustomProgress implements DownloadServiceListener {
Container surfaceContainer = null;
AppletStub appletStub = null;
JProgressBar progressBar = null;
JLabel statusLabel = null;
boolean uiCreated = false;
public CustomProgress(Object surface) {
init(surface, null);
}
public CustomProgress(Object surface, Object stub) {
init(surface, stub);
}
public void init(Object surface, Object stub) {
try {
surfaceContainer = (Container) surface;
appletStub = (AppletStub) stub;
} catch (ClassCastException cce) {
cce.printStackTrace();
}
}
public void downloadFailed(java.net.URL url, java.lang.String version) {
}
public void progress(URL url, String version, long readSoFar, long total, int overallPercent) {
// check progress of download and update display
updateProgressUI(overallPercent);
}
public void upgradingArchive(java.net.URL url, java.lang.String version, int patchPercent, int overallPercent) {
updateProgressUI(overallPercent);
}
public void validating(java.net.URL url, java.lang.String version, long entry, long total, int overallPercent) {
updateProgressUI(overallPercent);
}
private void updateProgressUI(int overallPercent) {
if (!uiCreated && overallPercent > 0 && overallPercent < 100) {
// create custom progress indicator's UI only if
// there is more work to do, meaning overallPercent > 0 and < 100
// this prevents flashing when RIA is loaded from cache
create();
uiCreated = true;
}
if (uiCreated) {
progressBar.setValue(overallPercent);
}
}
private void create() {
JPanel top = createComponents();
if (surfaceContainer != null) {
// lay out the loading progress indicator UI in the given Container
surfaceContainer.add(top, BorderLayout.NORTH);
surfaceContainer.invalidate();
surfaceContainer.validate();
}
}
private JPanel createComponents() {
JPanel top = new JPanel();
top.setBackground(Color.WHITE);
top.setLayout(new BorderLayout(20, 20));
// get applet parameter using an instance of the AppletStub class
// "tagLine" parameter specified in applet's JNLP file
String tagLine = "";
if (appletStub != null) {
tagLine = appletStub.getParameter("tagLine");
}
String lblText = "<html><font color=red size=+2>JDK Documentation</font><br/>" +
tagLine + " <br/></html>";
JLabel lbl = new JLabel(lblText);
top.add(lbl, BorderLayout.NORTH);
// use JSObject.getWindow(null) method to retrieve a reference to
// the web page and make JavaScript calls. Duke logo displayed if
// displayLogo variable set to "true" in the web page
// String displayLogo = "true";
// if (displayLogo.equals("true")) {
// lbl = new JLabel();
// ImageIcon logo = createImageIcon("images/DukeWave.gif", "logo");
// lbl.setIcon(logo);
// top.add(lbl, BorderLayout.EAST);
// }
statusLabel = new JLabel("<html><font color=green size=-2>Loading applet...</font></html>");
top.add(statusLabel, BorderLayout.CENTER);
progressBar = new JProgressBar(0, 100);
progressBar.setValue(0);
progressBar.setStringPainted(true);
top.add(progressBar, BorderLayout.SOUTH);
return top;
}
/** Returns an ImageIcon, or null if the path was invalid. */
protected static ImageIcon createImageIcon(String path, String description) {
java.net.URL imgURL = CustomProgress.class.getResource(path);
if (imgURL != null) {
return new ImageIcon(imgURL, description);
} else {
System.err.println("Couldn't find file: " + path);
return null;
}
}
}
And last but not least, my (very basic) applet:
package test;
import javax.swing.*;
public class MyApplet extends JApplet {
@Override
public void init() {
add(new JLabel("This is an applet .. "));
}
}
As you can see, everything is kept as simple as possible.. :)
Now, if I deploy the web application, and then access the file index.jsp, the following happens: There are two java consoles available, one for the "DownloadServiceListener", and another for the "real" applet. On the first, one can find the following message:
Error generating custom progress : java.lang.ClassNotFoundException: progress.CustomProgress
The second console is empty, and the applet is correctly displayed, showing the text: "This is an applet ..".
There are the following entries in my access-log: (Note that the whole java cache has been cleared before every test).
24/08/2011:14:57:09 +0200 'GET /AppletTest/ HTTP/1.1' 200 - 24/08/2011:14:57:09 +0200
127.0.0.1 24/08/2011:14:57:12 +0200 'GET /AppletTest/lib/applet.jnlp HTTP/1.1' 200 1041 24/08/2011:14:57:12 +0200
127.0.0.1 24/08/2011:14:57:13 +0200 'GET /AppletTest/lib/MyApplet__V1.0.jar HTTP/1.1' 404 - 24/08/2011:14:57:13 +0200
127.0.0.1 24/08/2011:14:57:13 +0200 'GET /AppletTest/lib/progress__V0.1.jar HTTP/1.1' 404 - 24/08/2011:14:57:13 +0200
127.0.0.1 24/08/2011:14:57:13 +0200 'GET /AppletTest/lib/progress.jar?version-id=0.1 HTTP/1.1' 200 3987 24/08/2011:14:57:13 +0200
127.0.0.1 24/08/2011:14:57:13 +0200 'GET /AppletTest/lib/MyApplet.jar?version-id=1.0 HTTP/1.1' 200 2460484 24/08/2011:14:57:13 +0200
127.0.0.1 24/08/2011:14:57:13 +0200 'GET /AppletTest/lib/progress.jar HTTP/1.1' 404 - 24/08/2011:14:57:13 +0200
127.0.0.1 24/08/2011:14:57:13 +0200 'GET /AppletTest/lib/progress.jar HTTP/1.1' 404 - 24/08/2011:14:57:13 +0200
127.0.0.1 24/08/2011:14:57:13 +0200 'GET /AppletTest/lib/MyApplet.jar HTTP/1.1' 404 - 24/08/2011:14:57:13 +0200
127.0.0.1 24/08/2011:14:57:13 +0200 'GET /AppletTest/lib/MyApplet.jar HTTP/1.1' 404 - 24/08/2011:14:57:13 +0200
127.0.0.1 24/08/2011:14:57:13 +0200 'GET /AppletTest/lib/progress.jar HTTP/1.1' 404 - 24/08/2011:14:57:13 +0200
127.0.0.1 24/08/2011:14:57:13 +0200 'GET /AppletTest/lib/progress.jar HTTP/1.1' 404 - 24/08/2011:14:57:13 +0200
127.0.0.1 24/08/2011:14:57:13 +0200 'GET /AppletTest/lib/MyApplet.jar HTTP/1.1' 404 - 24/08/2011:14:57:13 +0200
127.0.0.1 24/08/2011:14:57:13 +0200 'GET /AppletTest/lib/MyApplet.jar HTTP/1.1' 404 - 24/08/2011:14:57:13 +0200
127.0.0.1 24/08/2011:14:57:14 +0200 'GET /AppletTest/lib/applet.jnlp HTTP/1.1' 304 - 24/08/2011:14:57:14 +0200
When I look in the "Java Control Panel", the applet "MyApplet" appears, and all jar-files were loaded as expected.
When I remove the "jnlp.versionEnabled"-property, skip the version parameters of the jar-entries, and add the version number to the jar-file-names, then the custom DownloadServiceListener works perfectly.
Is it possible, that the two things (versioned jar-files & custom DSL) can not be used together? Did I miss something? Any ideas?
精彩评论