Coverage Report - net.wotonomy.web.util.BrowserLauncher
 
Classes in this File Line Coverage Branch Coverage Complexity
BrowserLauncher
0% 
0% 
15.714
 
 1  
 //package edu.stanford.ejalbert;
 2  
 package net.wotonomy.web.util;
 3  
 
 4  
 import java.io.BufferedReader;
 5  
 import java.io.InputStreamReader;
 6  
 import java.io.File;
 7  
 import java.io.IOException;
 8  
 import java.lang.reflect.Constructor;
 9  
 import java.lang.reflect.Field;
 10  
 import java.lang.reflect.InvocationTargetException;
 11  
 import java.lang.reflect.Method;
 12  
 
 13  
 /** <p> BrowserLauncher is a class that provides one static method,
 14  
  * openURL, which opens the default web browser for the current user
 15  
  * of the system to the given URL.  It may support other protocols
 16  
  * depending on the system -- mailto, ftp, etc. -- but that has not
 17  
  * been rigorously tested and is not guaranteed to work. </p>
 18  
  *
 19  
  * <p> Yes, this is platform-specific code, and yes, it may rely on
 20  
  * classes on certain platforms that are not part of the standard JDK.
 21  
  * What we're trying to do, though, is to take something that's
 22  
  * frequently desirable but inherently platform-specific -- opening a
 23  
  * default browser -- and allow programmers (you, for example) to do
 24  
  * so without worrying about dropping into native code or doing
 25  
  * anything else similarly evil. </p>
 26  
  *
 27  
  * <p> Anyway, this code is completely in Java and will run on all JDK
 28  
  * 1.1-compliant systems without modification or a need for additional
 29  
  * libraries.  All classes that are required on certain platforms to
 30  
  * allow this to run are dynamically loaded at runtime via reflection
 31  
  * and, if not found, will not cause this to do anything other than
 32  
  * returning an error when opening the browser. </p>
 33  
  *
 34  
  * <p> There are certain system requirements for this class, as it's
 35  
  * running through Runtime.exec(), which is Java's way of making a
 36  
  * native system call.  Currently, this requires that a Macintosh have
 37  
  * a Finder which supports the GURL event, which is true for Mac OS
 38  
  * 8.0 and 8.1 systems that have the Internet Scripting AppleScript
 39  
  * dictionary installed in the Scripting Additions folder in the
 40  
  * Extensions folder (which is installed by default as far as I know
 41  
  * under Mac OS 8.0 and 8.1), and for all Mac OS 8.5 and later
 42  
  * systems.  On Windows, it only runs under Win32 systems (Windows 95,
 43  
  * 98, and NT 4.0, as well as later versions of all).  On other
 44  
  * systems, this drops back from the inherently platform-sensitive
 45  
  * concept of a default browser and simply attempts to launch Netscape
 46  
  * via a shell command. </p>
 47  
  *
 48  
  * <p> This code is Copyright 1999-2001 by Eric Albert
 49  
  * (ejalbert@cs.stanford.edu) and may be redistributed or modified in
 50  
  * any form without restrictions as long as the portion of this
 51  
  * comment from this paragraph through the end of the comment is not
 52  
  * removed.  The author requests that he be notified of any
 53  
  * application, applet, or other binary that makes use of this code,
 54  
  * but that's more out of curiosity than anything and is not required.
 55  
  * This software includes no warranty.  The author is not repsonsible
 56  
  * for any loss of data or functionality or any adverse or unexpected
 57  
  * effects of using this software. </p>
 58  
  *
 59  
  * Credits: <br> Steven Spencer, JavaWorld magazine (<a
 60  
  * href="http://www.javaworld.com/javaworld/javatips/jw-javatip66.html">Java
 61  
  * Tip 66</a>) <br> Thanks also to Ron B. Yeh, Eric Shapiro, Ben
 62  
  * Engber, Paul Teitlebaum, Andrea Cantatore, Larry Barowski, Trevor
 63  
  * Bedzek, Frank Miedrich, and Ron Rabakukk
 64  
  *
 65  
  *
 66  
  * original author Eric Albert (<a
 67  
  * href="mailto:ejalbert@cs.stanford.edu">ejalbert@cs.stanford.edu</a>)
 68  
  * author's version 1.4b1 (Released June 20, 2001)
 69  
  * @author Copyright 2001, Intersect Software Corporation
 70  
  * @version $Revision: 905 $ $Date: 2006-02-19 01:44:03 +0000 (Sun, 19 Feb 2006) $
 71  
  */
 72  
 
 73  0
 public class BrowserLauncher {
 74  
     /** The Java virtual machine that we are running on.  Actually, in
 75  
      * most cases we only care about the operating system, but some
 76  
      * operating systems require us to switch on the VM. */
 77  
     private static int jvm;
 78  
 
 79  
     /** The browser for the system */
 80  
     private static Object browser;
 81  
 
 82  
     /** Caches whether any classes, methods, and fields that are not
 83  
      * part of the JDK and need to be dynamically loaded at runtime
 84  
      * loaded successfully.  <p> Note that if this is
 85  
      * <code>false</code>, <code>openURL()</code> will always return
 86  
      * an IOException.  */
 87  
     private static boolean loadedWithoutErrors;
 88  
 
 89  
     /** The com.apple.mrj.MRJFileUtils class */
 90  
     private static Class mrjFileUtilsClass;
 91  
 
 92  
     /** The com.apple.mrj.MRJOSType class */
 93  
     private static Class mrjOSTypeClass;
 94  
 
 95  
     /** The com.apple.MacOS.AEDesc class */
 96  
     private static Class aeDescClass;
 97  
 
 98  
     /** The <init>(int) method of com.apple.MacOS.AETarget */
 99  
     private static Constructor aeTargetConstructor;
 100  
 
 101  
     /** The <init>(int, int, int) method of com.apple.MacOS.AppleEvent */
 102  
     private static Constructor appleEventConstructor;
 103  
 
 104  
     /** The <init>(String) method of com.apple.MacOS.AEDesc */
 105  
     private static Constructor aeDescConstructor;
 106  
 
 107  
     /** The findFolder method of com.apple.mrj.MRJFileUtils */
 108  
     private static Method findFolder;
 109  
 
 110  
     /** The getFileCreator method of com.apple.mrj.MRJFileUtils */
 111  
     private static Method getFileCreator;
 112  
 
 113  
     /** The getFileType method of com.apple.mrj.MRJFileUtils */
 114  
     private static Method getFileType;
 115  
 
 116  
     /** The openURL method of com.apple.mrj.MRJFileUtils */
 117  
     private static Method openURL;
 118  
 
 119  
     /** The makeOSType method of com.apple.MacOS.OSUtils */
 120  
     private static Method makeOSType;
 121  
 
 122  
     /** The putParameter method of com.apple.MacOS.AppleEvent */
 123  
     private static Method putParameter;
 124  
 
 125  
     /** The sendNoReply method of com.apple.MacOS.AppleEvent */
 126  
     private static Method sendNoReply;
 127  
 
 128  
     /** Actually an MRJOSType pointing to the System Folder on a Macintosh */
 129  
     private static Object kSystemFolderType;
 130  
 
 131  
     /** The keyDirectObject AppleEvent parameter type */
 132  
     private static Integer keyDirectObject;
 133  
 
 134  
     /** The kAutoGenerateReturnID AppleEvent code */
 135  
     private static Integer kAutoGenerateReturnID;
 136  
 
 137  
     /** The kAnyTransactionID AppleEvent code */
 138  
     private static Integer kAnyTransactionID;
 139  
 
 140  
     /** The linkage object required for JDirect 3 on Mac OS X. */
 141  
     private static Object linkage;
 142  
 
 143  
     /** The framework to reference on Mac OS X */
 144  
     private static final String JDirect_MacOSX = "/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/HIToolbox";
 145  
 
 146  
     /** JVM constant for MRJ 2.0 */
 147  
     private static final int MRJ_2_0 = 0;
 148  
 
 149  
     /** JVM constant for MRJ 2.1 or later */
 150  
     private static final int MRJ_2_1 = 1;
 151  
 
 152  
     /** JVM constant for Java on Mac OS X 10.0 (MRJ 3.0) */
 153  
     private static final int MRJ_3_0 = 3;
 154  
 
 155  
     /** JVM constant for MRJ 3.1 */
 156  
     private static final int MRJ_3_1 = 4;
 157  
 
 158  
     /** JVM constant for any Windows NT JVM */
 159  
     private static final int WINDOWS_NT = 5;
 160  
 
 161  
     /** JVM constant for any Windows 9x JVM */
 162  
     private static final int WINDOWS_9x = 6;
 163  
 
 164  
     /** JVM constant for any other platform */
 165  
     private static final int OTHER = -1;
 166  
 
 167  
     /** The file type of the Finder on a Macintosh.  Hardcoding
 168  
      * "Finder" would keep non-U.S. English systems from working
 169  
      * properly.  */
 170  
     private static final String FINDER_TYPE = "FNDR";
 171  
 
 172  
     /** The creator code of the Finder on a Macintosh, which is needed
 173  
      * to send AppleEvents to the application.  */
 174  
     private static final String FINDER_CREATOR = "MACS";
 175  
 
 176  
     /** The name for the AppleEvent type corresponding to a GetURL event. */
 177  
     private static final String GURL_EVENT = "GURL";
 178  
 
 179  
     /** The first parameter that needs to be passed into
 180  
      * Runtime.exec() to open the default web browser on Windows.  */
 181  
     private static final String FIRST_WINDOWS_PARAMETER = "/c";
 182  
 
 183  
     /** The second parameter for Runtime.exec() on Windows. */
 184  
     private static final String SECOND_WINDOWS_PARAMETER = "start";
 185  
 
 186  
     /** The third parameter for Runtime.exec() on Windows.  This is a
 187  
      * "title" parameter that the command line expects.  Setting this
 188  
      * parameter allows URLs containing spaces to work.  */
 189  
     private static final String THIRD_WINDOWS_PARAMETER = "\"\"";
 190  
 
 191  
     /** The shell parameters for Netscape that opens a given URL in an
 192  
      * already-open copy of Netscape on many command-line systems.  */
 193  
     private static final String NETSCAPE_REMOTE_PARAMETER = "-remote";
 194  
     //private static final String NETSCAPE_OPEN_PARAMETER_START = "'openURL(";
 195  
     //private static final String NETSCAPE_OPEN_PARAMETER_END = ")'";
 196  
     private static final String NETSCAPE_OPEN_PARAMETER_START = "openURL(";
 197  
     private static final String NETSCAPE_OPEN_PARAMETER_END = ")";
 198  
 
 199  
     /** The message from any exception thrown throughout the
 200  
      * initialization process.  */
 201  
     private static String errorMessage;
 202  
 
 203  
     /** An initialization block that determines the operating system
 204  
      * and loads the necessary runtime data.  */
 205  
     static {
 206  0
         loadedWithoutErrors = true;
 207  0
         String osName = System.getProperty("os.name");
 208  0
         if (osName.startsWith("Mac OS")) {
 209  0
             String mrjVersion = System.getProperty("mrj.version");
 210  0
             String majorMRJVersion = mrjVersion.substring(0, 3);
 211  
             try {
 212  0
                 double version = Double.valueOf(majorMRJVersion).doubleValue();
 213  0
                 if (version == 2) {
 214  0
                     jvm = MRJ_2_0;
 215  0
                 } else if (version >= 2.1 && version < 3) {
 216  
                     // Assume that all 2.x versions of MRJ work the
 217  
                     // same.  MRJ 2.1 actually works via
 218  
                     // Runtime.exec() and 2.2 supports that but has an
 219  
                     // openURL() method as well that we currently
 220  
                     // ignore.
 221  0
                     jvm = MRJ_2_1;
 222  0
                 } else if (version == 3.0) {
 223  0
                     jvm = MRJ_3_0;
 224  0
                 } else if (version >= 3.1) {
 225  
                     // Assume that all 3.1 and later versions of MRJ
 226  
                     // work the same.
 227  0
                     jvm = MRJ_3_1;
 228  0
                 } else {
 229  0
                     loadedWithoutErrors = false;
 230  0
                     errorMessage = "Unsupported MRJ version: " + version;
 231  
                 }
 232  0
             } catch (NumberFormatException nfe) {
 233  0
                 loadedWithoutErrors = false;
 234  0
                 errorMessage = "Invalid MRJ version: " + mrjVersion;
 235  0
             }
 236  0
         } else if (osName.startsWith("Windows")) {
 237  0
             if (osName.indexOf("9") != -1) {
 238  0
                 jvm = WINDOWS_9x;
 239  0
             } else {
 240  0
                 jvm = WINDOWS_NT;
 241  
             }
 242  0
         } else {
 243  0
             jvm = OTHER;
 244  
         }
 245  
 
 246  0
         if (loadedWithoutErrors) {        // if we haven't hit any errors yet
 247  0
             loadedWithoutErrors = loadClasses();
 248  
         }
 249  0
     }
 250  
 
 251  
     /** This class should be never be instantiated; this just ensures so. */
 252  0
     private BrowserLauncher() { }
 253  
 
 254  
     /** Called by a static initializer to load any classes, fields,
 255  
      * and methods required at runtime to locate the user's web
 256  
      * browser.
 257  
      * @return <code>true</code> if all intialization succeeded
 258  
      * <code>false</code> if any portion of the initialization failed */
 259  
     private static boolean loadClasses() {
 260  0
         switch (jvm) {
 261  
         case MRJ_2_0:
 262  
             try {
 263  0
                 Class aeTargetClass = Class.forName("com.apple.MacOS.AETarget");
 264  0
                 Class osUtilsClass = Class.forName("com.apple.MacOS.OSUtils");
 265  0
                 Class appleEventClass = Class.forName
 266  0
                     ("com.apple.MacOS.AppleEvent");
 267  0
                 Class aeClass = Class.forName("com.apple.MacOS.ae");
 268  0
                 aeDescClass = Class.forName("com.apple.MacOS.AEDesc");
 269  
 
 270  0
                 aeTargetConstructor = aeTargetClass.getDeclaredConstructor
 271  0
                     (new Class [] { int.class });
 272  0
                 appleEventConstructor = appleEventClass.getDeclaredConstructor
 273  0
                     (new Class[] { int.class, int.class, aeTargetClass,
 274  0
                                    int.class, int.class });
 275  0
                 aeDescConstructor = aeDescClass.getDeclaredConstructor
 276  0
                     (new Class[] { String.class });
 277  
 
 278  0
                 makeOSType = osUtilsClass.getDeclaredMethod
 279  0
                     ("makeOSType", new Class [] { String.class });
 280  0
                 putParameter = appleEventClass.getDeclaredMethod
 281  0
                     ("putParameter", new Class[] { int.class, aeDescClass });
 282  0
                 sendNoReply = appleEventClass.getDeclaredMethod
 283  0
                     ("sendNoReply", new Class[] { });
 284  
 
 285  0
                 Field keyDirectObjectField = aeClass.getDeclaredField
 286  0
                     ("keyDirectObject");
 287  0
                 keyDirectObject = (Integer) keyDirectObjectField.get(null);
 288  0
                 Field autoGenerateReturnIDField = appleEventClass
 289  0
                     .getDeclaredField("kAutoGenerateReturnID");
 290  0
                 kAutoGenerateReturnID = (Integer) autoGenerateReturnIDField
 291  0
                     .get(null);
 292  0
                 Field anyTransactionIDField = appleEventClass.getDeclaredField
 293  0
                     ("kAnyTransactionID");
 294  0
                 kAnyTransactionID = (Integer) anyTransactionIDField.get(null);
 295  0
             } catch (ClassNotFoundException cnfe) {
 296  0
                 errorMessage = cnfe.getMessage();
 297  0
                 return false;
 298  0
             } catch (NoSuchMethodException nsme) {
 299  0
                 errorMessage = nsme.getMessage();
 300  0
                 return false;
 301  0
             } catch (NoSuchFieldException nsfe) {
 302  0
                 errorMessage = nsfe.getMessage();
 303  0
                 return false;
 304  0
             } catch (IllegalAccessException iae) {
 305  0
                 errorMessage = iae.getMessage();
 306  0
                 return false;
 307  0
             }
 308  0
             break;
 309  
         case MRJ_2_1:
 310  
             try {
 311  0
                 mrjFileUtilsClass = Class.forName("com.apple.mrj.MRJFileUtils");
 312  0
                 mrjOSTypeClass = Class.forName("com.apple.mrj.MRJOSType");
 313  0
                 Field systemFolderField = mrjFileUtilsClass.getDeclaredField
 314  0
                     ("kSystemFolderType");
 315  0
                 kSystemFolderType = systemFolderField.get(null);
 316  0
                 findFolder = mrjFileUtilsClass.getDeclaredMethod
 317  0
                     ("findFolder", new Class[] { mrjOSTypeClass });
 318  0
                 getFileCreator = mrjFileUtilsClass.getDeclaredMethod
 319  0
                     ("getFileCreator", new Class[] { File.class });
 320  0
                 getFileType = mrjFileUtilsClass.getDeclaredMethod
 321  0
                     ("getFileType", new Class[] { File.class });
 322  0
             } catch (ClassNotFoundException cnfe) {
 323  0
                 errorMessage = cnfe.getMessage();
 324  0
                 return false;
 325  0
             } catch (NoSuchFieldException nsfe) {
 326  0
                 errorMessage = nsfe.getMessage();
 327  0
                 return false;
 328  0
             } catch (NoSuchMethodException nsme) {
 329  0
                 errorMessage = nsme.getMessage();
 330  0
                 return false;
 331  0
             } catch (SecurityException se) {
 332  0
                 errorMessage = se.getMessage();
 333  0
                 return false;
 334  0
             } catch (IllegalAccessException iae) {
 335  0
                 errorMessage = iae.getMessage();
 336  0
                 return false;
 337  0
             }
 338  0
             break;
 339  
         case MRJ_3_0:
 340  
             try {
 341  0
                 Class linker = Class.forName("com.apple.mrj.jdirect.Linker");
 342  0
                 Constructor constructor = linker.getConstructor
 343  0
                     (new Class[]{ Class.class });
 344  0
                 linkage = constructor.newInstance(new Object[]
 345  0
                     { BrowserLauncher.class });
 346  0
             } catch (ClassNotFoundException cnfe) {
 347  0
                 errorMessage = cnfe.getMessage();
 348  0
                 return false;
 349  0
             } catch (NoSuchMethodException nsme) {
 350  0
                 errorMessage = nsme.getMessage();
 351  0
                 return false;
 352  0
             } catch (InvocationTargetException ite) {
 353  0
                 errorMessage = ite.getMessage();
 354  0
                 return false;
 355  0
             } catch (InstantiationException ie) {
 356  0
                 errorMessage = ie.getMessage();
 357  0
                 return false;
 358  0
             } catch (IllegalAccessException iae) {
 359  0
                 errorMessage = iae.getMessage();
 360  0
                 return false;
 361  0
             }
 362  0
             break;
 363  
         case MRJ_3_1:
 364  
             try {
 365  0
                 mrjFileUtilsClass = Class.forName("com.apple.mrj.MRJFileUtils");
 366  0
                 openURL = mrjFileUtilsClass.getDeclaredMethod
 367  0
                     ("openURL", new Class[] { String.class });
 368  0
             } catch (ClassNotFoundException cnfe) {
 369  0
                 errorMessage = cnfe.getMessage();
 370  0
                 return false;
 371  0
             } catch (NoSuchMethodException nsme) {
 372  0
                 errorMessage = nsme.getMessage();
 373  0
                 return false;
 374  0
             }
 375  0
             break;
 376  
         default:
 377  
             break;
 378  
         }
 379  0
         return true;
 380  
     }
 381  
 
 382  
     /** Attempts to locate the default web browser on the local
 383  
      * system.  Caches results so it only locates the browser once *
 384  
      * for each use of this class per JVM instance.
 385  
      * @return The browser for the system.  Note that this may not be
 386  
      * what you would consider to be a standard web browser; instead,
 387  
      * it's the application that gets called to open the default web
 388  
      * browser.  In some cases, this will be a non-String object that
 389  
      * provides the means of calling the default browser. */
 390  
     private static Object locateBrowser() {
 391  0
         if (browser != null) {
 392  0
             return browser;
 393  
         }
 394  0
         switch (jvm) {
 395  
         case MRJ_2_0:
 396  
             try {
 397  0
                 Integer finderCreatorCode = (Integer) makeOSType.invoke
 398  0
                     (null, new Object[] { FINDER_CREATOR });
 399  0
                 Object aeTarget = aeTargetConstructor.newInstance
 400  0
                     (new Object[] { finderCreatorCode });
 401  0
                 Integer gurlType = (Integer) makeOSType.invoke
 402  0
                     (null, new Object[] { GURL_EVENT });
 403  0
                 Object appleEvent = appleEventConstructor.newInstance
 404  0
                     (new Object[] { gurlType, gurlType, aeTarget,
 405  0
                                     kAutoGenerateReturnID, kAnyTransactionID });
 406  
                 // Don't set browser = appleEvent because then the
 407  
                 // next time we call locateBrowser(), we'll get the
 408  
                 // same AppleEvent, to which we'll already have added
 409  
                 // the relevant parameter. Instead, regenerate the
 410  
                 // AppleEvent every time.  There's probably a way to
 411  
                 // do this better; if any has any ideas, please let me
 412  
                 // know.
 413  0
                 return appleEvent;
 414  0
             } catch (IllegalAccessException iae) {
 415  0
                 browser = null;
 416  0
                 errorMessage = iae.getMessage();
 417  0
                 return browser;
 418  0
             } catch (InstantiationException ie) {
 419  0
                 browser = null;
 420  0
                 errorMessage = ie.getMessage();
 421  0
                 return browser;
 422  0
             } catch (InvocationTargetException ite) {
 423  0
                 browser = null;
 424  0
                 errorMessage = ite.getMessage();
 425  0
                 return browser;
 426  
             }
 427  
         case MRJ_2_1:
 428  
             File systemFolder;
 429  
             try {
 430  0
                 systemFolder = (File) findFolder.invoke(null, new Object[]
 431  0
                     { kSystemFolderType });
 432  0
             } catch (IllegalArgumentException iare) {
 433  0
                 browser = null;
 434  0
                 errorMessage = iare.getMessage();
 435  0
                 return browser;
 436  0
             } catch (IllegalAccessException iae) {
 437  0
                 browser = null;
 438  0
                 errorMessage = iae.getMessage();
 439  0
                 return browser;
 440  0
             } catch (InvocationTargetException ite) {
 441  0
                 browser = null;
 442  0
                 errorMessage = ite.getTargetException().getClass() + ": " +
 443  0
                     ite.getTargetException().getMessage();
 444  0
                 return browser;
 445  0
             }
 446  0
             String[] systemFolderFiles = systemFolder.list();
 447  
             // Avoid a FilenameFilter because that can't be stopped mid-list
 448  0
             for(int i = 0; i < systemFolderFiles.length; i++) {
 449  
                 try {
 450  0
                     File file = new File(systemFolder, systemFolderFiles[i]);
 451  0
                     if (!file.isFile()) {
 452  0
                         continue;
 453  
                     }
 454  
                     // We're looking for a file with a creator code of
 455  
                     // 'MACS' and a type of 'FNDR'.  Only requiring
 456  
                     // the type results in non-Finder applications
 457  
                     // being picked up on certain Mac OS 9 systems,
 458  
                     // especially German ones, and sending a GURL
 459  
                     // event to those applications results in a logout
 460  
                     // under Multiple Users.
 461  0
                     Object fileType = getFileType.invoke
 462  0
                         (null, new Object[] { file });
 463  0
                     if (FINDER_TYPE.equals(fileType.toString())) {
 464  0
                         Object fileCreator = getFileCreator.invoke
 465  0
                             (null, new Object[] { file });
 466  0
                         if (FINDER_CREATOR.equals(fileCreator.toString())) {
 467  0
                             browser = file.toString(); // Actually the
 468  
                             // Finder, but that's OK
 469  0
                             return browser;
 470  
                         }
 471  
                     }
 472  0
                 } catch (IllegalArgumentException iare) {
 473  
                     //WTF? browser = browser;
 474  0
                     errorMessage = iare.getMessage();
 475  0
                     return null;
 476  0
                 } catch (IllegalAccessException iae) {
 477  0
                     browser = null;
 478  0
                     errorMessage = iae.getMessage();
 479  0
                     return browser;
 480  0
                 } catch (InvocationTargetException ite) {
 481  0
                     browser = null;
 482  0
                     errorMessage = ite.getTargetException().getClass() + ": "
 483  0
                         + ite.getTargetException().getMessage();
 484  0
                     return browser;
 485  0
                 }
 486  
             }
 487  0
             browser = null;
 488  0
             break;
 489  
         case MRJ_3_0:
 490  
         case MRJ_3_1:
 491  0
             browser = "";        // Return something non-null
 492  0
             break;
 493  
         case WINDOWS_NT:
 494  0
             browser = "cmd.exe";
 495  0
             break;
 496  
         case WINDOWS_9x:
 497  0
             browser = "command.com";
 498  0
             break;
 499  
         case OTHER:
 500  
         default:
 501  0
             browser = "netscape";
 502  
             break;
 503  
         }
 504  0
         return browser;
 505  
     }
 506  
 
 507  
     /** Attempts to open the default web browser to the given URL.
 508  
      * @param url The URL to open
 509  
      * @throws IOException If the web browser could not be located or
 510  
      * does not run */
 511  
     public static void openURL(String url) throws IOException {
 512  0
         if (!loadedWithoutErrors) {
 513  0
             throw new IOException("Exception in finding browser: "
 514  0
                                   + errorMessage);
 515  
         }
 516  0
         Object browser = locateBrowser();
 517  0
         if (browser == null) {
 518  0
             throw new IOException("Unable to locate browser: " + errorMessage);
 519  
         }
 520  
 
 521  0
         switch (jvm) {
 522  
         case MRJ_2_0:
 523  0
             Object aeDesc = null;
 524  
             try {
 525  0
                 aeDesc = aeDescConstructor.newInstance(new Object[] { url });
 526  0
                 putParameter.invoke(browser, new Object[]
 527  0
                     { keyDirectObject, aeDesc });
 528  0
                 sendNoReply.invoke(browser, new Object[] { });
 529  0
             } catch (InvocationTargetException ite) {
 530  0
                 throw new IOException("InvocationTargetException while creating"
 531  0
                                       +" AEDesc: " + ite.getMessage());
 532  0
             } catch (IllegalAccessException iae) {
 533  0
                 throw new IOException("IllegalAccessException while building "
 534  0
                                       + "AppleEvent: " + iae.getMessage());
 535  0
             } catch (InstantiationException ie) {
 536  0
                 throw new IOException("InstantiationException while creating "
 537  0
                                       + "AEDesc: " + ie.getMessage());
 538  0
             } finally {
 539  0
                 aeDesc = null; // Encourage it to get disposed if it
 540  
                 // was created
 541  0
                 browser = null;        // Ditto
 542  0
             }
 543  0
             break;
 544  
         case MRJ_2_1:
 545  0
             Runtime.getRuntime().exec(new String[] { (String) browser, url } );
 546  0
             break;
 547  
         case MRJ_3_0:
 548  0
             int[] instance = new int[1];
 549  0
             int result = ICStart(instance, 0);
 550  0
             if (result == 0) {
 551  0
                 int[] selectionStart = new int[] { 0 };
 552  0
                 byte[] urlBytes = url.getBytes();
 553  0
                 int[] selectionEnd = new int[] { urlBytes.length };
 554  0
                 result = ICLaunchURL(instance[0], new byte[] { 0 }, urlBytes,
 555  0
                 urlBytes.length, selectionStart,
 556  0
                 selectionEnd);
 557  0
                 if (result == 0) {
 558  
                     // Ignore the return value; the URL was launched
 559  
                     // successfully regardless of what happens here.
 560  0
                     ICStop(instance);
 561  0
                 } else {
 562  0
                     throw new IOException("Unable to launch URL: " + result);
 563  
                 }
 564  0
             } else {
 565  0
                 throw new IOException("Unable to create an Internet Config "
 566  0
                                       + "instance: " + result);
 567  
             }
 568  0
             break;
 569  
         case MRJ_3_1:
 570  
             try {
 571  0
                 openURL.invoke(null, new Object[] { url });
 572  0
             } catch (InvocationTargetException ite) {
 573  0
                 throw new IOException("InvocationTargetException while calling "
 574  0
                                       + "openURL: " + ite.getMessage());
 575  0
             } catch (IllegalAccessException iae) {
 576  0
                 throw new IOException("IllegalAccessException while calling "
 577  0
                                       + "openURL: " + iae.getMessage());
 578  0
             }
 579  0
             break;
 580  
         case WINDOWS_NT:
 581  
             // Add quotes around the URL to allow ampersands and other special
 582  
             // characters to work.
 583  0
             Process process = Runtime.getRuntime().exec(new String[]
 584  0
                 { (String) browser, FIRST_WINDOWS_PARAMETER,
 585  0
                   SECOND_WINDOWS_PARAMETER, THIRD_WINDOWS_PARAMETER,
 586  0
                   '"' + url + '"' });
 587  
             // This avoids a memory leak on some versions of Java on
 588  
             // Windows.  That's hinted at in
 589  
             // <http://developer.java.sun.com/developer/qow/archive/68/>.
 590  
             try {
 591  0
                 process.waitFor();
 592  0
                 process.exitValue();
 593  0
             } catch (InterruptedException ie) {
 594  0
                 throw new IOException("InterruptedException while launching "
 595  0
                                       + "browser: " + ie.getMessage());
 596  0
             }
 597  0
             break;
 598  
         case WINDOWS_9x:
 599  
             // Add quotes around the URL to allow ampersands and other special
 600  
             // characters to work.
 601  
             // Note: windows 98 doesn't expect the THIRD_WINDOWS_PARAMETER for
 602  
             // its title.
 603  0
             process = Runtime.getRuntime().exec(new String[]
 604  0
                 { (String) browser, FIRST_WINDOWS_PARAMETER,
 605  0
                   SECOND_WINDOWS_PARAMETER, '"' + url + '"' });
 606  
             // This avoids a memory leak on some versions of Java on
 607  
             // Windows.  That's hinted at in
 608  
             // <http://developer.java.sun.com/developer/qow/archive/68/>.
 609  
             try {
 610  0
                 process.waitFor();
 611  0
                 process.exitValue();
 612  0
             } catch (InterruptedException ie) {
 613  0
                 throw new IOException("InterruptedException while launching "
 614  0
                                       + "browser: " + ie.getMessage());
 615  0
             }
 616  0
             break;
 617  
         case OTHER:
 618  
             // Assume that we're on Unix and that Netscape is installed
 619  
 
 620  
             // First, attempt to open the URL in a currently running
 621  
             // session of Netscape
 622  0
             process = Runtime.getRuntime().exec(new String[]
 623  0
                 {(String)browser, NETSCAPE_REMOTE_PARAMETER,
 624  0
                  NETSCAPE_OPEN_PARAMETER_START + url +
 625  0
                  NETSCAPE_OPEN_PARAMETER_END });
 626  
             try {
 627  0
                 int exitCode = process.waitFor();
 628  0
                 if (exitCode != 0) {        // if the command had an error
 629  0
                     Runtime.getRuntime().exec(new String[]
 630  0
                         { (String) browser, url } );
 631  0
                 } else if(process.getErrorStream() != null) {
 632  
                     // Netscape may not be open, so the command may not have an
 633  
                     // error, it just wouldn't have a process to attach to...
 634  0
                     BufferedReader reader = new BufferedReader
 635  0
                         (new InputStreamReader(process.getErrorStream()));
 636  0
                     String errorStr = reader.readLine();
 637  
 
 638  0
                     if (  errorStr != null ) {
 639  
                         // Command failed, start up the browser
 640  0
                         process = Runtime.getRuntime().exec(new String[] {
 641  0
                             (String) browser, url });
 642  
                     }
 643  
                 }
 644  0
             } catch (InterruptedException ie) {
 645  0
                 throw new IOException("InterruptedException while launching "
 646  0
                                       + "browser: " + ie.getMessage());
 647  0
             }
 648  0
             break;
 649  
         default:
 650  
             // This should never occur, but if it does, we'll try the
 651  
             // simplest thing possible
 652  0
             Runtime.getRuntime().exec(new String[] { (String) browser, url });
 653  
             break;
 654  
         }
 655  0
     }
 656  
 
 657  
     /** Methods required for Mac OS X.  The presence of native methods
 658  
      * does not cause any problems on other platforms.  */
 659  
     private native static int ICStart(int[] instance, int signature);
 660  
     private native static int ICStop(int[] instance);
 661  
     private native static int ICLaunchURL
 662  
         (int instance, byte[] hint, byte[] data, int len, int[] selectionStart,
 663  
          int[] selectionEnd);
 664  
 }
 665