Java/Network Protocol/JNLP Web Start

Материал из Java эксперт
Перейти к: навигация, поиск

Hello Java Web start

   <source lang="java">

/* From http://java.sun.ru/docs/books/tutorial/index.html */ /*

* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* -Redistribution of source code must retain the above copyright notice, this
*  list of conditions and the following disclaimer.
*
* -Redistribution in binary form must reproduce the above copyright notice,
*  this list of conditions and the following disclaimer in the documentation
*  and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or intended
* for use in the design, construction, operation or maintenance of any
* nuclear facility.
*/

import java.awt.Font; import javax.swing.JFrame; import javax.swing.JLabel; public class HelloJWS {

 /**
  * Create the GUI and show it. For thread safety, this method should be
  * invoked from the event-dispatching thread.
  */
 private static void createAndShowGUI() {
   //Make sure we have nice window decorations.
   JFrame.setDefaultLookAndFeelDecorated(true);
   //Create and set up the window.
   JFrame frame = new JFrame("HelloJWS");
   frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   //Add the "HelloJWS" label.
   JLabel label = new JLabel();
   label.setHorizontalAlignment(JLabel.CENTER);
   label.setFont(label.getFont().deriveFont(Font.PLAIN));
   frame.getContentPane().add(label);
   //Set label text after testing for Java Web Start.
   String text = null;
   try {
     Class sm = javax.jnlp.ServiceManager.class;
     //If we reach this line, we"re running in an environment
     //such as Java Web Start that provides JNLP services.
     text = "<html>You"re running an application "
         + "using JavaTM "
         + "Web Start!</html>";
   } catch (java.lang.NoClassDefFoundError e) {
     //If no ServiceManager, we"re not in Java Web Start.
     text = "<html>You"re running an application, "
         + "but not using "
         + "JavaTM "
         + "Web Start!</html>";
   }
   label.setText(text);
   //Display the window.
   frame.pack();
   frame.setVisible(true);
 }
 public static void main(String[] args) {
   //Schedule a job for the event-dispatching thread:
   //creating and showing this application"s GUI.
   javax.swing.SwingUtilities.invokeLater(new Runnable() {
     public void run() {
       createAndShowGUI();
     }
   });
 }

}


 </source>
   
  
 
  



Java Web Start Demo

   <source lang="java">

      

File: example.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD>

  <TITLE>Java Web Start Demo</TTLE>    

</HEAD> <BODY>

Java Web Start Demo

Java Web Start: File Chooser Demo Project

   <source lang="java">

/*

* Copyright (c) 1995 - 2008 Sun Microsystems, Inc.  All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*   - Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*
*   - Redistributions in binary form must reproduce the above copyright
*     notice, this list of conditions and the following disclaimer in the
*     documentation and/or other materials provided with the distribution.
*
*   - Neither the name of Sun Microsystems nor the names of its
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ 

package components; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.ByteArrayInputStream; import java.io.IOException; import javax.swing.*; import javax.swing.filechooser.*; import javax.swing.SwingUtilities; import javax.jnlp.*; /*

* JWSFileChooserDemo.java must be compiled with jnlp.jar.  For
* example, if jnlp.jar is in a subdirectory named jars:
* 
*   javac -classpath .:jars/jnlp.jar JWSFileChooserDemo.java [UNIX]
*   javac -classpath .;jars/jnlp.jar JWSFileChooserDemo.java [Microsoft Windows]
*
* JWSFileChooserDemo.java requires the following files when executing:
*   images/Open16.gif
*   images/Save16.gif
*/

public class JWSFileChooserDemo extends JPanel

                               implements ActionListener {
   static private final String newline = "\n";
   JButton openButton, saveButton;
   JTextArea log;
   public JWSFileChooserDemo() {
       super(new BorderLayout());
       //Create the log first, because the action listeners
       //need to refer to it.
       log = new JTextArea(5,20);
       log.setMargin(new Insets(5,5,5,5));
       log.setEditable(false);
       JScrollPane logScrollPane = new JScrollPane(log);
       //Create the open button.  We use the image from the JLF
       //Graphics Repository (but we extracted it from the jar).
       openButton = new JButton("Open a File...", 
                                createImageIcon("images/Open16.gif"));
       openButton.addActionListener(this);
       //Create the save button.  We use the image from the JLF
       //Graphics Repository (but we extracted it from the jar).
       saveButton = new JButton("Save a File...",
                                createImageIcon("images/Save16.gif"));
       saveButton.addActionListener(this); 
       //For layout purposes, put the buttons in a separate panel
       JPanel buttonPanel = new JPanel();
       buttonPanel.add(openButton);
       buttonPanel.add(saveButton);
       //Add the buttons and the log to this panel.
       add(buttonPanel, BorderLayout.PAGE_START);
       add(logScrollPane, BorderLayout.CENTER);
   }
   public void actionPerformed(ActionEvent e) {
       //Handle open button action.
       if (e.getSource() == openButton) {
           FileOpenService fos = null;
           FileContents fileContents = null;
           try {
               fos = (FileOpenService)ServiceManager.
                         lookup("javax.jnlp.FileOpenService"); 
           } catch (UnavailableServiceException exc) { }
           if (fos != null) {
               try {
                   fileContents = fos.openFileDialog(null, null); 
               } catch (Exception exc) {
                   log.append("Open command failed: "
                              + exc.getLocalizedMessage()
                              + newline);
                   log.setCaretPosition(log.getDocument().getLength());
               }
           }
           if (fileContents != null) {
               try {
                   //This is where a real application would do something
                   //with the file.
                   log.append("Opened file: " + fileContents.getName()
                              + "." + newline);
               } catch (IOException exc) {
                   log.append("Problem opening file: "
                              + exc.getLocalizedMessage()
                              + newline);
               }
           } else {
               log.append("User canceled open request." + newline);
           }
           log.setCaretPosition(log.getDocument().getLength());
       }
       //Handle save button action.
       if (e.getSource() == saveButton) {
           FileSaveService fss = null;
           FileContents fileContents = null;
           ByteArrayInputStream is = new ByteArrayInputStream(
                   (new String("Saved by JWSFileChooserDemo").getBytes()));
                                            //XXX YIKES! If they select an
                                            //XXX existing file, this will
                                            //XXX overwrite that file.
           try {
               fss = (FileSaveService)ServiceManager.
                         lookup("javax.jnlp.FileSaveService"); 
           } catch (UnavailableServiceException exc) { }
           if (fss != null) {
               try {
                   fileContents = fss.saveFileDialog(null,
                                                     null,
                                                     is,
                                                     "JWSFileChooserDemo.txt"); 
               } catch (Exception exc) {
                   log.append("Save command failed: "
                              + exc.getLocalizedMessage()
                              + newline);
                   log.setCaretPosition(log.getDocument().getLength());
               }
           }
           if (fileContents != null) {
               try {
                   log.append("Saved file: " + fileContents.getName()
                              + "." + newline);
               } catch (IOException exc) {
                   log.append("Problem saving file: "
                              + exc.getLocalizedMessage()
                              + newline);
               }
           } else {
               log.append("User canceled save request." + newline);
           }
           log.setCaretPosition(log.getDocument().getLength());
       }
   }
   /** Returns an ImageIcon, or null if the path was invalid. */
   protected static ImageIcon createImageIcon(String path) {
       java.net.URL imgURL = JWSFileChooserDemo.class.getResource(path);
       if (imgURL != null) {
           return new ImageIcon(imgURL);
       } else {
           System.err.println("Couldn"t find file: " + path);
           return null;
       }
   }
   /**
    * Create the GUI and show it.  For thread safety,
    * this method should be invoked from the
    * event dispatch thread.
    */
   private static void createAndShowGUI() {
       //Create and set up the window.
       JFrame frame = new JFrame("JWSFileChooserDemo");
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       //Add content to the window.
       frame.add(new JWSFileChooserDemo());
       //Display the window.
       frame.pack();
       frame.setVisible(true);
   }
   public static void main(String[] args) {
       //Schedule a job for the event dispatch thread:
       //creating and showing this application"s GUI.
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
               //Turn off metal"s use of bold fonts
               UIManager.put("swing.boldMetal", Boolean.FALSE);
               createAndShowGUI();
           }
       });
   }

}


 </source>
   
  
 
  



JNLP-based applet launcher class for deploying applets that use extension libraries containing native code.

   <source lang="java">

/*

* $RCSfile: JNLPAppletLauncher.java,v $
*
* Copyright 2007 Sun Microsystems, Inc.  All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistribution of source code must retain the above copyright
*   notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
*   notice, this list of conditions and the following disclaimer in
*   the documentation and/or other materials provided with the
*   distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any
* kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
* EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
* NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
* USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
* ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
* CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
* REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
* INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or
* intended for use in the design, construction, operation or
* maintenance of any nuclear facility.
*
* $Revision: 1.29 $
* $Date: 2008/10/21 21:33:50 $
* $State: Exp $
*/

import java.applet.Applet; import java.applet.AppletContext; import java.applet.AppletStub; import java.awt.BorderLayout; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Method; import java.net.URL; import java.net.URLConnection; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.logging.Logger; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.SwingUtilities; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /**

* The JNLPAppletLauncher is a general purpose JNLP-based applet
* launcher class for deploying applets that use extension libraries
* containing native code. It allows applets to use extensions like
* Java 3D, JOGL, and JOAL very easily, with just a few additional
* parameters to the <applet> tag, on Java SE
* versions as far back as 1.4.2.
*
* <p>
*
* Like Java Web Start, the JNLPAppletLauncher uses an extension"s
* .jnlp file to locate the native resources for a given extension.
* The applet developer only needs to specify the platform-independent
* .jar files containing the .class files for the extension. The
* platform-specific "nativelib" .jar files are downloaded
* automatically from the same server that hosts the extension"s Java
* Web Start binaries.
*
* <p>
*
* Extensions that support JNLPAppletLauncher include Java 3D, JOGL,
* and JOAL. More can be added without needing to modify the
* JNLPAppletLauncher. See the section below on </h2>
*
* <p>
*
* If you are the author of an extension like JOGL which requires some
* native code, with only a simple code change you can make your
* extension work with the JNLPAppletLauncher. Simply add the
* following method somewhere in your code:
*
*
 *  private static void loadLibraryInternal(String libraryName) {
 *      String sunAppletLauncher = System.getProperty("sun.jnlp.applet.launcher");
 *      boolean usingJNLPAppletLauncher =
 *          Boolean.valueOf(sunAppletLauncher).booleanValue();
 *
 *      boolean loaded = false;
 *      if (usingJNLPAppletLauncher) {
 *          try {
 *              Class jnlpAppletLauncherClass =
 *                  Class.forName("org.jdesktop.applet.util.JNLPAppletLauncher");
 *              Method jnlpLoadLibraryMethod =
 *                  jnlpAppletLauncherClass.getDeclaredMethod("loadLibrary",
 *                                                            new Class[] { String.class });
 *              jnlpLoadLibraryMethod.invoke(null, new Object[] { libraryName });
 *              loaded = true;
 *          } catch (ClassNotFoundException ex) {
 *              System.err.println("loadLibrary(" + libName + ")");
 *              System.err.println(ex);
 *              System.err.println("Attempting to use System.loadLibrary instead");
 *          } catch (Exception e) {
 *              Throwable t = e;
 *              if (t instanceof InvocationTargetException) {
 *                  t = ((InvocationTargetException) t).getTargetException();
 *              }
 *              if (t instanceof Error)
 *                  throw (Error) t;
 *              if (t instanceof RuntimeException) {
 *                  throw (RuntimeException) t;
 *              }
 *              // Throw UnsatisfiedLinkError for best compatibility with System.loadLibrary()
 *              throw (UnsatisfiedLinkError) new UnsatisfiedLinkError().initCause(e);
 *          }
 *      }
 *
 *      if (!loaded) {
 *          System.loadLibrary(libraryName);
 *      }
 *  }
 * 
*
* <p>
*
* and wherever you would call System.loadLibrary() (from
* within an AccessController.doPrivileged() block) to
* load your extension"s native code, call the above
* loadLibraryInternal method instead.
*
* <p>
*
* Note again that because the applet-launcher.jar and
* the nativelib jars for all extensions must currently be signed with
* the same certificate, this implies that you must resign both the
* applet launcher as well as any other extensions your applet relies
* on (unless yours is a Sun-standard extension and can be signed with
* Sun"s code signing certificate).
*
*

Acknowledgments

*
* <p>
*
* The JNLPAppletLauncher was developed by Kevin Rushforth, Kenneth
* Russell, and Chien Yang. It is based on the earlier
* JOGLAppletLauncher developed by Lilian Chamontin.
*/

public class JNLPAppletLauncher extends Applet {

   private static final boolean VERBOSE = false;
   private static final boolean DEBUG = false;
   // Indicated that the applet was successfully initialized
   private boolean isInitOk = false;
   // True the first time start is called, false afterwards
   private boolean firstStart = true;
   // Indicates that the applet was started successfully
   private boolean appletStarted = false;
   // The applet we have to start
   private Applet subApplet;
   // Class name of applet to load (required)
   private String subAppletClassName; // from applet PARAM subapplet.classname
   // String representing the name of the applet (optional)
   private String subAppletDisplayName; // from applet PARAM subapplet.displayname
   // URL to an image that we will display while installing (optional)
   private URL subAppletImageURL; // from applet PARAM subapplet.image
   // Panel that will hold the splash-screen image and progress bar while loading
   private JPanel loaderPanel;
   // Helpers for updating deployment.properties with -Dsun.java2d.noddraw=true
   private static final String JRE_PREFIX = "deployment.javapi.jre.";
   private static final String NODDRAW_PROP = "-Dsun.java2d.noddraw=true";
   private static final String DONT_ASK = ".dont_ask";
   // Optional progress bar
   private JProgressBar progressBar = null;
   /*
    * The following variables are defined per-applet, but we can assert that
    * they will not differ for each applet that is loaded by the same
    * ClassLoader. This means we can just cache the values from the first
    * applet. We will check the values for subsequent applets and throw an
    * exception if there are any differences.
    */
   // Flag indicating that this is the first applet
   private static boolean firstApplet = true;
   // List of extension JNLP files. This is saved for the first applet
   // and verified for each subsequent applet.
   private static List/*<URL>*/ jnlpExtensions = null;
   // Code base and archive tag for all applets that use the same ClassLoader
   private static URL codeBase;
   private static String archive = null;
   // Persistent cache directory for storing native libraries and time stamps.
   // The directory is of the form:
   //
   //     ${user.home}/.jnlp-applet/cache/<HOSTNAME>/<DIGEST-OF-CODEBASE-ARCHIVE>
   //
   private static File cacheDir;
   // Set of jar files specified in the JNLP files.
   // Currently unused.
   private static Set/*<URL>*/ jarFiles;
   // Set of native jar files to be loaded. We need to download these
   // native jars, verify the signatures, verify the security certificates,
   // and extract the native libraries from each jar.
   private static Set/*<URL>*/ nativeJars;
   // Native library prefix (e.g., "lib") and suffix (e.g. ".dll" or ".so")
   private static String nativePrefix;
   private static String nativeSuffix;
   // A HashMap of native libraries that can be loaded with System.load()
   // The key is the string name of the library as passed into the loadLibrary
   // call; it is the file name without the directory or the platform-dependent
   // library prefix and suffix. The value is the absolute path name to the
   // unpacked library file in nativeTmpDir.
   private static Map/*<String, String>*/ nativeLibMap;
   /*
    * The following variables are per-ClassLoader static globals.
    */
   // Flag indicating that we got a fatal error in the static initializer.
   // If this happens we will not attempt to start any applets.
   private static boolean staticInitError = false;
   // Base temp directory used by JNLPAppletLauncher. This is set to:
   //
   // ${java.io.tmpdir}/jnlp-applet
   //
   private static File tmpBaseDir;
   // String representing the name of the temp root directory relative to the
   // tmpBaseDir. Its value is "jlnNNNNN", which is the unique filename created
   // by File.createTempFile() without the ".tmp" extension.
   //
   private static String tmpRootPropValue;
   // Root temp directory for this JVM instance. Used to store the individual,
   // per-ClassLoader directories that will be used to load native code. The
   // directory name is:
   //
   // <tmpBaseDir>/<tmpRootPropValue>
   //
   // Old temp directories are cleaned up the next time a JVM is launched that
   // uses JNLPAppletLauncher.
   //
   private static File tmpRootDir;
   // Temporary directory for loading native libraries for this instance of
   // the class loader. The directory name is:
   //
   // <tmpRootDir>/jlnMMMMM
   //
   // where jlnMMMMM is the unique filename created by File.createTempFile()
   // without the ".tmp" extension.
   //
   private static File nativeTmpDir;
   /*
    * IMPLEMENTATION NOTES
    *
    * Assumptions:
    *
    * A. Multiple applets can be launched from the same class loader, and thus
    *    share the same set of statics and same set of native library symbols.
    *    This can only happen if the codebase and set of jar files as specified
    *    in the archive tag are identical. Applets launched from different code
    *    bases or whose set of jar files are different will always get a
    *    different ClassLoader. If this assumption breaks, too many other
    *    things wouldn"t work properly, so we can be assured that it will hold.
    *    However, we cannot assume that the converse is true; it is possible
    *    that two applets with the same codebase and archive tag will be loaded
    *    from a different ClassLoader.
    *
    * B. Given the above, this means that we must store the native libraries,
    *    and keep track of which ones have already been loaded statically, that
    *    is, per-ClassLoader rather than per-Applet. This is a good thing,
    *    because it turns out to be difficult (at best) to find the instance of
    *    the Applet at loadLibrary time.
    *
    * Our solution is as follows:
    *
    *    Use the same criteria for determining the cache dir that JPI
    *    uses to determine the class loader to use. More precisely, we will
    *    create a directory based on the codebase and complete set of jar files
    *    specified by the archive tag. To support the case where each applet is
    *    in a unique class loader, we will copy the native libraries into a
    *    unique-per-ClassLoader temp directory and do the System.load() from
    *    there. For a robust solution, we need to lock the cache directory
    *    during validation, since multiple threads, or even multiple processes,
    *    can access it concurrently.
    *
    * TODO: We need a way to clear the cache.
    *
    * We also considered, but rejected, the following solutions:
    *
    * 1. Use a temporary directory for native jars, download, verify, unpack,
    *    and loadLibrary in this temp dir. No persistent cache.
    *
    * 2. Cache the native libraries in a directory based on the codebase and
    *    the extension jars (i.e., the subset of the jars in the archive tag
    *    that also appear in one of the extension JNLP files). Copy the native
    *    libraries into a unique-per-ClassLoader temp directory and load from
    *    there. Note that this has the potential problem of violating the
    *    assertion that two different applets that share the same ClassLoader
    *    must map to the same cache directory.
    *
    * 3. Use the exact criteria for determining the cache dir that JPI
    *    uses to determine which class loader to use (as in our proposed
    *    solution above), unpack the native jars into the cache directory and
    *    load from there. This obviates the need for locking, but it will break
    *    if the JPI ever isolates each applet into its own ClassLoader.
    */
   /**
    * Constructs an instance of the JNLPAppletLauncher class. This is called by
    * Java Plug-in, and should not be called directly by an application or
    * applet.
    */
   public JNLPAppletLauncher() {
   }
   /* @Override */
   public void init() {
       if (VERBOSE) {
           System.err.println();
       }
       if (DEBUG) {
           System.err.println("Applet.init");
       }
       if (staticInitError) {
           return;
       }
       subAppletClassName = getParameter("subapplet.classname");
       if (subAppletClassName == null) {
           displayError("Init failed : Missing subapplet.classname parameter");
           return;
       }
       subAppletDisplayName = getParameter("subapplet.displayname");
       if (subAppletDisplayName == null) {
           subAppletDisplayName = "Applet";
       }
       subAppletImageURL = null;
       try {
           String subAppletImageStr = getParameter("subapplet.image");
           if (subAppletImageStr != null && subAppletImageStr.length() > 0) {
               subAppletImageURL = new URL(subAppletImageStr);
           }
       } catch (IOException ex) {
           ex.printStackTrace();
           // Continue with a null subAppletImageURL
       }
       if (DEBUG) {
           System.err.println("subapplet.classname = " + subAppletClassName);
           System.err.println("subapplet.displayname = " + subAppletDisplayName);
           if (subAppletImageURL != null) {
               System.err.println("subapplet.image = " + subAppletImageURL.toExternalForm());
           }
       }
       initLoaderLayout();
       isInitOk = true;
   }
   /* @Override */
   public void start() {
       if (DEBUG) {
           System.err.println("Applet.start");
       }
       if (isInitOk) {
           if (firstStart) { // first time
               firstStart = false;
               Thread startupThread = new Thread() {
                   public void run() {
                       initAndStartApplet();
                   }
               };
               startupThread.setName("AppletLauncher-Startup");
               startupThread.setPriority(Thread.NORM_PRIORITY - 1);
               startupThread.start();
           } else if (appletStarted) {
               checkNoDDrawAndUpdateDeploymentProperties();
               // We have to start again the applet (start can be called multiple times,
               // e.g once per tabbed browsing
               subApplet.start();
           }
       }
   }
   /* @Override */
   public void stop(){
       if (subApplet != null) {
           subApplet.stop();
       }
   }
   /* @Override */
   public void destroy(){
       if (subApplet != null) {
           subApplet.destroy();
       }
   }
   /** Helper method to make it easier to call methods on the
       sub-applet from JavaScript. */
   public Applet getSubApplet() {
       return subApplet;
   }
   //----------------------------------------------------------------------
   // Support for forwarding notifications about dragged-out applets
   //
   public void appletDragStarted() {
       try {
           Method m = getSubApplet().getClass().getMethod("appletDragStarted", null);
           m.invoke(getSubApplet(), null);
       } catch (Throwable t) {
       }
   }
   public void appletDragFinished() {
       try {
           Method m = getSubApplet().getClass().getMethod("appletDragFinished", null);
           m.invoke(getSubApplet(), null);
       } catch (Throwable t) {
       }
   }
   public void appletRestored() {
       try {
           Method m = getSubApplet().getClass().getMethod("appletRestored", null);
           m.invoke(getSubApplet(), null);
       } catch (Throwable t) {
       }
   }
   public boolean isAppletDragStart(MouseEvent e) {
       try {
           Method m = getSubApplet().getClass().getMethod("isAppletDragStart",
                                                          new Class[] { MouseEvent.class });
           return ((Boolean) m.invoke(getSubApplet(), new Object[] { e })).booleanValue();
       } catch (Throwable t) {
           // Throw an exception back to the Java Plug-In to cause it
           // to use the default functionality
           throw new RuntimeException(t);
       }
   }
   public void setAppletCloseListener(ActionListener l) {
       try {
           Method m = getSubApplet().getClass().getMethod("setAppletCloseListener",
                                                          new Class[] { ActionListener.class });
           m.invoke(getSubApplet(), new Object[] { l });
       } catch (Throwable t) {
           // Throw an exception back to the Java Plug-In to cause it
           // to use the default functionality
           throw new RuntimeException(t);
       }
   }
   /**
    * This method is called by the static initializer to create / initialize
    * the temp root directory that will hold the temp directories for this
    * instance of the JVM. This is done as follows:
    *
    *     1. Synchronize on a global lock. Note that for this purpose we will
    *        use System.out in the absence of a true global lock facility.
    *        We are careful not to hold this lock too long.
    *
    *     2. Check for the existence of the "jnlp.applet.launcher.tmproot"
    *        system property.
    *
    *         a. If set, then some other thread in a different ClassLoader has
    *            already created the tmprootdir, so we just need to
    *            use it. The remaining steps are skipped.
    *
    *         b. If not set, then we are the first thread in this JVM to run,
    *            and we need to create the the tmprootdir.
    *
    *     3. Create the tmprootdir, along with the appropriate locks.
    *        Note that we perform the operations in the following order,
    *        prior to creating tmprootdir itself, to work around the fact that
    *        the file creation and file lock steps are not atomic, and we need
    *        to ensure that a newly-created tmprootdir isn"t reaped by a
    *        concurrently running JVM.
    *
    *            create jlnNNNN.tmp using File.createTempFile()
    *            lock jlnNNNN.tmp
    *            create jlnNNNN.lck while holding the lock on the .tmp file
    *            lock jlnNNNN.lck
    *
    *        Since the Reaper thread will enumerate the list of *.lck files
    *        before starting, we can guarantee that if there exists a *.lck file
    *        for an active process, then the corresponding *.tmp file is locked
    *        by that active process. This guarantee lets us avoid reaping an
    *        active process" files.
    *
    *     4. Set the "jnlp.applet.launcher.tmproot" system property.
    *
    *     5. Add a shutdown hook to cleanup jlnNNNN.lck and jlnNNNN.tmp. We
    *        don"t actually expect that this shutdown hook will ever be called,
    *        but the act of doing this, ensures that the locks never get
    *        garbage-collected, which is necessary for correct behavior when
    *        the first ClassLoader is later unloaded, while subsequent Applets
    *        are still running.
    *
    *     6. Start the Reaper thread to cleanup old installations.
    */
   private static void initTmpRoot() throws IOException {
       if (VERBOSE) {
           System.err.println("---------------------------------------------------");
       }
       synchronized (System.out) {
           // Get the name of the tmpbase directory.
           String tmpBaseName = System.getProperty("java.io.tmpdir") +
                   File.separator + "jnlp-applet";
           tmpBaseDir = new File(tmpBaseName);
           // Get the value of the tmproot system property
           final String tmpRootPropName = "jnlp.applet.launcher.tmproot";
           tmpRootPropValue = System.getProperty(tmpRootPropName);
           if (tmpRootPropValue == null) {
               // Create the tmpbase directory if it doesn"t already exist
               tmpBaseDir.mkdir();
               if (!tmpBaseDir.isDirectory()) {
                   throw new IOException("Cannot create directory " + tmpBaseDir);
               }
               // Create ${tmpbase}/jlnNNNN.tmp then lock the file
               File tmpFile = File.createTempFile("jln", ".tmp", tmpBaseDir);
               if (VERBOSE) {
                   System.err.println("tmpFile = " + tmpFile.getAbsolutePath());
               }
               final FileOutputStream tmpOut = new FileOutputStream(tmpFile);
               final FileChannel tmpChannel = tmpOut.getChannel();
               final FileLock tmpLock = tmpChannel.lock();
               // Strip off the ".tmp" to get the name of the tmprootdir
               String tmpFileName = tmpFile.getAbsolutePath();
               String tmpRootName = tmpFileName.substring(0, tmpFileName.lastIndexOf(".tmp"));
               // create ${tmpbase}/jlnNNNN.lck then lock the file
               String lckFileName = tmpRootName + ".lck";
               File lckFile = new File(lckFileName);
               if (VERBOSE) {
                   System.err.println("lckFile = " + lckFile.getAbsolutePath());
               }
               lckFile.createNewFile();
               final FileOutputStream lckOut = new FileOutputStream(lckFile);
               final FileChannel lckChannel = lckOut.getChannel();
               final FileLock lckLock = lckChannel.lock();
               // Create tmprootdir
               tmpRootDir = new File(tmpRootName);
               if (DEBUG) {
                   System.err.println("tmpRootDir = " + tmpRootDir.getAbsolutePath());
               }
               if (!tmpRootDir.mkdir()) {
                   throw new IOException("Cannot create " + tmpRootDir);
               }
               // Add shutdown hook to cleanup the OutputStream, FileChannel,
               // and FileLock for the jlnNNNN.lck and jlnNNNN.lck files.
               // We do this so that the locks never get garbage-collected.
               Runtime.getRuntime().addShutdownHook(new Thread() {
                   /* @Override */
                   public void run() {
                       // NOTE: we don"t really expect that this code will ever
                       // be called. If it does, we will close the output
                       // stream, which will in turn close the channel.
                       // We will then release the lock.
                       try {
                           tmpOut.close();
                           tmpLock.release();
                           lckOut.close();
                           lckLock.release();
                       } catch (IOException ex) {
                           // Do nothing
                       }
                   }
               });
               // Set the system property...
               tmpRootPropValue = tmpRootName.substring(tmpRootName.lastIndexOf(File.separator) + 1);
               System.setProperty(tmpRootPropName, tmpRootPropValue);
               if (VERBOSE) {
                   System.err.println("Setting " + tmpRootPropName + "=" + tmpRootPropValue);
               }
               // Start a new Reaper thread to do stuff...
               Thread reaperThread = new Thread() {
                   /* @Override */
                   public void run() {
                       deleteOldTempDirs();
                   }
               };
               reaperThread.setName("AppletLauncher-Reaper");
               reaperThread.start();
           } else {
               // Make sure that the property is not set to an illegal value
               if (tmpRootPropValue.indexOf("/") >= 0 ||
                       tmpRootPropValue.indexOf(File.separatorChar) >= 0) {
                   throw new IOException("Illegal value of: " + tmpRootPropName);
               }
               // Set tmpRootDir = ${tmpbase}/${jnlp.applet.launcher.tmproot}
               if (VERBOSE) {
                   System.err.println("Using existing value of: " +
                           tmpRootPropName + "=" + tmpRootPropValue);
               }
               tmpRootDir = new File(tmpBaseDir, tmpRootPropValue);
               if (DEBUG) {
                   System.err.println("tmpRootDir = " + tmpRootDir.getAbsolutePath());
               }
               if (!tmpRootDir.isDirectory()) {
                   throw new IOException("Cannot access " + tmpRootDir);
               }
           }
       }
   }
   /**
    * Called by the Reaper thread to delete old temp directories
    * Only one of these threads will run per JVM invocation.
    */
   private static void deleteOldTempDirs() {
       if (VERBOSE) {
           System.err.println("*** Reaper: deleteOldTempDirs in " +
                   tmpBaseDir.getAbsolutePath());
       }
       // enumerate list of jnl*.lck files, ignore our own jlnNNNN file
       final String ourLockFile = tmpRootPropValue + ".lck";
       FilenameFilter lckFilter = new FilenameFilter() {
           /* @Override */
           public boolean accept(File dir, String name) {
               return name.endsWith(".lck") && !name.equals(ourLockFile);
           }
       };
       // For each file <file>.lck in the list we will first try to lock
       // <file>.tmp if that succeeds then we will try to lock <file>.lck
       // (which should always succeed unless there is a problem). If we can
       // get the lock on both files, then it must be an old installation, and
       // we will delete it.
       String[] fileNames = tmpBaseDir.list(lckFilter);
       if (fileNames != null) {
           for (int i = 0; i < fileNames.length; i++) {
               String lckFileName = fileNames[i];
               String tmpDirName = lckFileName.substring(0, lckFileName.lastIndexOf(".lck"));
               String tmpFileName = tmpDirName + ".tmp";
               File lckFile = new File(tmpBaseDir, lckFileName);
               File tmpFile = new File(tmpBaseDir, tmpFileName);
               File tmpDir = new File(tmpBaseDir, tmpDirName);
               if (lckFile.exists() && tmpFile.exists() && tmpDir.isDirectory()) {
                   FileOutputStream tmpOut = null;
                   FileChannel tmpChannel = null;
                   FileLock tmpLock = null;
                   try {
                       tmpOut = new FileOutputStream(tmpFile);
                       tmpChannel = tmpOut.getChannel();
                       tmpLock = tmpChannel.tryLock();
                   } catch (Exception ex) {
                       // Ignore exceptions
                       if (DEBUG) {
                           ex.printStackTrace();
                       }
                   }
                   if (tmpLock != null) {
                       FileOutputStream lckOut = null;
                       FileChannel lckChannel = null;
                       FileLock lckLock = null;
                       try {
                           lckOut = new FileOutputStream(lckFile);
                           lckChannel = lckOut.getChannel();
                           lckLock = lckChannel.tryLock();
                       } catch (Exception ex) {
                           if (DEBUG) {
                               ex.printStackTrace();
                           }
                       }
                       if (lckLock != null) {
                           // Recursively remove the old tmpDir and all of
                           // its contents
                           removeAll(tmpDir);
                           // Close the streams and delete the .lck and .tmp
                           // files. Note that there is a slight race condition
                           // in that another process could open a stream at
                           // the same time we are trying to delete it, which will
                           // prevent deletion, but we won"t worry about it, since
                           // the worst that will happen is we might have an
                           // occasional 0-byte .lck or .tmp file left around
                           try {
                               lckOut.close();
                           } catch (IOException ex) {
                           }
                           lckFile.delete();
                           try {
                               tmpOut.close();
                           } catch (IOException ex) {
                           }
                           tmpFile.delete();
                       } else {
                           try {
                               // Close the file and channel for the *.lck file
                               if (lckOut != null) {
                                   lckOut.close();
                               }
                               // Close the file/channel and release the lock
                               // on the *.tmp file
                               tmpOut.close();
                               tmpLock.release();
                           } catch (IOException ex) {
                               if (DEBUG) {
                                   ex.printStackTrace();
                               }
                           }
                       }
                   }
               } else {
                   if (VERBOSE) {
                       System.err.println("    Skipping: " + tmpDir.getAbsolutePath());
                   }
               }
           }
       }
   }
   /**
    * Remove the specified file or directory. If "path" is a directory, then
    * recursively remove all entries, then remove the directory itself.
    */
   private static void removeAll(File path) {
       if (VERBOSE) {
           System.err.println("removeAll(" + path + ")");
       }
       if (path.isDirectory()) {
           // Recursively remove all files/directories in this directory
           File[] list = path.listFiles();
           if (list != null) {
               for (int i = 0; i < list.length; i++) {
                   removeAll(list[i]);
               }
           }
       }
       path.delete();
   }
   /**
    * This method is executed from outside the Event Dispatch Thread. It
    * initializes, downloads, and unpacks the required native libraries into
    * the cache, and then starts the applet on the EDT.
    */
   private void initAndStartApplet() {
       // Parse the extension JNLP files and download the native resources
       try {
           initResources();
       } catch (Exception ex) {
           ex.printStackTrace();
           displayError(toErrorString(ex));
           return;
       }
       // Indicate that we are starting the applet
       displayMessage("Starting applet " + subAppletDisplayName);
       setProgress(0);
       // Now schedule the starting of the subApplet on the EDT
       SwingUtilities.invokeLater(new Runnable() {
           public void run() {
               // start the subapplet
               startSubApplet();
           }
       });
   }
   /**
    * Initializes, downloads, and extracts the native resources needed by this
    * applet.
    */
   private void initResources() throws IOException {
       synchronized (JNLPAppletLauncher.class) {
           if (firstApplet) {
               // Save codeBase and archive parameter for assertion checking
               codeBase = getCodeBase();
               assert codeBase != null;
               archive = getParameter("archive");
               if (archive == null || archive.length() == 0) {
                   throw new IllegalArgumentException("Missing archive parameter");
               }
               // Initialize the collections of resources
               jarFiles = new HashSet/*<URL>*/();
               nativeJars = new HashSet/*<URL>*/();
               nativeLibMap = new HashMap/*<String, String>*/();
           } else {
               // The following must hold for applets in the same ClassLoader
               assert getCodeBase().equals(codeBase);
               assert getParameter("archive").equals(archive);
           }
           int jnlpNumExt = -1;
           String numParamString = getParameter("jnlpNumExtensions");
           if (numParamString != null) {
               try {
                   jnlpNumExt = Integer.parseInt(numParamString);
               } catch (NumberFormatException ex) {
               }
               if (jnlpNumExt <= 0) {
                   throw new IllegalArgumentException("Missing or invalid jnlpNumExtensions parameter");
               }
           }
           List/*<URL>*/ urls = new ArrayList/*<URL>*/();
           for (int i = 1; i <= jnlpNumExt; i++) {
               String paramName = "jnlpExtension" + i;
               String urlString = getParameter(paramName);
               if (urlString == null || urlString.length() == 0) {
                   throw new IllegalArgumentException("Missing " + paramName + " parameter");
               }
               URL url = new URL(urlString);
               urls.add(url);
           }
           // If this is the first time, process the list of extensions and
           // save the results. Otherwise, verify that the list of extensions
           // is the same as the first applet.
           if (firstApplet) {
               jnlpExtensions = urls;
               parseJNLPExtensions(urls);
               if (VERBOSE) {
                   System.err.println();
                   System.err.println("All files successfully parsed");
                   printResources();
               }
               if (nativeJars.size() > 0) {
                   // Create the cache directory if not already created.
                   // Create the temporary directory that will hold a copy of
                   // the extracted native libraries, then copy each native
                   // library into the temp dir so we can call System.load().
                   createCacheDir();
                   createTmpDir();
                   // Download and validate the set of native jars, if the
                   // cache is out of date. Then extract the native DLLs,
                   // creating a list of native libraries to be loaded.
                   for (Iterator iter = nativeJars.iterator(); iter.hasNext(); ) {
                       URL url = (URL) iter.next();
                       processNativeJar(url);
                   }
               }
               // Set a system property that libraries can use to know when to call
               // JNLPAppletLauncher.loadLibrary instead of System.loadLibrary
               System.setProperty("sun.jnlp.applet.launcher", "true");
           } else {
               // Verify that the list of jnlpExtensions is the same as the
               // first applet
               if (!jnlpExtensions.equals(urls)) {
                   throw new IllegalArgumentException(
                           "jnlpExtension parameters do not match previously loaded applet");
               }
           }
           firstApplet = false;
       }
   }
   /**
    * Detemine the cache directory location based on the codebase and archive
    * tag. Create the cache directory if not already created.
    */
   private void createCacheDir() throws IOException {
       StringBuffer cacheBaseName = new StringBuffer();
       cacheBaseName.append(System.getProperty("user.home")).append(File.separator).
               append(".jnlp-applet").append(File.separator).
               append("cache");
       File cacheBaseDir = new File(cacheBaseName.toString());
       if (VERBOSE) {
           System.err.println("cacheBaseDir = " + cacheBaseDir.getAbsolutePath());
       }
       cacheDir = new File(cacheBaseDir, getCacheDirName());
       if (VERBOSE) {
           System.err.println("cacheDir = " + cacheDir.getAbsolutePath());
       }
       // Create cache directory and load native library
       if (!cacheDir.isDirectory()) {
           if (!cacheDir.mkdirs()) {
               throw new IOException("Cannot create directory " + cacheDir);
           }
       }
       assert cacheBaseDir.isDirectory();
   }
   /**
    * Returns a directory name of the form: hostname/hash(codebase,archive)
    */
   private String getCacheDirName() {
       final String codeBasePath = getCodeBase().toExternalForm();
       // Extract the host name; replace characters in the set ".:\[]" with "_"
       int hostIdx1 = -1;
       int hostIdx2 = -1;
       String hostNameDir = "UNKNOWN";
       hostIdx1 = codeBasePath.indexOf("://");
       if (hostIdx1 >= 0) {
           hostIdx1 += 3; // skip the "://"
           // Verify that the character immediately following the "://"
           // exists and is not a "/"
           if (hostIdx1 < codeBasePath.length() &&
                   codeBasePath.charAt(hostIdx1) != "/") {
               hostIdx2 = codeBasePath.indexOf("/", hostIdx1);
               if (hostIdx2 > hostIdx1) {
                   hostNameDir = codeBasePath.substring(hostIdx1, hostIdx2).
                           replace(".", "_").
                           replace(":", "_").
                           replace("\\", "_").
                           replace("[", "_").
                           replace("]", "_");
               }
           }
       }
       // Now concatenate the codebase and the list of jar files in the archive
       // Separate them by an "out-of-band" character which cannot appear in
       // either the codeBasePath or archive list.
       StringBuffer key = new StringBuffer();
       key.append(codeBasePath).
               append("\n").
               append(getParameter("archive"));
       if (VERBOSE) {
           System.err.println("key = " + key);
       }
       StringBuffer result = new StringBuffer();
       result.append(hostNameDir).
               append(File.separator).
               append(sha1Hash(key.toString()));
       if (VERBOSE) {
           System.err.println("result = " + result);
       }
       return result.toString();
   }
   /**
    * Produces a 40-byte SHA-1 hash of the input string.
    */
   private static String sha1Hash(String str) {
       MessageDigest sha1 = null;
       try {
           sha1 = MessageDigest.getInstance("SHA-1");
       } catch (NoSuchAlgorithmException ex) {
           throw new RuntimeException(ex);
       }
       byte[] digest = sha1.digest(str.getBytes());
       if (digest == null || digest.length == 0) {
           throw new RuntimeException("Error reading message digest");
       }
       StringBuffer res = new StringBuffer();
       for (int i = 0; i < digest.length; i++) {
           int val = (int)digest[i] & 0xFF;
           if (val < 0x10) {
               res.append("0");
           }
           res.append(Integer.toHexString(val));
       }
       return res.toString();
   }
   /**
    * Create the temp directory in tmpRootDir. To do this, we create a temp
    * file with a ".tmp" extension, and then create a directory of the
    * same name but without the ".tmp". The temp file, directory, and all
    * files in the directory will be reaped the next time this is started.
    * We avoid deleteOnExit, because it doesn"t work reliably.
    */
   private void createTmpDir() throws IOException {
       if (VERBOSE) {
           System.err.println("---------------------------------------------------");
       }
       File tmpFile = File.createTempFile("jln", ".tmp", tmpRootDir);
       String tmpFileName = tmpFile.getAbsolutePath();
       String tmpDirName = tmpFileName.substring(0, tmpFileName.lastIndexOf(".tmp"));
       nativeTmpDir = new File(tmpDirName);
       if (VERBOSE) {
           System.err.println("tmpFile = " + tmpFile.getAbsolutePath() +
                   "  tmpDir = " + nativeTmpDir.getAbsolutePath());
       }
       if (!nativeTmpDir.mkdir()) {
           throw new IOException("Cannot create " + nativeTmpDir);
       }
   }
   /**
    * Download, cache, verify, and unpack the specified native jar file.
    * Before downloading, check the cached time stamp for the jar file
    * against the server. If the time stamp is valid and matches that of the
    * server, then we will use the locally cached files. This method assumes
    * that cacheDir and nativeTmpDir both exist.
    *
    * An IOException is thrown if the files cannot loaded for some reason.
    */
   private void processNativeJar(URL url) throws IOException {
       assert cacheDir.isDirectory();
       assert nativeTmpDir.isDirectory();
       // 6618105: Map "\" to "/" prior to stripping off the path
       String urlString = url.toExternalForm().replace("\\", "/");
       String nativeFileName = urlString.substring(urlString.lastIndexOf("/") + 1);
       File nativeFile = new File(cacheDir, nativeFileName);
       // Make sure the file is not "." or ".."
       if (nativeFile.isDirectory()) {
           throw new IOException(nativeFile + " is a directory");
       }
       String tmpStr = nativeFileName;
       int idx = nativeFileName.lastIndexOf(".");
       if (idx > 0) {
           tmpStr = nativeFileName.substring(0, idx);
       }
       String indexFileName = tmpStr + ".idx";
       File indexFile = new File(cacheDir, indexFileName);
       if (VERBOSE) {
           System.err.println("nativeFile = " + nativeFile);
           System.err.println("indexFile = " + indexFile);
       }
       displayMessage("Loading: " + nativeFileName);
       setProgress(0);
       URLConnection conn = url.openConnection();
       conn.connect();
       Map/*<String,List<String>>*/ headerFields = conn.getHeaderFields();
       if (VERBOSE) {
           for (Iterator iter = headerFields.entrySet().iterator(); iter.hasNext(); ) {
               Entry/*<String,List<String>>*/ e = (Entry) iter.next();
               for (Iterator iter2 = ((List/*<String>*/) e.getValue()).iterator(); iter2.hasNext(); ) {
                   String s = (String) iter2.next();
                   if (e.getKey() != null) {
                       System.err.print(e.getKey() + ": ");
                   }
                   System.err.print(s + " ");
               }
               System.err.println();
           }
           System.err.println();
       }
       // Validate the cache, download the jar if needed
       // TODO: rather than synchronizing on System.out during cache validation,
       // we should use a System property as a lock token (protected by
       // System.out) so we don"t hold a synchronized lock on System.out during
       // a potentially long download operation.
       synchronized (System.out) {
           validateCache(conn, nativeFile, indexFile);
       }
       // Unpack the jar file
       displayMessage("Unpacking: " + nativeFileName);
       setProgress(0);
       // Enumerate the jar file looking for native libraries
       JarFile jarFile = new JarFile(nativeFile);
       Set/*<String>*/ rootEntries = getRootEntries(jarFile);
       Set/*<String>*/ nativeLibNames = getNativeLibNames(rootEntries);
       // Validate certificates; throws exception upon validation error
       validateCertificates(jarFile, rootEntries);
       // Extract native libraries from the jar file
       extractNativeLibs(jarFile, rootEntries, nativeLibNames);
       if (VERBOSE) {
           System.err.println();
       }
   }
   // Validate the cached file. If the cached file is out of date or otherwise
   // invalid, download the file and store the new time stamp.
   // This method must be called with a global lock being held such that
   // no other thread -- even in another class loader -- can executed this
   // method concurrently.
   private void validateCache(URLConnection conn,
           File nativeFile,
           File indexFile) throws IOException {
       // Lock the cache directory
       final String lckFileName = "cache.lck";
       File lckFile = new File(cacheDir, lckFileName);
       lckFile.createNewFile();
       final FileOutputStream lckOut = new FileOutputStream(lckFile);
       final FileChannel lckChannel = lckOut.getChannel();
       final FileLock lckLock = lckChannel.lock();
       try {
           // Check to see whether the cached jar file exists and is valid
           boolean valid = false;
           long cachedTimeStamp = readTimeStamp(indexFile);
           long urlTimeStamp = conn.getLastModified();
           if (nativeFile.exists() &&
                   urlTimeStamp > 0 &&
                   urlTimeStamp == readTimeStamp(indexFile)) {
               valid = true;
           }
           // Validate the cache, download the jar if needed
           if (!valid) {
               if (VERBOSE) {
                   System.err.println("processNativeJar: downloading " + nativeFile.getAbsolutePath());
               }
               indexFile.delete();
               nativeFile.delete();
               // Copy from URL to File
               int len = conn.getContentLength();
               if (VERBOSE) {
                   System.err.println("Content length = " + len + " bytes");
               }
               int totalNumBytes = copyURLToFile(conn, nativeFile);
               if (DEBUG) {
                   System.err.println("processNativeJar: " + conn.getURL().toString() +
                           " --> " + nativeFile.getAbsolutePath() + " : " +
                           totalNumBytes + " bytes written");
               }
               // Write timestamp to index file.
               writeTimeStamp(indexFile, urlTimeStamp);
           } else {
               if (DEBUG) {
                   System.err.println("processNativeJar: using previously cached: " +
                           nativeFile.getAbsolutePath());
               }
           }
       } finally {
           // Unlock the cache directory
           lckLock.release();
       }
   }
   private long readTimeStamp(File indexFile) {
       try {
           BufferedReader reader = new BufferedReader(new FileReader(indexFile));
           try {
               String str = reader.readLine();
               return Long.parseLong(str);
           } finally {
               reader.close();
           }
       } catch (Exception ex) {
       }
       return -1;
   }
   private void writeTimeStamp(File indexFile, long timestamp) {
       try {
           BufferedWriter writer = new BufferedWriter(new FileWriter(indexFile));
           try {
               writer.write("" + timestamp + "\n");
               writer.flush();
           } finally {
               writer.close();
           }
       } catch (Exception ex) {
           displayError("Error writing time stamp for native libraries");
       }
   }
   // Copy the specified URL to the specified File
   private int copyURLToFile(URLConnection inConnection,
           File outFile) throws IOException {
       int totalNumBytes = 0;
       InputStream in = new BufferedInputStream(inConnection.getInputStream());
       try {
           OutputStream out = new BufferedOutputStream(new FileOutputStream(outFile));
           try {
               totalNumBytes = copyStream(in, out, inConnection.getContentLength());
           } finally {
               out.close();
           }
       } finally {
           in.close();
       }
       return totalNumBytes;
   }
   /**
    * Copy the specified input stream to the specified output stream. The total
    * number of bytes written is returned. If the close flag is set, both
    * streams are closed upon completeion.
    */
   private int copyStream(InputStream in, OutputStream out,
           int totalNumBytes) throws IOException {
       int numBytes = 0;
       final int BUFFER_SIZE = 1000;
       final float pctScale = 100.0f / (float)totalNumBytes;
       byte[] buf = new byte[BUFFER_SIZE];
       setProgress(0);
       while (true) {
           int count;
           if ((count = in.read(buf)) == -1) {
               break;
           }
           out.write(buf, 0, count);
           numBytes += count;
           if (totalNumBytes > 0) {
               setProgress((int)Math.round((float)numBytes * pctScale));
           }
       }
       setProgress(100);
       return numBytes;
   }
   /**
    * Enumerate the list of entries in the jar file and return those that are
    * the root entries.
    */
   private Set/*<String>*/ getRootEntries(JarFile jarFile) {
       if (VERBOSE) {
           System.err.println("getRootEntries:");
       }
       Set/*<String>*/ names = new HashSet/*<String>*/();
       Enumeration/*<JarEntry>*/ entries = jarFile.entries();
       while (entries.hasMoreElements()) {
           JarEntry entry = (JarEntry) entries.nextElement();
           String entryName = entry.getName();
           if (VERBOSE) {
               System.err.println("JarEntry : " + entryName);
           }
           // only look at entries with no "/"
           if (entryName.indexOf("/") == -1 &&
               entryName.indexOf(File.separatorChar) == -1) {
               names.add(entryName);
           }
       }
       return names;
   }
   /**
    * Filter the root entries in the jar file and return those that
    * are native library names.
    */
   private Set/*<String>*/ getNativeLibNames(Set/*<String>*/ entryNames) {
       if (VERBOSE) {
           System.err.println("getNativeLibNames:");
       }
       Set/*<String>*/ names = new HashSet/*<String>*/();
       for (Iterator iter = entryNames.iterator(); iter.hasNext(); ) {
           String name = (String) iter.next();
           String lowerCaseName = name.toLowerCase();
           // Match entries with correct prefix and suffix (ignoring case)
           if (lowerCaseName.startsWith(nativePrefix) &&
               lowerCaseName.endsWith(nativeSuffix)) {
               names.add(name);
           }
       }
       return names;
   }
   /**
    * Validate the certificates for each native Lib in the jar file.
    * Throws an IOException if any certificate is not valid.
    */
   private void validateCertificates(JarFile jarFile,
           Set/*<String>*/ nativeLibNames) throws IOException {
       if (DEBUG) {
           System.err.println("validateCertificates:");
       }
       byte[] buf = new byte[1000];
       Enumeration/*<JarEntry>*/ entries = jarFile.entries();
       while (entries.hasMoreElements()) {
           JarEntry entry = (JarEntry) entries.nextElement();
           String entryName = entry.getName();
           if (VERBOSE) {
               System.err.println("JarEntry : " + entryName);
           }
           if (nativeLibNames.contains(entryName)) {
               if (DEBUG) {
                   System.err.println("VALIDATE: " + entryName);
               }
               if (!checkNativeCertificates(jarFile, entry, buf)) {
                   throw new IOException("Cannot validate certificate for " + entryName);
               }
           }
       }
   }
   /**
    * Check the native certificates with the ones in the jar file containing the
    * certificates for the JNLPAppletLauncher class (all must match).
    */
   private boolean checkNativeCertificates(JarFile jar, JarEntry entry,
           byte[] buf) throws IOException {
       // API states that we must read all of the data from the entry"s
       // InputStream in order to be able to get its certificates
       InputStream is = jar.getInputStream(entry);
       int totalLength = (int) entry.getSize();
       int len;
       while ((len = is.read(buf)) > 0) {
       }
       is.close();
       // locate JNLPAppletLauncher certificates
       Certificate[] appletLauncherCerts = JNLPAppletLauncher.class.getProtectionDomain().
               getCodeSource().getCertificates();
       if (appletLauncherCerts == null || appletLauncherCerts.length == 0) {
           throw new IOException("Cannot find certificates for JNLPAppletLauncher class");
       }
       // Get the certificates for the JAR entry
       Certificate[] nativeCerts = entry.getCertificates();
       if (nativeCerts == null || nativeCerts.length == 0) {
           return false;
       }
       int checked = 0;
       for (int i = 0; i < appletLauncherCerts.length; i++) {
           for (int j = 0; j < nativeCerts.length; j++) {
               if (nativeCerts[j].equals(appletLauncherCerts[i])){
                   checked++;
                   break;
               }
           }
       }
       return  (checked == appletLauncherCerts.length);
   }
   /**
    * Extract the specified set of native libraries in the given jar file.
    */
   private void extractNativeLibs(JarFile jarFile,
                                  Set/*<String>*/ rootEntries,
                                  Set/*<String>*/ nativeLibNames) throws IOException {
       if (DEBUG) {
           System.err.println("extractNativeLibs:");
       }
       Enumeration/*<JarEntry>*/ entries = jarFile.entries();
       while (entries.hasMoreElements()) {
           JarEntry entry = (JarEntry) entries.nextElement();
           String entryName = entry.getName();
           if (VERBOSE) {
               System.err.println("JarEntry : " + entryName);
           }
           // In order to be compatible with Java Web Start, we need
           // to extract all root entries from the jar file. However,
           // we only allow direct loading of the previously
           // discovered native library names.
           if (rootEntries.contains(entryName)) {
               // strip prefix & suffix
               String libName = entryName.substring(nativePrefix.length(),
                       entryName.length() - nativeSuffix.length());
               if (DEBUG) {
                   System.err.println("EXTRACT: " + entryName + "(" + libName + ")");
               }
               File nativeLib = new File(nativeTmpDir, entryName);
               InputStream in = new BufferedInputStream(jarFile.getInputStream(entry));
               OutputStream out = new BufferedOutputStream(new FileOutputStream(nativeLib));
               int numBytesWritten = copyStream(in, out, -1);
               in.close();
               out.close();
               if (nativeLibNames.contains(entryName)) {
                   nativeLibMap.put(libName, nativeLib.getAbsolutePath());
               }
           }
       }
   }
   /**
    * The true start of the sub applet (invoked in the EDT)
    */
   private void startSubApplet() {
       try {
           subApplet = (Applet)
               Class.forName(subAppletClassName,
                             true,
                             Thread.currentThread().getContextClassLoader()).newInstance(); 
           subApplet.setStub(new AppletStubProxy());
       } catch (ClassNotFoundException ex) {
           ex.printStackTrace();
           displayError("Class not found: " + subAppletClassName);
           return;
       } catch (Exception ex) {
           ex.printStackTrace();
           displayError("Unable to start " + subAppletDisplayName);
           return;
       }
       add(subApplet, BorderLayout.CENTER);
       try {
           subApplet.init();
           remove(loaderPanel);
           validate();
           checkNoDDrawAndUpdateDeploymentProperties();
           subApplet.start();
           appletStarted = true;
       } catch (Exception ex) {
           ex.printStackTrace();
       }
   }
   /**
    * Method called by an extension such as JOGL or Java 3D to load the
    * specified library. Applications and applets should not call this method.
    *
    * @param libraryName name of the library to be loaded
    *
    * @throws SecurityException if the caller does not have permission to
    * call System.load
    */
   public static void loadLibrary(String libraryName) {
       if (VERBOSE) {
           System.err.println("-----------");
           Thread.dumpStack();
       }
       if (DEBUG) {
           System.err.println("JNLPAppletLauncher.loadLibrary(\"" + libraryName + "\")");
       }
       String fullLibraryName = (String) nativeLibMap.get(libraryName);
       if (fullLibraryName == null) {
           // Throw UnsatisfiedLinkError to try to match behavior of System.loadLibrary()
           throw new UnsatisfiedLinkError(libraryName);
       }
       if (DEBUG) {
           System.err.println("    loading: " + fullLibraryName + "");
       }
       System.load(fullLibraryName);
   }
   
   public static void removeLibrary(String libraryName) {
       String fullLibraryName = (String) nativeLibMap.get(libraryName);
       if (fullLibraryName == null) {
           return;
       }
       if (DEBUG) {
           System.err.println("    removing: " + fullLibraryName + "");
       }
       new File(fullLibraryName).delete();
       nativeLibMap.remove(libraryName);
   }
   private static String toErrorString(Throwable throwable) {
       StringBuffer errStr = new StringBuffer(throwable.toString());
       Throwable cause = throwable.getCause();
       while (cause != null) {
           errStr.append(": ").append(cause);
           cause = cause.getCause();
       }
       return errStr.toString();
   }
   private void displayMessage(final String message) {
       if (progressBar != null) {
           SwingUtilities.invokeLater(new Runnable() {
               public void run() {
                   progressBar.setString(message);
               }
           });
       }
   }
   private void displayError(final String errorMessage) {
       // Log message on Java console and display in applet progress bar
       Logger.getLogger("global").severe(errorMessage);
       if (progressBar != null) {
           SwingUtilities.invokeLater(new Runnable() {
               public void run() {
                   progressBar.setString("Error : " + errorMessage);
               }
           });
       }
   }
   private void setProgress(final int value) {
       if (progressBar != null) {
           SwingUtilities.invokeLater(new Runnable() {
               public void run() {
                   progressBar.setValue(value);
               }
           });
       }
   }
   private void initLoaderLayout() {
       setLayout(new BorderLayout());
       loaderPanel = new JPanel(new BorderLayout());
       if (getBooleanParameter("progressbar")) {
           progressBar = new JProgressBar(0, 100);
           progressBar.setBorderPainted(true);
           progressBar.setStringPainted(true);
           progressBar.setString("Loading...");
       }
       boolean includeImage = false;
       ImageIcon image = null;
       if (subAppletImageURL != null) {
           image = new ImageIcon(subAppletImageURL);
           includeImage = true;
       }
       add(loaderPanel, BorderLayout.SOUTH);
       if (includeImage) {
           loaderPanel.add(new JLabel(image), BorderLayout.CENTER);
           if (progressBar != null) {
               loaderPanel.add(progressBar, BorderLayout.SOUTH);
           }
       } else {
           if (progressBar != null) {
               loaderPanel.add(progressBar, BorderLayout.CENTER);
           }
       }
   }
   private void parseJNLPExtensions(List/*<URL>*/ urls) throws IOException {
       for (Iterator iter = urls.iterator(); iter.hasNext(); ) {
           URL url = (URL) iter.next();
           JNLPParser parser = new JNLPParser(this, url);
           parser.parse();
       }
   }
   private void addJarFile(URL jarFile) {
       jarFiles.add(jarFile);
   }
   private void addNativeJar(URL nativeJar) {
       nativeJars.add(nativeJar);
   }
   /*
    * Debug method to print out resources from the JNLP file
    */
   private static void printResources() {
       System.err.println("  Resources:");
       System.err.println("    Class Jars:");
       doPrint(jarFiles);
       System.err.println();
       System.err.println("    Native Jars:");
       doPrint(nativeJars);
   }
   /*
    * Debug method to print out resources from the JNLP file
    */
   private static void doPrint(Collection/*<URL>*/ urls) {
       for (Iterator iter = urls.iterator(); iter.hasNext(); ) {
           URL url = (URL) iter.next();
           String urlString = url.toExternalForm();
           System.err.println("      " + urlString);
       }
   }
   // Static initializer for JNLPAppletLauncher
   static {
       System.err.println("JNLPAppletLauncher: static initializer");
       String systemOsName = System.getProperty("os.name").toLowerCase();
       if (systemOsName.startsWith("mac")) {
           // Mac OS X
           nativePrefix = "lib";
           nativeSuffix = ".jnilib";
       } else if (systemOsName.startsWith("windows")) {
           // Microsoft Windows
           nativePrefix = "";
           nativeSuffix = ".dll";
       } else {
           // Unix of some variety
           nativePrefix = "lib";
           nativeSuffix = ".so";
       }
       if (DEBUG) {
           System.err.println("os.name = " + systemOsName);
           System.err.println("nativePrefix = " + nativePrefix + "  nativeSuffix = " + nativeSuffix);
       }
       // Create / initialize the temp root directory, starting the Reaper
       // thread to reclaim old installations if necessary. If we get an
       // exception, set an error code so we don"t try to start the applets.
       try {
           initTmpRoot();
       } catch (Exception ex) {
           ex.printStackTrace();
           staticInitError = true;
       }
   }
   private static final String _javaVersionProperty =
           System.getProperty("java.version");
   private static final boolean _atLeast13 =
           (!_javaVersionProperty.startsWith("1.2"));
   private static final boolean _atLeast14 = (_atLeast13 &&
           !_javaVersionProperty.startsWith("1.3"));
   private static final boolean _atLeast15 = (_atLeast14 &&
           !_javaVersionProperty.startsWith("1.4"));
   private static final String vendorURL =
           System.getProperty("java.vendor.url");
   private static final String sunVendorURL = "http://java.sun.ru/";
   private static boolean isJavaVersionAtLeast15() {
       return _atLeast15;
   }
   private static boolean isJavaVersion142Family() {
       return _javaVersionProperty.startsWith("1.4.2");
   }
   // -----------------------------------------------------------------------
   /**
    * Proxy implementation class of AppletStub. Delegates to the
    * JNLPAppletLauncher class.
    */
   private class AppletStubProxy implements AppletStub {
       public boolean isActive() {
           return JNLPAppletLauncher.this.isActive();
       }
       public URL getDocumentBase() {
           return JNLPAppletLauncher.this.getDocumentBase();
       }
       public URL getCodeBase() {
           return JNLPAppletLauncher.this.getCodeBase();
       }
       public String getParameter(String name) {
           return JNLPAppletLauncher.this.getParameter(name);
       }
       public AppletContext getAppletContext() {
           return JNLPAppletLauncher.this.getAppletContext();
       }
       public void appletResize(int width, int height) {
           JNLPAppletLauncher.this.resize(width, height);
       }
   }
   /**
    * Parser class for JNLP files for the applet launcher. For simplicitly, we
    * assume that everything of interest is within a single "jnlp" tag and
    * that the "resources" tags are not nested.
    */
   private static class JNLPParser {
       // The following represents the various states we can be in
       private static final int INITIAL = 1;
       private static final int STARTED = 2;
       private static final int IN_JNLP = 3;
       private static final int IN_RESOURCES = 4;
       private static final int SKIP_ELEMENT = 5;
       private static SAXParserFactory factory;
       private static String systemOsName;
       private static String systemOsArch;
       private JNLPAppletLauncher launcher;
       private URL url;
       private InputStream in;
       private JNLPHandler handler;
       private String codebase = "";
       private int state = INITIAL;
       private int prevState = INITIAL;
       private int depth = 0;
       private int skipDepth = -1;
       private JNLPParser(JNLPAppletLauncher launcher, URL url) throws IOException {
           this.launcher = launcher;
           this.url = url;
           this.handler = new JNLPHandler();
       }
       private void parse() throws IOException {
           if (VERBOSE) {
               System.err.println("JNLPParser: " + url.toString());
           }
           try {
               URLConnection conn = url.openConnection();
               conn.connect();
               InputStream in = new BufferedInputStream(conn.getInputStream());
               SAXParser parser = factory.newSAXParser();
               parser.parse(in, handler);
               in.close();
           } catch (ParserConfigurationException ex) {
               throw (IOException) new IOException().initCause(ex);
           } catch (SAXException ex) {
               throw (IOException) new IOException().initCause(ex);
           }
       }
       // Static initializer for JNLPParser
       static {
           if (sunVendorURL.equals(vendorURL)) {
               // set the "javax.xml.parsers.SAXParserFactory" to the default
               // implementation, so we won"t pull down all the JARs listed
               // in the archive tag to look for a SAXParserFactory implementation
               if (isJavaVersionAtLeast15()) {
                   System.setProperty("javax.xml.parsers.SAXParserFactory",
                           "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl");
               } else if (isJavaVersion142Family()) {
                   System.setProperty("javax.xml.parsers.SAXParserFactory",
                           "org.apache.crimson.jaxp.SAXParserFactoryImpl");
               }
           }
           factory = SAXParserFactory.newInstance();
           systemOsName = System.getProperty("os.name").toLowerCase();
           systemOsArch = System.getProperty("os.arch").toLowerCase();
           if (DEBUG) {
               System.err.println("os.name = " + systemOsName);
               System.err.println("os.arch = " + systemOsArch);
           }
       }
       /**
        * Handler class containing callback methods for the parser.
        */
       private class JNLPHandler extends DefaultHandler {
           JNLPHandler() {
           }
           /* @Override */
           public void startDocument() {
               if (VERBOSE) {
                   System.err.println("START DOCUMENT: " + url);
               }
               state = STARTED;
               if (VERBOSE) {
                   System.err.println("state = " + state);
               }
           }
           /* @Override */
           public void endDocument() {
               if (VERBOSE) {
                   System.err.println("END DOCUMENT");
               }
               state = INITIAL;
               if (VERBOSE) {
                   System.err.println("state = " + state);
               }
           }
           /* @Override */
           public void startElement(String uri,
                   String localName,
                   String qName,
                   Attributes attributes) throws SAXException {
               ++depth;
               if (VERBOSE) {
                   System.err.println("<" + qName + ">" + " : depth=" + depth);
                   for (int i = 0; i < attributes.getLength(); i++) {
                       System.err.println("    [" + i + "]  " + attributes.getQName(i) +
                               " = \"" + attributes.getValue(i) + "\"");
                   }
               }
               // Parse qName based on current state
               switch (state) {
               case STARTED:
                   if (qName.equals("jnlp")) {
                       state = IN_JNLP;
                       codebase = attributes.getValue("codebase");
                       if (codebase == null) {
                           throw new SAXException("<jnlp> unable to determine codebase");
                       }
                       if (codebase.lastIndexOf("/") != codebase.length()-1) {
                           codebase = codebase + "/";
                       }
                       if (VERBOSE) {
                           System.err.println("JNLP : codebase=" + codebase);
                       }
                       if (VERBOSE) {
                           System.err.println("state = " + state);
                       }
                   } else if (qName.equals("resources")) {
                       throw new SAXException("<resources> tag not within <jnlp> tag");
                   }
                   // Ignore all other tags
                   break;
               case IN_JNLP:
                   if (qName.equals("jnlp")) {
                       throw new SAXException("Nested <jnlp> tags");
                   } else if (qName.equals("resources")) {
                       String osName = attributes.getValue("os");
                       String osArch = attributes.getValue("arch");
                       if ((osName == null || systemOsName.startsWith(osName.toLowerCase())) &&
                               (osArch == null || systemOsArch.startsWith(osArch.toLowerCase()))) {
                           if (VERBOSE) {
                               System.err.println("Loading resources : os=" + osName + "  arch=" + osArch);
                           }
                           state = IN_RESOURCES;
                       } else {
                           prevState = state;
                           skipDepth = depth - 1;
                           state = SKIP_ELEMENT;
                       }
                       if (VERBOSE) {
                           System.err.println("Resources : os=" + osName + "  arch=" + osArch + "  state = " + state);
                       }
                   }
                   break;
               case IN_RESOURCES:
                   try {
                       if (qName.equals("jnlp")) {
                           throw new SAXException("Nested <jnlp> tags");
                       } else if (qName.equals("resources")) {
                           throw new SAXException("Nested <resources> tags");
                       } else if (qName.equals("jar")) {
                           String str = attributes.getValue("href");
                           if (str == null || str.length() == 0) {
                               throw new SAXException("<jar> tag missing href attribute");
                           }
                           String jarFileStr = codebase + str;
                           if (VERBOSE) {
                               System.err.println("Jar: " + jarFileStr);
                           }
                           URL jarFile = new URL(jarFileStr);
                           launcher.addJarFile(jarFile);
                       } else if (qName.equals("nativelib")) {
                           String str = attributes.getValue("href");
                           if (str == null || str.length() == 0) {
                               throw new SAXException("<nativelib> tag missing href attribute");
                           }
                           String nativeLibStr = codebase + str;
                           if (VERBOSE) {
                               System.err.println("Native Lib: " + nativeLibStr);
                           }
                           URL nativeLib = new URL(nativeLibStr);
                           launcher.addNativeJar(nativeLib);
                       } else if (qName.equals("extension")) {
                           String extensionURLString = attributes.getValue("href");
                           if (extensionURLString == null || extensionURLString.length() == 0) {
                               throw new SAXException("<extension> tag missing href attribute");
                           }
                           if (VERBOSE) {
                               System.err.println("Extension: " + extensionURLString);
                           }
                           URL extensionURL = new URL(extensionURLString);
                           JNLPParser parser = new JNLPParser(launcher, extensionURL);
                           parser.parse();
                       } else {
                           prevState = state;
                           skipDepth = depth - 1;
                           state = SKIP_ELEMENT;
                           if (VERBOSE) {
                               System.err.println("state = " + state);
                           }
                       }
                   } catch (IOException ex) {
                       throw (SAXException) new SAXException(ex).initCause(ex);
                   }
                   break;
               case INITIAL:
               case SKIP_ELEMENT:
               default:
                   break;
               }
           }
           /* @Override */
           public void endElement(String uri,
                   String localName,
                   String qName) throws SAXException {
               --depth;
               if (VERBOSE) {
                   System.err.println("</" + qName + ">");
               }
               // Parse qName based on current state
               switch (state) {
               case IN_JNLP:
                   if (qName.equals("jnlp")) {
                       state = STARTED;
                       if (VERBOSE) {
                           System.err.println("state = " + state);
                       }
                   }
                   break;
               case IN_RESOURCES:
                   if (qName.equals("resources")) {
                       state = IN_JNLP;
                       if (VERBOSE) {
                           System.err.println("state = " + state);
                       }
                   }
                   break;
               case SKIP_ELEMENT:
                   if (depth == skipDepth) {
                       state = prevState;
                       skipDepth = -1;
                       if (VERBOSE) {
                           System.err.println("state = " + state);
                       }
                   }
                   break;
               case INITIAL:
               case STARTED:
               default:
                   break;
               }
           }
       }
   }
   //----------------------------------------------------------------------
   // Helper routines for adding -Dsun.java2d.noddraw=true to deployment.properties
   // Get a "boolean" parameter
   private boolean getBooleanParameter(String parameterName) {
       return Boolean.valueOf(getParameter(parameterName)).booleanValue();
   }
   private String getDeploymentPropsDir() {
       final String osName = System.getProperty("os.name").toLowerCase();
       StringBuffer result = new StringBuffer();
       result.append(System.getProperty("user.home"));
       if (osName.startsWith("windows")) {
           if (osName.indexOf("vista") != -1) {
               result.append(File.separator).append("AppData").
                       append(File.separator).append("LocalLow");
           } else {
               result.append(File.separator).append("Application Data");
           }
           result.append(File.separator).append("Sun").
                   append(File.separator).append("Java").
                   append(File.separator).append("Deployment");
       } else if (osName.startsWith("mac")) {
           result.append(File.separator).append("Library").
                   append(File.separator).append("Caches").
                   append(File.separator).append("Java");
       } else {
           result.append(File.separator).append(".java").
                   append(File.separator).append("deployment");
       }
       return result.toString();
   }
   private void checkNoDDrawAndUpdateDeploymentProperties() {
       if (!getBooleanParameter("noddraw.check"))
           return;
       if (System.getProperty("os.name").toLowerCase().startsWith("windows") &&
           !"true".equalsIgnoreCase(System.getProperty("sun.java2d.noddraw"))) {
           if (!SwingUtilities.isEventDispatchThread()) {
               try {
                   SwingUtilities.invokeAndWait(new Runnable() {
                           public void run() {
                               updateDeploymentPropertiesImpl();
                           }
                       });
               } catch (Exception e) {
               }
           } else {
               updateDeploymentPropertiesImpl();
           }
       }
   }
   private void updateDeploymentPropertiesImpl() {
       String userHome = System.getProperty("user.home");
       File dontAskFile = new File(userHome + File.separator + ".jnlp-applet" +
                                   File.separator + DONT_ASK);
       if (dontAskFile.exists())
           return; // User asked us not to prompt again
       int option = 0;
       if (!getBooleanParameter("noddraw.check.silent")) {
           option = JOptionPane.showOptionDialog(null,
                                                 "For best robustness of OpenGL applets on Windows,\n" +
                                                 "we recommend disabling Java2D"s use of DirectDraw.\n" +
                                                 "This setting will affect all applets, but is unlikely\n" +
                                                 "to slow other applets down significantly. May we update\n" +
                                                 "your deployment.properties to turn off DirectDraw for\n" +
                                                 "applets? You can change this back later if necessary\n" +
                                                 "using the Java Control Panel, Java tab, under Java\n" +
                                                 "Applet Runtime Settings.",
                                                 "Update deployment.properties?",
                                                 JOptionPane.YES_NO_CANCEL_OPTION,
                                                 JOptionPane.QUESTION_MESSAGE,
                                                 null,
                                                 new Object[] {
                                                     "Yes",
                                                     "No",
                                                     "No, Don"t Ask Again"
                                                 },
                                                 "Yes");
       }
       if (option < 0 ||
           option == 1)
           return; // No
       if (option == 2) {
           try {
               dontAskFile.createNewFile();
           } catch (IOException e) {
           }
           return; // No, Don"t Ask Again
       }
       try {
           // Must update deployment.properties
           File propsDir = new File(getDeploymentPropsDir());
           if (!propsDir.exists()) {
               // Don"t know what"s going on or how to set this permanently
               return;
           }
           File propsFile = new File(propsDir, "deployment.properties");
           if (!propsFile.exists()) {
               // Don"t know what"s going on or how to set this permanently
               return;
           }
           Properties props = new Properties();
           InputStream input = new BufferedInputStream(new FileInputStream(propsFile));
           props.load(input);
           input.close();
           // Search through the keys looking for JRE versions
           Set/*<String>*/ jreVersions = new HashSet/*<String>*/();
           for (Iterator/*<String>*/ iter = props.keySet().iterator(); iter.hasNext(); ) {
               String key = (String) iter.next();
               if (key.startsWith(JRE_PREFIX)) {
                   int idx = key.lastIndexOf(".");
                   if (idx >= 0 && idx > JRE_PREFIX.length()) {
                       String jreVersion = key.substring(JRE_PREFIX.length(), idx);
                       jreVersions.add(jreVersion);
                   }
               }
           }
           // Make sure the currently-running JRE shows up in this set to
           // avoid repeated displays of the dialog. It might not in some
           // upgrade scenarios where there was a pre-existing
           // deployment.properties and the new Java Control Panel hasn"t
           // been run yet.
           jreVersions.add(System.getProperty("java.version"));
           // OK, now that we know all JRE versions covered by the
           // deployment.properties, check out the args for each and update
           // them
           for (Iterator/*<String>*/ iter = jreVersions.iterator(); iter.hasNext(); ) {
               String version = (String) iter.next();
               String argKey = JRE_PREFIX + version + ".args";
               String argVal = props.getProperty(argKey);
               if (argVal == null) {
                   argVal = NODDRAW_PROP;
               } else if (argVal.indexOf(NODDRAW_PROP) < 0) {
                   argVal = argVal + " " + NODDRAW_PROP;
               }
               props.setProperty(argKey, argVal);
           }
           OutputStream output = new BufferedOutputStream(new FileOutputStream(propsFile));
           props.store(output, null);
           output.close();
           if (!getBooleanParameter("noddraw.check.silent")) {
               // Tell user we"re done
               JOptionPane.showMessageDialog(null,
                                             "For best robustness, we recommend you now exit and\n" +
                                             "restart your web browser. (Note: clicking \"OK\" will\n" +
                                             "not exit your browser.)",
                                             "Browser Restart Recommended",
                                             JOptionPane.INFORMATION_MESSAGE);
           }
       } catch (IOException e) {
           e.printStackTrace();
       }
   }

}

 </source>
   
  
 
  



JNLP Demo

JNLP Download Servlet

This sample demonstrates features of the JNLPRandomAccessFile API

This sample demonstrates on how to use CORBA within Java Web Start

This sample is the Java Web Start JRE auto-download installer

Webpad: word processing application allows users to Create, Open, Modify, Save and Print files

Web start and JNLP demo

   <source lang="java">

/* <?xml version="1.0" encoding="UTF-8"?> <jnlp spec="1.0+"

codebase="file:///c:/"
href="start.jnlp">

<information>

<title>JNLP Test</title>
<vendor>Java Source and Support, Inc.</vendor>
<homepage href="http://www.jexp.ru" />
<description>Demonstration of JNLP</description>

</information> <resources>

<j2se version="1.2+" />
<jar href="JNLPTest.jar"/>

</resources> <application-desc main-class="JNLPTest" /> </jnlp>

  • /

import java.awt.BorderLayout; import java.awt.Container; import javax.swing.JFrame; import javax.swing.JLabel; public class JNLPTest {

 public static void main(String args[]) {
   JFrame frame = new JFrame();
   Container content = frame.getContentPane();
   JLabel label = new JLabel("Hello, JNLP");
   content.add(label, BorderLayout.CENTER);
   frame.setSize(200, 200);
   frame.show();
   try {
     Thread.sleep(5000);
   } catch (InterruptedException ignored) {
   } finally {
     System.exit(0);
   }
 }

}


 </source>