Quadcap Embeddable Server

luxor/browser/albert/BrowserLauncher.java

Go to the documentation of this file.
00001 /* 00002 ** Luxor - XML User Interface Language (XUL) Toolkit 00003 ** Copyright (c) 2001, 2002 by Gerald Bauer 00004 ** 00005 ** This program is free software. 00006 ** 00007 ** You may redistribute it and/or modify it under the terms of the GNU 00008 ** General Public License as published by the Free Software Foundation. 00009 ** Version 2 of the license should be included with this distribution in 00010 ** the file LICENSE, as well as License.html. If the license is not 00011 ** included with this distribution, you may find a copy at the FSF web 00012 ** site at 'www.gnu.org' or 'www.fsf.org', or you may write to the 00013 ** Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139 USA. 00014 ** 00015 ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, 00016 ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR 00017 ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY 00018 ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR 00019 ** REDISTRIBUTION OF THIS SOFTWARE. 00020 ** 00021 */ 00022 00023 package luxor.browser.albert; 00024 00025 import java.io.File; 00026 import java.io.FileNotFoundException; 00027 import java.io.IOException; 00028 import java.lang.reflect.Constructor; 00029 import java.lang.reflect.Field; 00030 import java.lang.reflect.InvocationTargetException; 00031 import java.lang.reflect.Method; 00032 00033 /** 00034 * BrowserLauncher is a class that provides one static method, openURL, which 00035 * opens the default web browser for the current user of the system to the 00036 * given URL. It may support other protocols depending on the system -- mailto, 00037 * ftp, etc. -- but that has not been rigorously tested and is not guaranteed 00038 * to work. <p> 00039 * 00040 * Yes, this is platform-specific code, and yes, it may rely on classes on 00041 * certain platforms that are not part of the standard JDK. What we're trying 00042 * to do, though, is to take something that's frequently desirable but 00043 * inherently platform-specific -- opening a default browser -- and allow 00044 * programmers (you, for example) to do so without worrying about dropping into 00045 * native code or doing anything else similarly evil. <p> 00046 * 00047 * Anyway, this code is completely in Java and will run on all JDK 00048 * 1.1-compliant systems without modification or a need for additional 00049 * libraries. All classes that are required on certain platforms to allow this 00050 * to run are dynamically loaded at runtime via reflection and, if not found, 00051 * will not cause this to do anything other than returning an error when 00052 * opening the browser. <p> 00053 * 00054 * There are certain system requirements for this class, as it's running 00055 * through Runtime.exec(), which is Java's way of making a native system call. 00056 * Currently, this requires that a Macintosh have a Finder which supports the 00057 * GURL event, which is true for Mac OS 8.0 and 8.1 systems that have the 00058 * Internet Scripting AppleScript dictionary installed in the Scripting 00059 * Additions folder in the Extensions folder (which is installed by default as 00060 * far as I know under Mac OS 8.0 and 8.1), and for all Mac OS 8.5 and later 00061 * systems. On Windows, it only runs under Win32 systems (Windows 95, 98, and 00062 * NT 4.0, as well as later versions of all). On other systems, this drops back 00063 * from the inherently platform-sensitive concept of a default browser and 00064 * simply attempts to launch Netscape via a shell command. <p> 00065 * 00066 * This code is Copyright 1999-2001 by Eric Albert (ejalbert@cs.stanford.edu) 00067 * and may be redistributed or modified in any form without restrictions as 00068 * long as the portion of this comment from this paragraph through the end of 00069 * the comment is not removed. The author requests that he be notified of any 00070 * application, applet, or other binary that makes use of this code, but that's 00071 * more out of curiosity than anything and is not required. This software 00072 * includes no warranty. The author is not repsonsible for any loss of data or 00073 * functionality or any adverse or unexpected effects of using this software. 00074 * <p> 00075 * 00076 * Credits: <br> 00077 * Steven Spencer, JavaWorld magazine (<a 00078 * href="http://www.javaworld.com/javaworld/javatips/jw-javatip66.html">Java 00079 * Tip 66</a> ) <br> 00080 * Thanks also to Ron B. Yeh, Eric Shapiro, Ben Engber, Paul Teitlebaum, Andrea 00081 * Cantatore, Larry Barowski, Trevor Bedzek, Frank Miedrich, and Ron Rabakukk 00082 * 00083 *@author Eric Albert (<a href="mailto:ejalbert@cs.stanford.edu"> 00084 * ejalbert@cs.stanford.edu</a> ) 00085 *@version 1.4b1 (Released June 20, 2001) 00086 */ 00087 public class BrowserLauncher 00088 { 00089 00090 /** 00091 * The creator code of the Finder on a Macintosh, which is needed to send 00092 * AppleEvents to the application. 00093 */ 00094 private final static String FINDER_CREATOR = "MACS"; 00095 00096 /** 00097 * The file type of the Finder on a Macintosh. Hardcoding "Finder" would 00098 * keep non-U.S. English systems from working properly. 00099 */ 00100 private final static String FINDER_TYPE = "FNDR"; 00101 00102 /** 00103 * The first parameter that needs to be passed into Runtime.exec() to open 00104 * the default web browser on Windows. 00105 */ 00106 private final static String FIRST_WINDOWS_PARAMETER = "/c"; 00107 00108 /** 00109 * The name for the AppleEvent type corresponding to a GetURL event. 00110 */ 00111 private final static String GURL_EVENT = "GURL"; 00112 00113 /** 00114 * The framework to reference on Mac OS X 00115 */ 00116 private final static String JDirect_MacOSX = "/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/HIToolbox"; 00117 00118 /** 00119 * JVM constant for MRJ 2.0 00120 */ 00121 private final static int MRJ_2_0 = 0; 00122 00123 /** 00124 * JVM constant for MRJ 2.1 or later 00125 */ 00126 private final static int MRJ_2_1 = 1; 00127 00128 /** 00129 * JVM constant for Java on Mac OS X 10.0 (MRJ 3.0) 00130 */ 00131 private final static int MRJ_3_0 = 3; 00132 00133 /** 00134 * JVM constant for MRJ 3.1 00135 */ 00136 private final static int MRJ_3_1 = 4; 00137 private final static String NETSCAPE_OPEN_PARAMETER_END = ")'"; 00138 private final static String NETSCAPE_OPEN_PARAMETER_START = "'openURL("; 00139 00140 /** 00141 * The shell parameters for Netscape that opens a given URL in an 00142 * already-open copy of Netscape on many command-line systems. 00143 */ 00144 private final static String NETSCAPE_REMOTE_PARAMETER = "-remote"; 00145 00146 /** 00147 * JVM constant for any other platform 00148 */ 00149 private final static int OTHER = -1; 00150 00151 /** 00152 * The second parameter for Runtime.exec() on Windows. 00153 */ 00154 private final static String SECOND_WINDOWS_PARAMETER = "start"; 00155 00156 /** 00157 * The third parameter for Runtime.exec() on Windows. This is a "title" 00158 * parameter that the command line expects. Setting this parameter allows 00159 * URLs containing spaces to work. 00160 */ 00161 private final static String THIRD_WINDOWS_PARAMETER = "\"\""; 00162 00163 /** 00164 * JVM constant for any Windows 9x JVM 00165 */ 00166 private final static int WINDOWS_9x = 6; 00167 00168 /** 00169 * JVM constant for any Windows NT JVM 00170 */ 00171 private final static int WINDOWS_NT = 5; 00172 00173 /** 00174 * The com.apple.MacOS.AEDesc class 00175 */ 00176 private static Class aeDescClass; 00177 00178 00179 /** 00180 * The <init>(String) method of com.apple.MacOS.AEDesc 00181 */ 00182 private static Constructor aeDescConstructor; 00183 00184 /** 00185 * The <init>(int) method of com.apple.MacOS.AETarget 00186 */ 00187 private static Constructor aeTargetConstructor; 00188 00189 /** 00190 * The <init>(int, int, int) method of com.apple.MacOS.AppleEvent 00191 */ 00192 private static Constructor appleEventConstructor; 00193 00194 /** 00195 * The browser for the system 00196 */ 00197 private static Object browser; 00198 00199 /** 00200 * The message from any exception thrown throughout the initialization 00201 * process. 00202 */ 00203 private static String errorMessage; 00204 00205 /** 00206 * The findFolder method of com.apple.mrj.MRJFileUtils 00207 */ 00208 private static Method findFolder; 00209 00210 /** 00211 * The getFileCreator method of com.apple.mrj.MRJFileUtils 00212 */ 00213 private static Method getFileCreator; 00214 00215 /** 00216 * The getFileType method of com.apple.mrj.MRJFileUtils 00217 */ 00218 private static Method getFileType; 00219 00220 /** 00221 * The Java virtual machine that we are running on. Actually, in most cases 00222 * we only care about the operating system, but some operating systems 00223 * require us to switch on the VM. 00224 */ 00225 private static int jvm; 00226 00227 /** 00228 * The kAnyTransactionID AppleEvent code 00229 */ 00230 private static Integer kAnyTransactionID; 00231 00232 /** 00233 * The kAutoGenerateReturnID AppleEvent code 00234 */ 00235 private static Integer kAutoGenerateReturnID; 00236 00237 /** 00238 * Actually an MRJOSType pointing to the System Folder on a Macintosh 00239 */ 00240 private static Object kSystemFolderType; 00241 00242 /** 00243 * The keyDirectObject AppleEvent parameter type 00244 */ 00245 private static Integer keyDirectObject; 00246 00247 /** 00248 * The linkage object required for JDirect 3 on Mac OS X. 00249 */ 00250 private static Object linkage; 00251 00252 /** 00253 * Caches whether any classes, methods, and fields that are not part of the 00254 * JDK and need to be dynamically loaded at runtime loaded successfully. <p> 00255 * 00256 * Note that if this is <code>false</code>, <code>openURL()</code> will 00257 * always return an IOException. 00258 */ 00259 private static boolean loadedWithoutErrors; 00260 00261 /** 00262 * The makeOSType method of com.apple.MacOS.OSUtils 00263 */ 00264 private static Method makeOSType; 00265 00266 /** 00267 * The com.apple.mrj.MRJFileUtils class 00268 */ 00269 private static Class mrjFileUtilsClass; 00270 00271 /** 00272 * The com.apple.mrj.MRJOSType class 00273 */ 00274 private static Class mrjOSTypeClass; 00275 00276 /** 00277 * The openURL method of com.apple.mrj.MRJFileUtils 00278 */ 00279 private static Method openURL; 00280 00281 /** 00282 * The putParameter method of com.apple.MacOS.AppleEvent 00283 */ 00284 private static Method putParameter; 00285 00286 /** 00287 * The sendNoReply method of com.apple.MacOS.AppleEvent 00288 */ 00289 private static Method sendNoReply; 00290 00291 /** 00292 * This class should be never be instantiated; this just ensures so. 00293 */ 00294 private BrowserLauncher() { } 00295 00296 /** 00297 * Attempts to open the default web browser to the given URL. 00298 * 00299 *@param url The URL to open 00300 *@throws IOException If the web browser could not be located or does not 00301 * run 00302 */ 00303 public static void openURL( String url ) throws IOException 00304 { 00305 if( !loadedWithoutErrors ) 00306 { 00307 throw new IOException( "Exception in finding browser: " + errorMessage ); 00308 } 00309 Object browser = locateBrowser(); 00310 if( browser == null ) 00311 { 00312 throw new IOException( "Unable to locate browser: " + errorMessage ); 00313 } 00314 00315 switch ( jvm ) 00316 { 00317 case MRJ_2_0: 00318 Object aeDesc = null; 00319 try 00320 { 00321 aeDesc = aeDescConstructor.newInstance( new Object[]{url} ); 00322 putParameter.invoke( browser, new Object[]{keyDirectObject, aeDesc} ); 00323 sendNoReply.invoke( browser, new Object[]{} ); 00324 } 00325 catch( InvocationTargetException ite ) 00326 { 00327 throw new IOException( "InvocationTargetException while creating AEDesc: " + ite.getMessage() ); 00328 } 00329 catch( IllegalAccessException iae ) 00330 { 00331 throw new IOException( "IllegalAccessException while building AppleEvent: " + iae.getMessage() ); 00332 } 00333 catch( InstantiationException ie ) 00334 { 00335 throw new IOException( "InstantiationException while creating AEDesc: " + ie.getMessage() ); 00336 } 00337 finally 00338 { 00339 aeDesc = null; 00340 // Encourage it to get disposed if it was created 00341 browser = null; 00342 // Ditto 00343 } 00344 break; 00345 case MRJ_2_1: 00346 Runtime.getRuntime().exec( new String[]{( String ) browser, url} ); 00347 break; 00348 case MRJ_3_0: 00349 int[] instance = new int[1]; 00350 int result = ICStart( instance, 0 ); 00351 if( result == 0 ) 00352 { 00353 int[] selectionStart = new int[]{0}; 00354 byte[] urlBytes = url.getBytes(); 00355 int[] selectionEnd = new int[]{urlBytes.length}; 00356 result = ICLaunchURL( instance[0], new byte[]{0}, urlBytes, 00357 urlBytes.length, selectionStart, 00358 selectionEnd ); 00359 if( result == 0 ) 00360 { 00361 // Ignore the return value; the URL was launched successfully 00362 // regardless of what happens here. 00363 ICStop( instance ); 00364 } 00365 else 00366 { 00367 throw new IOException( "Unable to launch URL: " + result ); 00368 } 00369 } 00370 else 00371 { 00372 throw new IOException( "Unable to create an Internet Config instance: " + result ); 00373 } 00374 break; 00375 case MRJ_3_1: 00376 try 00377 { 00378 openURL.invoke( null, new Object[]{url} ); 00379 } 00380 catch( InvocationTargetException ite ) 00381 { 00382 throw new IOException( "InvocationTargetException while calling openURL: " + ite.getMessage() ); 00383 } 00384 catch( IllegalAccessException iae ) 00385 { 00386 throw new IOException( "IllegalAccessException while calling openURL: " + iae.getMessage() ); 00387 } 00388 break; 00389 case WINDOWS_NT: 00390 case WINDOWS_9x: 00391 // Add quotes around the URL to allow ampersands and other special 00392 // characters to work. 00393 Process process = Runtime.getRuntime().exec( new String[]{( String ) browser, 00394 FIRST_WINDOWS_PARAMETER, 00395 SECOND_WINDOWS_PARAMETER, 00396 THIRD_WINDOWS_PARAMETER, 00397 '"' + url + '"'} ); 00398 // This avoids a memory leak on some versions of Java on Windows. 00399 // That's hinted at in <http://developer.java.sun.com/developer/qow/archive/68/>. 00400 try 00401 { 00402 process.waitFor(); 00403 process.exitValue(); 00404 } 00405 catch( InterruptedException ie ) 00406 { 00407 throw new IOException( "InterruptedException while launching browser: " + ie.getMessage() ); 00408 } 00409 break; 00410 case OTHER: 00411 // Assume that we're on Unix and that Netscape is installed 00412 00413 // First, attempt to open the URL in a currently running session of Netscape 00414 process = Runtime.getRuntime().exec( new String[]{( String ) browser, 00415 NETSCAPE_REMOTE_PARAMETER, 00416 NETSCAPE_OPEN_PARAMETER_START + 00417 url + 00418 NETSCAPE_OPEN_PARAMETER_END} ); 00419 try 00420 { 00421 int exitCode = process.waitFor(); 00422 if( exitCode != 0 ) 00423 { 00424 // if Netscape was not open 00425 Runtime.getRuntime().exec( new String[]{( String ) browser, url} ); 00426 } 00427 } 00428 catch( InterruptedException ie ) 00429 { 00430 throw new IOException( "InterruptedException while launching browser: " + ie.getMessage() ); 00431 } 00432 break; 00433 default: 00434 // This should never occur, but if it does, we'll try the simplest thing possible 00435 Runtime.getRuntime().exec( new String[]{( String ) browser, url} ); 00436 break; 00437 } 00438 } 00439 00440 private native static int ICLaunchURL( int instance, byte[] hint, byte[] data, int len, 00441 int[] selectionStart, int[] selectionEnd ); 00442 00443 /** 00444 * Methods required for Mac OS X. The presence of native methods does not 00445 * cause any problems on other platforms. 00446 */ 00447 private native static int ICStart( int[] instance, int signature ); 00448 00449 private native static int ICStop( int[] instance ); 00450 00451 /** 00452 * Called by a static initializer to load any classes, fields, and methods 00453 * required at runtime to locate the user's web browser. 00454 * 00455 *@return <code>true</code> if all intialization succeeded <code>false</code> 00456 * if any portion of the initialization failed 00457 */ 00458 private static boolean loadClasses() 00459 { 00460 switch ( jvm ) 00461 { 00462 case MRJ_2_0: 00463 try 00464 { 00465 Class aeTargetClass = Class.forName( "com.apple.MacOS.AETarget" ); 00466 Class osUtilsClass = Class.forName( "com.apple.MacOS.OSUtils" ); 00467 Class appleEventClass = Class.forName( "com.apple.MacOS.AppleEvent" ); 00468 Class aeClass = Class.forName( "com.apple.MacOS.ae" ); 00469 aeDescClass = Class.forName( "com.apple.MacOS.AEDesc" ); 00470 00471 aeTargetConstructor = aeTargetClass.getDeclaredConstructor( new Class[]{int.class} ); 00472 appleEventConstructor = appleEventClass.getDeclaredConstructor( new Class[]{int.class, int.class, aeTargetClass, int.class, int.class} ); 00473 aeDescConstructor = aeDescClass.getDeclaredConstructor( new Class[]{String.class} ); 00474 00475 makeOSType = osUtilsClass.getDeclaredMethod( "makeOSType", new Class[]{String.class} ); 00476 putParameter = appleEventClass.getDeclaredMethod( "putParameter", new Class[]{int.class, aeDescClass} ); 00477 sendNoReply = appleEventClass.getDeclaredMethod( "sendNoReply", new Class[]{} ); 00478 00479 Field keyDirectObjectField = aeClass.getDeclaredField( "keyDirectObject" ); 00480 keyDirectObject = ( Integer ) keyDirectObjectField.get( null ); 00481 Field autoGenerateReturnIDField = appleEventClass.getDeclaredField( "kAutoGenerateReturnID" ); 00482 kAutoGenerateReturnID = ( Integer ) autoGenerateReturnIDField.get( null ); 00483 Field anyTransactionIDField = appleEventClass.getDeclaredField( "kAnyTransactionID" ); 00484 kAnyTransactionID = ( Integer ) anyTransactionIDField.get( null ); 00485 } 00486 catch( ClassNotFoundException cnfe ) 00487 { 00488 errorMessage = cnfe.getMessage(); 00489 return false; 00490 } 00491 catch( NoSuchMethodException nsme ) 00492 { 00493 errorMessage = nsme.getMessage(); 00494 return false; 00495 } 00496 catch( NoSuchFieldException nsfe ) 00497 { 00498 errorMessage = nsfe.getMessage(); 00499 return false; 00500 } 00501 catch( IllegalAccessException iae ) 00502 { 00503 errorMessage = iae.getMessage(); 00504 return false; 00505 } 00506 break; 00507 case MRJ_2_1: 00508 try 00509 { 00510 mrjFileUtilsClass = Class.forName( "com.apple.mrj.MRJFileUtils" ); 00511 mrjOSTypeClass = Class.forName( "com.apple.mrj.MRJOSType" ); 00512 Field systemFolderField = mrjFileUtilsClass.getDeclaredField( "kSystemFolderType" ); 00513 kSystemFolderType = systemFolderField.get( null ); 00514 findFolder = mrjFileUtilsClass.getDeclaredMethod( "findFolder", new Class[]{mrjOSTypeClass} ); 00515 getFileCreator = mrjFileUtilsClass.getDeclaredMethod( "getFileCreator", new Class[]{File.class} ); 00516 getFileType = mrjFileUtilsClass.getDeclaredMethod( "getFileType", new Class[]{File.class} ); 00517 } 00518 catch( ClassNotFoundException cnfe ) 00519 { 00520 errorMessage = cnfe.getMessage(); 00521 return false; 00522 } 00523 catch( NoSuchFieldException nsfe ) 00524 { 00525 errorMessage = nsfe.getMessage(); 00526 return false; 00527 } 00528 catch( NoSuchMethodException nsme ) 00529 { 00530 errorMessage = nsme.getMessage(); 00531 return false; 00532 } 00533 catch( SecurityException se ) 00534 { 00535 errorMessage = se.getMessage(); 00536 return false; 00537 } 00538 catch( IllegalAccessException iae ) 00539 { 00540 errorMessage = iae.getMessage(); 00541 return false; 00542 } 00543 break; 00544 case MRJ_3_0: 00545 try 00546 { 00547 Class linker = Class.forName( "com.apple.mrj.jdirect.Linker" ); 00548 Constructor constructor = linker.getConstructor( new Class[]{Class.class} ); 00549 linkage = constructor.newInstance( new Object[]{BrowserLauncher.class} ); 00550 } 00551 catch( ClassNotFoundException cnfe ) 00552 { 00553 errorMessage = cnfe.getMessage(); 00554 return false; 00555 } 00556 catch( NoSuchMethodException nsme ) 00557 { 00558 errorMessage = nsme.getMessage(); 00559 return false; 00560 } 00561 catch( InvocationTargetException ite ) 00562 { 00563 errorMessage = ite.getMessage(); 00564 return false; 00565 } 00566 catch( InstantiationException ie ) 00567 { 00568 errorMessage = ie.getMessage(); 00569 return false; 00570 } 00571 catch( IllegalAccessException iae ) 00572 { 00573 errorMessage = iae.getMessage(); 00574 return false; 00575 } 00576 break; 00577 case MRJ_3_1: 00578 try 00579 { 00580 mrjFileUtilsClass = Class.forName( "com.apple.mrj.MRJFileUtils" ); 00581 openURL = mrjFileUtilsClass.getDeclaredMethod( "openURL", new Class[]{String.class} ); 00582 } 00583 catch( ClassNotFoundException cnfe ) 00584 { 00585 errorMessage = cnfe.getMessage(); 00586 return false; 00587 } 00588 catch( NoSuchMethodException nsme ) 00589 { 00590 errorMessage = nsme.getMessage(); 00591 return false; 00592 } 00593 break; 00594 default: 00595 break; 00596 } 00597 return true; 00598 } 00599 00600 /** 00601 * Attempts to locate the default web browser on the local system. Caches 00602 * results so it only locates the browser once for each use of this class 00603 * per JVM instance. 00604 * 00605 *@return The browser for the system. Note that this may not be what you 00606 * would consider to be a standard web browser; instead, it's the 00607 * application that gets called to open the default web browser. In some 00608 * cases, this will be a non-String object that provides the means of 00609 * calling the default browser. 00610 */ 00611 private static Object locateBrowser() 00612 { 00613 if( browser != null ) 00614 { 00615 return browser; 00616 } 00617 switch ( jvm ) 00618 { 00619 case MRJ_2_0: 00620 try 00621 { 00622 Integer finderCreatorCode = ( Integer ) makeOSType.invoke( null, new Object[]{FINDER_CREATOR} ); 00623 Object aeTarget = aeTargetConstructor.newInstance( new Object[]{finderCreatorCode} ); 00624 Integer gurlType = ( Integer ) makeOSType.invoke( null, new Object[]{GURL_EVENT} ); 00625 Object appleEvent = appleEventConstructor.newInstance( new Object[]{gurlType, gurlType, aeTarget, kAutoGenerateReturnID, kAnyTransactionID} ); 00626 // Don't set browser = appleEvent because then the next time we call 00627 // locateBrowser(), we'll get the same AppleEvent, to which we'll already have 00628 // added the relevant parameter. Instead, regenerate the AppleEvent every time. 00629 // There's probably a way to do this better; if any has any ideas, please let 00630 // me know. 00631 return appleEvent; 00632 } 00633 catch( IllegalAccessException iae ) 00634 { 00635 browser = null; 00636 errorMessage = iae.getMessage(); 00637 return browser; 00638 } 00639 catch( InstantiationException ie ) 00640 { 00641 browser = null; 00642 errorMessage = ie.getMessage(); 00643 return browser; 00644 } 00645 catch( InvocationTargetException ite ) 00646 { 00647 browser = null; 00648 errorMessage = ite.getMessage(); 00649 return browser; 00650 } 00651 case MRJ_2_1: 00652 File systemFolder; 00653 try 00654 { 00655 systemFolder = ( File ) findFolder.invoke( null, new Object[]{kSystemFolderType} ); 00656 } 00657 catch( IllegalArgumentException iare ) 00658 { 00659 browser = null; 00660 errorMessage = iare.getMessage(); 00661 return browser; 00662 } 00663 catch( IllegalAccessException iae ) 00664 { 00665 browser = null; 00666 errorMessage = iae.getMessage(); 00667 return browser; 00668 } 00669 catch( InvocationTargetException ite ) 00670 { 00671 browser = null; 00672 errorMessage = ite.getTargetException().getClass() + ": " + ite.getTargetException().getMessage(); 00673 return browser; 00674 } 00675 String[] systemFolderFiles = systemFolder.list(); 00676 // Avoid a FilenameFilter because that can't be stopped mid-list 00677 for( int i = 0; i < systemFolderFiles.length; i++ ) 00678 { 00679 try 00680 { 00681 File file = new File( systemFolder, systemFolderFiles[i] ); 00682 if( !file.isFile() ) 00683 { 00684 continue; 00685 } 00686 // We're looking for a file with a creator code of 'MACS' and 00687 // a type of 'FNDR'. Only requiring the type results in non-Finder 00688 // applications being picked up on certain Mac OS 9 systems, 00689 // especially German ones, and sending a GURL event to those 00690 // applications results in a logout under Multiple Users. 00691 Object fileType = getFileType.invoke( null, new Object[]{file} ); 00692 if( FINDER_TYPE.equals( fileType.toString() ) ) 00693 { 00694 Object fileCreator = getFileCreator.invoke( null, new Object[]{file} ); 00695 if( FINDER_CREATOR.equals( fileCreator.toString() ) ) 00696 { 00697 browser = file.toString(); 00698 // Actually the Finder, but that's OK 00699 return browser; 00700 } 00701 } 00702 } 00703 catch( IllegalArgumentException iare ) 00704 { 00705 browser = browser; 00706 errorMessage = iare.getMessage(); 00707 return null; 00708 } 00709 catch( IllegalAccessException iae ) 00710 { 00711 browser = null; 00712 errorMessage = iae.getMessage(); 00713 return browser; 00714 } 00715 catch( InvocationTargetException ite ) 00716 { 00717 browser = null; 00718 errorMessage = ite.getTargetException().getClass() + ": " + ite.getTargetException().getMessage(); 00719 return browser; 00720 } 00721 } 00722 browser = null; 00723 break; 00724 case MRJ_3_0: 00725 case MRJ_3_1: 00726 browser = ""; 00727 // Return something non-null 00728 break; 00729 case WINDOWS_NT: 00730 browser = "cmd.exe"; 00731 break; 00732 case WINDOWS_9x: 00733 browser = "command.com"; 00734 break; 00735 case OTHER: 00736 default: 00737 browser = "netscape"; 00738 break; 00739 } 00740 return browser; 00741 } 00742 00743 static 00744 { 00745 loadedWithoutErrors = true; 00746 String osName = System.getProperty( "os.name" ); 00747 if( osName.startsWith( "Mac OS" ) ) 00748 { 00749 String mrjVersion = System.getProperty( "mrj.version" ); 00750 String majorMRJVersion = mrjVersion.substring( 0, 3 ); 00751 try 00752 { 00753 double version = Double.valueOf( majorMRJVersion ).doubleValue(); 00754 if( version == 2 ) 00755 { 00756 jvm = MRJ_2_0; 00757 } 00758 else if( version >= 2.1 && version < 3 ) 00759 { 00760 // Assume that all 2.x versions of MRJ work the same. MRJ 2.1 actually 00761 // works via Runtime.exec() and 2.2 supports that but has an openURL() method 00762 // as well that we currently ignore. 00763 jvm = MRJ_2_1; 00764 } 00765 else if( version == 3.0 ) 00766 { 00767 jvm = MRJ_3_0; 00768 } 00769 else if( version >= 3.1 ) 00770 { 00771 // Assume that all 3.1 and later versions of MRJ work the same. 00772 jvm = MRJ_3_1; 00773 } 00774 else 00775 { 00776 loadedWithoutErrors = false; 00777 errorMessage = "Unsupported MRJ version: " + version; 00778 } 00779 } 00780 catch( NumberFormatException nfe ) 00781 { 00782 loadedWithoutErrors = false; 00783 errorMessage = "Invalid MRJ version: " + mrjVersion; 00784 } 00785 } 00786 else if( osName.startsWith( "Windows" ) ) 00787 { 00788 if( osName.indexOf( "9" ) != -1 ) 00789 { 00790 jvm = WINDOWS_9x; 00791 } 00792 else 00793 { 00794 jvm = WINDOWS_NT; 00795 } 00796 } 00797 else 00798 { 00799 jvm = OTHER; 00800 } 00801 00802 if( loadedWithoutErrors ) 00803 { 00804 // if we haven't hit any errors yet 00805 loadedWithoutErrors = loadClasses(); 00806 } 00807 } 00808 }