Chapter 10. Deploying Applications and Applets

JAR FILES

Java WEB START

APPLETS

STORAGE OF APPLICATION PREFERENCES

At this point, you should be comfortable with using most of the features of the Java programming language, and you have had a pretty thorough introduction to basic graphics programming in Java. Now that you are ready to create applications for your users, you will want to know how to package them for deployment on your users’ computers. The traditional deployment choice—which was responsible for the unbelievable hype during the first few years of Java’s life—is to use 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. Therefore, we start this chapter with instructions for packaging applications. We then turn to the Java Web Start mechanism, an alternative approach for Internet-based application delivery, which fixes some of the problems of applets. Finally, we cover applets and show you in which circumstances you still want to use them.

We also discuss how your applications can store configuration information and user preferences.

JAR Files

When you package your application, you want to give your users a single file, not a directory structure filled with class files. Java Archive (JAR) files were designed for this purpose. A JAR file can contain both class files and other file types such as image and sound files. Moreover, JAR files are compressed, using the familiar ZIP compression format.


Image Tip

Java SE 5.0 introduced 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% for class files. See http://java.sun.com/javase/6/docs/technotes/guide/deployment/deployment-guide/pack200.html for more information.


You use the jar tool to make JAR files. (In the default JDK 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 following format:

jar options File1 File2 . . .

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

Table 10–1. jar Program Options

Image

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

The Manifest

In addition to class files, images, and other resources, each JAR file contains 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.0
lines describing this archive

Name: Woozle.class
lines describing this file

Name: 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 update 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


Image Note

See http://java.sun.com/javase/6/docs/technotes/guides/jar for more information on the JAR and manifest file formats.


Executable JAR Files

As of Java SE 6, you can use the e option of the jar command to specify the entry point of your program—the class that you would normally specify when invoking the java program launcher:

jar cvfe MyProgram.jar com.mycompany.mypkg.MainAppClass files to add

Users can now simply start the program as

java -jar MyProgram.jar

In older versions of the JDK, you had to specify the main class of your program, following this procedure:

Main-Class: com.mycompany.mypkg.MainAppClass

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


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


Depending on the operating system configuration, you may be able to launch the application by double-clicking on the JAR file icon. Here are behaviors for various operating systems:

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

However, a Java program in a JAR file does not have the same feel as a native application. On Windows, you can use third-party wrapper utilities that turn JAR files into Windows executables. A wrapper is a Windows program with the familiar .exe extension that locates and launches the Java virtual machine (JVM), or tells the user what to do when no JVM is found. There are a number of commercial and open source products, such as JSmooth (http://jsmooth.sourceforge.net) and Launch4J (http://launch4j.sourceforge.net). The open source installer generator IzPack (http://izpack.org) also contains a native launcher. For more information on this topic, see http://www.javalobby.org/articles/java2exe.

On the Macintosh, the situation is a bit easier. The application package utility MRJAppBuilder lets you turn a JAR file into a first-class Mac application. For more information, see http://java.sun.com/developer/technicalArticles/JavaLP/JavaToMac3.

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; or

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

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


Image 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. It is up to each program to access and interpret the resource data.


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

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

Image

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 inside the 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. If the resource is an image or audio file, call getResource(filename) to get the resource location as a URL. Then read it with the getImage or getAudioClip method.

3. For resources other than images or audio files, use the getResourceAsStream method to read the data in the file.

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 = ResourceTest.class.getResource("about.jpg");
Image img = Toolkit.getDefaultToolkit().getImage(url);

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

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

InputStream stream = ResourceTest.class.getResourceAsStream("about.txt");
Scanner in = new Scanner(stream);

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 5 of Volume II, supports a standard method for organizing and accessing these localization files.

Listing 10–1 shows the source code of the program that demonstrates resource loading. Compile, build a JAR file, and execute it:

javac ResourceTest.java
jar cvfm ResourceTest.jar ResourceTest.mf *.class *.gif *.txt
java -jar ResourceTest.jar

Move the JAR file to a different directory and run it again to check that the program reads the resource files from the JAR file, not the current directory.

Listing 10–1. ResourceTest.java

Image

Image


java.lang.Class 1.0

Image

URL getResource(String name) 1.1

InputStream getResourceAsStream(String name) 1.1

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

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 technology for delivering applications over the Internet. Java Web Start applications have the following characteristics:

• They are typically delivered through a browser. Once a Java Web Start application has been downloaded, it can be started without using a browser.

• They do not live inside a browser window. The application is displayed in its own frame, outside the browser.

• They do not use the Java implementation of the browser. 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.

• Digitally signed applications can be given arbitrary access rights on the local machine. Unsigned applications run in a “sandbox,” which prohibits potentially dangerous operations.

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.

You also need to ensure 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.


Image Tip

To experiment with Java Web Start, install Tomcat from http://jakarta.apache.org/tomcat. Tomcat is a container for servlets and JSP pages, but it also serves web pages. It is preconfigured to serve the correct MIME type for 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:

Image

Image

(Note that the version number must be 1.5.0, not 5.0. As of Java SE 6, you can use java instead of j2se for the tag name.)

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

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:

Image

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

Figure 10–2. Launching Java Web Start

Image

11. Soon afterward, the calculator should come up, with a border marking it as a Java application (see Figure 10–3).

Figure 10–3. The calculator delivered by Java Web Start

Image

12. 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–4). As of Java SE 5.0, that control panel is used both for applets and Java Web Start applications. In Windows, look for the Java Plug-in control inside the Windows control panel. Under Linux, run jdk /jre/bin/ControlPanel.

Figure 10–4. The application cache

Image


Image Tip

If you don’t want to run a web server while you are testing your JNLP configuration, you can temporarily override the codebase URL in the launch file by running

javaws -codebase file:// programDirectory JNLPfile

For example, in UNIX, you can simply issue this command from the directory containing the JNLP file:

javaws -codebase file://`pwd` WebStartCalculator.jnlp


Of course, you don’t want to tell your users to launch the cache viewer whenever they want to run your application again. You can have the installer offer to install desktop and menu shortcuts. Add these lines to the JNLP file:

Image

When the user first downloads the application, a “desktop integration warning” is displayed (see Figure 10–5).

Figure 10–5. The desktop integration warning

Image

You should also supply an icon for the menu shortcut and the launch screen. Sun recommends that you supply a 32 × 32 and a 64 × 64 icon. Place the icon files on the web server, together with the JNLP and JAR files. Add these lines to the information section of the JNLP file:

<icon href="calc_icon32.png" />
<icon href="calc_icon64.png" />

Note that these icons are not related to the application icon. If you want the application to have an icon, you need to add a separate icon image into the JAR file and call the setIconImage method on the frame class. (See Listing 10–1 for an example.)

The Sandbox

Whenever code is loaded from a remote site and then executed locally, security becomes vital. Clicking a single link can launch a Java Web Start application. Visiting a web page automatically starts all applets on the page. If clicking a link or visiting a web page could install arbitrary code on the user’s computer, criminals would have an easy time stealing confidential information, accessing financial data, or taking over users’ machines to send spam.

To ensure that the Java technology cannot be used for nefarious purposes, Java has an elaborate security model that we discuss in detail in Volume II. A security manager checks access to all system resources. By default, it only allows those operations that are harmless. To allow additional operations, the code must be digitally signed and the user must approve the signing certificate.

What can remote code do on all platforms? It is always ok to show images and play sounds, get keystrokes and mouse clicks from the user, and send user input back to the host from which the code was loaded. That is enough functionality to show facts and figures or to get user input for placing an order. The restricted execution environment is often called the “sandbox.” Code that plays in the sandbox cannot alter the user’s system or spy on it.

In particular, programs in the sandbox have the following restrictions:

• They can never run any local executable program.

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

• They cannot find out any information about the local computer, except for the Java version used and a few harmless operating system details. In particular, code in the sandbox cannot find out the user’s name, e-mail address, and so on.

• Remotely loaded programs 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 “remote code can only phone home.” The rule protects users from code that might try to spy on intranet resources. (As of Java SE 6, a Java Web Start application can make other network connections, but the program user must consent.)

• All pop-up windows carry a warning message. This message is a security feature to ensure that users do not mistake the window for a local application. The fear is that an unsuspecting user could visit a web page, be tricked into running remote code, and then type in a password or credit card number, which can be sent back to the web server. In early versions of the JDK, that message was very ominous: “Untrusted Java Applet Window”. Every successive version watered down the warning a bit—“Unauthenticated Java Applet Window”, then “Warning: Java Applet Window”. Now it is simply “Java Applet Window” or “Java Application Window”.

Signed Code

The sandbox restrictions are too restrictive for many situations. For example, on a corporate intranet, you can certainly imagine a Web Start application or applet wanting to access local files. It is possible to control in great detail which rights to grant a particular application; we discuss this in Chapter 9 of Volume II. Of course, an application can simply request to have all permissions of a desktop application, and quite a few Java Web Start applications do just that. This is accomplished by adding the following tags to the JNLP file:

Image

To run outside the sandbox, the JAR files of a Java Web Start application must be digitally signed. A signed JAR file carries with it a certificate that indicates the identity of the signer. Cryptographic techniques ensure that such a certificate cannot be forged, and that any effort to tamper with the signed file will be detected.

For example, suppose you receive an application that is produced and digitally signed by yWorks GmbH, using a certificate issued by Thawte (see Figure 10–6). When you receive the application, you will be assured of the following:

1. The code is exactly as it was when it was signed; no third party has tampered with it.

2. The signature really is from yWorks.

3. The certificate really was issued by Thawte. (Java Web Start knows how to check certificates from Thawte and a few other vendors.)

Unfortunately, that’s all you know. You do not know that the code is inherently safe. In fact, if you click on the “More Information” link, you are told that the application will run without the security restrictions normally provided by Java. Should you install and run the application? That really depends on your trust in yWorks GmbH.

Figure 10–6. A secure certificate

Image

Getting a certificate from one of the supported vendors costs hundreds of dollars per year. Many developers simply generate their own and use them for code signing. Of course, Java Web Start has no way of checking the accuracy of these certificates. When you receive such an application, then you know:

1. The code is exactly as it was when it was signed; no other party has tampered with it.

2. Someone has signed the code, but Java Web Start cannot verify who it was.

This is quite worthless; anyone could have tampered with the code and then signed it, claiming to be the author. Nevertheless, Java Web Start will be perfectly happy to present the certificate for your approval (see Figure 10–7). It is theoretically possible to verify the certificate through another way, but few users have the technical savvy to do that.

Figure 10–7. An insecure certificate

Image

Of course, many people download and run applications from the Internet every day. If you find that your users trust your application and your web infrastructure, go ahead and use a self-signed certificate. (See http://java.sun.com/javase/6/docs/technotes/guides/javaws/developersguide/development.html for details.) If not, give your users the benefit of safety and stay within the sandbox. With the JNLP API (which we discuss in the next section), you can still allow your program to selectively access resources, subject to user approval.

The JNLP API

The JNLP API allows unsigned applications to run in the sandbox and at the same time access local resources in a secure way. For example, there are services to load and save files. 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–8). 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 “file open” and “file save” actions, but as much system information as possible is hidden from untrusted applications.

Figure 10–8. A Java Web Start security advisory

Image

The API provides the following services:

• Loading and saving files

• Accessing the clipboard

• Printing

• Downloading a file

• Displaying a document in the default browser

• Storing and retrieving persistent configuration information

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

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

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

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


Image 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 Listing 10–2 on page 511 uses the following strategy:

1. It creates a ByteArrayOutputStream to hold the bytes to be saved.

2. It creates a PrintStream that sends its data to the ByteArrayOutputStream.

3. It prints the information to be saved to the PrintStream.

4. It creates a ByteArrayInputStream to read the saved bytes.

5. It passes that stream to the saveFileDialog method.

You will learn more about streams in Chapter 1 of Volume II. 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 and getOutputStream methods to read and write the file data. If the user didn’t choose a file, then the openFileDialog method returns null.

Image

Note that your application does not know the name or location of the file. Conversely, if you want to open a specific file, you use the ExtendedService:

Image

The user of your program must agree to the file access (see Figure 10–9).

Figure 10–9. File access warning

Image

To display a document on the default browser, use the BasicService interface. Note that some systems may not have a default browser.

Image

A rudimentary PersistenceService lets an application store small amounts of configuration information and retrieve it when the application runs again. 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.


Image Note

Starting with Java SE 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 Chapter 7 of Volume II.


The program in Listing 10–2 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–10).

Figure 10–10. The WebStartCalculator application

Image

Listing 10–2. WebStartCalculator.java

Image

Image

Image

Image

Image

Image

Image

javax.jnlp.ServiceManager

Image

static String[] getServiceNames()

returns the names of all available services.

static Object lookup(String name)

returns a service with a given name.

javax.jnlp.BasicService

Image

URL getCodeBase()

returns the codebase of this application.

boolean isWebBrowserSupported()

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

boolean showDocument(URL url)

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

javax.jnlp.FileContents

Image

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

returns true if the underlying file is readable or writable.

javax.jnlp.FileOpenService

Image

FileContents openFileDialog(String pathHint, String[] extensions)

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

displays a user warning and a file chooser. Returns content descriptors of the file or files that the user selected, or null if the user didn’t choose a file.

javax.jnlp.FileSaveService

Image

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

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

displays a user warning and a file chooser. Writes the data and returns content descriptors of the file or files that the user selected, or null if the user didn’t choose a file.

javax.jnlp.PersistenceService

Image

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.

Applets

Applets are Java programs that are included in an HTML page. 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 where to get the class files, and 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.

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, it seamlessly plugs in to a variety of browsers and enables them 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.


Image 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.com for download and configuration information.


A Simple Applet

For tradition’s sake, let’s write a NotHelloWorld program as an applet. An applet is simply a Java class that extends the java.applet.Applet class. 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–11, JApplet is an immediate subclass of the ordinary Applet class.

Figure 10–11. Applet inheritance diagram

Image


Image Note

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


Listing 10–3 on page 518 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.

Listing 10–3. NotHelloWorldApplet.java

Image

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" 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–12 shows the applet viewer displaying this applet.

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

Image


Image Tip

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

Image

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.



Image Tip

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


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, simply load the HTML file into the browser (see Figure 10–13). If the applet doesn’t show up, you need to install the Java Plug-in.

Figure 10–13. Viewing an applet in a browser

Image


Image 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 from the Java console. Launch the 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. Under Windows, open the Java Plug-in control in the Windows control panel. Under Linux, run jcontrol and request that the Java console be displayed. The console will pop up whenever an applet is loaded.


Converting Applications to Applets

It is easy to convert a graphical Java application 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:

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.


Image Note

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


java.applet.Applet 1.0

Image

void init()

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

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.

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.

The Applet HTML Tag and Its Attributes

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

<applet code="NotHelloWorldApplet.class" 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:

Image

You can use the following attributes within the applet tag:

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. The attribute values are the same as for the align attribute of the HTML img tag.

vspace, hspace

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

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:

Image

then you use the following tag in MyPage.html:

<applet code="MyApplet.class" codebase="myApplets" height="150">

archive

This optional attribute lists the JAR file or files containing classes and other resources for the applet. 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:

Image

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 1 of Volume II.) 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="MyApplet.ser" 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.


Image 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 (O’Reilly Media, Inc., 2006).


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

<applet code="MyApplet.class" name="mine">
</applet>

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

var myApplet = document.applets.mine;

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

myApplet.init();

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.


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


alt

Java may be deactivated in the browser, perhaps by a paranoid system administrator. You can then use the alt attribute to display a message to these unfortunate souls.

<applet code="MyApplet.class" height="150"
   alt="If you activated Java, you would see my applet here">

If a browser cannot process applets at all, it 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:

Image

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:

Image

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:

Image

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

Image


Image 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:

Image

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

Image


Image Note

A case-insensitive comparison is used when matching the name attribute value in the param tag and the argument of the getParameter method.


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–14.

Figure 10–14. A chart applet

Image

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–14 looks like:

Image

Image

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.

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

Listing 10–4. Chart.java

Image

Image

Image

Image

Image

java.applet.Applet 1.0

Image

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"

Accessing Image and Audio Files

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.

You specify the locations of image and audio files with relative URLs. The base URL is usually obtained by calling the getDocumentBase or getCodeBase method. The former gets the URL of the HTML page in which the applet is contained, the latter the URL of the applet’s codebase directory.


Image Note

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


Give the base URL and the file location to the getImage or getAudioClip method. For example:

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

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");

java.applet.Applet 1.0

Image

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)

returns 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" 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 Chapter 13.) Here is a loop that prints the class names of all applets on the current page:

Image

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");


Image 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

Image


Image Note

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


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

Quite a few years ago, a Saturday Night Live skit poked fun at a television commercial, showing a couple that argued about a 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 turn an applet into 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–15 and 10–16 show the same program, viewed inside the applet viewer as an applet and launched from the command line as an application.

Figure 10–15. It’s an applet!

Image

Figure 10–16. It’s an application!

Image

A GUI application constructs a frame object and invokes setVisible(true) on it. Since you cannot call setVisible on a naked applet, the applet must be placed inside a frame. We provide a class AppletFrame whose constructor adds the applet into the content pane:

Image

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

Image

(Note that we simply extend the existing applet.)

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

Image

There is one catch. If the program is started as an application, 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. 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). We supply the bare minimum functionality that is necessary to implement these two interfaces.

Image

Image

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

Image

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.

Listings 10–5 and 10–6 list the code. Try running both the applet and the application:

appletviewer AppletApplication.java
java AppletApplication

Listing 10–5. AppletFrame.java

Image

Image

Image

Image

Image

Listing 10–6. AppletApplication.java

Image

java.applet.Applet 1.2

Image

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.


java.applet.AppletContext 1.0

Image

Enumeration<Applet> getApplets()

returns an enumeration (see Chapter 13) 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)

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

Storage of Application Preferences

Users of your applications will usually expect that their preferences and customizations are saved, and later restored when the application starts again. First, we cover the simple approach for storing configuration information in property files that Java applications have traditionally taken. We then turn to the powerful preferences mechanism that was introduced in Java SE 1.4.

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 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("width", "200");
settings.put("title", "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 program.properties. The second argument is a comment that is included in the file.

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

The sample set gives the following output:

#Program Properties
#Mon Apr 30 07:22:52  2007
width=200
title=Hello, World!

To load the properties from a file, use

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

It is customary to store program properties in a subdirectory of the user’s home directory. The directory name is often chosen to start with a dot—on a UNIX system, this convention indicates a system directory that is hidden from the user. Our sample program follows this convention.

To find the user’s home directory, you can call the System.getProperties method, which, as it happens, also uses a Properties object to describe the system information. The home directory has the key "user.home". There is also a convenience method to read a single key:

String userDir = System.getProperty("user.home");

It is a good idea to provide defaults for our program properties, in case a user edits the file by hand. 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 title = settings.getProperty("title", "Default title");

If there is a "title" property in the property map, title is set to that string. Otherwise, title is set to "Default title".

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 primary property map.

Properties defaultSettings = new Properties();
defaultSettings.put("width", "300");
defaultSettings.put("height", "200");
defaultSettings.put("title", "Default title");
. . .
Properties settings = new Properties(defaultSettings);

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.

Listing 10–7 shows how you can use properties for storing and loading program state. The program remembers the frame position, size, and title. You can also manually edit the file .corejava/program.properties in your home directory to change the program’s appearance to the way you want


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


Listing 10–7. PropertiesTest.java

Image

Image

Image

Image

Image

java.util.Properties 1.0

Image

Properties()

creates an empty property map.

Properties(Properties defaults)

creates an empty property map with a set of defaults.

Image

String getProperty(String key)

gets a property map. 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.

Image

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.

Image

void load(InputStream in) throws IOException

loads a property map from an input stream.

Image

void store(OutputStream out, String header) 1.2

saves a property map to an output stream.

Image

java.lang.System 1.0

Image

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 following properties can always be retrieved:

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


Image Note

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


The Preferences API

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

• 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 Java SE 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:

Image

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 Listing 10–8 demonstrates this technique. The program simply saves the position, size, and title 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.

Listing 10–8. PreferencesTest.java

Image

Image

Image

Image

Image

Image

java.util.prefs.Preferences 1.4

Image

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)

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

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

stores 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 runtime. 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
3.22.71.106