Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/gui/JspmBrowserLauncher.java


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