Doing More With Java Rich Internet Applications

«« Previous
Next »»

Doing More With Java Rich Internet Applications

Applets launched by using the Java Network Launch Protocol (JNLP) have capabilities similar to those of Java Web Start applications. This lesson contains topics common to the development and deployment of applets and Java Web Start applications (together known as rich Internet applications).

Setting Trusted Arguments and Secure Properties

You can set certain Java Virtual Machine arguments and secure properties for your rich Internet application (RIA) in the RIA's Java Network Launch Protocol (JNLP) file. For applets, you can also set arguments in the java_arguments parameter of the <applet> tag. Although there is a predefined set of secure properties, you can also define new secure properties by prefixing the property name with "jnlp." or "javaws.". Properties can be retrieved in your RIA by using the System.getProperty method.

Consider the Properties and Arguments Demo applet. The following Java Virtual Machine arguments and properties are set in the applet's JNLP file, appletpropsargs.jnlp.

◉ -Xmx – A secure argument set equal to "256M"
◉ sun.java2d.noddraw – A predefined secure property set equal to "true"
◉ jnlp.myProperty – A user-defined secure property set equal to "a user-defined property"

<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="" href="">
    <information>
        <title>Properties and Arguments Demo Applet</title>
        <vendor>Dynamic Team</vendor>
    </information>
    <resources>
        <!-- Application Resources -->
        <j2se version="1.6+"
              href="http://java.sun.com/products/autodl/j2se"
              <!-- secure java vm argument -->
              java-vm-args="-Xmx256M"/>
        <jar href="applet_PropertiesAndVMArgs.jar"
            main="true" />
            <!-- secure properties -->
        <property name="sun.java2d.noddraw"
            value="true"/>
        <property name="jnlp.myProperty"
            value="a user-defined property"/>
    </resources>
    <applet-desc
         name="Properties and Arguments Demo Applet"
         main-class="PropertiesArgsDemoApplet"
         width="800"
         height="50">           
     </applet-desc>
     <update check="background"/>
</jnlp>

The PropertiesArgsDemoApplet class uses the System.getProperty method to retrieve the java.version property and other properties that are set in the JNLP file. The PropertiesArgsDemoApplet class also displays the properties.

import javax.swing.JApplet;
import javax.swing.SwingUtilities;
import javax.swing.JLabel;

public class PropertiesArgsDemoApplet extends JApplet {
    public void init() {
        final String javaVersion = System.getProperty("java.version");
        final String  swing2dNoDrawProperty = System.getProperty("sun.java2d.noddraw");
        final String  jnlpMyProperty = System.getProperty("jnlp.myProperty");     

        try {
            SwingUtilities.invokeAndWait(new Runnable() {
                public void run() {
                    createGUI(javaVersion, swing2dNoDrawProperty, jnlpMyProperty);
                }
            });
        } catch (Exception e) {
            System.err.println("createGUI didn't successfully complete");
        }
    }
    private void createGUI(String javaVersion, String swing2dNoDrawProperty, String jnlpMyProperty) {
        String text = "Properties: java.version = " + javaVersion +
                ",  sun.java2d.noddraw = " + swing2dNoDrawProperty +
                ",   jnlp.myProperty = " + jnlpMyProperty;
        JLabel lbl = new JLabel(text);
        add(lbl);
    }
}

Note: If you don't see the example running, you might need to enable the JavaScript interpreter in your browser so that the Deployment Toolkit script can function properly.

System Properties

This topic lists system properties that can be accessed by rich Internet applications (RIAs) that are restricted to the security sandbox and are launched with or without the Java Network Launch Protocol (JNLP). Some system properties cannot be accessed by sandbox RIAs.

Secure System Properties Accessible by All RIAs

All RIAs can retrieve the following secure system properties:

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

Secure System Properties Accessible by RIAs Launched by Using JNLP

RIAs launched by using JNLP can set and retrieve the following secure properties:

◉ awt.useSystemAAFontSettings
◉ http.agent
◉ http.keepAlive
◉ java.awt.syncLWRequests
◉ java.awt.Window.locationByPlatform
◉ javaws.cfg.jauthenticator
◉ javax.swing.defaultlf
◉ sun.awt.noerasebackground
◉ sun.awt.erasebackgroundonresize
◉ sun.java2d.d3d
◉ sun.java2d.dpiaware
◉ sun.java2d.noddraw
◉ sun.java2d.opengl
◉ swing.boldMetal
◉ swing.metalTheme
◉ swing.noxp
◉ swing.useSystemFontSettings

Forbidden System Properties

Sandbox RIAs cannot access the following system properties:

◉ java.class.path
◉ java.home
◉ user.dir
◉ user.home
◉ user.name

JNLP API


Rich Internet applications (RIAs) can use the Java Network Launch Protocol (JNLP) API to perform extensive operations on the user's environment. When launched by using JNLP, even unsigned RIAs can perform the following operations with the user's permission:

◉ They can use the FileOpenService and FileSaveService API to access the user's file system..
◉ They can use the ClipboardService API to access the shared system-wide clipboard.
◉ They can use the PrintService API to access printing functions.
◉ They can use the PersistenceService API to access persistence storage.
◉ They can use the DownloadService API to control how the RIA is downloaded and cached.
◉ They can use the DownloadServiceListener API to determine progress of the RIA's download.
◉ They can use the SingleInstanceService API to decide how to handle arguments when multiple instances of the RIA are launched.
◉ They can use the ExtendedService API to request permission to open certain files that have not been opened before.

Accessing the Client Using JNLP API

When launched by using the Java Network Launch Protocol (JNLP), rich Internet applications (RIAs) can access the client with the user's permission. Consider the Text Editor applet example to understand how to use JNLP API based services. The Text Editor has a text area and buttons labeled Open, Save, and SaveAs. The Text Editor can be used to open an existing text file, edit it, and save it back to disk.

The Text Editor applet is shown next.

Note:  If you don't see the applet running, you need to install at least the Java SE Development Kit (JDK) 6 update 10 release.
Note:  If you don't see the example running, you might need to enable the JavaScript interpreter in your browser so that the Deployment Toolkit script can function properly.

The TextEditor and TextEditorApplet classes lay out the user interface and display it as an applet. The FileHandler class contains the core functionality with respect to using JNLP API based services.

Remember, the techniques described in this topic apply to Java Web Start applications as well.

To make use of a JNLP service, first retrieve a reference to the service. The initialize method of the FileHandler class retrieves references to JNLP services as shown in the following code snippet:

private static synchronized void initialize() {
    ...
    try {
        fos = (FileOpenService)
            ServiceManager.lookup("javax.jnlp.FileOpenService");
        fss = (FileSaveService)
            ServiceManager.lookup("javax.jnlp.FileSaveService");
    } catch (UnavailableServiceException e) {
        ...
    }
}

After you have a reference to the required services, invoke methods on the service to perform the necessary operations. The open method of the FileHandler class invokes the openFileDialog method of the FileOpenService class to display a file chooser. The open method returns the contents of the selected file.

public static String open() {
    initialize();
    try {
        fc = fos.openFileDialog(null, null);
        return readFromFile(fc);
    } catch (IOException ioe) {
        ioe.printStackTrace(System.out);
        return null;
    }
}

Similarly, the save and saveAs methods of the FileHandler class invoke corresponding methods of the FileSaveService class to enable the user to select a file name and save the contents of the text area to disk.

public static void saveAs(String txt) {
    initialize();
    try {
        if (fc == null) {
            // If not already saved.
            // Save-as is like save
            save(txt);
        } else {
            fc = fss.saveAsFileDialog(null, null,
                                         fc);
            save(txt);
        }
    } catch (IOException ioe) {
        ioe.printStackTrace(System.out);
    }
}

At runtime, when the RIA attempts to open or save a file, users see a security dialog asking them if they want to allow the action. The operation will proceed only if users allow the RIA to access their environment.

The complete source of the FileHandler class is shown next.

// add javaws.jar to the classpath during compilation 
import javax.jnlp.FileOpenService;
import javax.jnlp.FileSaveService;
import javax.jnlp.FileContents;
import javax.jnlp.ServiceManager;
import javax.jnlp.UnavailableServiceException;
import java.io.*;

public class FileHandler {

    static private FileOpenService fos = null;
    static private FileSaveService fss = null;
    static private FileContents fc = null;

    // retrieves a reference to the JNLP services
    private static synchronized void initialize() {
        if (fss != null) {
            return;
        }
        try {
            fos = (FileOpenService) ServiceManager.lookup("javax.jnlp.FileOpenService");
            fss = (FileSaveService) ServiceManager.lookup("javax.jnlp.FileSaveService");
        } catch (UnavailableServiceException e) {
            fos = null;
            fss = null;
        }
    }

    // displays open file dialog and reads selected file using FileOpenService
    public static String open() {
        initialize();
        try {
            fc = fos.openFileDialog(null, null);
            return readFromFile(fc);
        } catch (IOException ioe) {
            ioe.printStackTrace(System.out);
            return null;
        }
    }

    // displays saveFileDialog and saves file using FileSaveService
    public static void save(String txt) {
        initialize();
        try {
            // Show save dialog if no name is already given
            if (fc == null) {
                fc = fss.saveFileDialog(null, null,
                        new ByteArrayInputStream(txt.getBytes()), null);
                // file saved, done
                return;
            }
            // use this only when filename is known
            if (fc != null) {
                writeToFile(txt, fc);
            }
        } catch (IOException ioe) {
            ioe.printStackTrace(System.out);
        }
    }

    // displays saveAsFileDialog and saves file using FileSaveService
    public static void saveAs(String txt) {
        initialize();
        try {
            if (fc == null) {
                // If not already saved. Save-as is like save
                save(txt);
            } else {
                fc = fss.saveAsFileDialog(null, null, fc);
                save(txt);
            }
        } catch (IOException ioe) {
            ioe.printStackTrace(System.out);
        }
    }

    private static void writeToFile(String txt, FileContents fc) throws IOException {
        int sizeNeeded = txt.length() * 2;
        if (sizeNeeded > fc.getMaxLength()) {
            fc.setMaxLength(sizeNeeded);
        }
        BufferedWriter os = new BufferedWriter(new OutputStreamWriter(fc.getOutputStream(true)));
        os.write(txt);
        os.close();
    }

    private static String readFromFile(FileContents fc) throws IOException {
        if (fc == null) {
            return null;
        }
        BufferedReader br = new BufferedReader(new InputStreamReader(fc.getInputStream()));
        StringBuffer sb = new StringBuffer((int) fc.getLength());
        String line = br.readLine();
        while (line != null) {
            sb.append(line);
            sb.append("\n");
            line = br.readLine();
        }
        br.close();
        return sb.toString();
    }
}

Note: To compile Java code that has a reference to classes in the javax.jnlp package, include <your JDK path>/jre/lib/javaws.jar in your classpath. At runtime, the Java Runtime Environment software automatically makes these classes available to RIAs.

Cookies

Web applications are typically a series of Hypertext Transfer Protocol (HTTP) requests and responses. As HTTP is a stateless protocol, information is not automatically saved between HTTP requests. Web applications use cookies to store state information on the client. Cookies can be used to store information about the user, the user's shopping cart, and so on.

Types of Cookies

The two types of cookies follow:

◉ Session cookies – Session cookies are stored in memory and are accessible as long as the user is using the web application. Session cookies are lost when the user exits the web application. Such cookies are identified by a session ID and are most commonly used to store details of a shopping cart.

◉ Permanent cookies – Permanent cookies are used to store long-term information such as user preferences and user identification information. Permanent cookies are stored in persistent storage and are not lost when the user exits the application. Permanent cookies are lost when they expire.

Cookie Support in Rich Internet Applications

Rich Internet applications (applets and Java Web Start applications) support session and permanent cookies. The underlying cookie store depends on the browser and the operating system on the client.

Accessing Cookies

You can set and retrieve cookies in your rich Internet application (RIA). Cookies can enhance the capabilities of your RIA. For example, consider the scenario where you have applets on various web pages. An applet on a web page cannot directly access or share information with an applet on another web page. In this scenario, cookies provide an important connection between applets and help one applet pass information to another applet on a different web page. Java Web Start applications can also use cookies to store information on the client.

The Cookie Applet example has a CookieAccessor class that retrieves and sets cookies.

Retrieving Cookies

The following code snippet shows the getCookieUsingCookieHandler method of the CookieAccessor class:

public void getCookieUsingCookieHandler() {
    try {     
        // Instantiate CookieManager;
        // make sure to set CookiePolicy
        CookieManager manager = new CookieManager();
        manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
        CookieHandler.setDefault(manager);

        // get content from URLConnection;
        // cookies are set by web site
        URL url = new URL("http://host.example.com");
        URLConnection connection = url.openConnection();
        connection.getContent();

        // get cookies from underlying
        // CookieStore
        CookieStore cookieJar =  manager.getCookieStore();
        List <HttpCookie> cookies =
            cookieJar.getCookies();
        for (HttpCookie cookie: cookies) {
          System.out.println("CookieHandler retrieved cookie: " + cookie);
        }
    } catch(Exception e) {
        System.out.println("Unable to get cookie using CookieHandler");
        e.printStackTrace();
    }
}

The CookieManager class is the main entry point for cookie management. Create an instance of the CookieManager class and set its CookiePolicy. Set this instance of the CookieManager as the default CookieHandler.

Open a URLConnection to the website of your choice.

Next, retrieve cookies from the underlying CookieStore by using the getCookies method.

Setting Cookies

The following code snippet shows the setCookieUsingCookieHandler method of the CookieAccessor class:

public void setCookieUsingCookieHandler() {
    try {
        // instantiate CookieManager
        CookieManager manager = new CookieManager();
        CookieHandler.setDefault(manager);
        CookieStore cookieJar =  manager.getCookieStore();

        // create cookie
        HttpCookie cookie = new HttpCookie("UserName", "John Doe");

        // add cookie to CookieStore for a
        // particular URL
        URL url = new URL("http://host.example.com");
        cookieJar.add(url.toURI(), cookie);
        System.out.println("Added cookie using cookie handler");
    } catch(Exception e) {
        System.out.println("Unable to set cookie using CookieHandler");
        e.printStackTrace();
    }
}

As shown in Retrieving Cookies, the CookieManager class is the main entry point for cookie management. Create an instance of the CookieManager class and set the instance as the default CookieHandler.

Create the desired HttpCookie with the necessary information. In our example, we have created a new HttpCookie that sets the UserName as John Doe.

Next, add the cookie to the underlying cookie store.

Running the Cookie Applet Example

To access cookies, you must sign your RIA JAR file and request permission to run outside of the security sandbox.

Customizing the Loading Experience
.
Rich Internet applications (RIAs) may take a few seconds to load depending on factors such as network speed and resources required by the RIA. Customize the RIA loading experience by providing a splash screen or a customized loading progress indicator to engage the end user during the loading process and to communicate measurable progress information.

While the overall mechanisms to customize the loading experience for applets and Java Web Start applications are similar, there are subtle differences in syntax and implementation. See the following topics for step-by-step instructions and conceptual information about customizing the RIA loading experience:

◉ Customizing the Loading Screen
◉ Displaying a Customized Loading Progress Indicator for an applet
◉ Displaying a Customized Loading Progress Indicator for a Java Web Start application
◉ Customizing the Loading Experience (conceptual information, client Java Runtime Environment (JRE) software capabilities)

Security in Rich Internet Applications

The security model behind rich Internet applications (RIAs) works to protect the user from malicious Internet applications. This topic discusses security aspects that are common to applets and Java Web Start applications.

RIAs can be restricted to the Java security sandbox or request permission to access resources outside the sandbox. The first time an RIA is launched, the user is prompted for permission to run. The dialog shown provides information about the signer's certificate and indicates if the RIA requests permission to run outside the sandbox. The user can then make an informed decision about running the application.

Apply the following guidelines to help secure your RIAs.

◉ Sign the JAR file of the RIA with a certificate from a recognized certificate authority.

◉ If the RIA requires access outside of the security sandbox, specify the all-permissions element in the JNLP file for the RIA. Otherwise, let the RIA default to running in the security sandbox. The following code snippet shows the all-permissions element in the RIA's JNLP file.

<security>
   <all-permissions/>
</security>

◉ A JNLP file can only include JAR files signed by the same certificate. If you have JAR files that are signed using different certificates, specify them in separate JNLP files. In the RIA's main JNLP file, specify the component-desc element to include the other JNLP files as component extensions.

◉ The security model for RIAs does not allow JavaScript code from a web page to invoke security-sensitive code in a signed JAR file unless you explicitly enable this. In the signed JAR file, wrap the section of code that you want JavaScript code to be able to invoke in a AccessController.doPrivileged block. This allows the JavaScript code to run with elevated permissions when executing the code in the doPrivileged code block.

◉ Avoid mixing privileged and sandbox components in a RIA, if possible, as they can raise security warnings about mixed code.

◉ Include the Permissions and Codebase attributes in the JAR file manifest to ensure that your RIA requests only the permissions you specify, and that the RIA is accessed from the correct location.

◉ JAR file manifest attributes enable you to restrict access to your RIA and help to ensure that your code is not tampered with.

Guidelines for Securing Rich Internet Applications

The following guidelines provide steps you can take to reduce the vulnerability of the Rich Internet Applications (RIAs) that you provide to users.

Follow Secure Coding Guidelines

Follow the recommendations in the Secure Coding Guidelines for the Java Programming Language. Section 4, "Accessibility and Extensibility" describes how to limit accessibility to classes and packages, which reduces the vulnerability of your code.

JavaScript code is considered insecure and is restricted to the security sandbox by default. Minimize interactions between your RIA and JavaScript code. Use the AccessController.doPrivileged block with care because it allows access from any HTML page or JavaScript code.

Test with the Latest Version of the JRE

Make sure that your RIA runs on the latest, secure version of the JRE. The Java platform supports the ability for RIAs to specify the Java version that is needed to run the RIA, however, requiring users to maintain more than one version of the JRE, especially older, insecure versions, is a security risk for the user.

One of the benefits of RIAs is that updated versions of the RIA are automatically download to a user's system. Test your RIA against each update of the JRE and make sure that it works. If changes are needed, update your RIA on the server so that users can install the latest JRE and still run the RIA.

Include Manifest Attributes

Add attributes to the JAR file manifest that describe the properties of the RIA. Values in the JNLP file or the applet tag are compared to values in the manifest to verify that the correct code is run.

Request sandbox permissions when your RIA does not require access beyond the security sandbox. The Java sandbox provides additional protections for users, and users might not run a privileged application if they do not understand why it requests unrestricted access to their system.

Manifest attributes can also be used to identify the locations from which your RIA can be accessed. This includes locations from which JavaScript code can call your RIA, and locations of JNLP files or applet tags that can start your RIA. 

Use a Signed JNLP File

If your RIA needs to access non-secure system properties or JVM arguments, use a signed JNLP. If some variation between the external and internal JNLP files is required, use JNLP templates. 

To access non-secure system properties or JVM arguments, include the property or argument in the JNLP file as described in Setting Trusted Arguments and Secure Properties.

Sign and Time Stamp JAR Files

Obtain a code signing certificate from a trusted certificate authority and use it to sign the JAR files for your RIA. Deploy to users only RIAs that are signed with a valid certificate.

When you sign your JAR file, also time stamp the signature. Time stamping verifies that the certificate was valid at the time that the JAR was signed, so the RIA is not automatically blocked when the certificate expires. 

Self-signed and unsigned RIAs are considered unsafe and are not allowed to run unless an exception site list or deployment rule set is set up to allow specific applications. However, self-signing can be useful for testing purposes. To test using your self-signed RIA, you can import the self-signed certificate into the trusted keystore.

Use the HTTPS Protocol

Use the HTTPS protocol for the web server from which users get your RIA. The HTTPS protocol is encrypted and validated by the server, making it more difficult for anyone to tamper with your RIA.

Avoid Local RIAs

Local RIAs are not intended for use in production. To ensure that users run the code that you intend for them to run, host your RIA on an application server.

For testing, the use of a web server is recommended. Another option is to add your application to the exception site list, which is managed in the Security tab of the Java Control Panel.

Questions and Exercises: Doing More With Rich Internet Applications

Questions

1. True or False: Rich Internet applications (RIAs) can set secure properties by prefixing the property name with "jnlp.".
2. True or False: Only signed RIAs can use JNLP API to access files on the client.

Exercises

To the following JNLP file, add a secure property called jnlp.foo and set its value to true.
<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="" href="">
    <information>
        <title>Dynamic Tree Demo</title>
        <vendor>Dynamic Team</vendor>

    </information>
    <resources>
        <!-- Application Resources -->
        <j2se version="1.6+" href=
            "http://java.sun.com/products/autodl/j2se" />
        <jar href="DynamicTreeDemo.jar" main="true" />
    </resources>
    <applet-desc 
       name="Dynamic Tree Demo Applet"
       main-class="components.DynamicTreeApplet"
       width="300"
       height="300">
     </applet-desc>
     <update check="background"/>
</jnlp>

Answers to Questions and Exercises: Doing More With Rich Internet Applications

Questions

1. Question: True or False: Rich Internet applications (RIAs) can set secure properties by prefixing the property name with "jnlp.".

Answer: True: Rich Internet applications (RIAs) can set secure properties by prefixing the property name with "jnlp." or "javaws.".

2. Question: True or False: Only signed RIAs can use JNLP API to access files on the client.

Answer: False: Unsigned RIAs can also use JNLP API to access files on the client.

Exercises

1. Exercise: To the following JNLP file, add a secure property called jnlp.foo and set its value to true.

<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="" href="">
    <information>
        <title>Dynamic Tree Demo</title>
        <vendor>Dynamic Team</vendor>

    </information>
    <resources>
        <!-- Application Resources -->
        <j2se version="1.6+"
              href="http://java.sun.com/products/autodl/j2se" />
        <jar href="DynamicTreeDemo.jar" main="true" />
    </resources>
    <applet-desc 
         name="Dynamic Tree Demo Applet"
         main-class="components.DynamicTreeApplet"
         width="300"
         height="300">
     </applet-desc>
     <update check="background"/>
</jnlp>                           

Answer:

<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="" href="">
    <information>
        <title>Dynamic Tree Demo</title>
        <vendor>Dynamic Team</vendor>

    </information>
    <resources>
        <!-- Application Resources -->
        <j2se version="1.6+"
              href="http://java.sun.com/products/autodl/j2se" />
        <jar href="DynamicTreeDemo.jar" main="true" />
        <property name="jnlp.foo" value="true"/>
    </resources>
    <applet-desc 
         name="Dynamic Tree Demo Applet"
         main-class="components.DynamicTreeApplet"
         width="300"
         height="300">
     </applet-desc>
     <update check="background"/>
</jnlp>

«« Previous
Next »»

0 comments:

Post a Comment