Java Web Start

   


Java Web Start is a newer technology that aims to improve on the user experience of Java programs that are delivered over the Internet. Here are the principal differences between Java Web Start applications and applets.

  • Java Web Start delivers regular Java applications that are started by a call to the main method of a class. There is no need to inherit from Applet.

  • A Java Web Start application does not live inside a browser. It is displayed outside the browser.

  • A Java Web Start application can be launched through the browser, but the underlying mechanism is quite different from the launch of an applet. Browsers are tightly integrated with a Java runtime environment that executes applets. The Java Web Start integration is much looser. The browser simply launches an external application whenever it loads a Java Web Start application descriptor. That is the same mechanism that is used to launch other helper applications such as Adobe Acrobat or RealAudio. Even hostile browser vendors won't be able to interfere with this mechanism.

  • Once a Java Web Start application has been downloaded, it can be started outside the browser.

  • Java Web Start has a slightly relaxed "sandbox" that allows unsigned applications some access to local resources.

To prepare an application for delivery by Java Web Start, you package it in one or more JAR files. Then you prepare a descriptor file in Java Network Launch Protocol (JNLP) format. Place these files on a web server. Next, make sure that your web server reports a MIME type of application/x-java-jnlp-file for files with extension .jnlp. (Browsers use the MIME type to determine which helper application to launch.) Consult your web server documentation for details.

TIP

To experiment with Java Web Start, install Tomcat from jakarta.apache.org/tomcat. Tomcat is a container for servlets and JSP pages, but it also serves web pages. The current version is preconfigured to handle JNLP files.


Let's try out Java Web Start to deliver the calculator application from Chapter 9. Follow these steps.

1.

Compile Calculator.java.

2.

Prepare a manifest file Calculator.mf with the line

 Main-Class: Calculator 

3.

Produce a JAR file with the command

 jar cvfm Calculator.jar Calculator.mf *.class 

4.

Prepare the launch file Calculator.jnlp with the following contents:

 <?xml version="1.0" encoding="utf-8"?> <jnlp spec="1.0+"codebase="http://localhost:8080/calculator/" href="Calculator.jnlp">   <information>     <title>Calculator Demo Application</title>     <vendor>Cay S. Horstmann</vendor>     <description>A Calculator</description>     <offline-allowed/>   </information>   <resources>     <j2se version="5.0+"/>     <jar href="Calculator.jar"/>   </resources>   <application-desc/> </jnlp> 

The launch file format is fairly self-explanatory. For a full specification, see http://java.sun.com/products/javawebstart/docs/developersguide.htm.

5.

If you use Tomcat, make a directory tomcat/webapps/calculator, where tomcat is the base directory of your Tomcat installation. Make a subdirectory tomcat/webapps/calculator/WEB-INF, and place the following minimal web.xml file inside the WEB-INF subdirectory:

 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"    "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> </web-app> 

6.

Place the JAR file and the launch file on your web server so that the URL matches the codebase enTRy in the JNLP file. If you use Tomcat, put them into the tomcat/webapps/calculator directory.

7.

Make sure that your browser has been configured for Java Web Start, by checking that the application/x-java-jnlp-file MIME type is associated with the javaws application. If you installed the JDK, the configuration should be automatic.

8.

Start Tomcat.

9.

Point your browser to the JNLP file. For example, if you use Tomcat, go to http://localhost:8080/calculator/Calculator.jnlp.

10.

You should see the launch window for Java Web Start (see Figure 10-12). Soon afterward, the calculator should come up, with a border marking it as a Java Web Start application (see Figure 10-13).

Figure 10-12. Launching Java Web Start


Figure 10-13. The calculator delivered by Java Web Start


11.

When you access the JNLP file again, the application is retrieved from the cache. You can review the cache content by using the Java Plug-in control panel (see Figure 10-14). As of JDK 5.0, that control panel is used both for applets and Java Web Start applications. In Windows, look for the Java Plug-in control panel inside the Windows control panel. Under Linux, run jdk/jre/bin/ControlPanel.

Figure 10-14. The application cache


The JNLP API

For a Java Web Start application to be granted full access to the local machine, the application must be digitally signed. (See Chapter 9 of Volume 2 for more information on digital signatures.) Just as with applets, an unsigned Java Web Start application that is downloaded from the Internet is inherently risky and runs in a sandbox, with minimal access to the local computer. However, with minimal security privileges, the JNLP API provides application developers some tools for accessing local resources.

For example, there are services to load and save files, but they are quite restrictive. The application can't look at the file system and it can't specify file names. Instead, a file dialog is popped up, and the program user selects the file. Before the file dialog is popped up, the program user is alerted and must agree to proceed (see Figure 10-15). Furthermore, the API doesn't actually give the program access to a File object. In particular, the application has no way of finding out the file location. Thus, programmers are given the tools to implement standard "file open" and "file save" actions, but as much system information as possible is hidden from untrusted applications.

Figure 10-15. A Java Web Start security advisory


The API provides the following services:

  • Loading and saving files

  • Accessing the clipboard

  • Downloading a file

  • Printing

  • Storing and retrieving persistent configuration information

  • Displaying a document in the default browser

  • Ensuring that only a single instance of an application executes (added in JDK 5.0)

To access a service, you use the ServiceManager, like this:

FileSaveService service = (FileSaveService) ServiceManager.lookup("javax.jnlp .FileSaveService");

This call throws an UnavailableServiceException if the service is not available.

NOTE

You must include the file javaws.jar in the class path if you want to compile programs that use the JNLP API. That file is included in the jre/lib subdirectory of the JDK.


We now discuss the most useful JNLP services. To save a file, you provide suggestions for the initial path name and file extensions for the file dialog, the data to be saved, and a suggested file name. For example,

 service.saveFileDialog(".", new String[] { "txt" }, data, "calc.txt"); 

The data must be delivered in an InputStream. That can be somewhat tricky to arrange. The program in Example 10-13 uses the following strategy:

1.

Create a ByteArrayOutputStream to hold the bytes to be saved.

2.

Create a PrintStream that sends its data to the ByteArrayOutputStream.

3.

Print the information to be saved to the PrintStream.

4.

Create a ByteArrayInputStream to read the saved bytes.

5.

Pass that stream to the saveFileDialog method.

You will learn more about streams in Chapter 12. For now, you can just gloss over the details in the sample program.

To read data from a file, you use the FileOpenService instead. Its openFileDialog receives suggestions for the initial path name and file extensions for the file dialog and returns a FileContents object. You can then call the getInputStream method to read the file data. If the user didn't choose a file, then the openFileDialog method returns null.

FileOpenService service = (FileOpenService) ServiceManager.lookup("javax.jnlp .FileOpenService"); FileContents contents = service.openFileDialog(".", new String[] { "txt" }); if (contents != null) { InputStream in = contents.getInputStream(); . . . }

To display a document on the default browser (similar to the showDocument method of an applet), use the BasicService interface. Note that some systems (in particular, many UNIX and Linux systems) may not have a default browser.

 BasicService service = (BasicService) ServiceManager.lookup("javax.jnlp.BasicService"); if (service.isWebBrowserSupported())    service.showDocument(url); else . . . 

A rudimentary PersistenceService lets an application store small amounts of configuration information and retrieve it when the application runs again. This service is necessary because an untrusted application cannot specify a location for a configuration file.

The mechanism is similar to HTTP cookies. The persistent store uses URLs as keys. The URLs don't have to point to a real web resource. The service simply uses them as a convenient hierarchical naming scheme. For any given URL key, an application can store arbitrary binary data. (The store may restrict the size of the data block.)

So that applications are isolated from each other, a particular application can only use URL keys that start with its codebase (as specified in the JNLP file). For example, if an application is downloaded from http://myserver.com/apps, then it can only use keys of the form http://myserver.com/apps/subkey1/subkey2/... Attempts to access other keys will fail.

An application can call the getCodeBase method of the BasicService to find its codebase.

You create a new key with the create method of the PersistenceService.

 URL url = new URL(codeBase, "mykey"); service.create(url, maxSize); 

To access the information associated with a particular key, call the get method. That method returns a FileContents object through which you can read and write the key data. For example,

 FileContents contents = service.get(url); InputStream in = contents.getInputStream(); OutputStream out = contents.getOutputStream(true); // true = overwrite 

Unfortunately, there is no convenient way to find out whether a key already exists or whether you need to create it. You can hope that the key exists and call get. If the call throws a FileNotFoundException, then you need to create the key.

NOTE

Starting with JDK 5.0, both Java Web Start applications and applets can print, using the normal printing API. A security dialog pops up, asking the user for permission to access the printer. For more information on the printing API, turn to the Advanced AWT chapter in Volume 2.


The program in Example 10-13 is a simple enhancement of the calculator application. This calculator has a virtual paper tape that keeps track of all calculations. You can save and load the calculation history. To demonstrate the persistent store, the application lets you set the frame title. If you run the application again, it retrieves your title choice from the persistent store (see Figure 10-16).

Example 10-13. WebStartCalculator.java

   1. import java.awt.*;   2. import java.awt.event.*;   3. import java.io.*;   4. import java.net.*;   5. import javax.swing.*;   6. import javax.swing.text.*;   7. import javax.jnlp.*;   8.   9. /**  10.    A calculator with a calculation history that can be  11.    deployed as a Java Web Start application.  12. */  13. public class WebStartCalculator  14. {  15.    public static void main(String[] args)  16.    {  17.       CalculatorFrame frame = new CalculatorFrame();  18.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  19.       frame.setVisible(true);  20.    }  21. }  22.  23. /**  24.    A frame with a calculator panel and a menu to load and  25.    save the calculator history.  26. */  27. class CalculatorFrame extends JFrame  28. {  29.    public CalculatorFrame()  30.    {  31.       setTitle();  32.       panel = new CalculatorPanel();  33.       add(panel);  34.  35.       JMenu fileMenu = new JMenu("File");  36.  37.       JMenuItem openItem = fileMenu.add("Open");  38.       openItem.addActionListener(new  39.          ActionListener()  40.          {  41.             public void actionPerformed(ActionEvent event)  42.             {  43.                open();  44.             }  45.          });  46.  47.       JMenuItem saveItem = fileMenu.add("Save");  48.       saveItem.addActionListener(new  49.          ActionListener()  50.          {  51.             public void actionPerformed(ActionEvent event)  52.             {  53.                save();  54.             }  55.          });  56.       JMenuBar menuBar = new JMenuBar();  57.       menuBar.add(fileMenu);  58.       setJMenuBar(menuBar);  59.  60.       pack();  61.    }  62.  63.    /**  64.       Gets the title from the persistent store or  65.       asks the user for the title if there is no prior entry.  66.    */  67.    public void setTitle()  68.    {  69.       try  70.       {  71.          String title = null;  72.  73.          BasicService basic = (BasicService) ServiceManager.lookup("javax.jnlp .BasicService");  74.          URL codeBase = basic.getCodeBase();  75.  76.          PersistenceService service  77.             = (PersistenceService) ServiceManager.lookup("javax.jnlp .PersistenceService");  78.          URL key = new URL(codeBase, "title");  79.  80.          try  81.          {  82.             FileContents contents = service.get(key);  83.             InputStream in = contents.getInputStream();  84.             BufferedReader reader = new BufferedReader(new InputStreamReader(in));  85.             title = reader.readLine();  86.          }  87.          catch (FileNotFoundException e)  88.          {  89.             title = JOptionPane.showInputDialog("Please supply a frame title:");  90.             if (title == null) return;  91.  92.             service.create(key, 100);  93.             FileContents contents = service.get(key);  94.             OutputStream out = contents.getOutputStream(true);  95.             PrintStream printOut = new PrintStream(out);  96.             printOut.print(title);  97.          }  98.          setTitle(title);  99.       } 100.       catch (UnavailableServiceException e) 101.       { 102.          JOptionPane.showMessageDialog(this, e); 103.       } 104.       catch (MalformedURLException e) 105.       { 106.          JOptionPane.showMessageDialog(this, e); 107.       } 108.       catch (IOException e) 109.       { 110.          JOptionPane.showMessageDialog(this, e); 111.       } 112.    } 113. 114.    /** 115.       Opens a history file and updates the display. 116.    */ 117.    public void open() 118.    { 119.       try 120.       { 121.          FileOpenService service 122.             = (FileOpenService) ServiceManager.lookup("javax.jnlp.FileOpenService"); 123.          FileContents contents = service.openFileDialog(".", new String[] { "txt" }); 124. 125.          JOptionPane.showMessageDialog(this, contents.getName()); 126.          if (contents != null) 127.          { 128.             InputStream in = contents.getInputStream(); 129.             BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 130.             String line; 131.             while ((line = reader.readLine()) != null) 132.             { 133.                panel.append(line); 134.                panel.append("\n"); 135.             } 136.          } 137.       } 138.       catch (UnavailableServiceException e) 139.       { 140.          JOptionPane.showMessageDialog(this, e); 141.       } 142.       catch (IOException e) 143.       { 144.          JOptionPane.showMessageDialog(this, e); 145.       } 146.    } 147. 148.    /** 149.       Saves the calculator history to a file. 150.    */ 151.    public void save() 152.    { 153.       try 154.       { 155.          ByteArrayOutputStream out = new ByteArrayOutputStream(); 156.          PrintStream printOut = new PrintStream(out); 157.          printOut.print(panel.getText()); 158.          InputStream data = new ByteArrayInputStream(out.toByteArray()); 159.          FileSaveService service 160.             = (FileSaveService) ServiceManager.lookup("javax.jnlp.FileSaveService"); 161.          service.saveFileDialog(".", new String[] { "txt" }, data, "calc.txt"); 162.       } 163.       catch (UnavailableServiceException e) 164.       { 165.          JOptionPane.showMessageDialog(this, e); 166.       } 167.       catch (IOException e) 168.       { 169.          JOptionPane.showMessageDialog(this, e); 170.       } 171.    } 172. 173.    private CalculatorPanel panel; 174. } 175. 176. 177. /** 178.    A panel with calculator buttons and a result display. 179. */ 180. class CalculatorPanel extends JPanel 181. { 182.    /** 183.       Lays out the panel. 184.    */ 185.    public CalculatorPanel() 186.    { 187.       setLayout(new BorderLayout()); 188. 189.       result = 0; 190.       lastCommand = "="; 191.       start = true; 192. 193.       // add the display 194. 195.       display = new JTextArea(10, 20); 196. 197.       add(new JScrollPane(display), BorderLayout.NORTH); 198. 199.       ActionListener insert = new InsertAction(); 200.       ActionListener command = new CommandAction(); 201. 202.       // add the buttons in a 4 x 4 grid 203. 204.       panel = new JPanel(); 205.       panel.setLayout(new GridLayout(4, 4)); 206. 207.       addButton("7", insert); 208.       addButton("8", insert); 209.       addButton("9", insert); 210.       addButton("/", command); 211. 212.       addButton("4", insert); 213.       addButton("5", insert); 214.       addButton("6", insert); 215.       addButton("*", command); 216. 217.       addButton("1", insert); 218.       addButton("2", insert); 219.       addButton("3", insert); 220.       addButton("-", command); 221. 222.       addButton("0", insert); 223.       addButton(".", insert); 224.       addButton("=", command); 225.       addButton("+", command); 226. 227.       add(panel, BorderLayout.CENTER); 228.    } 229. 230.    /** 231.       Gets the history text. 232.       @return the calculator history 233.    */ 234.    public String getText() 235.    { 236.       return display.getText(); 237.    } 238. 239.    /** 240.       Appends a string to the history text. 241.       @param s the string to append 242.    */ 243.    public void append(String s) 244.    { 245.       display.append(s); 246.    } 247. 248.    /** 249.       Adds a button to the center panel. 250.       @param label the button label 251.       @param listener the button listener 252.    */ 253.    private void addButton(String label, ActionListener listener) 254.    { 255.       JButton button = new JButton(label); 256.       button.addActionListener(listener); 257.       panel.add(button); 258.    } 259. 260.    /** 261.       This action inserts the button action string to the 262.       end of the display text. 263.    */ 264.    private class InsertAction implements ActionListener 265.    { 266.       public void actionPerformed(ActionEvent event) 267.       { 268.          String input = event.getActionCommand(); 269.          start = false; 270.          display.append(input); 271.       } 272.    } 273. 274.    /** 275.       This action executes the command that the button 276.       action string denotes. 277.    */ 278.    private class CommandAction implements ActionListener 279.    { 280.       public void actionPerformed(ActionEvent event) 281.       { 282.          String command = event.getActionCommand(); 283. 284.          if (start) 285.          { 286.             if (command.equals("-")) 287.             { 288.                display.append(command); 289.                start = false; 290.             } 291.             else 292.                lastCommand = command; 293.          } 294.          else 295.          { 296.             try 297.             { 298.                int lines = display.getLineCount(); 299.                int lineStart = display.getLineStartOffset(lines - 1); 300.                int lineEnd = display.getLineEndOffset(lines - 1); 301.                String value = display.getText(lineStart, lineEnd - lineStart); 302.                display.append(" "); 303.                display.append(command); 304.                calculate(Double.parseDouble(value)); 305.                if (command.equals("=")) 306.                   display.append("\n" + result); 307.                lastCommand = command; 308.                display.append("\n"); 309.                start = true; 310.             } 311.             catch (BadLocationException e) 312.             { 313.                e.printStackTrace(); 314.             } 315.          } 316.       } 317.    } 318. 319.    /** 320.       Carries out the pending calculation. 321.       @param x the value to be accumulated with the prior result. 322.    */ 323.    public void calculate(double x) 324.    { 325.       if (lastCommand.equals("+")) result += x; 326.       else if (lastCommand.equals("-")) result -= x; 327.       else if (lastCommand.equals("*")) result *= x; 328.       else if (lastCommand.equals("/")) result /= x; 329.       else if (lastCommand.equals("=")) result = x; 330.    } 331. 332.    private JTextArea display; 333.    private JPanel panel; 334.    private double result; 335.    private String lastCommand; 336.    private boolean start; 337. } 


 javax.jnlp.ServiceManager 

  • static String[] getServiceNames()

    returns the names of all available services.

  • static Object lookup(String name)

    returns a service with a given name.


 javax.jnlp.BasicService 

  • URL getCodeBase()

    returns the codebase of this application.

  • boolean isWebBrowserSupported()

    returns TRue if the Web Start environment can launch a web browser.

  • boolean showDocument(URL url)

    attempts to show the given URL in a browser. Returns true if the request succeeded.


 javax.jnlp.FileContents 

  • InputStream getInputStream()

    returns an input stream to read the contents of the file.

  • OutputStream getOutputStream(boolean overwrite)

    returns an output stream to write to the file. If overwrite is TRue, then the existing contents of the file are overwritten.

  • String getName()

    returns the file name (but not the full directory path).

  • boolean canRead()

  • boolean canWrite()

    return true if the underlying file is readable or writable.


 javax.jnlp.FileOpenService 

  • FileContents openFileDialog(String pathHint, String[] extensions)

  • FileContents[] openMultiFileDialog(String pathHint, String[] extensions)

    display a user warning and a file chooser. Return content descriptors of the file or files that the user selected, or null if the user didn't choose a file.


 javax.jnlp.FileSaveService 

  • FileContents saveFileDialog(String pathHint, String[] extensions, InputStream data, String nameHint)

  • FileContents saveAsFileDialog(String pathHint, String[] extensions, FileContents data)

    display a user warning and a file chooser. Write the data and return content descriptors of the file or files that the user selected, or null if the user didn't choose a file.


 javax.jnlp.PersistenceService 

  • long create(URL key, long maxsize)

    creates a persistent store entry for the given key. Returns the maximum size granted by the persistent store.

  • void delete(URL key)

    deletes the entry for the given key.

  • String[] getNames(URL url)

    returns the relative key names of all keys that start with the given URL.

  • FileContents get(URL key)

    gets a content descriptor through which you can modify the data associated with the given key. If no entry exists for the key, a FileNotFoundException is thrown.

Figure 10-16. The WebStartCalculator application



       
    top



    Core Java 2 Volume I - Fundamentals
    Core Java(TM) 2, Volume I--Fundamentals (7th Edition) (Core Series) (Core Series)
    ISBN: 0131482025
    EAN: 2147483647
    Year: 2003
    Pages: 132

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net