Search

Dark theme | Light theme

May 24, 2009

Wicket component for Java Deployment Toolkit to run applets

Since Java 6 update 10 Sun released the Deployment Toolkit. This kit is a Javascript file that can generate the required HTML to deploy Java applets and Web Start applications. The good thing is the script will take care of all odities between browsers, the applet and embed tags. And we can make sure the minimal Java version for our applet is available on the client's machine. If the required Java version is not available it will be downloaded and installed on the computer.

In this post we see a Wicket component which will replace an applet tag with Javascript. Attributes set for the applet tag are passed on to the Javascript. If we use another HTML element to be replaced, we must programmatically set all attributes.

Let's start with a sample page with the applet tag and a div tag. These tags are replaced with Javascript code by the Wicket component.

 
1
2
3
4
5
6
7
8
<html>
  <head>
  </head>
  <body>
    <applet wicket:id="applet" code="Applet.class" archive="applet.jar"
      width="100" height="200"/>
    <div wicket:id="appletDiv"/>
  </body>
</html>

We create our Wicket component by extending the org.apache.wicket.markup.html.WebComponent. We then override the onComponentTag and onComponentTagBody methods:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.mrhaki.wicket.components;
 
import org.apache.wicket.markup.html.WebComponent;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
 
public class DeployJava extends WebComponent {
     
    private static final long serialVersionUID = 1L;
     
    public DeployJava(final String id) {
        super(id);
    }
 
    @Override
    protected void onComponentTag(final ComponentTag tag) {
        super.onComponentTag(tag);
        tag.setName("script");
    }
     
    @Override
    protected void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) {
        final StringBuilder script = new StringBuilder();
        replaceComponentTagBody(markupStream, openTag, script.toString());
    }
}

This code doesn't do much yet. The only thing we have achieved is that the component will render a script tag with no content. Now it is time to add more functionality. We want to be able to render Javascript like this:

 
1
2
3
4
5
<script>
var attributes = { code: 'Applet.class', archive: 'applet.jar', width: 100, height: 200 };
var parameters = { text: 'Hello world' };
var version = "1.6";
javaDeploy.runApplet(attributes, parameters, version);
</script>

So we start by adding properties in our component to store attributes, parameters and version text. Next we can implement the code in onComponentTagBody to output the Javascript:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
...
import net.sf.json.JSONObject;
import org.apache.wicket.util.value.IValueMap;
import org.apache.wicket.util.value.ValueMap;
...
    private String minimalVersion;
    private IValueMap appletAttributes = new ValueMap();
    private IValueMap appletParameters = new ValueMap();
...
    @Override
    protected void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) {
        final StringBuilder script = new StringBuilder();
        if (appletAttributes.size() > 0) {
            final JSONObject jsonAttributes = JSONObject.fromObject(appletAttributes);
            script.append("var attributes = " + jsonAttributes + ";");
        } else {
            script.append("var attributes = {};");
        }
        if (appletParameters.size() > 0) {
            final JSONObject jsonParameters = JSONObject.fromObject(appletParameters);
            script.append("var parameters = " + jsonParameters + ";");
        } else {
            script.append("var parameters = {};");
        }
        if (minimalVersion != null) {
            script.append("var version = \"" + minimalVersion + "\";");
        } else {
            script.append("var version = null;");
        }
        script.append("deployJava.runApplet(attributes, parameters, version);");
        replaceComponentTagBody(markupStream, openTag, script.toString());
    }
...
    public void setMinimalVersion(final String minimalVersion) {
        this.minimalVersion = minimalVersion;
    }
     
    public void addParameter(final String key, final Object value) {
        appletParameters.put(key, value);
    }
     
    public void addAttributes(final String key, final Object value) {
        appletAttributes.put(key, value);
    }
...

We use the JSON-lib library to convert the attributes and parameters map to Javascript. We also add methods to set values for the properties.

Okay, the component displays the correct Javascript, but we need to add a reference to the Deployment Toolkit Javascript file, otherwise the method deployJava.runApplet is not recognized. We extend the Wicket component and implement the org.apache.wicket.markup.html.IHeaderContributor interface. Wicket components that implement this interface must implement the method renderHead. Wicket will invoke this method and add a Javascript reference in the HTML head section of our web page. For now we simply add a reference to http://java.com/js/deployJava.js.

 
1
2
3
4
5
6
7
8
import org.apache.wicket.markup.html.IHeaderResponse;
 
public class DeployJava extends WebComponent implements IHeaderContributor {
 
    public void renderHead(IHeaderResponse response) {
        response.renderJavascriptReference("http://java.com/js/deployJava.js");
    }
 
}

And that is it for a basic component which renders the correct Javascript. The following code shows a complete component, which can read attributes set on the applet tag and use them in the Javascript. We can also choose between adding the Javascript source file as a Wicket resource or as a reference to the public Sun website URL.

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
package com.mrhaki.wicket.components;
 
import net.sf.json.JSONObject;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.IHeaderContributor;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.markup.html.WebComponent;
import org.apache.wicket.markup.html.resources.CompressedResourceReference;
import org.apache.wicket.util.value.IValueMap;
import org.apache.wicket.util.value.ValueMap;
 
/**
 * <p>Wicket component to add the
 * <a href="http://java.sun.com/javase/6/docs/technotes/guides/jweb/deployment_advice.html">Sun's Deployment Toolkit</a>
 * Javascript.
 * The markup can be defined as applet. Attributes defined for the applet are
 * copied to the Javascript. The markup can also be another element, for example
 * a div element.</p>
 * <p>
 * Suppose we have the following applet markup:
 * <pre>
 * &lt;applet wicket:id="applet" width=200 height=120 code="SignatureApplet.class"
 *   archive="codesign.jar"&gt;&lt;/applet&gt;
 * </pre>
 * In a Wicket page we can create this component and add it to the page:
 * <pre>
 * final JavaDeploy javaDeploy = new JavaDeploy("applet");
 * add(javaDeploy);
 * </pre>
 * We get the following output:
 * <pre>
 * &lt;html&gt;
 *   &lt;head&gt;
 *     &lt;script type="text/Javascript" src="http://java.com/js/deployJava.js"&gt;&lt;/script&gt;
 *   &lt;/head&gt;
 *   &lt;body&gt;
 *     &lt;script&gt;
 *     var attributes = { "width":200,"height":120,"code":"SignatureApplet.class","archive":"codesign.jar"};
 *     var parameters = {};
 *     var version = null;
 *     deployJava.runApplet(attributes, parameters, version);
 *     &lt;/script&gt;
 *   &lt;/body&gt;
 * &lt;/html&gt;
 * </pre>
 * </p>
 */
public class DeployJava extends WebComponent implements IHeaderContributor {
 
    private static final long serialVersionUID = 1L;
 
    /**
     * Javascript URL on Sun's website for deployJava Javascript. (={@value})
     */
    private static final String JAVASCRIPT_URL = "http://java.com/js/deployJava.js";
 
    /**
     * Attribute to set the width of the applet. (={@value})
     */
    private static final String ATTRIBUTE_WIDTH = "width";
 
    /**
     * Attribute to set the height of the applet. (={@value})
     */
    private static final String ATTRIBUTE_HEIGHT = "height";
 
    /**
     * Attribute to set the applet classname. (={@value})
     */
    private static final String ATTRIBUTE_CODE = "code";
 
    /**
     * Attribute to set the codebase for the applet. (={@value})
     */
    private static final String ATTRIBUTE_CODEBASE = "codebase";
 
    /**
     * Attribute to set the archive neede by the applet. (={@value})
     */
    private static final String ATTRIBUTE_ARCHIVE = "archive";
 
    /**
     * Minimal Java version needed for the applet.
     */
    private String minimalVersion;
 
    /**
     * If true we use a local resource otherwise the URL from the Sun site.
     * For the local resource we must add the file deployJava.js next to
     * this class in our package structure.
     */
    private boolean useJavascriptResource;
 
    /**
     * Attributes for the javaDeploy.runApplet Javascript method.
     */
    private IValueMap appletAttributes = new ValueMap();
 
    /**
     * Parameters for the javaDeploy.runApplet Javascript method.
     */
    private IValueMap appletParameters = new ValueMap();
 
    /**
     * Default constructor with markup id.
     *
     * @param id Markup id for applet.
     */
    public DeployJava(String id) {
        super(id);
    }
 
    /**
     * Minimal Java version for the applet. E.g. Java 1.6 is "1.6".
     *
     * @param version Minimal Java version needed by the applet.
     */
    public void setMinimalVersion(final String version) {
        this.minimalVersion = version;
    }
 
    /**
     * Width of the applet.
     *
     * @param width Width of the applet on screen.
     */
    public void setWidth(final Integer width) {
        appletAttributes.put(ATTRIBUTE_WIDTH, width);
    }
 
    /**
     * Height of the applet.
     *
     * @param height Height of the applet on screen.
     */
    public void setHeight(final Integer height) {
        appletAttributes.put(ATTRIBUTE_HEIGHT, height);
    }
 
    /**
     * Applet classname.
     *
     * @param code Applet classname.
     */
    public void setCode(final String code) {
        appletAttributes.put(ATTRIBUTE_CODE, code);
    }
 
    /**
     * Codebase for the applet code.
     *
     * @param codebase Codebase for the applet code.
     */
    public void setCodebase(final String codebase) {
        appletAttributes.put(ATTRIBUTE_CODEBASE, codebase);
    }
 
    /**
     * Archive path for the applet.
     *
     * @param archive Archive location for the applet.
     */
    public void setArchive(final String archive) {
        appletAttributes.put(ATTRIBUTE_ARCHIVE, archive);
    }
 
    /**
     * Add a parameter to the applet.
     *
     * @param key Name of the parameter.
     * @param value Value for the parameter.
     */
    public void addParameter(final String key, final Object value) {
        appletParameters.put(key, value);
    }
 
    /**
     * Indicate if deployJava Javascript must be loaded from the Sun site or as local resource.
     *
     * @param useResource True local resource is used, otherwise Sun site.
     */
    public void setUseJavascriptResource(final boolean useResource) {
        this.useJavascriptResource = useResource;
    }
 
    /**
     * Get the applet attributes already set and assign them to the attribute
     * list for the Javascript code. And we change the tag name to "script".
     *
     * @param tag De current tag which is replaced.
     */
    @Override
    protected void onComponentTag(ComponentTag tag) {
        super.onComponentTag(tag);
        if ("applet".equalsIgnoreCase(tag.getName())) {
            final IValueMap tagAttributes = tag.getAttributes();
            // Save wicket:id so we can add again to the list of attributes.
            final String wicketId = tagAttributes.getString("wicket:id");
            appletAttributes.putAll(tagAttributes);
            tagAttributes.clear();
            tagAttributes.put("wicket:id", wicketId);
        }
        tag.setName("script");
    }
 
    /**
     * Create Javascript for deployJava.runApplet.
     *
     * @param markupStream MarkupStream to be replaced.
     * @param openTag Tag we are replacing.
     */
    @Override
    protected void onComponentTagBody(MarkupStream markupStream, ComponentTag openTag) {
        final StringBuilder script = new StringBuilder();
        if (appletAttributes.size() > 0) {
            final JSONObject jsonAttributes = JSONObject.fromObject(appletAttributes);
            script.append("var attributes = " + jsonAttributes + ";");
        } else {
            script.append("var attributes = {};");
        }
        if (appletParameters.size() > 0) {
            final JSONObject jsonParameters = JSONObject.fromObject(appletParameters);
            script.append("var parameters = " + jsonParameters + ";");
        } else {
            script.append("var parameters = {};");
        }
        if (minimalVersion != null) {
            script.append("var version = \"" + minimalVersion + "\";");
        } else {
            script.append("var version = null;");
        }
        script.append("deployJava.runApplet(attributes, parameters, version);");
        replaceComponentTagBody(markupStream, openTag, script.toString());
    }
 
    /**
     * Add Javascript src reference in the HTML head section of the web page.
     *
     * @param response Header response.
     */
    public void renderHead(IHeaderResponse response) {
        if (useJavascriptResource) {
            response.renderJavascriptReference(new CompressedResourceReference(JavaDeploy.class, "deployJava.js"));
        } else {
            response.renderJavascriptReference(JAVASCRIPT_URL);
        }
    }
}

If we look at the beginning of the post we see the HTML for a web page. We create the code for the page as follows:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.mrhaki.wicket.page;
 
public class AppletPage extends WebPage {
 
    public AppletPage() {
        final DeployJava applet = new DeployJava("applet");
        add(applet);
         
        final DeployJava div = new DeployJava("appletDiv");
        div.setWidth(100);
        div.setHeight(200);
        div.setCode("NewApplet.class");
        div.setCodeBase("../classes");
        div.addParameter("text", "Hello world");
        div.setMinimalVersion("1.6");
        add(div);
    }
}

When we run the page we get the following HTML:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<html>
  <head>
    <script type="text/javascript" src="http://java.com/js/deployJava.js"></script>
  </head>
  <body>
    <script>
      var attributes = { "code":"Applet.class", "archive":"applet.jar", "width":100, "height":200 };
      var parameters = {};
      var version = null;
      deployJava.runApplet(attributes, parameters, version);
    </script>
    <script>
      var attributes = { "code":"NewApplet.class", "codebaSE":"../classes", "width":100, "height":200 };
      var parameters = { "text":"Hello world" };
      var version = "1.6";
      deployJava.runApplet(attributes, parameters, version);
    </script>
  </body>
</html>