Chapter 10. Deploying Applets and Applications

At this point, you should be comfortable with using most of the features of the Java programming language, and you had a pretty thorough introduction to basic graphics programming in Java. We hope that you agree with us that Java is a nice (albeit not perfect), general-purpose OOP language, and the Swing user interface libraries are flexible and useful. That’s nice, but it isn’t what created the original hype around Java. The unbelievable hype during the first few years of Java’s life (as mentioned in Chapter 1) stemmed from applets. An applet is a special kind of Java program that a Java-enabled browser can download from the Internet and then run. The hopes were that users would be freed from the hassles of installing software and that they could access their software from any Java-enabled computer or device with an Internet connection.

For a number of reasons, applets never quite lived up to these expectations. Recently, Sun developed an alternative approach for Internet-based application delivery, called Java Web Start, that fixes some of the problems of applets.

This chapter shows you how to write and deploy applets and Java Web Start applications, how to package applets and applications for deployment, and how applications access and store configuration information.

Applet Basics

You use HTML (the hypertext markup language) to describe the layout of a web page. HTML is simply a vehicle to indicate elements of a hypertext page. For example, <title> indicates the title of the page, and any text that follows this tag becomes the title of the page. You indicate the end of the title with the </title> tag. (This is one of the general rules for tags: a slash followed by the name of the element indicates the end of the element.)

The basic idea of how to use applets in a web page is simple: the HTML page must tell the browser which applets to load and then where to put each applet on the web page. As you might expect, the tag needed to use an applet must tell the browser the following:

  • Where to get the class files;

  • How the applet is positioned on the web page (size, location, and so on).

The browser then retrieves the class files from the Internet (or from a directory on the user’s machine) and automatically runs the applet, using an external Java runtime environment or its built-in Java virtual machine.

In addition to the applet itself, the web page can contain all the other HTML elements you have seen in use on web pages: multiple fonts, bulleted lists, graphics, links, and so on. Applets are just one part of the hypertext page. It is always worth keeping in mind that the Java programming language is not a tool for designing HTML pages; it is a tool for bringing them to life. This is not to say that the GUI design elements in a Java applet are not important, but that they must work with (and, in fact, are subservient to) the underlying HTML design of the web page.

Note

Note

We do not cover general HTML tags; we assume that you know—or are working with someone who knows—the basics of HTML. Only a few special HTML tags are needed for Java applets. We do, of course, cover those later in this chapter. As for learning HTML, you can find dozens of HTML books at your local bookstore. One that covers what you need and that will not insult your intelligence is HTML and XHTML: The Definitive Guide by Chuck Musciano and Bill Kennedy from O’Reilly & Associates.

When applets were first developed, you had to use Sun’s HotJava browser to view web pages that contained applets. Naturally, few users were willing to use a separate browser just to enjoy a new web feature. Java applets became really popular when Netscape included a Java virtual machine in its Navigator browser. Microsoft Internet Explorer soon followed suit. Unfortunately, two problems happened. Netscape didn’t keep up with more modern versions of Java, and Microsoft vacillated between reluctantly supporting outdated Java versions and dropping Java support altogether.

To overcome this problem, Sun released a tool called the “Java Plug-in”. Using the various extension mechanisms of Internet Explorer or Navigator, it seamlessly plugs in to both Netscape and Internet Explorer and allows both browsers to execute Java applets by using an external Java runtime environment that Sun supplies. By keeping the Plug-in up-to-date, you can always take advantage of the latest and greatest features of Java.

Note

Note

To run the applets in this chapter in a browser, you need to install the current version of the Java Plug-in and make sure your browser is connected with the Plug-in. Go to http://java.sun.com/getjava for download and configuration information.

Admittedly, if you are designing web pages for a wide audience, it is probably unreasonable to ask the visitors to your web page to install the Java Plug-in, which is a fairly hefty (if one-time) download. Before turning to applets, you should check whether you can just use HTML forms, JavaScript, and animated GIF files to implement the client user interface. Then place the intelligence on the server, preferably with Java-based servlets and server pages.

On the other hand, if you roll out a very sophisticated Java program, you should ask yourself whether there is any benefit from using the web browser as a delivery vehicle. If not, then you can simply deliver Java applications that your users run on their local machines. You still have all the benefits of Java, such as platform independence, security management, and easy database and network access.

Of course, there are advantages to web deployment. For a user, it is often easier to locate an application on the Web than on the local file system. (This is particularly true for applications that aren’t used every day.) For an administrator, it is easier to maintain and update an application on the web server than to push out bug fixes and improvements to lots of client desktops.

Thus, among the most successful Java programs are corporate intranet applications that interface with corporate information systems. For example, many companies have put expense reimbursement calculators, benefit tracking tools, schedule and vacation planners, purchase order requests, and so on, on their corporate intranet. These programs are relatively small, need to interface with databases, need more flexibility than web forms can provide, and need to be customized to the operations of a particular company. Applets and the Java Web Start mechanisms are excellent delivery technologies for these programs. Because the user population is constrained, it is less of a problem to manage the installation of the Java run time.

We start out with applets, both for the sake of tradition and because understanding applets gives you a head start with the Java Web Start technology.

A Simple Applet

For tradition’s sake, let’s write a NotHelloWorld program as an applet. Before we do that, we want to point out that, from a programming point of view, an applet isn’t very strange. An applet is simply a Java class that extends the java.applet.Applet class. Note that although the java.applet package is not part of the AWT package, an applet is an AWT component, as the inheritance chain shown in Figure 10–1 illustrates. In this book, we will use Swing to implement applets. All of our applets will extend the JApplet class, the superclass for Swing applets. As you can see in Figure 10–1, JApplet is an immediate subclass of the ordinary Applet class.

Applet inheritance hierarchyinheritance hierarchyapplet

Figure 10–1. Applet inheritance hierarchy

Note

Note

If your applet contains Swing components, you must extend the JApplet class. Swing components inside a plain Applet don’t paint correctly.

Example 10–1 shows the code for an applet version of “Not Hello World”.

Notice how similar this is to the corresponding program from Chapter 7. However, because the applet lives inside a web page, there is no need to specify a method for exiting the applet.

Example 10–1. NotHelloWorldApplet.java

 1. /*
 2.   The following HTML tags are required to display this applet in a browser:
 3.   <applet code="NotHelloWorldApplet.class" width="300" height="100">
 4.   </applet>
 5. */
 6.
 7. import javax.swing.*;
 8.
 9. public class NotHelloWorldApplet extends JApplet
10. {
11.    public void init()
12.    {
13.       JLabel label = new JLabel("Not a Hello, World applet", SwingConstants.CENTER);
14.       add(label);
15.    }
16. }

Applet Viewing

To execute the applet, you carry out two steps:

  1. Compile your Java source files into class files.

  2. Create an HTML file that tells the browser which class file to load first and how to size the applet.

It is customary (but not necessary) to give the HTML file the same name as that of the applet class inside. So, following this tradition, we call the file NotHelloWorldApplet.html. Here are the contents of the file:

<applet code="NotHelloWorldApplet.class" width="300" height="300">
</applet>

Before you view the applet in a browser, it is a good idea to test it in the applet viewer program that is a part of the JDK. To use the applet viewer in our example, enter

appletviewer NotHelloWorldApplet.html

at the command line. The command-line argument for the applet viewer program is the name of the HTML file, not the class file. Figure 10–2 shows the applet viewer displaying this applet.

Viewing an applet in the applet viewerappletsviewer

Figure 10–2. Viewing an applet in the applet viewer

Tip

Tip

You can also run applets from inside your editor or integrated environment. In Emacs, select JDE -> Run Applet from the menu. In Eclipse, you select Run -> Run as -> Java Applet from the menu.

Tip

Tip

Here is a weird trick to avoid the additional HTML file. Add an applet tag as a comment inside the source file:

/*
  <applet code="MyApplet.class" width="300" height="300">
  </applet>
*/
public class MyApplet extends JApplet
. . .

Then run the applet viewer with the source file as its command-line argument:

appletviewer NotHelloWorldApplet.java

We aren’t recommending this as standard practice, but it can come in handy if you want to minimize the number of files that you need to worry about during testing.

The applet viewer is good for the first stage of testing, but at some point you need to run your applets in a browser to see them in the same way a user might use them. In particular, the applet viewer program shows you only the applet, not the surrounding HTML text. If an HTML file contains multiple applet tags, the applet viewer pops up multiple windows.

To properly view the applet, you need a Java 2-enabled browser. After installing and configuring the Java Plug-in, simply load the HTML file into the browser (see Figure 10–3). If the applet doesn’t show up, your browser probably uses its built-in virtual machine, and you need to configure it to use the Java Plug-in instead.

Viewing an applet in a browser

Figure 10–3. Viewing an applet in a browser

Tip

Tip

If you make a change to your applet and recompile, you need to restart the browser so that it loads the new class files. Simply refreshing the HTML page will not load the new code. This is a hassle when you are debugging an applet. You can avoid the painful browser restart if you launch the Java console and issue the x command, which clears the classloader cache. Then you can reload the HTML page, and the new applet code is used. You can launch the Java console in Netscape and Mozilla from the menu. Under Windows, open the Java Plug-in console in the Windows control panel and request that the Java console be displayed.

Note

Note

For older browsers (in particular, Netscape 4), you need to replace the applet tags with special object or embed tags in order to cause the browser to load the Plug-In. The page http://java.sun.com/j2se/5.0/docs/guide/plugin/developer_guide/html_converter.html describes this process and lets you download a tool that automates the web page conversion. With modern browsers, the conversion is no longer necessary.

Application Conversion to Applets

It is easy to convert a graphical Java application (that is, an application that uses the AWT and that you can start with the java program launcher) into an applet that you can embed in a web page. Essentially, all of the user interface code can stay the same.

Here are the specific steps for converting an application to an applet.

  1. Make an HTML page with the appropriate tag to load the applet code.

  2. Supply a subclass of the JApplet class. Make this class public. Otherwise, the applet cannot be loaded.

  3. Eliminate the main method in the application. Do not construct a frame window for the application. Your application will be displayed inside the browser.

  4. Move any initialization code from the frame window constructor to the init method of the applet. You don’t need to explicitly construct the applet object—the browser instantiates it for you and calls the init method.

  5. Remove the call to setSize; for applets, sizing is done with the width and height parameters in the HTML file.

  6. Remove the call to setDefaultCloseOperation. An applet cannot be closed; it terminates when the browser exits.

  7. If the application calls setTitle, eliminate the call to the method. Applets cannot have title bars. (You can, of course, title the web page itself, using the HTML title tag.)

  8. Don’t call setVisible(true). The applet is displayed automatically.

Note

Note

On page 520, you will see how to implement a program that is both an applet and an application.

As an example of this transformation, we will change the calculator application from Chapter 9 into an applet. In Figure 10–4, you can see how it looks, sitting inside a web page.

A calculator applet

Figure 10–4. A calculator applet

Example 10–2 shows the HTML page. Note that there is some text in addition to the applet tags.

Example 10–2. Calculator.html

1. <html>
2.    <head><title>A Calculator</title></head>
3.    <body>
4.       <p>Here is a calculator, just in case you can't find yours.</p>
5.       <applet code="CalculatorApplet.class" width="180" height="180">
6.       </applet>
7.    </body>
8. </html>

Example 10–3 is the code for the applet. We introduced a subclass of JApplet, placed the initialization code into the init method, and removed the calls to setTitle, setSize, setDefaultCloseOperation, and setVisible. The CalculatorPanel class is taken from Chapter 9, and its code is omitted.

Example 10–3. CalculatorApplet.java

 1. import java.awt.*;
 2. import javax.swing.*;
 3.
 4. public class CalculatorApplet extends JApplet
 5. {
 6.    public void init()
 7.    {
 8.       CalculatorPanel panel = new CalculatorPanel();
 9.       add(panel);
10.    }
11. }
CalculatorApplet.java
java.applet.Applet 1.0
  • void init()

    is called when the applet is first loaded. Override this method and place all initialization code here.

  • void resize(int width, int height)

    requests that the applet be resized. This would be a great method if it worked on web pages; unfortunately, it does not work in current browsers because it interferes with their page-layout mechanisms.

Life Cycle of an Applet

Four methods in the Applet class give you the framework on which you build any serious applet: init, start, stop, and destroy. What follows is a short description of these methods, occasions when these methods are called, and the code you should place into them.

  • init

    This method is intended for whatever initialization is needed for your applet. It is called after the param tags inside the applet tag have been processed. Common actions in an applet include processing param values (see page 508) and adding user interface components.

    Applets can have a default constructor, but it is customary to perform all initialization in the init method instead of the default constructor.

  • start

    This method is automatically called after the browser calls the init method. It is also called whenever the user returns to the page containing the applet after having gone off to other pages. This means that the start method can be called repeatedly, unlike the init method. For this reason, put the code that you want executed only once in the init method, rather than in the start method. The start method is where you usually restart a thread for your applet, for example, to resume an animation. If your applet does nothing that needs to be suspended when the user leaves the current web page, you do not need to implement this method (or the stop method).

  • stop

    This method is automatically called when the user moves off the page on which the applet sits. It can, therefore, be called repeatedly in the same applet. Its purpose is to give you a chance to stop a time-consuming activity from slowing the system when the user is not paying attention to the applet. You would implement this method to stop an animation or audio playback.

  • destroy

    This method is only called when the browser shuts down normally. Because applets are meant to live on an HTML page, you should not normally leave resources behind after a user leaves the page that contains the applet. But if you do, then you can close down the resources by overriding the destroy method.

Life Cycle of an Applet
java.applet.Applet 1.0
  • void start()

    override this method for code that needs to be executed every time the user visits the browser page containing this applet. A typical action is to reactivate a thread.

  • void stop()

    override this method for code that needs to be executed every time the user leaves the browser page containing this applet. A typical action is to deactivate a thread.

  • void destroy()

    override this method for code that needs to be executed when the user exits the browser.

Security Basics

Because applets are designed to be loaded from a remote site and then executed locally, security becomes vital. If a user enables Java in the browser, the browser will download all the applet code on the web page and execute it immediately. The user never gets a chance to confirm or to stop individual applets from running. For this reason, applets (unlike applications) are restricted in what they can do. The applet security manager throws a SecurityException whenever an applet attempts to violate one of the access rules.

What can applets do on all platforms? They can show images and play sounds, get keystrokes and mouse clicks from the user, and send user input back to the host from which they were loaded. That is enough functionality to show facts and figures or to get user input for placing an order. The restricted execution environment for applets is often called the “sandbox.” Applets playing in the “sandbox” cannot alter the user’s system or spy on it. In this chapter, we look only at applets that run inside the sandbox.

In particular, when running in the sandbox,

  • Applets can never run any local executable program.

  • Applets cannot communicate with any host other than the server from which they were downloaded; that server is called the originating host. This rule is often called “applets can only phone home.” The rule protects applet users from applets that might try to spy on intranet resources.

  • Applets cannot read from or write to the local computer’s file system.

  • Applets cannot find out any information about the local computer, except for the Java version used, the name and version of the operating system, and the characters used to separate files (for instance, / or ), paths (such as : or ;), and lines (such as or ). In particular, applets cannot find out the user’s name, e-mail address, and so on.

  • All windows that an applet pops up carry a warning message.

All this is possible only because applets are executed by the Java virtual machine and not directly by the CPU on the user’s computer. Because the virtual machine checks all critical instructions and program areas, a hostile (or poorly written) applet will almost certainly not be able to crash the computer, overwrite system memory, or change the privileges granted by the operating system.

These restrictions are too strong for some situations. For example, on a corporate intranet, you can certainly imagine an applet wanting to access local files. To allow for different levels of security under different situations, you can use signed applets. A signed applet carries with it a certificate that indicates the identity of the signer. Cryptographic techniques ensure that such a certificate cannot be forged. If you trust the signer, you can choose to give the applet additional rights. (We cover code signing in Volume 2.)

The point is that if you trust the signer of the applet, you can tell the browser to give the applet more privileges. You can, for example, give applets in your corporate intranet a higher level of trust than those from www.cracker.com. The configurable Java security model allows the continuum of privilege levels you need. You can give completely trusted applets the same privilege levels as local applications. Programs from vendors that are known to be somewhat flaky can be given access to some, but not all, privileges. Unknown applets can be restricted to the sandbox.

Note

Note

Java Web Start applications (discussed later in this chapter) have a slightly more versatile sandbox. Some system resources can be accessed, provided the program user agrees.

To sum up, Java has three separate mechanisms for enforcing security:

  1. Program code is interpreted by the Java virtual machine, not executed directly.

  2. A security manager checks all sensitive operations in the Java runtime library.

  3. Applets can be signed to identify their origin.

Note

Note

In contrast, the security model of the ActiveX technology by Microsoft relies solely on the third option. If you want to run an ActiveX control at all, you must trust it completely. That model works fine when you deal with a small number of trusted suppliers, but it simply does not scale up to the World Wide Web. If you use Internet Explorer, you will see the ActiveX mechanism at work. You’ll need to accept Sun’s certificate to install the Java Plug-in on Internet Explorer. The certificate tells you that the code came from Sun. It doesn’t tell you anything about the quality of the code. Once you accept the installation, the program runs without any further security checks.

Pop-Up Windows in Applets

An applet sits embedded in a web page, in a frame whose width is given by the width and height attributes in the applet tag. This can be quite limiting. Many programmers wonder whether they can have a pop-up window to make better use of the available space. It is indeed possible to create a pop-up frame. Here is a simple applet with a single button labeled Calculator. When you click on the button, a calculator pops up in a separate window.

The pop-up is easy to do. Simply use a JFrame, but don’t call setDefaultCloseOperation.

frame = new CalculatorFrame();
frame.setTitle("Calculator");
frame.setSize(200, 200);

When the user clicks the button, toggle the frame so that it is shown if it isn’t visible and hidden if it is. When you click on the calculator button, the dialog box pops up and floats over the web page. When you click on the button again, the calculator goes away.

JButton calcButton = new JButton("Calculator");
calcButton.addActionListener(new
   ActionListener()
   {

   public void actionPerformed(ActionEvent evt)
   {
      frame.setVisible(!frame.isVisible());
   }
});

There is, however, an issue that you need to consider before you put this applet on your web page. To see how the calculator looks to a potential user, load the web page from a browser, not the applet viewer. The calculator will be surrounded by a border with a warning message (see Figure 10–5).

A window that pops up over a browser

Figure 10–5. A window that pops up over a browser

In early browser versions, that message was very ominous: “Untrusted Java Applet Window”. Every successive version of the JDK watered down the warning a bit—“Unauthenticated Java Applet Window”, or “Warning: Java Applet Window”. Now it is simply “Java Applet Window”.

This message is a security feature of all web browsers. The browser wants to make sure that your applet does not launch a window that the user might mistake for a local application. The fear is that an unsuspecting user could visit a web page, which automatically launches the applets on it, and mistakenly type in a password or credit card number, which the applet could send back to its host.

To avoid any possibility of shenanigans like this, all pop-up windows launched by an applet bear a warning label. You can configure the Java Plug-in to omit the warning message for pop-up windows that are spawned by signed applets.

Example 10–4 shows the code for the PopupCalculatorApplet class.

Example 10–4. PopupCalculatorApplet.java

 1. import java.awt.*;
 2. import java.awt.event.*;
 3. import javax.swing.*;
 4.
 5. public class PopupCalculatorApplet extends JApplet
 6. {
 7.    public void init()
 8.    {
 9.       // create a frame with a calculator panel
10.
11.       final JFrame frame = new JFrame();
12.       frame.setTitle("Calculator");
13.       frame.setSize(200, 200);
14.       frame.add(new CalculatorPanel());
15.
16.       // add a button that pops up or hides the calculator
17.
18.       JButton calcButton = new JButton("Calculator");
19.       add(calcButton);
20.
21.       calcButton.addActionListener(new
22.          ActionListener()
23.          {
24.             public void actionPerformed(ActionEvent event)
25.             {
26.                frame.setVisible(!frame.isVisible());
27.             }
28.          });
29.    }
30. }

The Applet HTML Tags and Attributes

In its most basic form, an example for using the applet tag looks like this:

<applet code="NotHelloWorldApplet.class" width="300" height="100">

As you have seen, the code attribute gives the name of the class file and must include the .class extension; the width and height attributes size the window that will hold the applet. Both are measured in pixels. You also need a matching </applet> tag that marks the end of the HTML tagging needed for an applet. The text between the <applet> and </applet> tags is displayed only if the browser cannot show applets. The code, width, and height attributes are required. If any are missing, the browser cannot load your applet.

All this information would usually be embedded in an HTML page that, at the very least, might look like this:

<html>
   <head>
      <title>NotHelloWorldApplet</title>
   </head>
   <body>
      <p>The next line of text is displayed under the auspices of Java:</p>
      <applet code="NotHelloWorldApplet.class" width="100" height="100">
         If your browser could show Java, you would see an applet here.
      </applet>
   </body>
</html>

Note

Note

According to the HTML specification, the HTML tags and attributes such as applet can be in upper or lower case. We use lower case because that is the recommendation of the newer XHTML specification. However, the name of the applet class is case sensitive! The letter case may be significant in other attributes, such as names of JAR files, if the web server file system is case sensitive.

Applet Attributes for Positioning

What follows are short discussions of the various attributes that you can (or must) use within the applet tag to position your applet. Readers familiar with HTML will recognize that many of these attributes are similar to those used with the img tag for image placement on a web page.

  • width, height

    These attributes are required and give the width and height of the applet, measured in pixels. In the applet viewer, this is the initial size of the applet. You can resize any window that the applet viewer creates. In a browser, you cannot resize the applet. You will need to make a good guess about how much space your applet requires to show up well for all users.

  • align

    This attribute specifies the alignment of the applet. There are two basic choices. The applet can be a block with text flowing around it, or the applet can be inline, floating inside a line of text as if it were an oversized text character. The first two values (left and right) make the text flow around the applet. The others make the applet flow with the text.

    The choices are described in Table 10–1.

    Table 10–1. Applet Positioning Attributes

    Attribute

    What It Does

    left

    Places the applet at the left margin of the page. Text that follows on the page goes in the space to the right of the applet.

    right

    Places the applet at the right margin of the page. Text that follows on the page goes in the space to the left of the applet.

    top

    Places the top of the applet with the top of the current line.

    texttop

    Places the top of the applet with the top of the text in the current line.

    middle

    Places the middle of the applet with the baseline of the text in the current line.

    absmiddle

    Places the middle of the applet with the middle of the current line.

    baseline

    Places the bottom of the applet with the baseline of the text in the current line.

    bottom

    Places the bottom of the applet at the bottom of the text in the current line.

    absbottom

    Places the bottom of the applet with the bottom of the current line.

    vspace, hspace

    These optional attributes specify the number of pixels above and below the applet (vspace) and on each side of the applet (hspace).

    Figure 10–6 shows all alignment options for an applet that floats with the surrounding text. The vertical bar at the beginning of each line is an image. Because the image is taller than the text, you can see the difference between alignment with the top or bottom of the line and the top or bottom of the text.

    Applet alignment

    Figure 10–6. Applet alignment

Applet Attributes for Code

The following applet attributes tell the browser how to locate the applet code; here are short descriptions.

  • code

    This attribute gives the name of the applet’s class file. This name is taken relative to the codebase (see below) or relative to the current page if the codebase is not specified.

    The path name must match the package of the applet class. For example, if the applet class is in the package com.mycompany, then the attribute is code="com/mycompany/MyApplet.class". The alternative code="com.mycompany.MyApplet.class" is also permitted. But you cannot use absolute path names here. Use the codebase attribute if your class file is located elsewhere.

    The code attribute specifies only the name of the class that contains the applet class. Of course, your applet may contain other class files. Once the browser’s class loader loads the class containing the applet, it will realize that it needs more class files and will load them.

    Either the code or the object attribute (see below) is required.

  • codebase

    This optional attribute is a URL for locating the class files. You can use an absolute URL, even to a different server. Most commonly, though, this is a relative URL that points to a subdirectory. For example, if the file layout looks like this:

    Applet Attributes for Code

    then you use the following tag in MyPage.html:

    <applet code="CalculatorApplet.class" codebase="myApplets" width="100" height="150">
    
  • archive

    This optional attribute lists the Java archive file or files containing classes and other resources for the applet. (See the section on JAR files later in this chapter for more on Java archive files.) These files are fetched from the web server before the applet is loaded. This technique speeds up the loading process significantly because only one HTTP request is necessary to load a JAR file that contains many smaller files. The JAR files are separated by commas. For example:

    <applet code="CalculatorApplet.class"
       archive="CalculatorClasses.jar,corejava/CoreJavaClasses.jar"
       width="100" height="150">
    
  • object

    This tag lets you specify the name of a file that contains the serialized applet object. (An object is serialized when you write all its instance fields to a file. We discuss serialization in Chapter 12.) To display the applet, the object is deserialized from the file to return it to its previous state. When you use this attribute, the init method is not called, but the applet’s start method is called. Before serializing an applet object, you should call its stop method. This feature is useful for implementing a persistent browser that automatically reloads its applets and has them return to the same state that they were in when the browser was closed. This is a specialized feature, not normally encountered by web page designers.

    Either code or object must be present in every applet tag. For example,

    <applet object="CalculatorApplet.ser" width="100" height="150">
    
  • name

    Scripters will want to give the applet a name attribute that they can use to refer to the applet when scripting. Both Netscape and Internet Explorer let you call methods of an applet on a page through JavaScript. This is not a book on JavaScript, so we only give you a brief idea of the code that is required to call Java code from JavaScript.

    Note

    Note

    JavaScript is a scripting language that can be used inside web pages, invented by Netscape and originally called LiveScript. It has little to do with Java, except for some similarity in syntax. It was a marketing move to call it JavaScript. A subset (with the catchy name of ECMAScript) is standardized as ECMA-262. But, to nobody’s surprise, Netscape and Microsoft support incompatible extensions of that standard in their browsers. For more information on JavaScript, we recommend the book JavaScript: The Definitive Guide by David Flanagan, published by O’Reilly & Associates.

    To access an applet from JavaScript, you first have to give it a name.

    <applet code="CalculatorApplet.class" width="100" height="150" name="calc">
    </applet>
    

    You can then refer to the object as document.applets.appletname. For example,

    var calcApplet = document.applets.calc;
    

    Through the magic of the integration between Java and JavaScript that both Netscape and Internet Explorer provide, you can call applet methods:

    calcApplet.clear();
    

    (Our calculator applet doesn’t have a clear method; we just want to show you the syntax.)

    The name attribute is also essential when you want two applets on the same page to communicate with each other directly. You specify a name for each current applet instance.

    You pass this string to the getApplet method of the AppletContext class. We discuss this mechanism, called inter-applet communication, later in this chapter.

Note

Note

In http://www.javaworld.com/javatips/jw-javatip80.html, Francis Lu uses JavaScript-to-Java communication to solve an age-old problem: to resize an applet so that it isn’t bound by hardcoded width and height attributes. This is a good example of the integration between Java and JavaScript. It also shows how messy it is to write JavaScript that works on multiple browsers.

Applet Attributes for Java-Challenged Viewers

If a web page containing an applet tag is viewed by a browser that is not aware of Java applets, then the browser ignores the unknown applet and param tags. All text between the <applet> and </applet> tags is displayed by the browser. Conversely, Java-aware browsers do not display any text between the <applet> and </applet> tags. You can display messages inside these tags for those poor folks that use a prehistoric browser. For example,

<applet code="CalculatorApplet.class" width="100" height="150">
   If your browser could show Java, you would see a calculator here.
</applet>

Of course, nowadays most browsers know about Java, but Java may be deactivated, perhaps by the user or by a paranoid system administrator. You can then use the alt attribute to display a message to these unfortunate souls.

<applet code="CalculatorApplet.class" width="100" height="150"
   alt="If your browser could show Java, you would see a calculator here">

The object Tag

The object tag is part of the HTML 4.0 standard, and the W3 consortium suggests that people use it instead of the applet tag. There are 35 different attributes to the object tag, most of which (such as onkeydown) are relevant only to people writing Dynamic HTML. The various positioning attributes such as align and height work exactly as they did for the applet tag. The key attribute in the object tag for your Java applets is the classid attribute. This attribute specifies the location of the object. Of course, object tags can load different kinds of objects, such as Java applets or ActiveX components like the Java Plug-in itself. In the codetype attribute, you specify the nature of the object. For example, Java applets have a code type of application/java. Here is an object tag to load a Java applet:

<object
   codetype="application/java"
   classid="java:CalculatorApplet.class"
   width="100" height="150">

Note that the classid attribute can be followed by a codebase attribute that works exactly as it did with the applet tag.

Use of Parameters to Pass Information to Applets

Just as applications can use command-line information, applets can use parameters that are embedded in the HTML file. This is done by the HTML tag called param along with attributes that you define. For example, suppose you want to let the web page determine the style of the font to use in your applet. You could use the following HTML tags:

<applet code="FontParamApplet.class" width="200" height="200">
   <param name="font" value="Helvetica"/>
</applet>

You then pick up the value of the parameter, using the getParameter method of the Applet class, as in the following example:

public class FontParamApplet extends JApplet
{
   public void init()
   {
      String fontName = getParameter("font");
      . . .
   }
   . . .
}

Note

Note

You can call the getParameter method only in the init method of the applet, not in the constructor. When the applet constructor is executed, the parameters are not yet prepared. Because the layout of most nontrivial applets is determined by parameters, we recommend that you don’t supply constructors to applets. Simply place all initialization code into the init method.

Parameters are always returned as strings. You need to convert the string to a numeric type if that is what is called for. You do this in the standard way by using the appropriate method, such as parseInt of the Integer class.

For example, if we wanted to add a size parameter for the font, then the HTML code might look like this:

<applet code="FontParamApplet.class" width="200" height="200">
   <param name="font" value="Helvetica"/>
   <param name="size" value="24"/>
</applet>

The following source code shows how to read the integer parameter.

public class FontParamApplet extends JApplet
{
   public void init()
   {
      String fontName = getParameter("font");
      int fontSize = Integer.parseInt(getParameter("size"));
      . . .
    }
}

Note

Note

The strings used when you define the parameters with the param tag and those used in the getParameter method must match exactly. In particular, both are case sensitive.

In addition to ensuring that the parameters match in your code, you should find out whether or not the size parameter was left out. You do this with a simple test for null. For example:

int fontsize;
String sizeString = getParameter("size");
if (sizeString == null) fontSize = 12;
else fontSize = Integer.parseInt(sizeString);

Here is a useful applet that uses parameters extensively. The applet draws a bar chart, shown in Figure 10–7.

A chart appletchart applet

Figure 10–7. A chart applet

This applet takes the labels and the heights of the bars from the param values in the HTML file. Here is what the HTML file for Figure 10–7 looks like:

<applet code="Chart.class" width="400" height="300">
   <param name="title" value="Diameters of the Planets"/>
   <param name="values" value="9"/>
   <param name="name.1" value="Mercury"/>
   <param name="name.2" value="Venus"/>
   <param name="name.3" value="Earth"/>
   <param name="name.4" value="Mars"/>
   <param name="name.5" value="Jupiter"/>
   <param name="name.6" value="Saturn"/>
   <param name="name.7" value="Uranus"/>
   <param name="name.8" value="Neptune"/>
   <param name="name.9" value="Pluto"/>
   <param name="value.1" value="3100"/>
   <param name="value.2" value="7500"/>
   <param name="value.3" value="8000"/>
   <param name="value.4" value="4200"/>
   <param name="value.5" value="88000"/>
   <param name="value.6" value="71000"/>
   <param name="value.7" value="32000"/>
   <param name="value.8" value="30600"/>
   <param name="value.9" value="1430"/>
</applet>

You could have set up an array of strings and an array of numbers in the applet, but there are two advantages to using the parameter mechanism instead. You can have multiple copies of the same applet on your web page, showing different graphs: just put two applet tags with different sets of parameters on the page. And you can change the data that you want to chart. Admittedly, the diameters of the planets will stay the same for quite some time, but suppose your web page contains a chart of weekly sales data. It is easy to update the web page because it is plain text. Editing and recompiling a Java file weekly is more tedious.

In fact, there are commercial JavaBeans components (beans) that make much fancier graphs than the one in our chart applet. If you buy one, you can drop it into your web page and feed it parameters without ever needing to know how the applet renders the graphs.

Example 10–5 is the source code of our chart applet. Note that the init method reads the parameters, and the paintComponent method draws the chart.

Example 10–5. Chart.java

  1. import java.awt.*;
  2. import java.awt.font.*;
  3. import java.awt.geom.*;
  4. import javax.swing.*;
  5.
  6. public class Chart extends JApplet
  7. {
  8.    public void init()
  9.    {
 10.       String v = getParameter("values");
 11.       if (v == null) return;
 12.       int n = Integer.parseInt(v);
 13.       double[] values = new double[n];
 14.       String[] names = new String[n];
 15.       for (int i = 0; i < n; i++)
 16.       {
 17.          values[i] = Double.parseDouble(getParameter("value." + (i + 1)));
 18.          names[i] = getParameter("name." + (i + 1));
 19.       }
 20.
 21.       add(new ChartPanel(values, names, getParameter("title")));
 22.    }
 23. }
 24.
 25. /**
 26.    A panel that draws a bar chart.
 27. */
 28. class ChartPanel extends JPanel
 29. {
 30.    /**
 31.        Constructs a ChartPanel.
 32.        @param v the array of values for the chart
 33.        @param n the array of names for the values
 34.        @param t the title of the chart
 35.    */
 36.    public ChartPanel(double[] v, String[] n, String t)
 37.    {
 38.       values = v;
 39.       names = n;
 40.       title = t;
 41.    }
 42.
 43.    public void paintComponent(Graphics g)
 44.    {
 45.       super.paintComponent(g);
 46.       Graphics2D g2 = (Graphics2D) g;
 47.
 48.       // compute the minimum and maximum values
 49.       if (values == null) return;
 50.       double minValue = 0;
 51.       double maxValue = 0;
 52.       for (double v : values)
 53.       {
 54.          if (minValue > v) minValue = v;
 55.          if (maxValue < v) maxValue = v;
 56.       }
 57.       if (maxValue == minValue) return;
 58.
 59.       int panelWidth = getWidth();
 60.       int panelHeight = getHeight();
 61.
 62.       Font titleFont = new Font("SansSerif", Font.BOLD, 20);
 63.       Font labelFont = new Font("SansSerif", Font.PLAIN, 10);
 64.
 65.       // compute the extent of the title
 66.       FontRenderContext context = g2.getFontRenderContext();
 67.       Rectangle2D titleBounds = titleFont.getStringBounds(title, context);
 68.       double titleWidth = titleBounds.getWidth();
 69.       double top = titleBounds.getHeight();
 70.
 71.       // draw the title
 72.       double y = -titleBounds.getY(); // ascent
 73.       double x = (panelWidth - titleWidth) / 2;
 74.       g2.setFont(titleFont);
 75.       g2.drawString(title, (float) x, (float) y);
 76.
 77.       // compute the extent of the bar labels
 78.       LineMetrics labelMetrics = labelFont.getLineMetrics("", context);
 79.       double bottom = labelMetrics.getHeight();
 80.
 81.       y = panelHeight - labelMetrics.getDescent();
 82.       g2.setFont(labelFont);
 83.
 84.       // get the scale factor and width for the bars
 85.       double scale = (panelHeight - top - bottom) / (maxValue - minValue);
 86.       int barWidth = panelWidth / values.length;
 87.
 88.       // draw the bars
 89.       for (int i = 0; i < values.length; i++)
 90.       {
 91.          // get the coordinates of the bar rectangle
 92.          double x1 = i * barWidth + 1;
 93.          double y1 = top;
 94.          double height = values[i] * scale;
 95.          if (values[i] >= 0)
 96.             y1 += (maxValue - values[i]) * scale;
 97.          else
 98.          {
 99.             y1 += maxValue * scale;
100.             height = -height;
101.          }
102.
103.          // fill the bar and draw the bar outline
104.          Rectangle2D rect = new Rectangle2D.Double(x1, y1, barWidth - 2, height);
105.          g2.setPaint(Color.RED);
106.          g2.fill(rect);
107.          g2.setPaint(Color.BLACK);
108.          g2.draw(rect);
109.
110.          // draw the centered label below the bar
111.          Rectangle2D labelBounds = labelFont.getStringBounds(names[i], context);
112.
113.          double labelWidth = labelBounds.getWidth();
114.          x = x1 + (barWidth - labelWidth) / 2;
115.          g2.drawString(names[i], (float) x, (float) y);
116.       }
117.    }
118.
119.    private double[] values;
120.    private String[] names;
121.    private String title;
122. }
Chart.java
java.applet.Applet 1.0
  • public String getParameter(String name)

    gets the value of a parameter defined with a param tag in the web page loading the applet. The string name is case sensitive.

  • public String getAppletInfo()

    is a method that many applet authors override to return a string that contains information about the author, version, and copyright of the current applet. You need to create this information by overriding this method in your applet class.

  • public String[][] getParameterInfo()

    is a method that you can override to return an array of param tag options that this applet supports. Each row contains three entries: the name, the type, and a description of the parameter. Here is an example:

    "fps", "1-10", "frames per second"
    "repeat", "boolean", "repeat image loop?"
    "images", "url", "directory containing images"
    

Multimedia

Applets can handle both images and audio. As we write this, images must be in GIF, PNG, or JPEG form, audio files in AU, AIFF, WAV, or MIDI. Animated GIFs are supported, and the animation is displayed. Usually the files containing this information are specified as a URL, so we take up URLs first.

Encapsulating URLs

A URL is really nothing more than a description of a resource on the Internet. For example, "http://java.sun.com/index.html" tells the browser to use the hypertext transfer protocol on the file index.html located at java.sun.com. Java has the class URL that encapsulates URLs. The simplest way to make a URL is to give a string to the URL constructor:

URL u = new URL("http://java.sun.com/index.html");

This is called an absolute URL because we specify the entire resource name. Another useful URL constructor is a relative URL.

URL data = new URL(u, "data/planets.dat");

This specifies the file planets.dat, located in the data subdirectory of the URL u.

A common way of obtaining a URL is to ask an applet where it came from, in particular:

  • What is the URL of the HTML page in which the applet is contained?

  • What is the URL of the applet’s codebase directory?

To find the former, use the getDocumentBase method; to find the latter, use getCodeBase.

Note

Note

In prior versions of the JDK, there was considerable confusion about these methods—see bug #4456393 on the Java bug parade (http://bugs.sun.com/bugdatabase/index.jsp). The documentation has finally been clarified in JDK 5.0.

Note

Note

You can access secure web pages (https URLs) from applets and through the Java Plug-in—see http://java.sun.com/products/plugin/1.3/docs/https.html. This uses the SSL capabilities of the underlying browser.

Obtaining Multimedia Files

You can retrieve images and audio files with the getImage and getAudioClip methods. For example:

Image cat = getImage(getCodeBase(), "images/cat.gif");
AudioClip meow = getAudioClip(getCodeBase(), "audio/meow.au");

Here, we use the getCodeBase method that returns the URL from which your applet code is loaded. The second argument of the method calls specifies where the image or audio clip is located, relative to the base document.

Note

Note

The images and audio clips must be located on the same server that hosts the applet code. For security reasons, applets cannot access files on another server (“applets can only phone home”).

You saw in Chapter 7 how to display an image. To play an audio clip, simply invoke its play method. You can also call the play method of the Applet class without first loading the audio clip.

play(getCodeBase(), "audio/meow.au");

For faster downloading, multimedia objects can be stored in JAR files (see the section below). The getImage and getAudioClip/play methods automatically search the JAR files of the applet. If the image or audio file is contained in the JAR file, it is loaded immediately. Otherwise, the browser requests it from the web server.

Note
java.net.URL 1.0
  • URL(String name)

    creates a URL object from a string describing an absolute URL.

  • URL(URL base, String name)

    creates a relative URL object. If the string name describes an absolute URL, then the base URL is ignored. Otherwise, it is interpreted as a relative directory from the base URL.

Note
java.applet.Applet 1.0
  • URL getDocumentBase()

    gets the URL of the web page containing this applet.

  • URL getCodeBase()

    gets the URL of the codebase directory from which this applet is loaded. That is either the absolute URL of the directory referenced by the codebase attribute or the directory of the HTML file if no codebase is specified.

  • void play(URL url)

  • void play(URL url, String name)

    The first form plays an audio file specified by the URL. The second form uses the string to provide a path relative to the URL in the first parameter. Nothing happens if the audio clip cannot be found.

  • AudioClip getAudioClip(URL url)

  • AudioClip getAudioClip(URL url, String name)

    The first form gets an audio clip from the given URL. The second form uses the string to provide a path relative to the URL in the first argument. The methods return null if the audio clip cannot be found.

  • Image getImage(URL url)

  • Image getImage(URL url, String name)

    return an image object that encapsulates the image specified by the URL. If the image does not exist, immediately returns null. Otherwise, a separate thread is launched to load the image.

The Applet Context

An applet runs inside a browser or the applet viewer. An applet can ask the browser to do things for it, for example, fetch an audio clip, show a short message in the status line, or display a different web page. The ambient browser can carry out these requests, or it can ignore them. For example, if an applet running inside the applet viewer asks the applet viewer program to display a web page, nothing happens.

To communicate with the browser, an applet calls the getAppletContext method. That method returns an object that implements an interface of type AppletContext. You can think of the concrete implementation of the AppletContext interface as a communication path between the applet and the ambient browser. In addition to getAudioClip and getImage, the AppletContext interface contains several useful methods, which we discuss in the next few sections.

Inter-Applet Communication

A web page can contain more than one applet. If a web page contains multiple applets from the same codebase, they can communicate with each other. Naturally, this is an advanced technique that you probably will not need very often.

If you give name attributes to each applet in the HTML file, you can use the getApplet method of the AppletContext interface to get a reference to the applet. For example, if your HTML file contains the tag

<applet code="Chart.class" width="100" height="100" name="Chart1">

then the call

Applet chart1 = getAppletContext().getApplet("Chart1");

gives you a reference to the applet. What can you do with the reference? Provided you give the Chart class a method to accept new data and redraw the chart, you can call this method by making the appropriate cast.

((Chart) chart1).setData(3, "Earth", 9000);

You can also list all applets on a web page, whether or not they have a name attribute. The getApplets method returns an enumeration object. (You learn more about enumeration objects in Volume 2.) Here is a loop that prints the class names of all applets on the current page.

Enumeration e = getAppletContext().getApplets();
while (e.hasMoreElements())
{
   Object a = e.nextElement();
   System.out.println(a.getClass().getName());
}

An applet cannot communicate with an applet on a different web page.

Display of Items in the Browser

You have access to two areas of the ambient browsers: the status line and the web page display area. Both use methods of the AppletContext class.

You can display a string in the status line at the bottom of the browser with the showStatus message, for example,

showStatus("Loading data . . . please wait");

Tip

Tip

In our experience, showStatus is of limited use. The browser is also using the status line, and, more often than not, it will overwrite your precious message with chatter like “Applet running.” Use the status line for fluff messages like “Loading data . . . please wait,” but not for something that the user cannot afford to miss.

You can tell the browser to show a different web page with the showDocument method. There are several ways to do this. The simplest is with a call to showDocument with one argument, the URL you want to show.

URL u = new URL("http://java.sun.com/index.html");
getAppletContext().showDocument(u);

The problem with this call is that it opens the new web page in the same window as your current page, thereby displacing your applet. To return to your applet, the user must click the Back button of the browser.

You can tell the browser to show the document in another window by giving a second parameter in the call to showDocument (see Table 10–2). If you supply the special string "_blank", the browser opens a new window with the document, instead of displacing the current document. More important, if you take advantage of the frame feature in HTML, you can split a browser window into multiple frames, each of which has a name. You can put your applet into one frame and have it show documents in other frames. We show you an example of how to do this in the next section.

Table 10–2. The showDocument Method

Target Parameter

Location

"_self" or none

Show the document in the current frame.

"_parent"

Show the document in the parent frame.

"_top"

Show the document in the topmost frame.

"_blank"

Show in new, unnamed, top-level window.

Any other string

Show in the frame with that name. If no frame with that name exists, open a new window and give it that name.

Note

Note

Sun’s applet viewer does not show web pages. The showDocument method is ignored in the applet viewer.

Note
java.applet.Applet 1.2
  • public AppletContext getAppletContext()

    gives you a handle to the applet’s browser environment. On most browsers, you can use this information to control the browser in which the applet is running.

  • void showStatus(String msg)

    shows the string specified in the status line of the browser.

Note
java.applet.AppletContext 1.2
  • Enumeration getApplets()

    returns an enumeration (see Volume 2) of all the applets in the same context, that is, the same web page.

  • Applet getApplet(String name)

    returns the applet in the current context with the given name; returns null if none exists. Only the current web page is searched.

  • void showDocument(URL url)

  • void showDocument(URL url, String target)

    show a new web page in a frame in the browser. In the first form, the new page displaces the current page. The second form uses the target parameter to identify the target frame (see Table 10–2 on page 516).

A Bookmark Applet

This applet takes advantage of the frame feature in HTML. We divide the screen vertically into two frames. The left frame contains a Java applet that shows a list of bookmarks. When you select any of the bookmarks, the applet tells the browser to display the corresponding web page (see Figure 10–8).

A bookmark applet

Figure 10–8. A bookmark applet

Example 10–6 shows the HTML file that defines the frames.

Example 10–6. Bookmark.html

 1. <html>
 2.    <head>
 3.       <title>Bookmark Applet</title>
 4.    </head>
 5.    <frameset cols="320,*">
 6.       <frame name="left" src="Left.html"
 7.          marginheight="2" marginwidth="2"
 8.          scrolling="no" noresize="noresize"/>
 9.       <frame name="right" src="Right.html"
10.          marginheight="2" marginwidth="2"
11.          scrolling="yes" noresize="noresize"/>
12.    </frameset>
13. </html>

We do not go over the exact syntax elements. What is important is that each frame has two essential features: a name (given by the name attribute) and a URL (given by the src attribute). We could not think of any good names for the frames, so we simply named them "left" and "right".

The left frame uses the Left.html file (Example 10–7), which loads the applet into the left frame. It simply specifies the applet and the bookmarks. You can customize this file for your own web page by changing the bookmarks.

Example 10–7. Left.html

 1. <html>
 2.    <head><title>A Bookmark Applet</title></head>
 3.    <body>
 4.       <p>
 5.          Click on one of the radio buttons.
 6.          The corresponding web page
 7.          will be displayed in the frame on the right.
 8.       </p>
 9.       <applet code="Bookmark.class" width="290" height="300">
10.          <param name="link.1" value="http://java.sun.com"/>
11.          <param name="link.2" value="http://java.net"/>
12.          <param name="link.3" value="http://linuxtoday.com"/>
13.          <param name="link.4" value="http://www.horstmann.com"/>
14.          <param name="link.5" value="http://www.phptr.com"/>
15.          <param name="link.6" value="http://usps.com"/>
16.          <param name="link.7" value="http://www.cafeaulait.org"/>
17.       </applet>
18.    </body>
19. </html>

The right frame loads a dummy file that we called Right.html (Example 10–8). (Some browsers do not approve when you leave a frame blank, so we supply a dummy file for starters.)

Example 10–8. Right.html

1. <html>
2.    <head><title>Web pages will be displayed here.</title></head>
3.    <body>
4.       <p>Click on one of the radio buttons to the left.
5.       The corresponding web page will be displayed here.</p>
6.    </body>
7. </html>

The code for the bookmark applet that is given in Example 10–9 is simple. It reads the values of the parameters link.1, link.2, and so on, and turns each of them into a radio button. When you select one of the radio buttons, the showDocument method displays the corresponding page in the right frame.

Example 10–9. Bookmark.java

 1. import java.awt.*;
 2. import java.awt.event.*;
 3. import java.applet.*;
 4. import java.util.*;
 5. import java.net.*;
 6. import javax.swing.*;
 7.
 8. public class Bookmark extends JApplet
 9. {
10.    public void init()
11.    {
12.       Box box = Box.createVerticalBox();
13.       ButtonGroup group = new ButtonGroup();
14.
15.       int i = 1;
16.       String urlString;
17.
18.       // read all link.n parameters
19.       while ((urlString = getParameter("link." + i)) != null)
20.       {
21.
22.          try
23.          {
24.             final URL url = new URL(urlString);
25.
26.             // make a radio button for each link
27.             JRadioButton button = new JRadioButton(urlString);
28.             box.add(button);
29.             group.add(button);
30.
31.             // selecting the radio button shows the URL in the "right" frame
32.             button.addActionListener(new
33.                ActionListener()
34.                {
35.                   public void actionPerformed(ActionEvent event)
36.                   {
37.                      AppletContext context = getAppletContext();
38.                      context.showDocument(url, "right");
39.                   }
40.                });
41.          }
42.          catch (MalformedURLException e)
43.          {
44.             e.printStackTrace();
45.          }
46.
47.          i++;
48.       }
49.
50.       add(box);
51.    }
52. }

It’s an Applet. It’s an Application. It’s Both!

Quite a few years ago, a Saturday Night Live skit poking fun at a television commercial showed a couple arguing about a white, gelatinous substance. The husband said, “It’s a dessert topping.” The wife said, “It’s a floor wax.” And the announcer concluded triumphantly, “It’s both!”

Well, in this section, we show you how to write a Java program that is both an applet and an application. That is, you can load the program with the applet viewer or a browser, or you can start it from the command line with the java program launcher. We are not sure how often this comes up—we found it interesting that this could be done at all and thought you would, too.

The screen shots in Figures 10–9 and 10–10 show the same program, launched from the command line as an application and viewed inside the applet viewer as an applet.

The calculator as an applicationcalculatorscalculator application

Figure 10–9. The calculator as an application

The calculator as an appletcalculatorscalculator applet

Figure 10–10. The calculator as an applet

Let us see how this can be done. Every class file has exactly one public class. For the applet viewer to launch it, that class must derive from Applet. For Java to start the application, it must have a static main method. So far, we have

class MyAppletApplication extends JApplet
{
   public void init() { . . . }
   . . .
   public static void main(String[] args) { . . . }
}

What can we put into main? Normally, we make an object of the class and invoke setVisible(true) on it. But this case is not so simple. You cannot call setVisible on a naked applet. The applet must be placed inside a frame.

To provide a frame, we create the class AppletFrame, like this:

public class AppletFrame extends JFrame
{
   public AppletFrame(Applet anApplet)
   {
      applet = anApplet;
      add(applet);
      . . .
   }
   . . .
}

The constructor of the frame puts the applet (which is a Component) inside the content pane of the frame.

In the main method of the applet/application, we construct and show an AppletFrame.

class MyAppletApplication extends JApplet
{
   public void init() { . . . }
   . . .
   public static void main(String args[])
   {
      AppletFrame frame = new AppletFrame(new MyAppletApplication());
      frame.setTitle("MyAppletApplication");
      frame.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setVisible(true);
   }
}

When the applet starts, its init and start methods must be called. We achieve this by overriding the setVisible method of the AppletFrame class:

public void setVisible(boolean b)
{
   if (b)
   {
      applet.init();
      super.setVisible(true);
      applet.start();
   }
   else
   {
      applet.stop();
      super.setVisible(false);
      applet.destroy();
   }
}

There is one catch. If the program is started with the Java launcher and not the applet viewer, and it calls getAppletContext, it gets a null pointer because it has not been launched inside a browser. This causes a runtime crash whenever we have code like

getAppletContext().showStatus(message);

While we do not want to write a full-fledged browser, we do need to supply the bare minimum to make calls like this work. The call displays no message, but at least it will not crash the program. It turns out that all we need to do is implement two interfaces: AppletStub and AppletContext.

You have already seen applet contexts in action. They are responsible for fetching images and audio files and for displaying web pages. They can, however, politely refuse, and this is what our applet context will do. The major purpose of the AppletStub interface is to locate the applet context. Every applet has an applet stub (set with the setStub method of the Applet class).

In our case, AppletFrame implements both AppletStub and AppletContext. We supply the bare minimum functionality that is necessary to implement these two interfaces.

public class AppletFrame extends JFrame
   implements AppletStub, AppletContext
{
   . . .
   // AppletStub methods
   public boolean isActive() { return true; }
   public URL getDocumentBase() { return null; }
   public URL getCodeBase() { return null; }
   public String getParameter(String name) { return ""; }
   public AppletContext getAppletContext() { return this; }
   public void appletResize(int width, int height) {}

   // AppletContext methods
   public AudioClip getAudioClip(URL url) { return null; }
   public Image getImage(URL url) { return null; }
   public Applet getApplet(String name) { return null; }
   public Enumeration getApplets() { return null; }
   public void showDocument(URL url) {}
   public void showDocument(URL url, String target) {}
   public void showStatus(String status) {}
   public void setStream(String key, InputStream stream) {}
   public InputStream getStream(String key) { return null; }
   public Iterator getStreamKeys() { return null; }
}

Note

Note

When you compile this file with the JDK 1.3 compiler, you will get a warning that the class java.awt.Window also has a method called isActive that has package visibility. Because our class is not in the same package as the Window class, it cannot override the Window.isActive method. That is fine with us—we want to supply a new isActive method for the AppletStub interface. And, interestingly enough, it is entirely legal to add a new method with the same signature to the subclass. Whenever the object is accessed through a Window reference inside the java.awt package, the package-visible Window.isActive method is called. But whenever the call is made through an AppletFrame or AppletStub reference, the AppletFrame.isActive method is called.

Next, the constructor of the frame class calls setStub on the applet to make itself its stub.

public AppletFrame(Applet anApplet)
{
   applet = anApplet
   Container contentPane = getContentPane();
   contentPane.add(applet);
   applet.setStub(this);
}

One final twist is possible. Suppose we want to use the calculator as an applet and application simultaneously. Rather than moving the methods of the CalculatorApplet class into the CalculatorAppletApplication class, we will just use inheritance. Here is the code for the class that does this.

public class CalculatorAppletApplication extends CalculatorApplet
{
   public static void main(String args[])
   {
      AppletFrame frame = new AppletFrame(new CalculatorApplet());
      . . .
   }
}

You can do this with any applet, not just with the calculator applet. All you do is derive a class MyAppletApplication from your applet class and pass a new MyApplet() object to the AppletFrame in the main method. The result is a class that is both an applet and an application.

Just for fun, we use the previously mentioned trick of adding the applet tag as a comment to the source file. Then you can invoke the applet viewer with the source file without requiring an additional HTML file.

Examples 10–10 and 10–11 list the code. You need to copy the CalculatorApplet.java file into the same directory to compile the program. Try running both the applet and the application:

appletviewer CalculatorAppletApplication.java
java CalculatorAppletApplication

Example 10–10. AppletFrame.java

 1. import java.awt.*;
 2. import java.awt.event.*;
 3. import java.applet.*;
 4. import java.io.*;
 5. import java.net.*;
 6. import java.util.*;
 7. import javax.swing.*;
 8.
 9. public class AppletFrame extends JFrame
10.    implements AppletStub, AppletContext
11. {
12.    public AppletFrame(Applet anApplet)
13.    {
14.       applet = anApplet;
15.       add(applet);
16.       applet.setStub(this);
17.    }
18.
19.    public void setVisible(boolean b)
20.    {
21.       if (b)
22.       {
23.          applet.init();
24.          super.setVisible(true);
25.          applet.start();
26.       }
27.       else
28.       {
29.          applet.stop();
30.          super.setVisible(false);
31.          applet.destroy();
32.       }
33.    }
34.
35.    // AppletStub methods
36.    public boolean isActive() { return true; }
37.    public URL getDocumentBase() { return null; }
38.    public URL getCodeBase() { return null; }
39.    public String getParameter(String name) { return ""; }
40.    public AppletContext getAppletContext() { return this; }
41.    public void appletResize(int width, int height) {}
42.
43.    // AppletContext methods
44.    public AudioClip getAudioClip(URL url) { return null; }
45.    public Image getImage(URL url) { return null; }
46.    public Applet getApplet(String name) { return null; }
47.    public Enumeration getApplets() { return null; }
48.    public void showDocument(URL url) {}
49.    public void showDocument(URL url, String target) {}
50.    public void showStatus(String status) {}
51.    public void setStream(String key, InputStream stream) {}
52.    public InputStream getStream(String key) { return null; }
53.    public Iterator getStreamKeys() { return null; }
54.
55.    private Applet applet;
56. }

Example 10–11. CalculatorAppletApplication.java

 1. /*
 2.   The applet viewer reads the tags below if you call it with
 3.       appletviewer CalculatorAppletApplication.java (!)
 4.   No separate HTML file is required.
 5.   <applet code="CalculatorAppletApplication.class" width="200" height="200">
 6.   </applet>
 7. */
 8.
 9. import javax.swing.*;
10.
11. public class CalculatorAppletApplication
12.    extends CalculatorApplet
13. // It's an applet. It's an application. It's BOTH!
14. {
15.    public static void main(String[] args)
16.    {
17.       AppletFrame frame = new AppletFrame(new CalculatorApplet());
18.       frame.setTitle("CalculatorAppletApplication");
19.       frame.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
20.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
21.       frame.setVisible(true);
22.    }
23.
24.    public static final int DEFAULT_WIDTH = 200;
25.    public static final int DEFAULT_HEIGHT = 200;
26. }

JAR Files

The calculator applet from this chapter uses four classes: CalculatorApplet, CalculatorPanel, and two inner classes. You know that the applet tag references the class file that contains the applet class:

<applet code="CalculatorApplet.class" width="100" height="150">

When the browser reads this line, it makes a connection to the web server and fetches the file CalculatorApplet.class. The class loader of the browser’s virtual machine loads the CalculatorApplet class from that file. During the loading process, the class loader must resolve the other classes used in this class. After doing so, it then knows it needs more classes to run the applet. The browser, therefore, makes additional connections to the web server, one for each class file. Loading such an applet over a slow network connection can take many minutes.

Note

Note

It is important to remember that the reason for this long loading time is not the size of the class files—they are quite small. Rather it is because of the considerable overhead involved in establishing a connection to the web server.

Java supports an improved method for loading class files, allowing you to package all the needed class files into a single file. This file can then be downloaded with a single HTTP request to the server. Files that archive Java class files are called Java Archive (JAR) files. JAR files can contain both class files and other file types such as image and sound files. JAR files are compressed, using the familiar ZIP compression format, which further reduces the download time.

You use the jar tool to make JAR files. (In the default installation, it’s in the jdk/bin directory.) The most common command to make a new JAR file uses the following syntax:

jar cvf JARFileName File1 File2 . . .

For example,

jar cvf CalculatorClasses.jar *.class icon.gif

In general, the jar command has the format

jar options File1 File2 . . .

Table 10–3 lists all the options for the jar program. They are similar to the options of the UNIX tar command.

Table 10–3. jar Program Options

Option

Description

c

Creates a new or empty archive and adds files to it. If any of the specified file names are directories, then the jar program processes them recursively.

t

Displays the table of contents.

u

Updates an existing JAR file.

x

Extracts files. If you supply one or more file names, only those files are extracted. Otherwise, all files are extracted.

f

Specifies the JAR file name as the second command-line argument. If this parameter is missing, then jar will write the result to standard output (when creating a JAR file) or read it from standard input (when extracting or tabulating a JAR file).

v

Generates verbose output.

m

Adds a manifest to the JAR file. A manifest is a description of the archive contents and origin. Every archive has a default manifest, but you can supply your own if you want to authenticate the contents of the archive.

0

Stores without ZIP compression.

M

Does not create a manifest file for the entries.

i

Creates an index file (see below for more information).

C

Temporarily changes the directory. For example,

jar cvf JARFileName.jar -C classes *.class

changes to the classes subdirectory to add class files.

Once you have a JAR file, you need to reference it in the applet tag, as in the following example.

<applet code="CalculatorApplet.class" archive="CalculatorClasses.jar" width="100"
jar Program Options height="150">

Note that the code attribute must still be present. The code attribute tells the browser the name of the applet. The archive is merely a source where the applet class and other files may be located. Whenever a class, image, or sound file is needed, the browser searches the JAR files in the archive list first. Only if the file is not contained in the archive will it be fetched from the web server.

Tip

Tip

If you have a large applet, chances are that not all users require all of its functionality. To reduce the download time, you can break up the applet code into multiple JAR files and add an index to the main JAR file. The class loader then knows which JAR files contain a particular package or resource. See http://java.sun.com/j2se/5.0/docs/guide/jar/jar.html#JAR%20Index for details about the indexing procedure.

Tip

Tip

JDK 5.0 supports a new compression scheme, called “pack200”, that is specifically tuned to compress class files more efficiently than the generic ZIP compression algorithm. Sun claims a compression rate of close to 90% with multi-megabyte JAR files that contain only class files. In order to deploy these files, you need to configure the web server so that it serves standard files for traditional clients and compressed files for new clients that indicate in the HTTP request that they are able to decompress them. See http://java.sun.com/j2se/5.0/docs/guide/deployment/deployment-guide/pack200.html for detailed instructions.

Note

Note

The Java Plug-in will cache applet code. See http://java.sun.com/j2se/5.0/docs/guide/plugin/developer_guide/applet_caching.html for details on tweaking the cache.

Application Packaging

We now leave the world of applets and turn to the packaging of Java applications. When you ship an application, you don’t want to deploy a mess of class files. Just as with applets, you should package the class files and other resources required by your program in a JAR file. Once the program is packaged, it can be loaded with a simple command or, if the operating system is configured appropriately, by double-clicking on the JAR file.

The Manifest

You can package application programs, program components (sometimes called “beans”—see Chapter 8 of Volume 2), and code libraries into JAR files. For example, the runtime library of the JDK is contained in a very large file rt.jar.

A JAR file is simply a ZIP file that contains classes, other files that a program may need (such as icon images), and a manifest file that describes special features of the archive.

The manifest file is called MANIFEST.MF and is located in a special META-INF subdirectory of the JAR file. The minimum legal manifest is quite boring: just

Manifest-Version: 1.0

Complex manifests can have many more entries. The manifest entries are grouped into sections. The first section in the manifest is called the main section. It applies to the whole JAR file. Subsequent entries can specify properties of named entities such as individual files, packages, or URLs. Those entries must begin with a Name entry. Sections are separated by blank lines. For example,

Manifest-Version: 1.0lines describing this archiveName: Woozle.classlines describing this fileName: com/mycompany/mypkg/lines describing this package

To edit the manifest, place the lines that you want to add to the manifest into a text file. Then run

jar cfm JARFileName ManifestFileName . . .

For example, to make a new JAR file with a manifest, run:

jar cfm MyArchive.jar manifest.mf com/mycompany/mypkg/*.class

To add items to the manifest of an existing JAR file, place the additions into a text file and use a command such as

jar ufm MyArchive.jar manifest-additions.mf

See http://java.sun.com/j2se/5.0/docs/guide/jar/jar.html for more information on the JAR and manifest file formats.

Self-Running JAR Files

To package an application, place all files that your application needs into a JAR file and then add a manifest entry that specifies the main class of your program—the class that you would normally specify when invoking the java program launcher.

Make a file, say, mainclass.mf, containing a line such as

Main-Class: com/mycompany/mypkg/MainAppClass

Caution

Caution

The last line in the manifest must end with a newline character. Otherwise, the manifest will not be read correctly. It is a common error to produce a text file containing just the Main-Class line without a line terminator.

Do not add a .class extension to the main class name. Then run the jar command:

jar cvfm MyProgram.jar mainclass.mf files to add

Users can now simply start the program as

java -jar MyProgram.jar

Depending on the operating system configuration, you may be able to launch the application by double-clicking on the JAR file icon.

  • On Windows, the Java runtime installer creates a file association for the “.jar” extension that launches the file with the javaw -jar command. (Unlike the java command, the javaw command doesn’t open a shell window.)

  • On Solaris, the operating system recognizes the “magic number” of a JAR file and starts it with the java -jar command.

  • On Mac OS X, the operating system recognizes the “.jar” file extension and executes the Java program when you double-click on a JAR file. Furthermore, the application package utility MRJAppBuilder lets you turn a JAR file into a first-class, double-clickable Mac application with custom class paths, icons, and so on. For more information, see http://developer.apple.com/qa/java/java29.html.

Resources

Classes that are used in both applets and applications often use associated data files, such as

  • Image and sound files;

  • Text files with message strings and button labels;

  • Files with binary data, for example, to describe the layout of a map.

In Java, such an associated file is called a resource.

Note

Note

In Windows, the term “resource” has a more specialized meaning. Windows resources also consist of images, button labels, and so on, but they are attached to the executable file and accessed by a standard programming interface. In contrast, Java resources are stored as separate files, not as part of class files. And it is up to each class to access and interpret the resource data.

For example, consider a class, AboutPanel, that displays a message such as the one in Figure 10–11.

Displaying a resource from a JAR file

Figure 10–11. Displaying a resource from a JAR file

Of course, the book title and copyright year in the panel will change for the next edition of the book. To make it easy to track this change, we want to put the text inside a file and not hardcode it as a string.

But where should you put a file such as about.txt? Of course, it would be convenient if you simply placed it with the rest of the program files, for example, in a JAR file.

The class loader knows how to search for class files until it has located them somewhere on the class path, or in an archive, or on a web server. The resource mechanism gives you the same convenience for files that aren’t class files. Here are the necessary steps:

  1. Get the Class object of the class that has a resource, for example, AboutPanel.class.

  2. Call getResource(filename) to get the resource location as a URL.

  3. If the resource is an image or audio file, read it directly with the getImage or getAudioClip method.

  4. Otherwise, use the openStream method on the URL to read in the data in the file. (See Chapter 12 for more on streams.)

The point is that the class loader remembers how to locate the class and it can then search for the associated resource in the same location.

For example, to make an icon with the image file about.gif, do the following:

URL url = AboutPanel.class.getResource("about.gif");
ImageIcon icon = new ImageIcon(url);

That means “locate the about.gif file at the same place where you find AboutPanel.class.”

To read in the file about.txt, you can use similar commands:

URL url = AboutPanel.class.getResource("about.txt");
InputStream stream = url.openStream();

Because this combination is so common, there is a convenient shortcut method: getResourceAsStream returns an InputStream, not a URL.

InputStream stream = AboutPanel.class.getResourceAsStream("about.txt");

To read from this stream, you need to know how to process input (see Chapter 12 for details). In the sample program, we read the stream a line at a time with the following instructions:

InputStream stream = AboutPanel.class.getResourceAsStream("about.txt");
Scanner in = Scanner.create(stream);
while (in.hasNext())
   textArea.append(in.nextLine() + "
");

The Core Java example files include a JAR file named ResourceTest.jar that contains all the class files for this example and the resource files about.gif and about.txt. This demonstrates that the program locates the resource file in the same location as the class file, namely, inside the JAR file. Example 10–12 shows the source code.

Tip

Tip

As you saw earlier in this chapter, applets can locate image and audio files with the getImage and getAudioClip methods—these methods automatically search JAR files. But to load other files from a JAR file, applets still need the getResourceAsStream method.

Instead of placing a resource file inside the same directory as the class file, you can place it in a subdirectory. You can use a hierarchical resource name such as

data/text/about.txt

This is a relative resource name, and it is interpreted relative to the package of the class that is loading the resource. Note that you must always use the / separator, regardless of the directory separator on the system that actually stores the resource files. For example, on the Windows file system, the resource loader automatically translates / to separators.

A resource name starting with a / is called an absolute resource name. It is located in the same way that a class inside a package would be located. For example, a resource

/corejava/title.txt

is located in the corejava directory (which may be a subdirectory of the class path, inside a JAR file, or, for applets, on a web server).

Automating the loading of files is all that the resource loading feature does. There are no standard methods for interpreting the contents of a resource file. Each program must have its own way of interpreting the contents of its resource files.

Another common application of resources is the internationalization of programs. Language-dependent strings, such as messages and user interface labels, are stored in resource files, with one file for each language. The internationalization API, which is discussed in Chapter 10 of Volume 2, supports a standard method for organizing and accessing these localization files.

Example 10–12. ResourceTest.java

 1. import java.awt.*;
 2. import java.awt.event.*;
 3. import java.io.*;
 4. import java.net.*;
 5. import java.util.*;
 6. import javax.swing.*;
 7.
 8. public class ResourceTest
 9. {
10.    public static void main(String[] args)
11.    {
12.       ResourceTestFrame frame = new ResourceTestFrame();
13.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
14.       frame.setVisible(true);
15.    }
16. }
17.
18. /**
19.    A frame with a panel that has components demonstrating
20.    resource access from a JAR file.
21. */
22. class ResourceTestFrame extends JFrame
23. {
24.    public ResourceTestFrame()
25.    {
26.       setTitle("ResourceTest");
27.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
28.       add(new AboutPanel());
29.    }
30.
31.    public static final int DEFAULT_WIDTH = 300;
32.    public static final int DEFAULT_HEIGHT = 300;
33. }
34.
35. /**
36.    A panel with a text area and an "About" button. Pressing
37.    the button fills the text area with text from a resource.
38. */
39. class AboutPanel extends JPanel
40. {
41.    public AboutPanel()
42.    {
43.       setLayout(new BorderLayout());
44.
45.       // add text area
46.       textArea = new JTextArea();
47.       add(new JScrollPane(textArea), BorderLayout.CENTER);
48.
49.       // add About button
50.       URL aboutURL = AboutPanel.class.getResource("about.gif");
51.       JButton aboutButton = new JButton("About", new ImageIcon(aboutURL));
52.       aboutButton.addActionListener(new AboutAction());
53.       add(aboutButton, BorderLayout.SOUTH);
54.    }
55.
56.    private JTextArea textArea;
57.
58.    private class AboutAction implements ActionListener
59.    {
60.       public void actionPerformed(ActionEvent event)
61.       {
62.          InputStream stream = AboutPanel.class.getResourceAsStream("about.txt");
63.          Scanner in = new Scanner(stream);
64.          while (in.hasNext())
65.             textArea.append(in.nextLine() + "
");
66.       }
67.    }
68. }
ResourceTest.java
java.lang.Class 1.0
  • URL getResource(String name) 1.1

  • InputStream getResourceAsStream(String name) 1.1

    find the resource in the same place as the class and then return a URL or input stream you can use for loading the resource. Return null if the resource isn’t found, and so do not throw an exception for an I/O error.

    Parameters:

    name

    The resource name

Sealing

We mentioned in Chapter 4 that you can seal a Java language package to ensure that no further classes can add themselves to it. You would want to seal a package if you use package-visible classes, methods, and fields in your code. Without sealing, other classes can place themselves into the same package and thereby gain access to its package-visible features.

For example, if you seal the package com.mycompany.util, then no class outside the sealed archive can be defined with the statement

package com.mycompany.util;

To achieve this, you put all classes of the package into a JAR file. By default, packages in a JAR file are not sealed. You can change that global default by placing the line

Sealed: true

into the main section of the manifest. For each individual package, you can specify whether you want the package sealed or not, by adding another section to the JAR file manifest, like this:

Name: com/mycompany/util/
Sealed: true

Name: com/mycompany/misc/
Sealed: false

To seal a package, make a text file with the manifest instructions. Then run the jar command in the usual way:

jar cvfm MyArchive.jar manifest.mf files to add

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

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).

    Launching Java Web StartcalculatorsWeb Start example

    Figure 10–12. Launching Java Web Start

    The calculator delivered by 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.

    The application cache

    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.

A Java Web Start security advisorysecurityJava Web Start

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
A Java Web Start security advisorysecurityJava Web Start.FileSaveService");

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

Note

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
Note.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

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
WebStartCalculator.java.BasicService");
 74.          URL codeBase = basic.getCodeBase();
 75.
 76.          PersistenceService service
 77.             = (PersistenceService) ServiceManager.lookup("javax.jnlp
WebStartCalculator.java.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("
");
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("
" + result);
307.                lastCommand = command;
308.                display.append("
");
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. }
WebStartCalculator.java
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.

WebStartCalculator.java
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.

WebStartCalculator.java
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.

WebStartCalculator.java
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.

WebStartCalculator.java
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.

WebStartCalculator.java
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.

The WebStartCalculator application

Figure 10–16. The WebStartCalculator application

Storage of Application Preferences

Most programs can be configured by their users. The programs must then save the user preferences and restore them when the application starts again. You already saw how a Java Web Start application can use a persistent store for that purpose. However, JDK 1.4 introduced a different and more powerful mechanism for local applications. We describe that mechanism, but first we cover the much simpler approach for storing configuration information that Java applications have traditionally taken.

Property Maps

A property map is a data structure that stores key/value pairs. Property maps are often used for storing configuration information. Property maps have three particular characteristics:

  • The keys and values are strings.

  • The set can easily be saved to a file and loaded from a file.

  • There is a secondary table for default values.

The Java platform class that implements a property map is called Properties.

Property maps are useful in specifying configuration options for programs. For example,

Properties settings = new Properties();
settings.put("font", "Courier");

settings.put("size", "10");
settings.put("message", "Hello, World");

Use the store method to save this list of properties to a file. Here, we just save the property map in the file Myprog.properties. The second argument is a comment that is included in the file.

FileOutputStream out = new FileOutputStream("Myprog.properties");
settings.store(out, "Myprog Properties");

The sample set gives the following output.

#Myprog Properties
#Tue Jun 15 07:22:52  2004
font=Courier
size=10
message=Hello, World

To load the properties from a file, use

FileInputStream in = new FileInputStream("Myprog.properties");
settings.load(in);

We’ll put this technique to work so that your users can customize the NotHelloWorld program to their hearts’ content. We’ll allow them to specify the following in the configuration file CustomWorld.properties:

  • Window size

  • Font

  • Point size

  • Text color

  • Message string

If the user doesn’t specify some of the settings, we will provide defaults.

The Properties class has two mechanisms for providing defaults. First, whenever you look up the value of a string, you can specify a default that should be used automatically when the key is not present.

String font = settings.getProperty("font", "Courier");

If there is a "font" property in the property table, then font is set to that string. Otherwise, font is set to "Courier".

If you find it too tedious to specify the default in every call to getProperty, then you can pack all the defaults into a secondary property map and supply that map in the constructor of your lookup table.

Properties defaultSettings = new Properties();
defaultSettings.put("font", "Courier");
defaultSettings.put("size", "10");
defaultSettings.put("color.red", "0");
. . .
Properties settings = new Properties(defaultSettings);
FileInputStream in = new FileInputStream("CustomWorld.properties");
settings.load(in);
. . .

Yes, you can even specify defaults to defaults if you give another property map parameter to the defaultSettings constructor, but it is not something one would normally do.

Example 10–14 is the customizable "Hello, Custom World" program. Just edit the .properties file to change the program’s appearance to the way you want (see Figure 10–17).

The customized Hello, World program

Figure 10–17. The customized Hello, World program

Note

Note

Properties are simple tables without a hierarchical structure. It is common to introduce a fake hierarchy with key names such as window.main.color, window.main.title, and so on. But the Properties class has no methods that help organize such a hierarchy. If you store complex configuration information, you should use the Preferences class instead—see the next section.

Note

Note

The Properties class extends the Hashtable class. That means that all Hashtable methods are available to Properties objects. Some methods are useful. For example, size returns the number of possible properties (well, it isn’t that nice—it doesn’t count the defaults). Similarly, keys returns an enumeration of all keys, except for the defaults. There is also a second method, called propertyNames, that returns all keys. The put method is downright dangerous. It doesn’t check that you put strings into the table.

Does the is-a rule for using inheritance apply here? Is every property map a hash table? Not really. That these are true is really just an implementation detail. Maybe it is better to think of a property map as having a hash table. But then the hash table should be a private instance variable. Actually, in this case, a property map uses two hash tables: one for the defaults and one for the nondefault values.

We think a better design would be the following:

class Properties
{
   public String getProperty(String) { . . . }
   public void put(String, String) { . . . }
   . . .
   private Hashtable nonDefaults;
   private Hashtable defaults;
}

We don’t want to tell you to avoid the Properties class in the Java library. Provided you are careful to put nothing but strings in it, it works just fine. But think twice before using “quick and dirty” inheritance in your own programs.

Example 10–14. CustomWorld.java

 1. import java.awt.*;
 2. import java.awt.event.*;
 3. import java.util.*;
 4. import java.io.*;
 5. import javax.swing.*;
 6.
 7. /**
 8.    This program demonstrates how to customize a "Hello, World"
 9.    program with a properties file.
10. */
11. public class CustomWorld
12. {
13.    public static void main(String[] args)
14.    {
15.       CustomWorldFrame frame = new CustomWorldFrame();
16.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
17.       frame.setVisible(true);
18.    }
19. }
20.
21. /**
22.    This frame displays a message. The frame size, message text,
23.    font, and color are set in a properties file.
24. */
25. class CustomWorldFrame extends JFrame
26. {
27.    public CustomWorldFrame()
28.    {
29.       Properties defaultSettings = new Properties();
30.       defaultSettings.put("font", "Monospaced");
31.       defaultSettings.put("width", "300");
32.       defaultSettings.put("height", "200");
33.       defaultSettings.put("message", "Hello, World");
34.       defaultSettings.put("color.red", "0 50 50");
35.       defaultSettings.put("color.green", "50");
36.       defaultSettings.put("color.blue", "50");
37.       defaultSettings.put("ptsize", "12");
38.
39.       Properties settings = new Properties(defaultSettings);
40.       try
41.       {
42.          FileInputStream in = new FileInputStream("CustomWorld.properties");
43.          settings.load(in);
44.       }
45.       catch (IOException e)
46.       {
47.          e.printStackTrace();
48.       }
49.
50.       int red = Integer.parseInt(settings.getProperty("color.red"));
51.       int green = Integer.parseInt(settings.getProperty("color.green"));
52.       int blue = Integer.parseInt(settings.getProperty("color.blue"));
53.
54.       Color foreground = new Color(red, green, blue);
55.
56.       String name = settings.getProperty("font");
57.       int ptsize = Integer.parseInt(settings.getProperty("ptsize"));
58.       Font f = new Font(name, Font.BOLD, ptsize);
59.
60.       int hsize = Integer.parseInt(settings.getProperty("width"));
61.       int vsize = Integer.parseInt(settings.getProperty("height"));
62.       setSize(hsize, vsize);
63.       setTitle(settings.getProperty("message"));
64.
65.       JLabel label = new JLabel(settings.getProperty("message"), SwingConstants.CENTER);
66.       label.setFont(f);
67.       label.setForeground(foreground);
68.       add(label);
69.    }
70. }
CustomWorld.java
java.util.Properties 1.0
  • Properties()

    creates an empty property list.

  • Properties(Properties defaults)

    creates an empty property list with a set of defaults.

    Parameters:

    defaults

    The defaults to use for lookups

  • String getProperty(String key)

    gets a property association. Returns the string associated with the key, or the string associated with the key in the default table if it wasn’t present in the table, or null if the key wasn’t present in the default table either.

    Parameters:

    key

    The key whose associated string to get

  • String getProperty(String key, String defaultValue)

    gets a property with a default value if the key is not found. Returns the string associated with the key, or the default string if it wasn’t present in the table.

    Parameters:

    key

    The key whose associated string to get

    defaultValue

    The string to return if the key is not present

  • void load(InputStream in) throws IOException

    loads a property map from an input stream.

    Parameters:

    in

    The input stream

  • void store(OutputStream out, String header) 1.2

    saves a property map to an output stream.

    Parameters:

    out

    The output stream

    header

    The header in the first line of the stored file

System Information

Here’s another example of the ubiquity of the Properties set. Information about your system is stored in a Properties object that is returned by the static getProperties method of the System class:

Properties sysprops = System.getProperties();

To access a single property, call

String value = System.getProperty(key);

Applications that run without a security manager have complete access to this information, but applets and other untrusted programs can access only the following keys:

java.version
java.vendor
java.vendor.url
java.class.version
os.name
os.version
os.arch
file.separator
path.separator
line.separator
java.specification.version
java.vm.specification.version
java.vm.specification.vendor
java.vm.specification.name
java.vm.version
java.vm.vendor
java.vm.name

If an applet attempts to read another key (or all properties), then a security exception is thrown.

Note

Note

You can find the names of the freely accessible system properties in the file security/java.policy in the directory of the Java run time.

The program in Example 10–15 prints out the key/value pairs in the Properties object that stores the system properties.

Here is an example of what you would see when you run the program. You can see all the values stored in this Properties object. (What you would get will, of course, reflect your machine’s settings.)

#System Properties
#Sat May 08 08:27:34 PDT 2004
java.runtime.name=Java(TM) 2 Runtime Environment, Standard Edition
sun.boot.library.path=/usr/local/jdk5.0/jre/lib/i386
java.vm.version=5.0
java.vm.vendor=Sun Microsystems Inc.
java.vendor.url=http://java.sun.com/
path.separator=:
java.vm.name=Java HotSpot(TM) Client VM
file.encoding.pkg=sun.io
user.country=US
sun.os.patch.level=unknown
java.vm.specification.name=Java Virtual Machine Specification
user.dir=/home/cay/books/cj5/corejava/v1/v1ch10/SystemInfo
java.runtime.version=5.0
java.awt.graphicsenv=sun.awt.X11GraphicsEnvironment
java.endorsed.dirs=/usr/local/jdk5.0/jre/lib/endorsed
os.arch=i386
java.io.tmpdir=/tmp
line.separator=

java.vm.specification.vendor=Sun Microsystems Inc.
os.name=Linux
java.library.path=/usr/local/jdk5.0/jre/lib/i386/client:/usr/local/jdk5.0/jre/lib/i386:
Note/usr/local/jdk5.0/jre/../lib/i386
java.specification.name=Java Platform API Specification
java.class.version=48.0
sun.management.compiler=HotSpot Client Compiler
java.util.prefs.PreferencesFactory=java.util.prefs.FileSystemPreferencesFactory
os.version=2.4.20-8
user.home=/home/cay
user.timezone=America/Los_Angeles
java.awt.printerjob=sun.print.PSPrinterJob
file.encoding=UTF-8
java.specification.version=5.0
java.class.path=.
user.name=cay
java.vm.specification.version=1.0
java.home=/usr/local/jdk5.0/jre
sun.arch.data.model=32
user.language=en
java.specification.vendor=Sun Microsystems Inc.
java.vm.info=mixed mode
java.version=5.0
java.ext.dirs=/usr/local/jdk5.0/jre/lib/ext
sun.boot.class.path=/usr/local/jdk5.0/jre/lib/rt.jar:/usr/local/jdk5.0/jre/lib/i18n.jar:
Note/usr/local/jdk5.0/jre/lib/sunrsasign.jar:/usr/local/jdk5.0/jre/lib/jsse.jar:/usr/local
Note/jdk5.0/jre/lib/jce.jar:/usr/local/jdk5.0/jre/lib/charsets.jar:/usr/local/jdk5.0/jre/classes
java.vendor=Sun Microsystems Inc.
file.separator=/
java.vendor.url.bug=http://java.sun.com/cgi-bin/bugreport.cgi
sun.io.unicode.encoding=UnicodeLittle
sun.cpu.endian=little
sun.cpu.isalist=

Example 10–15. SystemInfo.java

 1. import java.applet.*;
 2. import java.io.*;
 3. import java.util.*;
 4
 5. /**
 6.    This program prints out all system properties.
 7. */
 8. public class SystemInfo
 9. {
10.    public static void main(String args[])
11.    {
12.       try
13.       {
14.          Properties sysprops = System.getProperties();
15.          sysprops.store(System.out, "System Properties");
16.       }
17.       catch (IOException e)
18.       {
19.          e.printStackTrace();
20.       }
21.    }
22. }
SystemInfo.java
java.lang.System 1.0
  • Properties getProperties()

    retrieves all system properties. The application must have permission to retrieve all properties or a security exception is thrown.

  • String getProperty(String key)

    retrieves the system property with the given key name. The application must have permission to retrieve the property or a security exception is thrown.

The Preferences API

As you have seen, the Properties class makes it simple to load and save configuration information. However, using property files has a number of disadvantages.

  • The configuration files cannot always be stored in the same location as the program because that location may not be writable. For example, it may be a read-only directory or a JAR file.

  • Multiple users may want to configure the same application in different ways.

  • Configuration files cannot always be stored in the user’s home directory. Some operating systems (such as Windows 9x) have no concept of a home directory.

  • There is no standard convention for naming configuration files, increasing the likelihood of name clashes as users install multiple Java applications.

Some operating systems have a central repository for configuration information. The best-known example is the registry in Microsoft Windows. The Preferences class of JDK 1.4 provides such a central repository in a platform-independent manner. In Windows, the Preferences class uses the registry for storage; on Linux, the information is stored in the local file system instead. Of course, the repository implementation is transparent to the programmer using the Preferences class.

The Preferences repository has a tree structure, with node path names such as /com/mycompany/myapp. As with package names, name clashes are avoided as long as programmers start the paths with reversed domain names. In fact, the designers of the API suggest that the configuration node paths match the package names in your program.

Each node in the repository has a separate table of key/value pairs that you can use to store numbers, strings, or byte arrays. No provision is made for storing serializable objects. The API designers felt that the serialization format is too fragile for long-term storage. Of course, if you disagree, you can save serialized objects in byte arrays.

For additional flexibility, there are multiple parallel trees. Each program user has one tree, and an additional tree, called the system tree, is available for settings that are common to all users. The Preferences class uses the operating system notion of the “current user” for accessing the appropriate user tree.

To access a node in the tree, start with the user or system root:

Preferences root = Preferences.userRoot();

or

Preferences root = Preferences.systemRoot();

Then access the node. You can simply provide a node path name:

Preferences node = root.node("/com/mycompany/myapp");

A convenient shortcut gets a node whose path name equals the package name of a class. Simply take an object of that class and call

Preferences node = Preferences.userNodeForPackage(obj.getClass());

or

Preferences node = Preferences.systemNodeForPackage(obj.getClass());

Typically, obj will be the this reference.

Once you have a node, you can access the key/value table with methods

String get(String key, String defval)
int getInt(String key, int defval)
long getLong(String key, long defval)
float getFloat(String key, float defval)
double getDouble(String key, double defval)
boolean getBoolean(String key, boolean defval)
byte[] getByteArray(String key, byte[] defval)

Note that you must specify a default value when reading the information, in case the repository data is not available. Defaults are required for several reasons. The data might be missing because the user never specified a preference. Certain resource-constrained platforms might not have a repository, and mobile devices might be temporarily disconnected from the repository.

Conversely, you can write data to the repository with put methods such as

put(String key, String value)
putInt(String key, int value)

and so on.

You can enumerate all keys stored in a node with the method

String[] keys

But there is currently no way to find out the type of the value of a particular key.

Central repositories such as the Windows registry traditionally suffer from two problems.

  • They turn into a “dumping ground,” filled with obsolete information.

  • Configuration data gets entangled into the repository, making it difficult to move preferences to a new platform.

The Preferences class has a solution for the second problem. You can export the preferences of a subtree (or, less commonly, a single node) by calling the methods

void exportSubtree(OutputStream out)
void exportNode(OutputStream out)

The data are saved in XML format. You can import them into another repository by calling

void importPreferences(InputStream in)

Here is a sample file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd">
<preferences EXTERNAL_XML_VERSION="1.0">
  <root type="user">
    <map/>
    <node name="com">
      <map/>
      <node name="horstmann">
        <map/>
        <node name="corejava">
          <map>
            <entry key="left" value="11"/>
            <entry key="top" value="9"/>
            <entry key="width" value="453"/>
            <entry key="height" value="365"/>
          </map>
        </node>
      </node>
    </node>
  </root>
</preferences>

If your program uses preferences, you should give your users the opportunity of exporting and importing them, so they can easily migrate their settings from one computer to another. The program in Example 10–16 demonstrates this technique. The program simply saves the position and size of the main window. Try resizing the window, then exit and restart the application. The window will be just like you left it when you exited.

Example 10–16. PreferencesTest.java

  1. import java.awt.*;
  2. import java.awt.event.*;
  3. import java.io.*;
  4. import java.util.logging.*;
  5. import java.util.prefs.*;
  6. import javax.swing.*;
  7. import javax.swing.event.*;
  8.
  9. /**
 10.    A program to test preference settings. The program
 11.    remembers the frame position and size.
 12. */
 13. public class PreferencesTest
 14. {
 15.    public static void main(String[] args)
 16.    {
 17.       PreferencesFrame frame = new PreferencesFrame();
 18.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 19.       frame.setVisible(true);
 20.    }
 21. }
 22.
 23. /**
 24.    A frame that restores position and size from user
 25.    preferences and updates the preferences upon exit.
 26. */
 27. class PreferencesFrame extends JFrame
 28. {
 29.    public PreferencesFrame()
 30.    {
 31.       setTitle("PreferencesTest");
 32.
 33.       // get position, size from preferences
 34.
 35.       Preferences root = Preferences.userRoot();
 36.       final Preferences node = root.node("/com/horstmann/corejava");
 37.       int left = node.getInt("left", 0);
 38.       int top = node.getInt("top", 0);
 39.       int width = node.getInt("width", DEFAULT_WIDTH);
 40.       int height = node.getInt("height", DEFAULT_HEIGHT);
 41.       setBounds(left, top, width, height);
 42.
 43.       // set up file chooser that shows XML files
 44.
 45.       final JFileChooser chooser = new JFileChooser();
 46.       chooser.setCurrentDirectory(new File("."));
 47.
 48.       // accept all files ending with .xml
 49.       chooser.setFileFilter(new
 50.          javax.swing.filechooser.FileFilter()
 51.          {
 52.             public boolean accept(File f)
 53.             {
 54.                return f.getName().toLowerCase().endsWith(".xml") || f.isDirectory();
 55.             }
 56.             public String getDescription()
 57.             {
 58.                return "XML files";
 59.             }
 60.          });
 61.
 62.
 63.       // set up menus
 64.       JMenuBar menuBar = new JMenuBar();
 65.       setJMenuBar(menuBar);
 66.       JMenu menu = new JMenu("File");
 67.       menuBar.add(menu);
 68.
 69.       JMenuItem exportItem = new JMenuItem("Export preferences");
 70.       menu.add(exportItem);
 71.       exportItem.addActionListener(new
 72.          ActionListener()
 73.          {
 74.             public void actionPerformed(ActionEvent event)
 75.             {
 76.                if(chooser.showSaveDialog(PreferencesFrame.this) == JFileChooser
PreferencesTest.java.APPROVE_OPTION)
 77.                {
 78.                   try
 79.                   {
 80.                      OutputStream out = new FileOutputStream(chooser.getSelectedFile());
 81.                      node.exportSubtree(out);
 82.                      out.close();
 83.                   }
 84.                   catch (Exception e)
 85.                   {
 86.                      e.printStackTrace();
 87.                   }
 88.                }
 89.             }
 90.          });
 91.
 92.       JMenuItem importItem = new JMenuItem("Import preferences");
 93.       menu.add(importItem);
 94.       importItem.addActionListener(new
 95.          ActionListener()
 96.          {
 97.             public void actionPerformed(ActionEvent event)
 98.             {
 99.                if(chooser.showOpenDialog(PreferencesFrame.this) == JFileChooser
PreferencesTest.java.APPROVE_OPTION)
100.                {
101.                   try
102.                   {
103.                      InputStream in = new FileInputStream(chooser.getSelectedFile());
104.                      node.importPreferences(in);
105.                      in.close();
106.                   }
107.                   catch (Exception e)
108.                  {
109.                     e.printStackTrace();
110.                  }
111.               }
112.            }
113.         });
114.
115.       JMenuItem exitItem = new JMenuItem("Exit");
116.       menu.add(exitItem);
117.       exitItem.addActionListener(new
118.          ActionListener()
119.          {
120.             public void actionPerformed(ActionEvent event)
121.             {
122.                node.putInt("left", getX());
123.                node.putInt("top", getY());
124.                node.putInt("width", getWidth());
125.                node.putInt("height", getHeight());
126.                System.exit(0);
127.             }
128.          });
129.    }
130.    public static final int DEFAULT_WIDTH = 300;
131.    public static final int DEFAULT_HEIGHT = 200;
132. }
PreferencesTest.java
java.util.prefs.Preferences 1.4
  • Preferences userRoot()

    returns the root preferences node of the user of the calling program.

  • Preferences systemRoot()

    returns the systemwide root preferences node.

  • Preferences node(String path)

    returns a node that can be reached from the current node by the given path. If path is absolute (that is, starts with a /), then the node is located starting from the root of the tree containing this preference node. If there isn’t a node with the given path, it is created.

  • Preferences userNodeForPackage(Class cl)

  • Preferences systemNodeForPackage(Class cl)

    return a node in the current user’s tree or the system tree whose absolute node path corresponds to the package name of the class cl.

  • String[] keys()

    returns all keys belonging to this node.

  • String get(String key, String defval)

  • int getInt(String key, int defval)

  • long getLong(String key, long defval)

  • float getFloat(String key, float defval)

  • double getDouble(String key, double defval)

  • boolean getBoolean(String key, boolean defval)

  • byte[] getByteArray(String key, byte[] defval)

    return the value associated with the given key, or the supplied default value if no value is associated with the key, or the associated value is not of the correct type, or the preferences store is unavailable.

  • void put(String key, String value)

  • void putInt(String key, int value)

  • void putLong(String key, long value)

  • void putFloat(String key, float value)

  • void putDouble(String key, double value)

  • void putBoolean(String key, boolean value)

  • void putByteArray(String key, byte[] value)

    store a key/value pair with this node.

  • void exportSubtree(OutputStream out)

    writes the preferences of this node and its children to the specified stream.

  • void exportNode(OutputStream out)

    writes the preferences of this node (but not its children) to the specified stream.

  • void importPreferences(InputStream in)

    imports the preferences contained in the specified stream.

This concludes our discussion of Java software deployment. In the next chapter, you learn how to use exceptions to tell your programs what to do when problems arise at run time. We also give you tips and techniques for testing and debugging so that not too many things will go wrong when your programs run.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset