JNLP and Java Web Start
Signed applets are powerful and can effectively take the place of an application, but they must run inside a Web browser. This requires the extra overhead of the browser running on the client machine, and also means that the user interface of the applet is limited and often visually confusing. The Web browser has its own set of menus and toolbars, which will appear above the applet.
The Java Network Launch Protocol (JNLP) solves the problem without sacrificing the advantages of applets. With a JNLP application, you can download and install a standalone Java application onto the client’s machine. This can be run from the command prompt, a desktop icon, or the application manager that is installed with your JNLP implementation. The application can even be run from the Web site from which it was originally downloaded.
A JNLP application can dynamically download resources from the Internet at run time, and the version can be automatically checked (if the user is connected to the Internet) . This means that it has all of the advantages of an applet together with the advantages of standalone applications.
Like applets, JNLP applications need to be treated with some caution by the client’s system. A JNLP application is Web-based and easy to download, so it might be malevolent. Because of this, JNLP applications are subject to the same sandbox security restrictions as applets. Like applets, they can be deployed in signed JAR files, giving the user the option to trust the signer. Unlike applets, if they are deployed in an unsigned JAR file, they can still request access to certain resources of the client’s system by means of services in the JNLP API (the user must approve the requests during program execution).
Because JNLP describes a protocol, not an implementation, you will need an implementation in order to use it. Java Web Start, or JAWS, is Sun’s freely-available official reference implementation. All you need to do is download and install it, and if you are using it for development, make sure that the JAR files are in your classpath. If you are deploying your JNLP application from a Web server, you have to ensure that your server recognizes the MIME type application/x-java-jnlp-file. If you are using a recent version of the Tomcat server (https://jakarta.apache.org/tomcat) this will already be configured. Consult the user guide for your particular server.
Creating a JNLP application is not difficult. You create a standard application that is archived in a JAR file, and then you provide a launch file, which is a simple XML file that gives the client all the information it needs to download and install your application. If you choose not to sign your JAR file, then you must make use of the services supplied by the JNLP API for each type of resource you want access to on the users machine.
Here is a variation of the example using the JFileChooser dialog, but this time using the JNLP services to open it, so that the class can be deployed as a JNLP application in an unsigned JAR file.
//: c14:jnlp:JnlpFileChooser.java
// Opening files on a local machine with JNLP.
// {Depends: javaws.jar}
package c14.jnlp;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.jnlp.*;
public class JnlpFileChooser extends JFrame {
private JTextField filename = new JTextField();
private JButton
open = new JButton("Open"),
save = new JButton("Save");
private JEditorPane ep = new JEditorPane();
private JScrollPane jsp = new JScrollPane();
private FileContents fileContents;
public JnlpFileChooser() {
JPanel p = new JPanel();
open.addActionListener(new OpenL());
p.add(open);
save.addActionListener(new SaveL());
p.add(save);
Container cp = getContentPane();
jsp.getViewport().add(ep);
cp.add(jsp, BorderLayout.CENTER);
cp.add(p, BorderLayout.SOUTH);
filename.setEditable(false);
p = new JPanel();
p.setLayout(new GridLayout(2,1));
p.add(filename);
cp.add(p, BorderLayout.NORTH);
ep.setContentType("text");
save.setEnabled(false);
}
class OpenL implements ActionListener {
public void actionPerformed(ActionEvent e) {
FileOpenService fs = null;
try {
fs = (FileOpenService)ServiceManager.lookup(
"javax.jnlp.FileOpenService");
} catch(UnavailableServiceException use) {
throw new RuntimeException(use);
}
if(fs != null) {
try {
fileContents = fs.openFileDialog(".",
new String[]{"txt", "*"});
if(fileContents == null)
return;
filename.setText(fileContents.getName());
ep.read(fileContents.getInputStream(), null);
} catch (Exception exc) {
throw new RuntimeException (exc);
}
save.setEnabled(true);
}
}
}
class SaveL implements ActionListener {
public void actionPerformed(ActionEvent e) {
FileSaveService fs = null;
try {
fs = (FileSaveService)ServiceManager.lookup(
"javax.jnlp.FileSaveService");
} catch(UnavailableServiceException use) {
throw new RuntimeException(use);
}
if(fs != null) {
try {
fileContents = fs.saveFileDialog(".",
new String[]{"txt"},
new ByteArrayInputStream(
ep.getText().getBytes()),
fileContents.getName());
if(fileContents == null)
return;
filename.setText(fileContents.getName());
} catch (Exception exc) {
throw new RuntimeException (exc);
}
}
}
}
public static void main(String[] args) {
JnlpFileChooser fc = new JnlpFileChooser();
fc.setSize(400, 300);
fc.setVisible(true);
}
} ///:~
Note that the FileOpenService and the FileCloseService classes are imported from the javax.jnlp package and that nowhere in the code is the JFileChooser dialog box referred to directly. The two services used here must be requested using the ServiceManager.lookup( ) method, and the resources on the client system can only be accessed via the objects returned from this method. In this case, the files on the client’s file system are being written to and read from using the FileContent interface, provided by the JNLP. Any attempt to access the resources directly by using, say, a File or a FileReader object would cause a SecurityException to be thrown in the same way that it would if you tried to use them from an unsigned applet. If you want to use these classes and not be restricted to the JNLP service interfaces, you must sign the JAR file (see the previous section on signing JAR files).
Now that we have a runnable class that makes use of the JNLP services, all that is needed is for the class to be put into a JAR file and a launch file to be written. Here is an appropriate launch file for the preceding example.
<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec = “1.0+”
codebase="file://C:\TIJ3code\c14\jnlp"
href="filechooser.jnlp">
<information>
<title>FileChooser demo application</title>
<vendor>Mindview Inc.</vendor>
<description>
Jnlp File choose Application
</description>
<description kind="short">
A demonstration of opening, reading and
writing a text file
</description>
<icon
href="images/tijicon.gif"/>
<offline-allowed/>
</information>
<resources>
<j2se version="1.3+"/>
<jar
href="jnlpfilechooser.jar" download="eager"/>
</resources>
<application-desc
main-class="c14.jnlp.JnlpFileChooser"/>
</jnlp>
This launch file needs to be saved as a .jnlp file, in this case, filechooser.jnlp, in the same directory as the JAR file.
As you can see, it is an XML file with one <jnlp> tag. This has a few subelements, which are mostly self-explanatory.
The spec attribute of the jnlp element tells the client system what version of the JNLP the application can be run with. The codebase attribute points to the directory where this launch file and the resources can be found. Typically, it would be an HTTP URL pointing to a Web server, but in this case it points to a directory on the local machine, which is a good means of testing the application. The href attribute must specify the name of this file.
The information tag has various subelements that provide information about the application. These are used by the Java Web Start administrative console or equivalent, which installs the JNLP application and allows the user to run it from the command line, make short cuts and so on.
The resources tag serves a similar purpose as the applet tag in an HTML file. The j2se subelement specifies the version of the j2se that is needed to run the application, and the jar subelement specifies the JAR file in which the class is archived. The jar element has an attribute download, which can have the values “eager” or “lazy” that tell the JNLP implementation whether or not the entire archive needs to be downloaded before the application can be run.
The application-desc attribute tells the JNLP implementation which class is the executable class, or entry point, to the JAR file.
Another useful subelement of the jnlp tag is the security tag, not shown here. Here’s what a security tag looks like:
<security>
<all-permissions/>
<security/>
You use the security tag when your application is deployed in a signed JAR file. It is not needed in the preceding example because the local resources are all accessed via the JNLP services.
There are a few other tags available, the details of which can be found in the specification https://java.sun.com/products/javawebstart/download-spec.html.
Now that the .jnlp is written, you will need to add a hypertext link to it in an HTML page. This will be its download page. You might have a complex layout with a detailed introduction to your application, but as long as you have something like:
<a
href="filechooser.jnlp">click here</a>
in your HTML file, then you will be able to initiate the installation of the JNLP application by clicking on the link. Once you have downloaded the application once, you will be able to configure it by using the administrative console. If you are using Java Web Start on Windows, then you will be prompted to make a short cut to your application the second time you use it. This behavior is configurable.
The source code for this book, downloadable from www.BruceEckel.com, contains complete working configuration files and an Ant build script to properly compile and build this project.
Only two of the JNLP services are covered here, but there are seven services in the current release. Each is designed for a specific task such as printing, or cutting, and pasting to the clipboard. An in-depth discussion of these services is beyond the scope of this chapter.