001    package net.provision.soap;
002    
003    import java.io.*;
004    
005    import java.lang.reflect.*;
006    
007    
008    /**
009     * BrowserLauncher is a class that provides one static method, openURL, which opens the
010     * default web browser for the current user of the system to the given URL.  It may
011     * support other protocols depending on the system -- mailto, ftp, etc. -- but that
012     * has not been rigorously tested and is not guaranteed to work.
013     * 
014     * <p>
015     * Yes, this is platform-specific code, and yes, it may rely on classes on certain
016     * platforms that are not part of the standard JDK.  What we're trying to do, though,
017     * is to take something that's frequently desirable but inherently platform-specific
018     * -- opening a default browser -- and allow programmers (you, for example) to do so
019     * without worrying about dropping into native code or doing anything else similarly
020     * evil.
021     * </p>
022     * 
023     * <p>
024     * Anyway, this code is completely in Java and will run on all JDK 1.1-compliant
025     * systems without modification or a need for additional libraries.  All classes that
026     * are required on certain platforms to allow this to run are dynamically loaded at
027     * runtime via reflection and, if not found, will not cause this to do anything other
028     * than returning an error when opening the browser.
029     * </p>
030     * 
031     * <p>
032     * There are certain system requirements for this class, as it's running through
033     * Runtime.exec(), which is Java's way of making a native system call.  Currently,
034     * this requires that a Macintosh have a Finder which supports the GURL event, which
035     * is true for Mac OS 8.0 and 8.1 systems that have the Internet Scripting AppleScript
036     * dictionary installed in the Scripting Additions folder in the Extensions folder
037     * (which is installed by default as far as I know under Mac OS 8.0 and 8.1), and for
038     * all Mac OS 8.5 and later systems.  On Windows, it only runs under Win32 systems
039     * (Windows 95, 98, and NT 4.0, as well as later versions of all).  On other systems,
040     * this drops back from the inherently platform-sensitive concept of a default browser
041     * and simply attempts to launch Netscape via a shell command.
042     * </p>
043     * 
044     * <p>
045     * This code is Copyright 1999-2001 by Eric Albert (ejalbert&#64;cs.stanford.edu) and
046     * may be redistributed or modified in any form without restrictions as long as the
047     * portion of this comment from this paragraph through the end of the comment is not
048     * removed.  The author requests that he be notified of any application, applet, or
049     * other binary that makes use of this code, but that's more out of curiosity than
050     * anything and is not required.  This software includes no warranty.  The author is
051     * not repsonsible for any loss of data or functionality or any adverse or unexpected
052     * effects of using this software.
053     * </p>
054     * 
055     * <p>
056     * Credits: <br>
057     * Steven Spencer, JavaWorld magazine (<a
058     * href="http://www.javaworld.com/javaworld/javatips/jw-javatip66.html">Java Tip
059     * 66</a>) <br>
060     * Thanks also to Ron B. Yeh, Eric Shapiro, Ben Engber, Paul Teitlebaum, Andrea
061     * Cantatore, Larry Barowski, Trevor Bedzek, Frank Miedrich, and Ron Rabakukk
062     * </p>
063     *
064     * @author Eric Albert (<a
065     *         href="mailto:ejalbert&#64;cs.stanford.edu">ejalbert&#64;cs.stanford.edu</a>)
066     * @version 1.4b1 (Released June 20, 2001)
067     */
068    public class BrowserLauncher {
069       /**
070        * The Java virtual machine that we are running on.  Actually, in most cases we only
071        * care about the operating system, but some operating systems require us to switch
072        * on the VM.
073        */
074       private static int jvm;
075    
076       /**
077        * The browser for the system
078        */
079       private static Object browser;
080    
081       /**
082        * Caches whether any classes, methods, and fields that are not part of the JDK and
083        * need to be dynamically loaded at runtime loaded successfully.
084        * 
085        * <p>
086        * Note that if this is <code>false</code>, <code>openURL()</code> will always
087        * return an IOException.
088        * </p>
089        */
090       private static boolean loadedWithoutErrors;
091    
092       /**
093        * The com.apple.mrj.MRJFileUtils class
094        */
095       private static Class mrjFileUtilsClass;
096    
097       /**
098        * The com.apple.mrj.MRJOSType class
099        */
100       private static Class mrjOSTypeClass;
101    
102       /**
103        * The com.apple.MacOS.AEDesc class
104        */
105       private static Class aeDescClass;
106    
107       /**
108        * The &#60;init&#62;(int) method of com.apple.MacOS.AETarget
109        */
110       private static Constructor aeTargetConstructor;
111    
112       /**
113        * The &#60;init&#62;(int, int, int) method of com.apple.MacOS.AppleEvent
114        */
115       private static Constructor appleEventConstructor;
116    
117       /**
118        * The &#60;init&#62;(String) method of com.apple.MacOS.AEDesc
119        */
120       private static Constructor aeDescConstructor;
121    
122       /**
123        * The findFolder method of com.apple.mrj.MRJFileUtils
124        */
125       private static Method findFolder;
126    
127       /**
128        * The getFileCreator method of com.apple.mrj.MRJFileUtils
129        */
130       private static Method getFileCreator;
131    
132       /**
133        * The getFileType method of com.apple.mrj.MRJFileUtils
134        */
135       private static Method getFileType;
136    
137       /**
138        * The openURL method of com.apple.mrj.MRJFileUtils
139        */
140       private static Method openURL;
141    
142       /**
143        * The makeOSType method of com.apple.MacOS.OSUtils
144        */
145       private static Method makeOSType;
146    
147       /**
148        * The putParameter method of com.apple.MacOS.AppleEvent
149        */
150       private static Method putParameter;
151    
152       /**
153        * The sendNoReply method of com.apple.MacOS.AppleEvent
154        */
155       private static Method sendNoReply;
156    
157       /**
158        * Actually an MRJOSType pointing to the System Folder on a Macintosh
159        */
160       private static Object kSystemFolderType;
161    
162       /**
163        * The keyDirectObject AppleEvent parameter type
164        */
165       private static Integer keyDirectObject;
166    
167       /**
168        * The kAutoGenerateReturnID AppleEvent code
169        */
170       private static Integer kAutoGenerateReturnID;
171    
172       /**
173        * The kAnyTransactionID AppleEvent code
174        */
175       private static Integer kAnyTransactionID;
176    
177       /**
178        * The linkage object required for JDirect 3 on Mac OS X.
179        */
180       private static Object linkage;
181    
182       /**
183        * The framework to reference on Mac OS X
184        */
185       private static final String JDirect_MacOSX = "/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/HIToolbox";
186    
187       /**
188        * JVM constant for MRJ 2.0
189        */
190       private static final int MRJ_2_0 = 0;
191    
192       /**
193        * JVM constant for MRJ 2.1 or later
194        */
195       private static final int MRJ_2_1 = 1;
196    
197       /**
198        * JVM constant for Java on Mac OS X 10.0 (MRJ 3.0)
199        */
200       private static final int MRJ_3_0 = 3;
201    
202       /**
203        * JVM constant for MRJ 3.1
204        */
205       private static final int MRJ_3_1 = 4;
206    
207       /**
208        * JVM constant for any Windows NT JVM
209        */
210       private static final int WINDOWS_NT = 5;
211    
212       /**
213        * JVM constant for any Windows 9x JVM
214        */
215       private static final int WINDOWS_9x = 6;
216    
217       /**
218        * JVM constant for any other platform
219        */
220       private static final int OTHER = -1;
221    
222       /**
223        * The file type of the Finder on a Macintosh.  Hardcoding "Finder" would keep
224        * non-U.S. English systems from working properly.
225        */
226       private static final String FINDER_TYPE = "FNDR";
227    
228       /**
229        * The creator code of the Finder on a Macintosh, which is needed to send
230        * AppleEvents to the application.
231        */
232       private static final String FINDER_CREATOR = "MACS";
233    
234       /**
235        * The name for the AppleEvent type corresponding to a GetURL event.
236        */
237       private static final String GURL_EVENT = "GURL";
238    
239       /**
240        * The first parameter that needs to be passed into Runtime.exec() to open the
241        * default web browser on Windows.
242        */
243       private static final String FIRST_WINDOWS_PARAMETER = "/c";
244    
245       /**
246        * The second parameter for Runtime.exec() on Windows.
247        */
248       private static final String SECOND_WINDOWS_PARAMETER = "start";
249    
250       /**
251        * The third parameter for Runtime.exec() on Windows.  This is a "title" parameter
252        * that the command line expects.  Setting this parameter allows URLs containing
253        * spaces to work.
254        */
255       private static final String THIRD_WINDOWS_PARAMETER = "\"\"";
256    
257       /**
258        * The shell parameters for Netscape that opens a given URL in an already-open copy
259        * of Netscape on many command-line systems.
260        */
261       private static final String NETSCAPE_REMOTE_PARAMETER = "-remote";
262       private static final String NETSCAPE_OPEN_PARAMETER_START = "'openURL(";
263       private static final String NETSCAPE_OPEN_PARAMETER_END = ")'";
264    
265       /**
266        * The message from any exception thrown throughout the initialization process.
267        */
268       private static String errorMessage;
269    
270       /**
271        * An initialization block that determines the operating system and loads the
272        * necessary runtime data.
273        */
274       static {
275          loadedWithoutErrors = true;
276    
277          String osName = System.getProperty("os.name");
278    
279          if(osName.startsWith("Mac OS")) {
280             String mrjVersion = System.getProperty("mrj.version");
281             String majorMRJVersion = mrjVersion.substring(0, 3);
282    
283             try {
284                double version = Double.valueOf(majorMRJVersion).doubleValue();
285    
286                if(version == 2) {
287                   jvm = MRJ_2_0;
288                } else if((version >= 2.1) && (version < 3)) {
289                   // Assume that all 2.x versions of MRJ work the same.  MRJ 2.1 actually
290                   // works via Runtime.exec() and 2.2 supports that but has an openURL() method
291                   // as well that we currently ignore.
292                   jvm = MRJ_2_1;
293                } else if(version == 3.0) {
294                   jvm = MRJ_3_0;
295                } else if(version >= 3.1) {
296                   // Assume that all 3.1 and later versions of MRJ work the same.
297                   jvm = MRJ_3_1;
298                } else {
299                   loadedWithoutErrors = false;
300                   errorMessage = "Unsupported MRJ version: " + version;
301                }
302             } catch(NumberFormatException nfe) {
303                loadedWithoutErrors = false;
304                errorMessage = "Invalid MRJ version: " + mrjVersion;
305             }
306          } else if(osName.startsWith("Windows")) {
307             if(osName.indexOf("9") != -1) {
308                jvm = WINDOWS_9x;
309             } else {
310                jvm = WINDOWS_NT;
311             }
312          } else {
313             jvm = OTHER;
314          }
315    
316          if(loadedWithoutErrors) { // if we haven't hit any errors yet
317             loadedWithoutErrors = loadClasses();
318          }
319       }
320    
321       /**
322        * This class should be never be instantiated; this just ensures so.
323        */
324       private BrowserLauncher() {
325       }
326    
327       /**
328        * Attempts to open the default web browser to the given URL.
329        *
330        * @param url The URL to open
331        *
332        * @throws IOException If the web browser could not be located or does not run
333        */
334       public static void openURL(String url) throws IOException {
335          if(!loadedWithoutErrors) {
336             throw new IOException("Exception in finding browser: " + errorMessage);
337          }
338    
339          Object browser = locateBrowser();
340    
341          if(browser == null) {
342             throw new IOException("Unable to locate browser: " + errorMessage);
343          }
344    
345          switch(jvm) {
346             case MRJ_2_0:
347    
348                Object aeDesc = null;
349    
350                try {
351                   aeDesc = aeDescConstructor.newInstance(new Object[]{ url });
352                   putParameter.invoke(browser, new Object[]{ keyDirectObject, aeDesc });
353                   sendNoReply.invoke(browser, new Object[]{  });
354                } catch(InvocationTargetException ite) {
355                   throw new IOException(
356                      "InvocationTargetException while creating AEDesc: " +
357                      ite.getMessage());
358                } catch(IllegalAccessException iae) {
359                   throw new IOException(
360                      "IllegalAccessException while building AppleEvent: " +
361                      iae.getMessage());
362                } catch(InstantiationException ie) {
363                   throw new IOException("InstantiationException while creating AEDesc: " +
364                      ie.getMessage());
365                } finally {
366                   aeDesc = null; // Encourage it to get disposed if it was created
367                   browser = null; // Ditto
368                }
369    
370                break;
371    
372             case MRJ_2_1:
373                Runtime.getRuntime().exec(new String[]{ (String)browser, url });
374    
375                break;
376    
377             case MRJ_3_0:
378    
379                int[] instance = new int[1];
380                int result = ICStart(instance, 0);
381    
382                if(result == 0) {
383                   int[] selectionStart = new int[]{ 0 };
384                   byte[] urlBytes = url.getBytes();
385                   int[] selectionEnd = new int[]{ urlBytes.length };
386                   result = ICLaunchURL(instance[0], new byte[]{ 0 }, urlBytes,
387                         urlBytes.length, selectionStart, selectionEnd);
388    
389                   if(result == 0) {
390                      // Ignore the return value; the URL was launched successfully
391                      // regardless of what happens here.
392                      ICStop(instance);
393                   } else {
394                      throw new IOException("Unable to launch URL: " + result);
395                   }
396                } else {
397                   throw new IOException("Unable to create an Internet Config instance: " +
398                      result);
399                }
400    
401                break;
402    
403             case MRJ_3_1:
404    
405                try {
406                   openURL.invoke(null, new Object[]{ url });
407                } catch(InvocationTargetException ite) {
408                   throw new IOException(
409                      "InvocationTargetException while calling openURL: " +
410                      ite.getMessage());
411                } catch(IllegalAccessException iae) {
412                   throw new IOException("IllegalAccessException while calling openURL: " +
413                      iae.getMessage());
414                }
415    
416                break;
417    
418             case WINDOWS_NT:
419             case WINDOWS_9x:
420    
421                // Add quotes around the URL to allow ampersands and other special
422                // characters to work.
423                Process process = Runtime.getRuntime().exec(new String[]{
424                         (String)browser, FIRST_WINDOWS_PARAMETER, SECOND_WINDOWS_PARAMETER,
425                         THIRD_WINDOWS_PARAMETER, '"' + url + '"'
426                      });
427    
428                // This avoids a memory leak on some versions of Java on Windows.
429                // That's hinted at in <http://developer.java.sun.com/developer/qow/archive/68/>.
430                try {
431                   process.waitFor();
432                   process.exitValue();
433                } catch(InterruptedException ie) {
434                   throw new IOException("InterruptedException while launching browser: " +
435                      ie.getMessage());
436                }
437    
438                break;
439    
440             case OTHER:
441    
442                // Assume that we're on Unix and that Netscape is installed
443                // First, attempt to open the URL in a currently running session of Netscape
444                process = Runtime.getRuntime().exec(new String[]{
445                         (String)browser, NETSCAPE_REMOTE_PARAMETER,
446                         
447                      NETSCAPE_OPEN_PARAMETER_START + url + NETSCAPE_OPEN_PARAMETER_END
448                      });
449    
450                try {
451                   int exitCode = process.waitFor();
452    
453                   if(exitCode != 0) { // if Netscape was not open
454                      Runtime.getRuntime().exec(new String[]{ (String)browser, url });
455                   }
456                } catch(InterruptedException ie) {
457                   throw new IOException("InterruptedException while launching browser: " +
458                      ie.getMessage());
459                }
460    
461                break;
462    
463             default:
464    
465                // This should never occur, but if it does, we'll try the simplest thing possible
466                Runtime.getRuntime().exec(new String[]{ (String)browser, url });
467    
468                break;
469          }
470       }
471    
472       /**
473        * DOCUMENT ME!
474        *
475        * @param instance DOCUMENT ME!
476        * @param hint DOCUMENT ME!
477        * @param data DOCUMENT ME!
478        * @param len DOCUMENT ME!
479        * @param selectionStart DOCUMENT ME!
480        * @param selectionEnd DOCUMENT ME!
481        *
482        * @return DOCUMENT ME!
483        */
484       private static native int ICLaunchURL(int instance, byte[] hint, byte[] data,
485          int len, int[] selectionStart, int[] selectionEnd);
486    
487       /**
488        * Methods required for Mac OS X.  The presence of native methods does not cause any
489        * problems on other platforms.
490        *
491        * @param instance DOCUMENT ME!
492        * @param signature DOCUMENT ME!
493        *
494        * @return DOCUMENT ME!
495        */
496       private static native int ICStart(int[] instance, int signature);
497    
498       /**
499        * DOCUMENT ME!
500        *
501        * @param instance DOCUMENT ME!
502        *
503        * @return DOCUMENT ME!
504        */
505       private static native int ICStop(int[] instance);
506    
507       /**
508        * Called by a static initializer to load any classes, fields, and methods required
509        * at runtime to locate the user's web browser.
510        *
511        * @return <code>true</code> if all intialization succeeded     <code>false</code>
512        *         if any portion of the initialization failed
513        */
514       private static boolean loadClasses() {
515          switch(jvm) {
516             case MRJ_2_0:
517    
518                try {
519                   Class aeTargetClass = Class.forName("com.apple.MacOS.AETarget");
520                   Class osUtilsClass = Class.forName("com.apple.MacOS.OSUtils");
521                   Class appleEventClass = Class.forName("com.apple.MacOS.AppleEvent");
522                   Class aeClass = Class.forName("com.apple.MacOS.ae");
523                   aeDescClass = Class.forName("com.apple.MacOS.AEDesc");
524    
525                   aeTargetConstructor = aeTargetClass.getDeclaredConstructor(new Class[]{
526                            int.class
527                         });
528                   appleEventConstructor = appleEventClass.getDeclaredConstructor(new Class[]{
529                            int.class, int.class, aeTargetClass, int.class, int.class
530                         });
531                   aeDescConstructor = aeDescClass.getDeclaredConstructor(new Class[]{
532                            String.class
533                         });
534    
535                   makeOSType = osUtilsClass.getDeclaredMethod("makeOSType",
536                         new Class[]{ String.class });
537                   putParameter = appleEventClass.getDeclaredMethod("putParameter",
538                         new Class[]{ int.class, aeDescClass });
539                   sendNoReply = appleEventClass.getDeclaredMethod("sendNoReply",
540                         new Class[]{  });
541    
542                   Field keyDirectObjectField = aeClass.getDeclaredField("keyDirectObject");
543                   keyDirectObject = (Integer)keyDirectObjectField.get(null);
544    
545                   Field autoGenerateReturnIDField = appleEventClass.getDeclaredField(
546                         "kAutoGenerateReturnID");
547                   kAutoGenerateReturnID = (Integer)autoGenerateReturnIDField.get(null);
548    
549                   Field anyTransactionIDField = appleEventClass.getDeclaredField(
550                         "kAnyTransactionID");
551                   kAnyTransactionID = (Integer)anyTransactionIDField.get(null);
552                } catch(ClassNotFoundException cnfe) {
553                   errorMessage = cnfe.getMessage();
554    
555                   return false;
556                } catch(NoSuchMethodException nsme) {
557                   errorMessage = nsme.getMessage();
558    
559                   return false;
560                } catch(NoSuchFieldException nsfe) {
561                   errorMessage = nsfe.getMessage();
562    
563                   return false;
564                } catch(IllegalAccessException iae) {
565                   errorMessage = iae.getMessage();
566    
567                   return false;
568                }
569    
570                break;
571    
572             case MRJ_2_1:
573    
574                try {
575                   mrjFileUtilsClass = Class.forName("com.apple.mrj.MRJFileUtils");
576                   mrjOSTypeClass = Class.forName("com.apple.mrj.MRJOSType");
577    
578                   Field systemFolderField = mrjFileUtilsClass.getDeclaredField(
579                         "kSystemFolderType");
580                   kSystemFolderType = systemFolderField.get(null);
581                   findFolder = mrjFileUtilsClass.getDeclaredMethod("findFolder",
582                         new Class[]{ mrjOSTypeClass });
583                   getFileCreator = mrjFileUtilsClass.getDeclaredMethod("getFileCreator",
584                         new Class[]{ File.class });
585                   getFileType = mrjFileUtilsClass.getDeclaredMethod("getFileType",
586                         new Class[]{ File.class });
587                } catch(ClassNotFoundException cnfe) {
588                   errorMessage = cnfe.getMessage();
589    
590                   return false;
591                } catch(NoSuchFieldException nsfe) {
592                   errorMessage = nsfe.getMessage();
593    
594                   return false;
595                } catch(NoSuchMethodException nsme) {
596                   errorMessage = nsme.getMessage();
597    
598                   return false;
599                } catch(SecurityException se) {
600                   errorMessage = se.getMessage();
601    
602                   return false;
603                } catch(IllegalAccessException iae) {
604                   errorMessage = iae.getMessage();
605    
606                   return false;
607                }
608    
609                break;
610    
611             case MRJ_3_0:
612    
613                try {
614                   Class linker = Class.forName("com.apple.mrj.jdirect.Linker");
615                   Constructor constructor = linker.getConstructor(new Class[]{ Class.class });
616                   linkage = constructor.newInstance(new Object[]{ BrowserLauncher.class });
617                } catch(ClassNotFoundException cnfe) {
618                   errorMessage = cnfe.getMessage();
619    
620                   return false;
621                } catch(NoSuchMethodException nsme) {
622                   errorMessage = nsme.getMessage();
623    
624                   return false;
625                } catch(InvocationTargetException ite) {
626                   errorMessage = ite.getMessage();
627    
628                   return false;
629                } catch(InstantiationException ie) {
630                   errorMessage = ie.getMessage();
631    
632                   return false;
633                } catch(IllegalAccessException iae) {
634                   errorMessage = iae.getMessage();
635    
636                   return false;
637                }
638    
639                break;
640    
641             case MRJ_3_1:
642    
643                try {
644                   mrjFileUtilsClass = Class.forName("com.apple.mrj.MRJFileUtils");
645                   openURL = mrjFileUtilsClass.getDeclaredMethod("openURL",
646                         new Class[]{ String.class });
647                } catch(ClassNotFoundException cnfe) {
648                   errorMessage = cnfe.getMessage();
649    
650                   return false;
651                } catch(NoSuchMethodException nsme) {
652                   errorMessage = nsme.getMessage();
653    
654                   return false;
655                }
656    
657                break;
658    
659             default:
660                break;
661          }
662    
663          return true;
664       }
665    
666       /**
667        * Attempts to locate the default web browser on the local system.  Caches results
668        * so it only locates the browser once for each use of this class per JVM instance.
669        *
670        * @return The browser for the system.  Note that this may not be what you would
671        *         consider     to be a standard web browser; instead, it's the application
672        *         that gets called to     open the default web browser.  In some cases,
673        *         this will be a non-String object     that provides the means of calling
674        *         the default browser.
675        */
676       private static Object locateBrowser() {
677          if(browser != null) {
678             return browser;
679          }
680    
681          switch(jvm) {
682             case MRJ_2_0:
683    
684                try {
685                   Integer finderCreatorCode = (Integer)makeOSType.invoke(null,
686                         new Object[]{ FINDER_CREATOR });
687                   Object aeTarget = aeTargetConstructor.newInstance(new Object[]{
688                            finderCreatorCode
689                         });
690                   Integer gurlType = (Integer)makeOSType.invoke(null,
691                         new Object[]{ GURL_EVENT });
692                   Object appleEvent = appleEventConstructor.newInstance(new Object[]{
693                            gurlType, gurlType, aeTarget, kAutoGenerateReturnID,
694                            kAnyTransactionID
695                         });
696    
697                   // Don't set browser = appleEvent because then the next time we call
698                   // locateBrowser(), we'll get the same AppleEvent, to which we'll already have
699                   // added the relevant parameter. Instead, regenerate the AppleEvent every time.
700                   // There's probably a way to do this better; if any has any ideas, please let
701                   // me know.
702                   return appleEvent;
703                } catch(IllegalAccessException iae) {
704                   browser = null;
705                   errorMessage = iae.getMessage();
706    
707                   return browser;
708                } catch(InstantiationException ie) {
709                   browser = null;
710                   errorMessage = ie.getMessage();
711    
712                   return browser;
713                } catch(InvocationTargetException ite) {
714                   browser = null;
715                   errorMessage = ite.getMessage();
716    
717                   return browser;
718                }
719    
720             case MRJ_2_1:
721    
722                File systemFolder;
723    
724                try {
725                   systemFolder = (File)findFolder.invoke(null,
726                         new Object[]{ kSystemFolderType });
727                } catch(IllegalArgumentException iare) {
728                   browser = null;
729                   errorMessage = iare.getMessage();
730    
731                   return browser;
732                } catch(IllegalAccessException iae) {
733                   browser = null;
734                   errorMessage = iae.getMessage();
735    
736                   return browser;
737                } catch(InvocationTargetException ite) {
738                   browser = null;
739                   errorMessage = ite.getTargetException().getClass() + ": " +
740                      ite.getTargetException().getMessage();
741    
742                   return browser;
743                }
744    
745                String[] systemFolderFiles = systemFolder.list();
746    
747                // Avoid a FilenameFilter because that can't be stopped mid-list
748                for(int i = 0; i < systemFolderFiles.length; i++) {
749                   try {
750                      File file = new File(systemFolder, systemFolderFiles[i]);
751    
752                      if(!file.isFile()) {
753                         continue;
754                      }
755    
756                      // We're looking for a file with a creator code of 'MACS' and
757                      // a type of 'FNDR'.  Only requiring the type results in non-Finder
758                      // applications being picked up on certain Mac OS 9 systems,
759                      // especially German ones, and sending a GURL event to those
760                      // applications results in a logout under Multiple Users.
761                      Object fileType = getFileType.invoke(null, new Object[]{ file });
762    
763                      if(FINDER_TYPE.equals(fileType.toString())) {
764                         Object fileCreator = getFileCreator.invoke(null,
765                               new Object[]{ file });
766    
767                         if(FINDER_CREATOR.equals(fileCreator.toString())) {
768                            browser = file.toString(); // Actually the Finder, but that's OK
769    
770                            return browser;
771                         }
772                      }
773                   } catch(IllegalArgumentException iare) {
774                      browser = browser;
775                      errorMessage = iare.getMessage();
776    
777                      return null;
778                   } catch(IllegalAccessException iae) {
779                      browser = null;
780                      errorMessage = iae.getMessage();
781    
782                      return browser;
783                   } catch(InvocationTargetException ite) {
784                      browser = null;
785                      errorMessage = ite.getTargetException().getClass() + ": " +
786                         ite.getTargetException().getMessage();
787    
788                      return browser;
789                   }
790                }
791    
792                browser = null;
793    
794                break;
795    
796             case MRJ_3_0:
797             case MRJ_3_1:
798                browser = ""; // Return something non-null
799    
800                break;
801    
802             case WINDOWS_NT:
803                browser = "cmd.exe";
804    
805                break;
806    
807             case WINDOWS_9x:
808                browser = "command.com";
809    
810                break;
811    
812             case OTHER:default:
813                browser = "netscape";
814    
815                break;
816          }
817    
818          return browser;
819       }
820    }