KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sslexplorer > boot > BrowserLauncher


1 package com.sslexplorer.boot;
2
3 import java.io.BufferedReader JavaDoc;
4 import java.io.File JavaDoc;
5 import java.io.IOException JavaDoc;
6 import java.io.InputStream JavaDoc;
7 import java.io.InputStreamReader JavaDoc;
8 import java.lang.reflect.Constructor JavaDoc;
9 import java.lang.reflect.Field JavaDoc;
10 import java.lang.reflect.InvocationTargetException JavaDoc;
11 import java.lang.reflect.Method JavaDoc;
12
13 /**
14  * BrowserLauncher is a class that provides one static method, openURL, which
15  * opens the default web browser for the current user of the system to the given
16  * URL. It may support other protocols depending on the system -- mailto, ftp,
17  * etc. -- but that has not been rigorously tested and is not guaranteed to
18  * work.
19  * <p>
20  * Yes, this is platform-specific code, and yes, it may rely on classes on
21  * certain platforms that are not part of the standard JDK. What we're trying to
22  * do, though, is to take something that's frequently desirable but inherently
23  * platform-specific -- opening a default browser -- and allow programmers (you,
24  * for example) to do so without worrying about dropping into native code or
25  * doing anything else similarly evil.
26  * <p>
27  * Anyway, this code is completely in Java and will run on all JDK 1.1-compliant
28  * systems without modification or a need for additional libraries. All classes
29  * that are required on certain platforms to allow this to run are dynamically
30  * loaded at runtime via reflection and, if not found, will not cause this to do
31  * anything other than returning an error when opening the browser.
32  * <p>
33  * There are certain system requirements for this class, as it's running through
34  * Runtime.exec(), which is Java's way of making a native system call.
35  * Currently, this requires that a Macintosh have a Finder which supports the
36  * GURL event, which is true for Mac OS 8.0 and 8.1 systems that have the
37  * Internet Scripting AppleScript dictionary installed in the Scripting
38  * Additions folder in the Extensions folder (which is installed by default as
39  * far as I know under Mac OS 8.0 and 8.1), and for all Mac OS 8.5 and later
40  * systems. On Windows, it only runs under Win32 systems (Windows 95, 98, and NT
41  * 4.0, as well as later versions of all). On other systems, this drops back
42  * from the inherently platform-sensitive concept of a default browser and
43  * simply attempts to launch Netscape via a shell command.
44  * <p>
45  * This code is Copyright 1999-2002 by Eric Albert (ejalbert@cs.stanford.edu)
46  * and may be redistributed or modified in any form without restrictions as long
47  * as the portion of this comment from this paragraph through the end of the
48  * comment is not removed. The author requests that he be notified of any
49  * application, applet, or other binary that makes use of this code, but that's
50  * more out of curiosity than anything and is not required. This software
51  * includes no warranty. The author is not repsonsible for any loss of data or
52  * functionality or any adverse or unexpected effects of using this software.
53  * <p>
54  * Credits: <br>
55  * Steven Spencer, JavaWorld magazine ( <a
56  * HREF="http://www.javaworld.com/javaworld/javatips/jw-javatip66.html">Java Tip
57  * 66 </a>) <br>
58  * Thanks also to Ron B. Yeh, Eric Shapiro, Ben Engber, Paul Teitlebaum, Andrea
59  * Cantatore, Larry Barowski, Trevor Bedzek, Frank Miedrich, Ron Rabakukk, and
60  * Glenn Vanderburg
61  *
62  * @author Eric Albert ( <a
63  * HREF="mailto:ejalbert@cs.stanford.edu">ejalbert@cs.stanford.edu </a>)
64  *
65  * <p>
66  * Additional
67  * <p>
68  * <p>
69  * Changes made by Brett Smith ( <a HREF="mailto:brett@3sp.com">brett@3sp,com.
70  * </a> to allow a command to be specified that will always be used. Copyright
71  * 2004 3SP <a HREF="http://3sp.com">3SP </a>.
72  */

73 public class BrowserLauncher {
74
75     /**
76      * The Java virtual machine that we are running on. Actually, in most cases
77      * we only care about the operating system, but some operating systems
78      * require us to switch on the VM.
79      */

80     private static int jvm;
81
82     /** The browser for the system */
83     private static Object JavaDoc browser;
84
85     /** Browser command to always use */
86     private static String JavaDoc browserCommand;
87
88     /**
89      * Caches whether any classes, methods, and fields that are not part of the
90      * JDK and need to be dynamically loaded at runtime loaded successfully.
91      * <p>
92      * Note that if this is <code>false</code>,<code>openURL()</code> will
93      * always return an IOException.
94      */

95     private static boolean loadedWithoutErrors;
96
97     /** The com.apple.mrj.MRJFileUtils class */
98     private static Class JavaDoc mrjFileUtilsClass;
99
100     /** The com.apple.mrj.MRJOSType class */
101     private static Class JavaDoc mrjOSTypeClass;
102
103     /** The com.apple.MacOS.AEDesc class */
104     private static Class JavaDoc aeDescClass;
105
106     /** The <init>(int) method of com.apple.MacOS.AETarget */
107     private static Constructor JavaDoc aeTargetConstructor;
108
109     /** The <init>(int, int, int) method of com.apple.MacOS.AppleEvent */
110     private static Constructor JavaDoc appleEventConstructor;
111
112     /** The <init>(String) method of com.apple.MacOS.AEDesc */
113     private static Constructor JavaDoc aeDescConstructor;
114
115     /** The findFolder method of com.apple.mrj.MRJFileUtils */
116     private static Method JavaDoc findFolder;
117
118     /** The getFileCreator method of com.apple.mrj.MRJFileUtils */
119     private static Method JavaDoc getFileCreator;
120
121     /** The getFileType method of com.apple.mrj.MRJFileUtils */
122     private static Method JavaDoc getFileType;
123
124     /** The openURL method of com.apple.mrj.MRJFileUtils */
125     private static Method JavaDoc openURL;
126
127     /** The makeOSType method of com.apple.MacOS.OSUtils */
128     private static Method JavaDoc makeOSType;
129
130     /** The putParameter method of com.apple.MacOS.AppleEvent */
131     private static Method JavaDoc putParameter;
132
133     /** The sendNoReply method of com.apple.MacOS.AppleEvent */
134     private static Method JavaDoc sendNoReply;
135
136     /** Actually an MRJOSType pointing to the System Folder on a Macintosh */
137     private static Object JavaDoc kSystemFolderType;
138
139     /** The keyDirectObject AppleEvent parameter type */
140     private static Integer JavaDoc keyDirectObject;
141
142     /** The kAutoGenerateReturnID AppleEvent code */
143     private static Integer JavaDoc kAutoGenerateReturnID;
144
145     /** The kAnyTransactionID AppleEvent code */
146     private static Integer JavaDoc kAnyTransactionID;
147
148     /** The linkage object required for JDirect 3 on Mac OS X */
149     // private static Object linkage;
150
/** The framework to reference on Mac OS X 10.0.x */
151     // private static final String JDirect_MacOSX =
152
// "/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/HIToolbox";
153
/** JVM constant for MRJ 2.0 */
154     private static final int MRJ_2_0 = 0;
155
156     /** JVM constant for MRJ 2.1.x and 2.2.x */
157     private static final int MRJ_2_1 = 1;
158
159     /** JVM constant for Java on Mac OS X 10.0 (MRJ 3.0) */
160     private static final int MRJ_3_0 = 3;
161
162     /** JVM constant for Java 1.3.x on Mac OS X 10.1 and later (MRJ 3.1 and 3.2) */
163     private static final int MRJ_3_1 = 4;
164
165     /** JVM constant for Java 1.4.x and later on Mac OS X */
166     private static final int MRJ_COCOA = 5;
167
168     /** JVM constant for any Windows NT JVM */
169     private static final int WINDOWS_NT = 6;
170
171     /** JVM constant for any Windows 9x JVM */
172     private static final int WINDOWS_9x = 7;
173
174     /** JVM constant for any other platform */
175     private static final int OTHER = -1;
176
177     /**
178      * The file type of the Finder on a Macintosh. Hardcoding "Finder" would
179      * keep non-U.S. English systems from working properly.
180      */

181     private static final String JavaDoc FINDER_TYPE = "FNDR";
182
183     /**
184      * The creator code of the Finder on a Macintosh, which is needed to send
185      * AppleEvents to the application.
186      */

187     private static final String JavaDoc FINDER_CREATOR = "MACS";
188
189     /** The name for the AppleEvent type corresponding to a GetURL event. */
190     private static final String JavaDoc GURL_EVENT = "GURL";
191
192     /**
193      * The first parameter that needs to be passed into Runtime.exec() to open
194      * the default web browser on Windows.
195      */

196     private static final String JavaDoc FIRST_WINDOWS_PARAMETER = "/c";
197
198     /** The second parameter for Runtime.exec() on Windows. */
199     private static final String JavaDoc SECOND_WINDOWS_PARAMETER = "start";
200
201     /**
202      * The third parameter for Runtime.exec() on Windows. This is a "title"
203      * parameter that the command line expects. Setting this parameter allows
204      * URLs containing spaces to work.
205      */

206     private static final String JavaDoc THIRD_WINDOWS_PARAMETER = "\"\"";
207
208     /**
209      * The shell parameters for Netscape that opens a given URL in an
210      * already-open copy of Netscape on many command-line systems.
211      */

212     private static final String JavaDoc NETSCAPE_REMOTE_PARAMETER = "-remote";
213     private static final String JavaDoc NETSCAPE_OPEN_PARAMETER_START = "'openURL(";
214     private static final String JavaDoc NETSCAPE_OPEN_PARAMETER_END = ")'";
215
216     /**
217      * The message from any exception thrown throughout the initialization
218      * process.
219      */

220     private static String JavaDoc errorMessage;
221
222     /**
223      * An initialization block that determines the operating system and loads
224      * the necessary runtime data.
225      */

226     static {
227         loadedWithoutErrors = true;
228         String JavaDoc osName = System.getProperty("os.name");
229         if (osName.startsWith("Mac OS")) {
230             String JavaDoc javaVersion = System.getProperty("java.version");
231             String JavaDoc majorJavaVersion = javaVersion.substring(0, 3);
232             try {
233                 double version = Double.valueOf(majorJavaVersion).doubleValue();
234                 if (version >= 1.4) {
235                     jvm = MRJ_COCOA;
236                 }
237             } catch (NumberFormatException JavaDoc nfe) {
238                 // Fall through to earlier versions of Java on the Mac.
239
}
240             if (jvm != MRJ_COCOA) {
241                 String JavaDoc mrjVersion = System.getProperty("mrj.version");
242                 String JavaDoc majorMRJVersion = mrjVersion.substring(0, 3);
243                 try {
244                     double version = Double.valueOf(majorMRJVersion).doubleValue();
245                     if (version == 2) {
246                         jvm = MRJ_2_0;
247                     } else if (version >= 2.1 && version < 3) {
248                         // Assume that all post-2.1 2.x versions of MRJ work the
249
// same. MRJ
250
// 2.1 actually
251
// works via Runtime.exec() and 2.2 supports that but
252
// has an
253
// openURL() method
254
// as well that we don't use because the Runtime.exec()
255
// method works
256
// fine.
257
jvm = MRJ_2_1;
258                     } else if (version == 3.0) {
259                         jvm = MRJ_3_0;
260                     } else if (version >= 3.1) {
261                         // Assume that all 3.1 and later versions of MRJ work
262
// the same.
263
jvm = MRJ_3_1;
264                     } else {
265                         loadedWithoutErrors = false;
266                         errorMessage = "Unsupported MRJ version: " + version;
267                     }
268                 } catch (NumberFormatException JavaDoc nfe) {
269                     loadedWithoutErrors = false;
270                     errorMessage = "Invalid MRJ version: " + mrjVersion;
271                 }
272             }
273         } else if (osName.startsWith("Windows")) {
274             if (osName.indexOf("9") != -1 || osName.indexOf("Me") != -1) {
275                 jvm = WINDOWS_9x;
276             } else {
277                 jvm = WINDOWS_NT;
278             }
279         } else {
280             jvm = OTHER;
281         }
282
283         if (loadedWithoutErrors) { // if we haven't hit any errors yet
284
loadedWithoutErrors = loadClasses();
285         }
286     }
287
288     /**
289      * This class should be never be instantiated; this just ensures so.
290      */

291     private BrowserLauncher() {
292     }
293
294     /**
295      * Manually set the browser command. If this is set then none the this
296      * command will <b>always </b> be used.
297      *
298      * @param browserCommand browser command
299      */

300     public static void setBrowserCommand(String JavaDoc browserCommand) {
301         BrowserLauncher.browserCommand = browserCommand;
302     }
303
304     /**
305      * Called by a static initializer to load any classes, fields, and methods
306      * required at runtime to locate the user's web browser.
307      *
308      * @return <code>true</code> if all intialization succeeded
309      * <code>false</code> if any portion of the initialization failed
310      */

311     private static boolean loadClasses() {
312         switch (jvm) {
313             case MRJ_2_0:
314                 return loadMRJ20Classes();
315             case MRJ_2_1:
316                 try {
317                     mrjFileUtilsClass = Class.forName("com.apple.mrj.MRJFileUtils");
318                     mrjOSTypeClass = Class.forName("com.apple.mrj.MRJOSType");
319                     Field JavaDoc systemFolderField = mrjFileUtilsClass.getDeclaredField("kSystemFolderType");
320                     kSystemFolderType = systemFolderField.get(null);
321                     findFolder = mrjFileUtilsClass.getDeclaredMethod("findFolder", new Class JavaDoc[] {
322                         mrjOSTypeClass
323                     });
324                     getFileCreator = mrjFileUtilsClass.getDeclaredMethod("getFileCreator", new Class JavaDoc[] {
325                         File JavaDoc.class
326                     });
327                     getFileType = mrjFileUtilsClass.getDeclaredMethod("getFileType", new Class JavaDoc[] {
328                         File JavaDoc.class
329                     });
330                 } catch (ClassNotFoundException JavaDoc cnfe) {
331                     errorMessage = cnfe.getMessage();
332                     return false;
333                 } catch (NoSuchFieldException JavaDoc nsfe) {
334                     errorMessage = nsfe.getMessage();
335                     return false;
336                 } catch (NoSuchMethodException JavaDoc nsme) {
337                     errorMessage = nsme.getMessage();
338                     return false;
339                 } catch (SecurityException JavaDoc se) {
340                     errorMessage = se.getMessage();
341                     return false;
342                 } catch (IllegalAccessException JavaDoc iae) {
343                     errorMessage = iae.getMessage();
344                     return false;
345                 }
346                 break;
347             case MRJ_3_0:
348                 try {
349                     Class JavaDoc linker = Class.forName("com.apple.mrj.jdirect.Linker");
350                     Constructor JavaDoc constructor = linker.getConstructor(new Class JavaDoc[] {
351                         Class JavaDoc.class
352                     });
353                     constructor.newInstance(new Object JavaDoc[] {
354                         BrowserLauncher.class
355                     });
356                 } catch (ClassNotFoundException JavaDoc cnfe) {
357                     errorMessage = cnfe.getMessage();
358                     return false;
359                 } catch (NoSuchMethodException JavaDoc nsme) {
360                     errorMessage = nsme.getMessage();
361                     return false;
362                 } catch (InvocationTargetException JavaDoc ite) {
363                     errorMessage = ite.getMessage();
364                     return false;
365                 } catch (InstantiationException JavaDoc ie) {
366                     errorMessage = ie.getMessage();
367                     return false;
368                 } catch (IllegalAccessException JavaDoc iae) {
369                     errorMessage = iae.getMessage();
370                     return false;
371                 }
372                 break;
373             case MRJ_3_1:
374             case MRJ_COCOA:
375                 String JavaDoc className;
376                 if (jvm == MRJ_3_1) {
377                     className = "com.apple.mrj.MRJFileUtils";
378                 } else {
379                     className = "com.apple.eio.FileManager";
380                 }
381                 try {
382                     mrjFileUtilsClass = Class.forName(className);
383                     openURL = mrjFileUtilsClass.getDeclaredMethod("openURL", new Class JavaDoc[] {
384                         String JavaDoc.class
385                     });
386                 } catch (ClassNotFoundException JavaDoc cnfe) {
387                     errorMessage = cnfe.getMessage();
388                     return false;
389                 } catch (NoSuchMethodException JavaDoc nsme) {
390                     errorMessage = nsme.getMessage();
391                     return false;
392                 }
393                 break;
394             default:
395                 break;
396         }
397         return true;
398     }
399
400     /**
401      * Loads the classes, fields, and methods needed when running under MRJ 2.0.
402      * Sets <code>errorMessage</code> if it fails.
403      *
404      * @return <code>true</code> if all operations succeeded;
405      * <code>false</code> otherwise
406      */

407     private static boolean loadMRJ20Classes() {
408         try {
409             Class JavaDoc aeTargetClass = Class.forName("com.apple.MacOS.AETarget");
410             Class JavaDoc osUtilsClass = Class.forName("com.apple.MacOS.OSUtils");
411             Class JavaDoc appleEventClass = Class.forName("com.apple.MacOS.AppleEvent");
412             Class JavaDoc aeClass = Class.forName("com.apple.MacOS.ae");
413             aeDescClass = Class.forName("com.apple.MacOS.AEDesc");
414
415             aeTargetConstructor = aeTargetClass.getDeclaredConstructor(new Class JavaDoc[] {
416                 int.class
417             });
418             appleEventConstructor = appleEventClass.getDeclaredConstructor(new Class JavaDoc[] {
419                             int.class, int.class, aeTargetClass, int.class, int.class
420             });
421             aeDescConstructor = aeDescClass.getDeclaredConstructor(new Class JavaDoc[] {
422                 String JavaDoc.class
423             });
424
425             makeOSType = osUtilsClass.getDeclaredMethod("makeOSType", new Class JavaDoc[] {
426                 String JavaDoc.class
427             });
428             putParameter = appleEventClass.getDeclaredMethod("putParameter", new Class JavaDoc[] {
429                             int.class, aeDescClass
430             });
431             sendNoReply = appleEventClass.getDeclaredMethod("sendNoReply", new Class JavaDoc[] {});
432
433             Field JavaDoc keyDirectObjectField = aeClass.getDeclaredField("keyDirectObject");
434             keyDirectObject = (Integer JavaDoc) keyDirectObjectField.get(null);
435             Field JavaDoc autoGenerateReturnIDField = appleEventClass.getDeclaredField("kAutoGenerateReturnID");
436             kAutoGenerateReturnID = (Integer JavaDoc) autoGenerateReturnIDField.get(null);
437             Field JavaDoc anyTransactionIDField = appleEventClass.getDeclaredField("kAnyTransactionID");
438             kAnyTransactionID = (Integer JavaDoc) anyTransactionIDField.get(null);
439         } catch (ClassNotFoundException JavaDoc cnfe) {
440             errorMessage = cnfe.getMessage();
441             return false;
442         } catch (NoSuchMethodException JavaDoc nsme) {
443             errorMessage = nsme.getMessage();
444             return false;
445         } catch (NoSuchFieldException JavaDoc nsfe) {
446             errorMessage = nsfe.getMessage();
447             return false;
448         } catch (IllegalAccessException JavaDoc iae) {
449             errorMessage = iae.getMessage();
450             return false;
451         }
452         return true;
453     }
454
455     /**
456      * Attempts to locate the default web browser on the local system. Caches
457      * the result so it only locates the browser once for each use of this class
458      * per JVM instance.
459      *
460      * @return The browser for the system. Note that this may not be what you
461      * would consider to be a standard web browser; instead, it's the
462      * application that gets called to open the default web browser. In
463      * some cases this will be a non-String object that provides the
464      * means of calling the default browser.
465      */

466     private static Object JavaDoc locateBrowser() {
467         if (browser != null) {
468             return browser;
469         }
470         switch (jvm) {
471             case MRJ_2_0:
472                 try {
473                     Integer JavaDoc finderCreatorCode = (Integer JavaDoc) makeOSType.invoke(null, new Object JavaDoc[] {
474                         FINDER_CREATOR
475                     });
476                     Object JavaDoc aeTarget = aeTargetConstructor.newInstance(new Object JavaDoc[] {
477                         finderCreatorCode
478                     });
479                     Integer JavaDoc gurlType = (Integer JavaDoc) makeOSType.invoke(null, new Object JavaDoc[] {
480                         GURL_EVENT
481                     });
482                     Object JavaDoc appleEvent = appleEventConstructor.newInstance(new Object JavaDoc[] {
483                                     gurlType, gurlType, aeTarget, kAutoGenerateReturnID, kAnyTransactionID
484                     });
485                     // Don't set browser = appleEvent because then the next time
486
// we call
487
// locateBrowser(), we'll get the same AppleEvent, to which
488
// we'll
489
// already have
490
// added the relevant parameter. Instead, regenerate the
491
// AppleEvent
492
// every time.
493
// There's probably a way to do this better; if any has any
494
// ideas,
495
// please let
496
// me know.
497
return appleEvent;
498                 } catch (IllegalAccessException JavaDoc iae) {
499                     browser = null;
500                     errorMessage = iae.getMessage();
501                     return browser;
502                 } catch (InstantiationException JavaDoc ie) {
503                     browser = null;
504                     errorMessage = ie.getMessage();
505                     return browser;
506                 } catch (InvocationTargetException JavaDoc ite) {
507                     browser = null;
508                     errorMessage = ite.getMessage();
509                     return browser;
510                 }
511             case MRJ_2_1:
512                 File JavaDoc systemFolder;
513                 try {
514                     systemFolder = (File JavaDoc) findFolder.invoke(null, new Object JavaDoc[] {
515                         kSystemFolderType
516                     });
517                 } catch (IllegalArgumentException JavaDoc iare) {
518                     browser = null;
519                     errorMessage = iare.getMessage();
520                     return browser;
521                 } catch (IllegalAccessException JavaDoc iae) {
522                     browser = null;
523                     errorMessage = iae.getMessage();
524                     return browser;
525                 } catch (InvocationTargetException JavaDoc ite) {
526                     browser = null;
527                     errorMessage = ite.getTargetException().getClass() + ": " + ite.getTargetException().getMessage();
528                     return browser;
529                 }
530                 String JavaDoc[] systemFolderFiles = systemFolder.list();
531                 // Avoid a FilenameFilter because that can't be stopped mid-list
532
for (int i = 0; i < systemFolderFiles.length; i++) {
533                     try {
534                         File JavaDoc file = new File JavaDoc(systemFolder, systemFolderFiles[i]);
535                         if (!file.isFile()) {
536                             continue;
537                         }
538                         // We're looking for a file with a creator code of
539
// 'MACS' and
540
// a type of 'FNDR'. Only requiring the type results in
541
// non-Finder
542
// applications being picked up on certain Mac OS 9
543
// systems,
544
// especially German ones, and sending a GURL event to
545
// those
546
// applications results in a logout under Multiple
547
// Users.
548
Object JavaDoc fileType = getFileType.invoke(null, new Object JavaDoc[] {
549                             file
550                         });
551                         if (FINDER_TYPE.equals(fileType.toString())) {
552                             Object JavaDoc fileCreator = getFileCreator.invoke(null, new Object JavaDoc[] {
553                                 file
554                             });
555                             if (FINDER_CREATOR.equals(fileCreator.toString())) {
556                                 browser = file.toString(); // Actually the
557
// Finder, but
558
// that's OK
559
return browser;
560                             }
561                         }
562                     } catch (IllegalArgumentException JavaDoc iare) {
563                         browser = null;
564                         errorMessage = iare.getMessage();
565                         return null;
566                     } catch (IllegalAccessException JavaDoc iae) {
567                         browser = null;
568                         errorMessage = iae.getMessage();
569                         return browser;
570                     } catch (InvocationTargetException JavaDoc ite) {
571                         browser = null;
572                         errorMessage = ite.getTargetException().getClass() + ": " + ite.getTargetException().getMessage();
573                         return browser;
574                     }
575                 }
576                 browser = null;
577                 break;
578             case MRJ_3_0:
579             case MRJ_3_1:
580                 browser = ""; // Return something non-null
581
break;
582             case WINDOWS_NT:
583                 browser = "cmd.exe";
584                 break;
585             case WINDOWS_9x:
586                 browser = "command.com";
587                 break;
588             case OTHER:
589             default:
590                 // On systems other than Windows and the Mac, we try via a
591
// rather Unix-
592
// specific hack to read the BROWSER environment variable
593
// <http://tuxedo.org/~esr/BROWSER/>. If we can't read that
594
// variable or
595
// it isn't set, we use Netscape.
596
// Note: This is commented out for now. It'll work soon.
597
browser = getEnvironmentBrowser();
598                 if (browser == null) {
599                     browser = "netscape";
600                 }
601                 break;
602         }
603         return browser;
604     }
605
606     /**
607      * Attempts to open the default web browser to the given URL.
608      *
609      * @param url The URL to open
610      * @throws IOException If the web browser could not be located or does not
611      * run
612      */

613     public static void openURL(String JavaDoc url) throws IOException JavaDoc {
614         if (!loadedWithoutErrors) {
615             throw new IOException JavaDoc("Exception in finding browser: " + errorMessage);
616         }
617
618         if (browserCommand != null) {
619             String JavaDoc[] cmd = Util.splitString(browserCommand, ' ', '"', '\\');
620             String JavaDoc[] cmde = new String JavaDoc[cmd.length + 1];
621             System.arraycopy(cmd, 0, cmde, 0, cmd.length);
622             cmde[cmd.length] = url;
623             Runtime.getRuntime().exec(cmde);
624             return;
625         }
626
627         Object JavaDoc browser = locateBrowser();
628         if (browser == null) {
629             throw new IOException JavaDoc("Unable to locate browser: " + errorMessage);
630         }
631
632         switch (jvm) {
633             case MRJ_2_0:
634                 Object JavaDoc aeDesc = null;
635                 try {
636                     aeDesc = aeDescConstructor.newInstance(new Object JavaDoc[] {
637                         url
638                     });
639                     putParameter.invoke(browser, new Object JavaDoc[] {
640                                     keyDirectObject, aeDesc
641                     });
642                     sendNoReply.invoke(browser, new Object JavaDoc[] {});
643                 } catch (InvocationTargetException JavaDoc ite) {
644                     throw new IOException JavaDoc("InvocationTargetException while creating AEDesc: " + ite.getMessage());
645                 } catch (IllegalAccessException JavaDoc iae) {
646                     throw new IOException JavaDoc("IllegalAccessException while building AppleEvent: " + iae.getMessage());
647                 } catch (InstantiationException JavaDoc ie) {
648                     throw new IOException JavaDoc("InstantiationException while creating AEDesc: " + ie.getMessage());
649                 } finally {
650                     aeDesc = null; // Encourage it to get disposed if it was
651
// created
652
browser = null; // Ditto
653
}
654                 break;
655             case MRJ_2_1:
656                 Runtime.getRuntime().exec(new String JavaDoc[] {
657                                 (String JavaDoc) browser, url
658                 });
659                 break;
660             case MRJ_3_0:
661                 int[] instance = new int[1];
662                 int result = ICStart(instance, 0);
663                 if (result == 0) {
664                     int[] selectionStart = new int[] {
665                         0
666                     };
667                     byte[] urlBytes = url.getBytes();
668                     int[] selectionEnd = new int[] {
669                         urlBytes.length
670                     };
671                     result = ICLaunchURL(instance[0], new byte[] {
672                         0
673                     }, urlBytes, urlBytes.length, selectionStart, selectionEnd);
674                     if (result == 0) {
675                         // Ignore the return value; the URL was launched
676
// successfully
677
// regardless of what happens here.
678
ICStop(instance);
679                     } else {
680                         throw new IOException JavaDoc("Unable to launch URL: " + result);
681                     }
682                 } else {
683                     throw new IOException JavaDoc("Unable to create an Internet Config instance: " + result);
684                 }
685                 break;
686             case MRJ_3_1:
687             case MRJ_COCOA:
688                 try {
689                     openURL.invoke(null, new Object JavaDoc[] {
690                         url
691                     });
692                 } catch (InvocationTargetException JavaDoc ite) {
693                     throw new IOException JavaDoc("InvocationTargetException while calling openURL: " + ite.getMessage());
694                 } catch (IllegalAccessException JavaDoc iae) {
695                     throw new IOException JavaDoc("IllegalAccessException while calling openURL: " + iae.getMessage());
696                 }
697                 break;
698             case WINDOWS_NT:
699             case WINDOWS_9x:
700                 // Add quotes around the URL to allow ampersands and other
701
// special
702
// characters to work.
703
String JavaDoc[] arguments;
704                 if (jvm == WINDOWS_9x) {
705                     arguments = new String JavaDoc[] {
706                                     (String JavaDoc) browser, FIRST_WINDOWS_PARAMETER, SECOND_WINDOWS_PARAMETER, null
707                     };
708                 } else {
709                     arguments = new String JavaDoc[] {
710                                     (String JavaDoc) browser, FIRST_WINDOWS_PARAMETER, SECOND_WINDOWS_PARAMETER, THIRD_WINDOWS_PARAMETER,
711                                     null
712                     };
713                 }
714                 arguments[arguments.length - 1] = '"' + url + '"';
715                 Process JavaDoc process = Runtime.getRuntime().exec(arguments);
716                 // This avoids a memory leak on some versions of Java on
717
// Windows.
718
// That's hinted at in
719
// <http://developer.java.sun.com/developer/qow/archive/68/>.
720
try {
721                     process.waitFor();
722                     process.exitValue();
723                 } catch (InterruptedException JavaDoc ie) {
724                     throw new IOException JavaDoc("InterruptedException while launching browser: " + ie.getMessage());
725                 }
726                 break;
727             case OTHER:
728                 // Assume that we're on Unix and that Netscape is installed
729

730                 // Attempt to open the URL in a currently running session of
731
// Netscape
732
process = Runtime.getRuntime().exec(
733                     new String JavaDoc[] {
734                                     (String JavaDoc) browser, NETSCAPE_REMOTE_PARAMETER,
735                                     NETSCAPE_OPEN_PARAMETER_START + url + NETSCAPE_OPEN_PARAMETER_END
736                     });
737                 try {
738                     int exitCode = process.waitFor();
739                     if (exitCode != 0) { // if Netscape was not open
740
Runtime.getRuntime().exec(new String JavaDoc[] {
741                                         (String JavaDoc) browser, url
742                         });
743                     }
744                 } catch (InterruptedException JavaDoc ie) {
745                     throw new IOException JavaDoc("InterruptedException while launching browser: " + ie.getMessage());
746                 }
747                 break;
748             default:
749                 // This should never occur, but if it does, we'll try the
750
// simplest thing
751
// possible
752
Runtime.getRuntime().exec(new String JavaDoc[] {
753                                 (String JavaDoc) browser, url
754                 });
755                 break;
756         }
757     }
758
759     /**
760      * Tries to read the BROWSER environment variable, which should be set to
761      * the absolute path of the user's preferred web browser as proposed at
762      * <http://tuxedo.org/~esr/BROWSER/>.
763      *
764      * @return The value of the BROWSER environment variable, or null if the
765      * variable does not exist or can't be read.
766      */

767     private static String JavaDoc getEnvironmentBrowser() {
768         String JavaDoc browser = null;
769         try {
770             String JavaDoc[] echoParams = {
771                             "/bin/sh", "-c", "echo ${BROWSER:-netscape}"
772             };
773             Process JavaDoc echoProcess = Runtime.getRuntime().exec(echoParams);
774             InputStream JavaDoc echoStream = echoProcess.getInputStream();
775             BufferedReader JavaDoc echoReader = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(echoStream));
776             browser = echoReader.readLine();
777             echoReader.close();
778         } catch (Throwable JavaDoc t) {
779             // If anything goes wrong, we'll return null.
780
}
781         return browser;
782     }
783
784     /*
785      * Methods required for Mac OS X 10.0.x. The presence of native methods does
786      * not cause any problems on other platforms.
787      */

788     private native static int ICStart(int[] instance, int signature);
789
790     private native static int ICStop(int[] instance);
791
792     private native static int ICLaunchURL(int instance, byte[] hint, byte[] data, int len, int[] selectionStart, int[] selectionEnd);
793 }
794
Popular Tags