Java ScreenShot
     

Screenshot Core Java 2: Volume I - Fundamentals

Table of Contents
 10.  Deploying Applets and apps


Java Web Start

Java Web Start is a new 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 apps and applets.

  • Java Web Start is used to deliver regular Java apps that are started by calling the main method of a class. There is no need to inherit from Applet.
  • A Java Web Start app does not live inside a browser. It is displayed outside the browser.
  • A Java Web Start app 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 app whenever it loads a Java Web Start app descriptor. That is the same mechanism that is used to launch other helper apps such as Adobe Acrobat or RealAudio. Even hostile browser vendors won't be able to interfere with this mechanism.
  • Once a Java Web Start app has been downloaded, it can be started outside the browser.
  • Java Web Start has stronger support for caching and automatically updating programs than the Java Plug-In. (However, these two products will eventually merge, so that they share the same capabilities for managing program deployment.)
  • Java Web Start has a slightly relaxed "sandbox," which allows unsigned apps some access to local resources.

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

Java graphics exclamatory_icon.gif

To experiment with Java Web Start, install Tomcat from jakarta.apache.org/tomcat. Tomcat is a container for servlets and Java server 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 app from . 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/examples/"
     href="calculator.jnlp">
     <information>
     <title>Calculator Demo app</title>
     <vendor>Cay S. Horstmann</vendor>
     <description>A Calculator</description>
     <offline-allowed/>
     </information>
     <resources>
     <j2se version="1.3+"/>
     <jar href="calculator.jar"/>
     </resources>
     <app-desc/>
    </jnlp>
    


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

  5. 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, you can put them into the webapps/examples directory.

  6. Make sure that Java Web Start has been configured correctly by checking that your browser associates the app/x-java-jnlp-file MIME type with the javaws app. If you installed the SDK, the installation should be automatic.

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

  8. You should see the launch window for Java Web Start (see Screenshot-12). Soon afterwards, the calculator should come up, with a border marking it as a Java Web Start app (see Screenshot-13).

    Screenshot-12. Launching Java Web Start

    Java graphics 10fig12.gif


    Screenshot-13. The Calculator delivered by Java Web Start

    Java graphics 10fig13.gif


  9. Now try launching the app offline. Open the Java Web Start app Manager (see Screenshot-14). In Windows, just select the Java Web Start icon in the Start menu. In Linux, launch the javaws program in your JRE directory. Locate the Calculator Demo app and start it. It will start even if your computer is not connected to the network.

    Screenshot-14. The Java Web Start app Manager

    Java graphics 10fig14.gif


The JNLP API

To allow a Java Web Start app full access to the local machine, the app must be digitally signed. (See of Volume 2 for more information on digital signatures.) Just as with applets, an unsigned applet 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 allows app developers some access to local resources. For example, there are services to load and save files, but they are quite restrictive. The app 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 popping up the file dialog, the program user is alerted and must agree to proceed (see Screenshot-15). Furthermore, the API doesn't actually give the program access to a File object. In particular, the app 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 apps.

Screenshot-15. A Java Web Start Security Advisory

Java graphics 10fig15.gif


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.

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

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


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

Java graphics notes_icon.gif

To compile a program that uses the JNLP API, you must include the file jnlp.jar. You do not need to include that JAR file with your app. It is included with the Java Web Start launcher.

We now discuss the most useful JNLP services. For a complete description, see http://java.oracle.com/products/javawebstart/docs/devvelopersguide.html. 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 byte stream.

  3. Print the information to be saved to the print stream.

  4. Create a ByteArrayInputStream that reads the saved bytes.

  5. Pass that stream to the saveFileDialog method.

You will learn more about streams in . 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. 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 . . .


There is a rudimentary PersistenceService that lets an app store small amounts of configuration information and retrieve it when the app runs again. This service is necessary because an untrusted app 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 app can store arbitrary binary data. (The store may restrict the size of the data block.) In order to isolate apps from each other, a particular app can only use URL keys that start with its codebase (as specified in the JNLP file). For example, if an app is downloaded from http://myserver.com/apps, then it can only use keys of the form http://myserver.com/apps/subkey1/subkey2/... Attempting to access other keys will fail. An app can call the getCodeBase method of the BasicService to find its code base. 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. The program in Example 10-13 is a simple enhancement of the calculator app. 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 app lets you set the frame title. If you run the app again, it retrieves your title choice from the persistent store.

Screenshot-16. The WebStartCalculator app

Java graphics 10fig16.gif


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


javax.jnlp.ServiceManager

Java graphics api_icon.gif
  • 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

Java graphics api_icon.gif
  • URL getCodeBase()

    returns the code base of this app.

  • 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

Java graphics api_icon.gif
  • 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 is overwritten.

  • String getName()

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

  • boolean canRead()
  • boolean canWrite()

    these methods return true if the underlying file is readable or writable.

javax.jnlp.FileOpenService

Java graphics api_icon.gif
  • FileContents openFileDialog(String pathHint, String[] extensions)
  • FileContents[] openMultiFileDialog(String pathHint, String[] extensions)

    These methods display a user warning and a file chooser. They 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

Java graphics api_icon.gif
  • FileContents saveFileDialog(String pathHint, String[] extensions, InputStream data, String nameHint)
  • FileContents saveFileDialog(String pathHint, String[] extensions, FileContents data)

    These methods display a user warning and a file chooser. They 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

Java graphics api_icon.gif
  • 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.

Screenshot

Java ScreenShot
     
Top
 

Comments