mev-4.0.01/source/org/tigr/util/BrowserLauncher.java

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