Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.util

File BrowserLauncher.java

 

Coverage histogram

../../img/srcFileCovDistChart0.png
56% of files have more coverage

Code metrics

38
235
6
1
947
548
77
0.33
39.17
6
12.83

Classes

Class Line # Actions
BrowserLauncher 82 235 77 279
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
7    * Jalview is free software: you can redistribute it and/or
8    * modify it under the terms of the GNU General Public License
9    * as published by the Free Software Foundation, either version 3
10    * of the License, or (at your option) any later version.
11    *
12    * Jalview is distributed in the hope that it will be useful, but
13    * WITHOUT ANY WARRANTY; without even the implied warranty
14    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15    * PURPOSE. See the GNU General Public License for more details.
16    *
17    * You should have received a copy of the GNU General Public License
18    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19    * The Jalview Authors are detailed in the 'AUTHORS' file.
20    */
21    package jalview.util;
22   
23    import java.io.File;
24    import java.io.IOException;
25    import java.lang.reflect.Constructor;
26    import java.lang.reflect.Field;
27    import java.lang.reflect.InvocationTargetException;
28    import java.lang.reflect.Method;
29   
30    /**
31    * BrowserLauncher is a class that provides one static method, openURL, which
32    * opens the default web browser for the current user of the system to the given
33    * URL. It may support other protocols depending on the system -- mailto, ftp,
34    * etc. -- but that has not been rigorously tested and is not guaranteed to
35    * work.
36    * <p>
37    * Yes, this is platform-specific code, and yes, it may rely on classes on
38    * certain platforms that are not part of the standard JDK. What we're trying to
39    * do, though, is to take something that's frequently desirable but inherently
40    * platform-specific -- opening a default browser -- and allow programmers (you,
41    * for example) to do so without worrying about dropping into native code or
42    * doing anything else similarly evil.
43    * <p>
44    * Anyway, this code is completely in Java and will run on all JDK 1.1-compliant
45    * systems without modification or a need for additional libraries. All classes
46    * that are required on certain platforms to allow this to run are dynamically
47    * loaded at runtime via reflection and, if not found, will not cause this to do
48    * anything other than returning an error when opening the browser.
49    * <p>
50    * There are certain system requirements for this class, as it's running through
51    * Runtime.exec(), which is Java's way of making a native system call.
52    * Currently, this requires that a Macintosh have a Finder which supports the
53    * GURL event, which is true for Mac OS 8.0 and 8.1 systems that have the
54    * Internet Scripting AppleScript dictionary installed in the Scripting
55    * Additions folder in the Extensions folder (which is installed by default as
56    * far as I know under Mac OS 8.0 and 8.1), and for all Mac OS 8.5 and later
57    * systems. On Windows, it only runs under Win32 systems (Windows 95, 98, and NT
58    * 4.0, as well as later versions of all). On other systems, this drops back
59    * from the inherently platform-sensitive concept of a default browser and
60    * simply attempts to launch Netscape via a shell command.
61    * <p>
62    * This code is Copyright 1999-2001 by Eric Albert (ejalbert\@cs.stanford.edu)
63    * and may be redistributed or modified in any form without restrictions as long
64    * as the portion of this comment from this paragraph through the end of the
65    * comment is not removed. The author requests that he be notified of any
66    * application, applet, or other binary that makes use of this code, but that's
67    * more out of curiosity than anything and is not required. This software
68    * includes no warranty. The author is not repsonsible for any loss of data or
69    * functionality or any adverse or unexpected effects of using this software.
70    * <p>
71    * Credits: <br>
72    * Steven Spencer, JavaWorld magazine
73    * (<a href="http://www.javaworld.com/javaworld/javatips/jw-javatip66.html">Java
74    * Tip 66</a>) <br>
75    * Thanks also to Ron B. Yeh, Eric Shapiro, Ben Engber, Paul Teitlebaum, Andrea
76    * Cantatore, Larry Barowski, Trevor Bedzek, Frank Miedrich, and Ron Rabakukk
77    *
78    * @author Eric Albert (<a href=
79    * "mailto:ejalbert@cs.stanford.edu">ejalbert@cs.stanford.edu</a>)
80    * @version 1.4b1 (Released June 20, 2001)
81    */
 
82    public class BrowserLauncher
83    {
84    /**
85    * The Java virtual machine that we are running on. Actually, in most cases we
86    * only care about the operating system, but some operating systems require us
87    * to switch on the VM.
88    */
89    private static int jvm;
90   
91    /** The browser for the system */
92    private static Object browser;
93   
94    /**
95    * Caches whether any classes, methods, and fields that are not part of the
96    * JDK and need to be dynamically loaded at runtime loaded successfully.
97    * <p>
98    * Note that if this is <code>false</code>, <code>openURL()</code> will always
99    * return an IOException.
100    */
101    private static boolean loadedWithoutErrors;
102   
103    /** The com.apple.mrj.MRJFileUtils class */
104    private static Class mrjFileUtilsClass;
105   
106    /** The com.apple.mrj.MRJOSType class */
107    private static Class mrjOSTypeClass;
108   
109    /** The com.apple.MacOS.AEDesc class */
110    private static Class aeDescClass;
111   
112    /** The &lt;init&gt;(int) method of com.apple.MacOS.AETarget */
113    private static Constructor aeTargetConstructor;
114   
115    /** The &lt;init&gt;(int, int, int) method of com.apple.MacOS.AppleEvent */
116    private static Constructor appleEventConstructor;
117   
118    /** The &lt;init&gt;(String) method of com.apple.MacOS.AEDesc */
119    private static Constructor aeDescConstructor;
120   
121    /** The findFolder method of com.apple.mrj.MRJFileUtils */
122    private static Method findFolder;
123   
124    /** The getFileCreator method of com.apple.mrj.MRJFileUtils */
125    private static Method getFileCreator;
126   
127    /** The getFileType method of com.apple.mrj.MRJFileUtils */
128    private static Method getFileType;
129   
130    /** The openURL method of com.apple.mrj.MRJFileUtils */
131    private static Method openURL;
132   
133    /** The makeOSType method of com.apple.MacOS.OSUtils */
134    private static Method makeOSType;
135   
136    /** The putParameter method of com.apple.MacOS.AppleEvent */
137    private static Method putParameter;
138   
139    /** The sendNoReply method of com.apple.MacOS.AppleEvent */
140    private static Method sendNoReply;
141   
142    /** Actually an MRJOSType pointing to the System Folder on a Macintosh */
143    private static Object kSystemFolderType;
144   
145    /** The keyDirectObject AppleEvent parameter type */
146    private static Integer keyDirectObject;
147   
148    /** The kAutoGenerateReturnID AppleEvent code */
149    private static Integer kAutoGenerateReturnID;
150   
151    /** The kAnyTransactionID AppleEvent code */
152    private static Integer kAnyTransactionID;
153   
154    /** The linkage object required for JDirect 3 on Mac OS X. */
155    private static Object linkage;
156   
157    /** The framework to reference on Mac OS X */
158    private static final String JDirect_MacOSX = "/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/HIToolbox";
159   
160    /** JVM constant for MRJ 2.0 */
161    private static final int MRJ_2_0 = 0;
162   
163    /** JVM constant for MRJ 2.1 or later */
164    private static final int MRJ_2_1 = 1;
165   
166    /** JVM constant for Java on Mac OS X 10.0 (MRJ 3.0) */
167    private static final int MRJ_3_0 = 3;
168   
169    /** JVM constant for MRJ 3.1 */
170    private static final int MRJ_3_1 = 4;
171   
172    /** JVM constant for any Windows NT JVM */
173    private static final int WINDOWS_NT = 5;
174   
175    /** JVM constant for any Windows 9x JVM */
176    private static final int WINDOWS_9x = 6;
177   
178    /** JVM constant for any other platform */
179    private static final int OTHER = -1;
180   
181    /**
182    * The file type of the Finder on a Macintosh. Hardcoding "Finder" would keep
183    * non-U.S. English systems from working properly.
184    */
185    private static final String FINDER_TYPE = "FNDR";
186   
187    /**
188    * The creator code of the Finder on a Macintosh, which is needed to send
189    * AppleEvents to the application.
190    */
191    private static final String FINDER_CREATOR = "MACS";
192   
193    /** The name for the AppleEvent type corresponding to a GetURL event. */
194    private static final String GURL_EVENT = "GURL";
195   
196    /**
197    * The first parameter that needs to be passed into Runtime.exec() to open the
198    * default web browser on Windows.
199    */
200    private static final String FIRST_WINDOWS_PARAMETER = "/c";
201   
202    /** The second parameter for Runtime.exec() on Windows. */
203    private static final String SECOND_WINDOWS_PARAMETER = "start";
204   
205    /**
206    * The third parameter for Runtime.exec() on Windows. This is a "title"
207    * parameter that the command line expects. Setting this parameter allows URLs
208    * containing spaces to work.
209    */
210    private static final String THIRD_WINDOWS_PARAMETER = "\"\"";
211   
212    /**
213    * The shell parameters for Netscape that opens a given URL in an already-open
214    * copy of Netscape on many command-line systems.
215    */
216    private static final String NETSCAPE_REMOTE_PARAMETER = "-remote";
217   
218    private static final String NETSCAPE_OPEN_PARAMETER_START = "openURL(";
219   
220    private static final String NETSCAPE_OPEN_NEW_WINDOW = ", new-window";
221   
222    private static final String NETSCAPE_OPEN_PARAMETER_END = ")";
223   
224    /**
225    * The message from any exception thrown throughout the initialization
226    * process.
227    */
228    private static String errorMessage;
229   
230    /**
231    * An initialization block that determines the operating system and loads the
232    * necessary runtime data.
233    */
 
234  0 toggle static
235    {
236   
237  0 loadedWithoutErrors = true;
238   
239    /**
240    *
241    * @j2sNative
242    *
243    */
244    {
245  0 String osName = System.getProperty("os.name");
246   
247  0 if (osName.startsWith("Mac OS"))
248    {
249  0 String mrjVersion = System.getProperty("mrj.version");
250  0 String majorMRJVersion;
251  0 if (mrjVersion == null)
252    {
253    // must be on some later build with mrj support
254  0 majorMRJVersion = "3.1";
255    }
256    else
257    {
258  0 majorMRJVersion = mrjVersion.substring(0, 3);
259    }
260   
261  0 try
262    {
263  0 double version = Double.valueOf(majorMRJVersion).doubleValue();
264   
265  0 if (version == 2)
266    {
267  0 jvm = MRJ_2_0;
268    }
269  0 else if ((version >= 2.1) && (version < 3))
270    {
271    // Assume that all 2.x versions of MRJ work the same. MRJ 2.1 actually
272    // works via Runtime.exec() and 2.2 supports that but has an openURL()
273    // method
274    // as well that we currently ignore.
275  0 jvm = MRJ_2_1;
276    }
277  0 else if (version == 3.0)
278    {
279  0 jvm = MRJ_3_0;
280    }
281  0 else if (version >= 3.1)
282    {
283    // Assume that all 3.1 and later versions of MRJ work the same.
284  0 jvm = MRJ_3_1;
285    }
286    else
287    {
288  0 loadedWithoutErrors = false;
289  0 errorMessage = "Unsupported MRJ version: " + version;
290    }
291    } catch (NumberFormatException nfe)
292    {
293  0 loadedWithoutErrors = false;
294  0 errorMessage = "Invalid MRJ version: " + mrjVersion;
295    }
296    }
297  0 else if (osName.startsWith("Windows"))
298    {
299  0 if (osName.indexOf("9") != -1)
300    {
301  0 jvm = WINDOWS_9x;
302    }
303    else
304    {
305  0 jvm = WINDOWS_NT;
306    }
307    }
308    else
309    {
310  0 jvm = OTHER;
311    }
312   
313  0 if (loadedWithoutErrors)
314    { // if we haven't hit any errors yet
315  0 loadedWithoutErrors = loadClasses();
316    }
317    }
318    }
319   
320    /**
321    * This class should be never be instantiated; this just ensures so.
322    */
 
323  0 toggle private BrowserLauncher()
324    {
325    }
326   
327    /**
328    * Called by a static initializer to load any classes, fields, and methods
329    * required at runtime to locate the user's web browser.
330    *
331    * @return <code>true</code> if all intialization succeeded <code>false</code>
332    * if any portion of the initialization failed
333    */
 
334  0 toggle private static boolean loadClasses()
335    {
336   
337    /**
338    * @j2sNative
339    *
340    */
341    {
342  0 switch (jvm)
343    {
344  0 case MRJ_2_0:
345   
346  0 try
347    {
348  0 Class aeTargetClass = Class.forName("com.apple.MacOS.AETarget");
349  0 Class osUtilsClass = Class.forName("com.apple.MacOS.OSUtils");
350  0 Class appleEventClass = Class.forName("com.apple.MacOS.AppleEvent");
351  0 Class aeClass = Class.forName("com.apple.MacOS.ae");
352  0 aeDescClass = Class.forName("com.apple.MacOS.AEDesc");
353   
354  0 aeTargetConstructor = aeTargetClass
355    .getDeclaredConstructor(new Class[]
356    { int.class });
357  0 appleEventConstructor = appleEventClass
358    .getDeclaredConstructor(new Class[]
359    { int.class, int.class, aeTargetClass, int.class,
360    int.class });
361  0 aeDescConstructor = aeDescClass
362    .getDeclaredConstructor(new Class[]
363    { String.class });
364   
365  0 makeOSType = osUtilsClass.getDeclaredMethod("makeOSType",
366    new Class[]
367    { String.class });
368  0 putParameter = appleEventClass.getDeclaredMethod("putParameter",
369    new Class[]
370    { int.class, aeDescClass });
371  0 sendNoReply = appleEventClass.getDeclaredMethod("sendNoReply",
372    new Class[] {});
373   
374  0 Field keyDirectObjectField = aeClass
375    .getDeclaredField("keyDirectObject");
376  0 keyDirectObject = (Integer) keyDirectObjectField.get(null);
377   
378  0 Field autoGenerateReturnIDField = appleEventClass
379    .getDeclaredField("kAutoGenerateReturnID");
380  0 kAutoGenerateReturnID = (Integer) autoGenerateReturnIDField
381    .get(null);
382   
383  0 Field anyTransactionIDField = appleEventClass
384    .getDeclaredField("kAnyTransactionID");
385  0 kAnyTransactionID = (Integer) anyTransactionIDField.get(null);
386    } catch (ClassNotFoundException cnfe)
387    {
388  0 errorMessage = cnfe.getMessage();
389   
390  0 return false;
391    } catch (NoSuchMethodException nsme)
392    {
393  0 errorMessage = nsme.getMessage();
394   
395  0 return false;
396    } catch (NoSuchFieldException nsfe)
397    {
398  0 errorMessage = nsfe.getMessage();
399   
400  0 return false;
401    } catch (IllegalAccessException iae)
402    {
403  0 errorMessage = iae.getMessage();
404   
405  0 return false;
406    }
407   
408  0 break;
409   
410  0 case MRJ_2_1:
411   
412  0 try
413    {
414  0 mrjFileUtilsClass = Class.forName("com.apple.mrj.MRJFileUtils");
415  0 mrjOSTypeClass = Class.forName("com.apple.mrj.MRJOSType");
416   
417  0 Field systemFolderField = mrjFileUtilsClass
418    .getDeclaredField("kSystemFolderType");
419  0 kSystemFolderType = systemFolderField.get(null);
420  0 findFolder = mrjFileUtilsClass.getDeclaredMethod("findFolder",
421    new Class[]
422    { mrjOSTypeClass });
423  0 getFileCreator = mrjFileUtilsClass
424    .getDeclaredMethod("getFileCreator", new Class[]
425    { File.class });
426  0 getFileType = mrjFileUtilsClass.getDeclaredMethod("getFileType",
427    new Class[]
428    { File.class });
429    } catch (ClassNotFoundException cnfe)
430    {
431  0 errorMessage = cnfe.getMessage();
432   
433  0 return false;
434    } catch (NoSuchFieldException nsfe)
435    {
436  0 errorMessage = nsfe.getMessage();
437   
438  0 return false;
439    } catch (NoSuchMethodException nsme)
440    {
441  0 errorMessage = nsme.getMessage();
442   
443  0 return false;
444    } catch (SecurityException se)
445    {
446  0 errorMessage = se.getMessage();
447   
448  0 return false;
449    } catch (IllegalAccessException iae)
450    {
451  0 errorMessage = iae.getMessage();
452   
453  0 return false;
454    }
455   
456  0 break;
457   
458  0 case MRJ_3_0:
459   
460  0 try
461    {
462  0 Class linker = Class.forName("com.apple.mrj.jdirect.Linker");
463  0 Constructor constructor = linker
464    .getConstructor(new Class[]
465    { Class.class });
466  0 linkage = constructor
467    .newInstance(new Object[]
468    { BrowserLauncher.class });
469    } catch (ClassNotFoundException cnfe)
470    {
471  0 errorMessage = cnfe.getMessage();
472   
473  0 return false;
474    } catch (NoSuchMethodException nsme)
475    {
476  0 errorMessage = nsme.getMessage();
477   
478  0 return false;
479    } catch (InvocationTargetException ite)
480    {
481  0 errorMessage = ite.getMessage();
482   
483  0 return false;
484    } catch (InstantiationException ie)
485    {
486  0 errorMessage = ie.getMessage();
487   
488  0 return false;
489    } catch (IllegalAccessException iae)
490    {
491  0 errorMessage = iae.getMessage();
492   
493  0 return false;
494    }
495   
496  0 break;
497   
498  0 case MRJ_3_1:
499   
500  0 try
501    {
502  0 mrjFileUtilsClass = Class.forName("com.apple.mrj.MRJFileUtils");
503  0 openURL = mrjFileUtilsClass.getDeclaredMethod("openURL",
504    new Class[]
505    { String.class });
506    } catch (ClassNotFoundException cnfe)
507    {
508  0 errorMessage = cnfe.getMessage();
509   
510  0 return false;
511    } catch (NoSuchMethodException nsme)
512    {
513  0 errorMessage = nsme.getMessage();
514   
515  0 return false;
516    }
517   
518  0 break;
519   
520  0 default:
521  0 break;
522    }
523   
524    }
525  0 return true;
526    }
527   
528    /**
529    * Attempts to locate the default web browser on the local system. s results
530    * so it only locates the browser once for each use of this class per JVM
531    * instance.
532    *
533    * @return The browser for the system. Note that this may not be what you
534    * would consider to be a standard web browser; instead, it's the
535    * application that gets called to open the default web browser. In
536    * some cases, this will be a non-String object that provides the
537    * means of calling the default browser.
538    */
 
539  0 toggle private static Object locateBrowser()
540    {
541    /**
542    * @j2sNative
543    *
544    */
545    {
546  0 if (browser != null)
547    {
548  0 return browser;
549    }
550   
551  0 switch (jvm)
552    {
553  0 case MRJ_2_0:
554   
555  0 try
556    {
557  0 Integer finderCreatorCode = (Integer) makeOSType.invoke(null,
558    new Object[]
559    { FINDER_CREATOR });
560  0 Object aeTarget = aeTargetConstructor
561    .newInstance(new Object[]
562    { finderCreatorCode });
563  0 Integer gurlType = (Integer) makeOSType.invoke(null,
564    new Object[]
565    { GURL_EVENT });
566  0 Object appleEvent = appleEventConstructor
567    .newInstance(new Object[]
568    { gurlType, gurlType, aeTarget, kAutoGenerateReturnID,
569    kAnyTransactionID });
570   
571    // Don't set browser = appleEvent because then the next time we call
572    // locateBrowser(), we'll get the same AppleEvent, to which we'll
573    // already have
574    // added the relevant parameter. Instead, regenerate the AppleEvent
575    // every time.
576    // There's probably a way to do this better; if any has any ideas,
577    // please let
578    // me know.
579  0 return appleEvent;
580    } catch (IllegalAccessException iae)
581    {
582  0 browser = null;
583  0 errorMessage = iae.getMessage();
584   
585  0 return browser;
586    } catch (InstantiationException ie)
587    {
588  0 browser = null;
589  0 errorMessage = ie.getMessage();
590   
591  0 return browser;
592    } catch (InvocationTargetException ite)
593    {
594  0 browser = null;
595  0 errorMessage = ite.getMessage();
596   
597  0 return browser;
598    }
599   
600  0 case MRJ_2_1:
601   
602  0 File systemFolder;
603   
604  0 try
605    {
606  0 systemFolder = (File) findFolder.invoke(null,
607    new Object[]
608    { kSystemFolderType });
609    } catch (IllegalArgumentException iare)
610    {
611  0 browser = null;
612  0 errorMessage = iare.getMessage();
613   
614  0 return browser;
615    } catch (IllegalAccessException iae)
616    {
617  0 browser = null;
618  0 errorMessage = iae.getMessage();
619   
620  0 return browser;
621    } catch (InvocationTargetException ite)
622    {
623  0 browser = null;
624  0 errorMessage = ite.getTargetException().getClass() + ": "
625    + ite.getTargetException().getMessage();
626   
627  0 return browser;
628    }
629   
630  0 String[] systemFolderFiles = systemFolder.list();
631   
632    // Avoid a FilenameFilter because that can't be stopped mid-list
633  0 for (int i = 0; i < systemFolderFiles.length; i++)
634    {
635  0 try
636    {
637  0 File file = new File(systemFolder, systemFolderFiles[i]);
638   
639  0 if (!file.isFile())
640    {
641  0 continue;
642    }
643   
644    // We're looking for a file with a creator code of 'MACS' and
645    // a type of 'FNDR'. Only requiring the type results in non-Finder
646    // applications being picked up on certain Mac OS 9 systems,
647    // especially German ones, and sending a GURL event to those
648    // applications results in a logout under Multiple Users.
649  0 Object fileType = getFileType.invoke(null, new Object[] { file });
650   
651  0 if (FINDER_TYPE.equals(fileType.toString()))
652    {
653  0 Object fileCreator = getFileCreator.invoke(null,
654    new Object[]
655    { file });
656   
657  0 if (FINDER_CREATOR.equals(fileCreator.toString()))
658    {
659  0 browser = file.toString(); // Actually the Finder, but that's OK
660   
661  0 return browser;
662    }
663    }
664    } catch (IllegalArgumentException iare)
665    {
666  0 errorMessage = iare.getMessage();
667   
668  0 return null;
669    } catch (IllegalAccessException iae)
670    {
671  0 browser = null;
672  0 errorMessage = iae.getMessage();
673   
674  0 return browser;
675    } catch (InvocationTargetException ite)
676    {
677  0 browser = null;
678  0 errorMessage = ite.getTargetException().getClass() + ": "
679    + ite.getTargetException().getMessage();
680   
681  0 return browser;
682    }
683    }
684   
685  0 browser = null;
686   
687  0 break;
688   
689  0 case MRJ_3_0:
690  0 case MRJ_3_1:
691  0 browser = ""; // Return something non-null
692   
693  0 break;
694   
695  0 case WINDOWS_NT:
696  0 browser = "cmd.exe";
697   
698  0 break;
699   
700  0 case WINDOWS_9x:
701  0 browser = "command.com";
702   
703  0 break;
704   
705  0 case OTHER:
706  0 default:
707  0 browser = jalview.bin.Cache.getDefault("DEFAULT_BROWSER", "firefox");
708   
709  0 break;
710    }
711   
712    }
713   
714  0 return browser;
715   
716    }
717   
718    /**
719    * used to ensure that browser is up-to-date after a configuration change
720    * (Unix DEFAULT_BROWSER property change).
721    */
 
722  0 toggle public static void resetBrowser()
723    {
724  0 browser = null;
725    }
726   
727    /**
728    * Attempts to open the default web browser to the given URL.
729    *
730    * @param url
731    * The URL to open
732    * @throws IOException
733    * If the web browser could not be located or does not run
734    */
 
735  0 toggle public static void openURL(String url) throws IOException
736    {
737   
738    /**
739    * @j2sNative
740    *
741    * window.open(url);
742    *
743    *
744    */
745   
746    {
747   
748  0 if (!loadedWithoutErrors)
749    {
750  0 throw new IOException(MessageManager
751    .formatMessage("exception.browser_not_found", new String[]
752    { errorMessage }));
753    }
754   
755  0 Object browser = locateBrowser();
756   
757  0 if (browser == null)
758    {
759  0 throw new IOException(MessageManager.formatMessage(
760    "exception.browser_unable_to_locate", new String[]
761    { errorMessage }));
762    }
763   
764  0 switch (jvm)
765    {
766  0 case MRJ_2_0:
767   
768  0 Object aeDesc = null;
769   
770  0 try
771    {
772  0 aeDesc = aeDescConstructor.newInstance(new Object[] { url });
773  0 putParameter.invoke(browser,
774    new Object[]
775    { keyDirectObject, aeDesc });
776  0 sendNoReply.invoke(browser, new Object[] {});
777    } catch (InvocationTargetException ite)
778    {
779  0 throw new IOException(MessageManager.formatMessage(
780    "exception.invocation_target_exception_creating_aedesc",
781    new String[]
782    { ite.getMessage() }));
783    } catch (IllegalAccessException iae)
784    {
785  0 throw new IOException(MessageManager.formatMessage(
786    "exception.illegal_access_building_apple_evt", new String[]
787    { iae.getMessage() }));
788    } catch (InstantiationException ie)
789    {
790  0 throw new IOException(MessageManager.formatMessage(
791    "exception.illegal_access_building_apple_evt", new String[]
792    { ie.getMessage() }));
793    } finally
794    {
795  0 aeDesc = null; // Encourage it to get disposed if it was created
796  0 browser = null; // Ditto
797    }
798   
799  0 break;
800   
801  0 case MRJ_2_1:
802  0 Runtime.getRuntime().exec(new String[] { (String) browser, url });
803   
804  0 break;
805   
806  0 case MRJ_3_0:
807   
808  0 int[] instance = new int[1];
809  0 int result = ICStart(instance, 0);
810   
811  0 if (result == 0)
812    {
813  0 int[] selectionStart = new int[] { 0 };
814  0 byte[] urlBytes = url.getBytes();
815  0 int[] selectionEnd = new int[] { urlBytes.length };
816  0 result = ICLaunchURL(instance[0], new byte[] { 0 }, urlBytes,
817    urlBytes.length, selectionStart, selectionEnd);
818   
819  0 if (result == 0)
820    {
821    // Ignore the return value; the URL was launched successfully
822    // regardless of what happens here.
823  0 ICStop(instance);
824    }
825    else
826    {
827  0 throw new IOException(MessageManager.formatMessage(
828    "exception.unable_to_launch_url", new String[]
829    { Integer.valueOf(result).toString() }));
830    }
831    }
832    else
833    {
834  0 throw new IOException(MessageManager.formatMessage(
835    "exception.unable_to_create_internet_config", new String[]
836    { Integer.valueOf(result).toString() }));
837    }
838   
839  0 break;
840   
841  0 case MRJ_3_1:
842   
843  0 try
844    {
845  0 openURL.invoke(null, new Object[] { url });
846    } catch (InvocationTargetException ite)
847    {
848  0 throw new IOException(MessageManager.formatMessage(
849    "exception.invocation_target_calling_url", new String[]
850    { ite.getMessage() }));
851    } catch (IllegalAccessException iae)
852    {
853  0 throw new IOException(MessageManager.formatMessage(
854    "exception.illegal_access_calling_url", new String[]
855    { iae.getMessage() }));
856    }
857   
858  0 break;
859   
860  0 case WINDOWS_NT:
861  0 case WINDOWS_9x:
862   
863    // Add quotes around the URL to allow ampersands and other special
864    // characters to work.
865  0 Process process = Runtime.getRuntime()
866    .exec(new String[]
867    { (String) browser, FIRST_WINDOWS_PARAMETER,
868    SECOND_WINDOWS_PARAMETER, THIRD_WINDOWS_PARAMETER,
869    '"' + url + '"' });
870   
871    // This avoids a memory leak on some versions of Java on Windows.
872    // That's hinted at in
873    // <http://developer.java.sun.com/developer/qow/archive/68/>.
874  0 try
875    {
876  0 process.waitFor();
877  0 process.exitValue();
878    } catch (InterruptedException ie)
879    {
880  0 throw new IOException(MessageManager.formatMessage(
881    "exception.interrupted_launching_browser", new String[]
882    { ie.getMessage() }));
883    }
884   
885  0 break;
886   
887  0 case OTHER:
888   
889    // Assume that we're on Unix and that Netscape (actually Firefox) is
890    // installed
891    // First, attempt to open the URL in a currently running session of
892    // Netscape
893    // JBPNote log debug
894   
895    /*
896    * System.out.println("Executing : "+browser+" "+
897    * NETSCAPE_REMOTE_PARAMETER+" "+ NETSCAPE_OPEN_PARAMETER_START + url +
898    * NETSCAPE_OPEN_NEW_WINDOW + NETSCAPE_OPEN_PARAMETER_END);
899    */
900  0 process = Runtime.getRuntime()
901    .exec(new String[]
902    { (String) browser, NETSCAPE_REMOTE_PARAMETER,
903   
904    NETSCAPE_OPEN_PARAMETER_START + url
905    + NETSCAPE_OPEN_NEW_WINDOW
906    + NETSCAPE_OPEN_PARAMETER_END });
907   
908  0 try
909    {
910  0 int exitCode = process.waitFor();
911   
912  0 if (exitCode != 0)
913    { // if Netscape was not open
914  0 Runtime.getRuntime().exec(new String[] { (String) browser, url });
915    }
916    } catch (InterruptedException ie)
917    {
918  0 throw new IOException(MessageManager.formatMessage(
919    "exception.interrupted_launching_browser", new String[]
920    { ie.getMessage() }));
921    }
922   
923  0 break;
924   
925  0 default:
926   
927    // This should never occur, but if it does, we'll try the simplest thing
928    // possible
929  0 Runtime.getRuntime().exec(new String[] { (String) browser, url });
930   
931  0 break;
932    }
933    }
934    }
935   
936   
937    /**
938    * Methods required for Mac OS X. The presence of native methods does not
939    * cause any problems on other platforms.
940    */
941    private native static int ICStart(int[] instance, int signature);
942   
943    private native static int ICStop(int[] instance);
944   
945    private native static int ICLaunchURL(int instance, byte[] hint,
946    byte[] data, int len, int[] selectionStart, int[] selectionEnd);
947    }