View Javadoc

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