PageRenderTime 53ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/src/java/org/jivesoftware/openfire/launcher/BrowserLauncher.java

https://github.com/aakus/Openfire
Java | 715 lines | 417 code | 57 blank | 241 comment | 39 complexity | d108e9f1c822a74aa19368a85627eb33 MD5 | raw file
  1. /**
  2. * $RCSfile$
  3. * $Revision: 128 $
  4. * $Date: 2004-10-25 20:42:00 -0300 (Mon, 25 Oct 2004) $
  5. *
  6. * Copyright (C) 2004-2008 Jive Software. All rights reserved.
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. package org.jivesoftware.openfire.launcher;
  21. import java.io.File;
  22. import java.io.IOException;
  23. import java.lang.reflect.Constructor;
  24. import java.lang.reflect.Field;
  25. import java.lang.reflect.InvocationTargetException;
  26. import java.lang.reflect.Method;
  27. import org.slf4j.Logger;
  28. import org.slf4j.LoggerFactory;
  29. /**
  30. * BrowserLauncher is a class that provides one static method, openURL, which opens the default
  31. * web browser for the current user of the system to the given URL. It may support other
  32. * protocols depending on the system -- mailto, ftp, etc. -- but that has not been rigorously
  33. * tested and is not guaranteed to work.
  34. * <p>Yes, this is platform-specific code, and yes, it may rely on classes on certain platforms
  35. * that are not part of the standard JDK. What we're trying to do, though, is to take something
  36. * that's frequently desirable but inherently platform-specific -- opening a default browser --
  37. * and allow programmers (you, for example) to do so without worrying about dropping into native
  38. * code or doing anything else similarly evil.
  39. * </p>
  40. * <p>Anyway, this code is completely in Java and will run on all JDK 1.1-compliant systems without
  41. * modification or a need for additional libraries. All classes that are required on certain
  42. * platforms to allow this to run are dynamically loaded at runtime via reflection and, if not
  43. * found, will not cause this to do anything other than returning an error when opening the
  44. * browser.
  45. * </p>
  46. * <p>There are certain system requirements for this class, as it's running through Runtime.exec(),
  47. * which is Java's way of making a native system call. Currently, this requires that a Macintosh
  48. * have a Finder which supports the GURL event, which is true for Mac OS 8.0 and 8.1 systems that
  49. * have the Internet Scripting AppleScript dictionary installed in the Scripting Additions folder
  50. * in the Extensions folder (which is installed by default as far as I know under Mac OS 8.0 and
  51. * 8.1), and for all Mac OS 8.5 and later systems. On Windows, it only runs under Win32 systems
  52. * (Windows 95, 98, and NT 4.0, as well as later versions of all). On other systems, this drops
  53. * back from the inherently platform-sensitive concept of a default browser and simply attempts
  54. * to launch Netscape via a shell command.
  55. * </p>
  56. * <p>This code is Copyright 1999-2001 by Eric Albert (ejalbert@cs.stanford.edu) and may be
  57. * redistributed or modified in any form without restrictions as long as the portion of this
  58. * comment from this paragraph through the end of the comment is not removed. The author
  59. * requests that he be notified of any application, applet, or other binary that makes use of
  60. * this code, but that's more out of curiosity than anything and is not required. This software
  61. * includes no warranty. The author is not repsonsible for any loss of data or functionality
  62. * or any adverse or unexpected effects of using this software.
  63. * </p>
  64. * Credits:
  65. * <br>Steven Spencer, JavaWorld magazine (<a href="http://www.javaworld.com/javaworld/javatips/jw-javatip66.html">Java Tip 66</a>)
  66. * <br>Thanks also to Ron B. Yeh, Eric Shapiro, Ben Engber, Paul Teitlebaum, Andrea Cantatore,
  67. * Larry Barowski, Trevor Bedzek, Frank Miedrich, and Ron Rabakukk
  68. *
  69. * @author Eric Albert (<a href="mailto:ejalbert@cs.stanford.edu">ejalbert@cs.stanford.edu</a>)
  70. * @version 1.4b1 (Released June 20, 2001)
  71. */
  72. public class BrowserLauncher {
  73. /**
  74. * The Java virtual machine that we are running on. Actually, in most cases we only care
  75. * about the operating system, but some operating systems require us to switch on the VM.
  76. */
  77. private static int jvm;
  78. /**
  79. * The browser for the system
  80. */
  81. private static Object browser;
  82. /**
  83. * Caches whether any classes, methods, and fields that are not part of the JDK and need to
  84. * be dynamically loaded at runtime loaded successfully.
  85. * <p/>
  86. * Note that if this is <code>false</code>, <code>openURL()</code> will always return an
  87. * IOException.
  88. */
  89. private static boolean loadedWithoutErrors;
  90. /**
  91. * The com.apple.mrj.MRJFileUtils class
  92. */
  93. private static Class mrjFileUtilsClass;
  94. /**
  95. * The com.apple.mrj.MRJOSType class
  96. */
  97. private static Class mrjOSTypeClass;
  98. /**
  99. * The com.apple.MacOS.AEDesc class
  100. */
  101. private static Class aeDescClass;
  102. /**
  103. * The <init>(int) method of com.apple.MacOS.AETarget
  104. */
  105. private static Constructor aeTargetConstructor;
  106. /**
  107. * The <init>(int, int, int) method of com.apple.MacOS.AppleEvent
  108. */
  109. private static Constructor appleEventConstructor;
  110. /**
  111. * The <init>(String) method of com.apple.MacOS.AEDesc
  112. */
  113. private static Constructor aeDescConstructor;
  114. /**
  115. * The findFolder method of com.apple.mrj.MRJFileUtils
  116. */
  117. private static Method findFolder;
  118. /**
  119. * The getFileCreator method of com.apple.mrj.MRJFileUtils
  120. */
  121. private static Method getFileCreator;
  122. /**
  123. * The getFileType method of com.apple.mrj.MRJFileUtils
  124. */
  125. private static Method getFileType;
  126. /**
  127. * The openURL method of com.apple.mrj.MRJFileUtils
  128. */
  129. private static Method openURL;
  130. /**
  131. * The makeOSType method of com.apple.MacOS.OSUtils
  132. */
  133. private static Method makeOSType;
  134. /**
  135. * The putParameter method of com.apple.MacOS.AppleEvent
  136. */
  137. private static Method putParameter;
  138. /**
  139. * The sendNoReply method of com.apple.MacOS.AppleEvent
  140. */
  141. private static Method sendNoReply;
  142. /**
  143. * Actually an MRJOSType pointing to the System Folder on a Macintosh
  144. */
  145. private static Object kSystemFolderType;
  146. /**
  147. * The keyDirectObject AppleEvent parameter type
  148. */
  149. private static Integer keyDirectObject;
  150. /**
  151. * The kAutoGenerateReturnID AppleEvent code
  152. */
  153. private static Integer kAutoGenerateReturnID;
  154. /**
  155. * The kAnyTransactionID AppleEvent code
  156. */
  157. private static Integer kAnyTransactionID;
  158. /**
  159. * The linkage object required for JDirect 3 on Mac OS X.
  160. */
  161. private static Object linkage;
  162. /**
  163. * The framework to reference on Mac OS X
  164. */
  165. private static final String JDirect_MacOSX = "/System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/HIToolbox";
  166. /**
  167. * JVM constant for MRJ 2.0
  168. */
  169. private static final int MRJ_2_0 = 0;
  170. /**
  171. * JVM constant for MRJ 2.1 or later
  172. */
  173. private static final int MRJ_2_1 = 1;
  174. /**
  175. * JVM constant for Java on Mac OS X 10.0 (MRJ 3.0)
  176. */
  177. private static final int MRJ_3_0 = 3;
  178. /**
  179. * JVM constant for MRJ 3.1
  180. */
  181. private static final int MRJ_3_1 = 4;
  182. /**
  183. * JVM constant for any Windows NT JVM
  184. */
  185. private static final int WINDOWS_NT = 5;
  186. /**
  187. * JVM constant for any Windows 9x JVM
  188. */
  189. private static final int WINDOWS_9x = 6;
  190. /**
  191. * JVM constant for any other platform
  192. */
  193. private static final int OTHER = -1;
  194. /**
  195. * The file type of the Finder on a Macintosh. Hardcoding "Finder" would keep non-U.S. English
  196. * systems from working properly.
  197. */
  198. private static final String FINDER_TYPE = "FNDR";
  199. /**
  200. * The creator code of the Finder on a Macintosh, which is needed to send AppleEvents to the
  201. * application.
  202. */
  203. private static final String FINDER_CREATOR = "MACS";
  204. /**
  205. * The name for the AppleEvent type corresponding to a GetURL event.
  206. */
  207. private static final String GURL_EVENT = "GURL";
  208. /**
  209. * The first parameter that needs to be passed into Runtime.exec() to open the default web
  210. * browser on Windows.
  211. */
  212. private static final String FIRST_WINDOWS_PARAMETER = "/c";
  213. /**
  214. * The second parameter for Runtime.exec() on Windows.
  215. */
  216. private static final String SECOND_WINDOWS_PARAMETER = "start";
  217. /**
  218. * The third parameter for Runtime.exec() on Windows. This is a "title"
  219. * parameter that the command line expects. Setting this parameter allows
  220. * URLs containing spaces to work.
  221. */
  222. private static final String THIRD_WINDOWS_PARAMETER = "\"\"";
  223. /**
  224. * The shell parameters for Netscape that opens a given URL in an already-open copy of Netscape
  225. * on many command-line systems.
  226. */
  227. private static final String NETSCAPE_REMOTE_PARAMETER = "-remote";
  228. private static final String NETSCAPE_OPEN_PARAMETER_START = "'openURL(";
  229. private static final String NETSCAPE_OPEN_PARAMETER_END = ")'";
  230. /**
  231. * The message from any exception thrown throughout the initialization process.
  232. */
  233. private static String errorMessage;
  234. /**
  235. * An initialization block that determines the operating system and loads the necessary
  236. * runtime data.
  237. */
  238. static {
  239. loadedWithoutErrors = true;
  240. String osName = System.getProperty("os.name");
  241. if (osName.startsWith("Mac OS")) {
  242. String mrjVersion = System.getProperty("mrj.version");
  243. String majorMRJVersion = mrjVersion.substring(0, 3);
  244. try {
  245. double version = Double.valueOf(majorMRJVersion).doubleValue();
  246. if (version == 2) {
  247. jvm = MRJ_2_0;
  248. }
  249. else if (version >= 2.1 && version < 3) {
  250. // Assume that all 2.x versions of MRJ work the same. MRJ 2.1 actually
  251. // works via Runtime.exec() and 2.2 supports that but has an openURL() method
  252. // as well that we currently ignore.
  253. jvm = MRJ_2_1;
  254. }
  255. else if (version == 3.0) {
  256. jvm = MRJ_3_0;
  257. }
  258. else if (version >= 3.1) {
  259. // Assume that all 3.1 and later versions of MRJ work the same.
  260. jvm = MRJ_3_1;
  261. }
  262. else {
  263. loadedWithoutErrors = false;
  264. errorMessage = "Unsupported MRJ version: " + version;
  265. }
  266. }
  267. catch (NumberFormatException nfe) {
  268. loadedWithoutErrors = false;
  269. errorMessage = "Invalid MRJ version: " + mrjVersion;
  270. }
  271. }
  272. else if (osName.startsWith("Windows")) {
  273. if (osName.indexOf("9") != -1) {
  274. jvm = WINDOWS_9x;
  275. }
  276. else {
  277. jvm = WINDOWS_NT;
  278. }
  279. }
  280. else {
  281. jvm = OTHER;
  282. }
  283. if (loadedWithoutErrors) { // if we haven't hit any errors yet
  284. loadedWithoutErrors = loadClasses();
  285. }
  286. }
  287. /**
  288. * This class should be never be instantiated; this just ensures so.
  289. */
  290. private BrowserLauncher() {
  291. }
  292. /**
  293. * Called by a static initializer to load any classes, fields, and methods required at runtime
  294. * to locate the user's web browser.
  295. *
  296. * @return <code>true</code> if all intialization succeeded
  297. * <code>false</code> if any portion of the initialization failed
  298. */
  299. private static boolean loadClasses() {
  300. switch (jvm) {
  301. case MRJ_2_0:
  302. try {
  303. Class aeTargetClass = Class.forName("com.apple.MacOS.AETarget");
  304. Class osUtilsClass = Class.forName("com.apple.MacOS.OSUtils");
  305. Class appleEventClass = Class.forName("com.apple.MacOS.AppleEvent");
  306. Class aeClass = Class.forName("com.apple.MacOS.ae");
  307. aeDescClass = Class.forName("com.apple.MacOS.AEDesc");
  308. aeTargetConstructor = aeTargetClass.getDeclaredConstructor(new Class[]{int.class});
  309. appleEventConstructor = appleEventClass.getDeclaredConstructor(new Class[]{int.class, int.class, aeTargetClass, int.class, int.class});
  310. aeDescConstructor = aeDescClass.getDeclaredConstructor(new Class[]{String.class});
  311. makeOSType = osUtilsClass.getDeclaredMethod("makeOSType", new Class[]{String.class});
  312. putParameter = appleEventClass.getDeclaredMethod("putParameter", new Class[]{int.class, aeDescClass});
  313. sendNoReply = appleEventClass.getDeclaredMethod("sendNoReply", new Class[]{});
  314. Field keyDirectObjectField = aeClass.getDeclaredField("keyDirectObject");
  315. keyDirectObject = (Integer)keyDirectObjectField.get(null);
  316. Field autoGenerateReturnIDField = appleEventClass.getDeclaredField("kAutoGenerateReturnID");
  317. kAutoGenerateReturnID = (Integer)autoGenerateReturnIDField.get(null);
  318. Field anyTransactionIDField = appleEventClass.getDeclaredField("kAnyTransactionID");
  319. kAnyTransactionID = (Integer)anyTransactionIDField.get(null);
  320. }
  321. catch (ClassNotFoundException cnfe) {
  322. errorMessage = cnfe.getMessage();
  323. return false;
  324. }
  325. catch (NoSuchMethodException nsme) {
  326. errorMessage = nsme.getMessage();
  327. return false;
  328. }
  329. catch (NoSuchFieldException nsfe) {
  330. errorMessage = nsfe.getMessage();
  331. return false;
  332. }
  333. catch (IllegalAccessException iae) {
  334. errorMessage = iae.getMessage();
  335. return false;
  336. }
  337. break;
  338. case MRJ_2_1:
  339. try {
  340. mrjFileUtilsClass = Class.forName("com.apple.mrj.MRJFileUtils");
  341. mrjOSTypeClass = Class.forName("com.apple.mrj.MRJOSType");
  342. Field systemFolderField = mrjFileUtilsClass.getDeclaredField("kSystemFolderType");
  343. kSystemFolderType = systemFolderField.get(null);
  344. findFolder = mrjFileUtilsClass.getDeclaredMethod("findFolder", new Class[]{mrjOSTypeClass});
  345. getFileCreator = mrjFileUtilsClass.getDeclaredMethod("getFileCreator", new Class[]{File.class});
  346. getFileType = mrjFileUtilsClass.getDeclaredMethod("getFileType", new Class[]{File.class});
  347. }
  348. catch (ClassNotFoundException cnfe) {
  349. errorMessage = cnfe.getMessage();
  350. return false;
  351. }
  352. catch (NoSuchFieldException nsfe) {
  353. errorMessage = nsfe.getMessage();
  354. return false;
  355. }
  356. catch (NoSuchMethodException nsme) {
  357. errorMessage = nsme.getMessage();
  358. return false;
  359. }
  360. catch (SecurityException se) {
  361. errorMessage = se.getMessage();
  362. return false;
  363. }
  364. catch (IllegalAccessException iae) {
  365. errorMessage = iae.getMessage();
  366. return false;
  367. }
  368. break;
  369. case MRJ_3_0:
  370. try {
  371. Class linker = Class.forName("com.apple.mrj.jdirect.Linker");
  372. Constructor constructor = linker.getConstructor(new Class[]{Class.class});
  373. linkage = constructor.newInstance(new Object[]{BrowserLauncher.class});
  374. }
  375. catch (ClassNotFoundException cnfe) {
  376. errorMessage = cnfe.getMessage();
  377. return false;
  378. }
  379. catch (NoSuchMethodException nsme) {
  380. errorMessage = nsme.getMessage();
  381. return false;
  382. }
  383. catch (InvocationTargetException ite) {
  384. errorMessage = ite.getMessage();
  385. return false;
  386. }
  387. catch (InstantiationException ie) {
  388. errorMessage = ie.getMessage();
  389. return false;
  390. }
  391. catch (IllegalAccessException iae) {
  392. errorMessage = iae.getMessage();
  393. return false;
  394. }
  395. break;
  396. case MRJ_3_1:
  397. try {
  398. mrjFileUtilsClass = Class.forName("com.apple.mrj.MRJFileUtils");
  399. openURL = mrjFileUtilsClass.getDeclaredMethod("openURL", new Class[]{String.class});
  400. }
  401. catch (ClassNotFoundException cnfe) {
  402. errorMessage = cnfe.getMessage();
  403. return false;
  404. }
  405. catch (NoSuchMethodException nsme) {
  406. errorMessage = nsme.getMessage();
  407. return false;
  408. }
  409. break;
  410. default:
  411. break;
  412. }
  413. return true;
  414. }
  415. /**
  416. * Attempts to locate the default web browser on the local system. Caches results so it
  417. * only locates the browser once for each use of this class per JVM instance.
  418. *
  419. * @return The browser for the system. Note that this may not be what you would consider
  420. * to be a standard web browser; instead, it's the application that gets called to
  421. * open the default web browser. In some cases, this will be a non-String object
  422. * that provides the means of calling the default browser.
  423. */
  424. private static Object locateBrowser() {
  425. if (browser != null) {
  426. return browser;
  427. }
  428. switch (jvm) {
  429. case MRJ_2_0:
  430. try {
  431. Integer finderCreatorCode = (Integer)makeOSType.invoke(null, new Object[]{FINDER_CREATOR});
  432. Object aeTarget = aeTargetConstructor.newInstance(new Object[]{finderCreatorCode});
  433. Integer gurlType = (Integer)makeOSType.invoke(null, new Object[]{GURL_EVENT});
  434. Object appleEvent = appleEventConstructor.newInstance(new Object[]{gurlType, gurlType, aeTarget, kAutoGenerateReturnID, kAnyTransactionID});
  435. // Don't set browser = appleEvent because then the next time we call
  436. // locateBrowser(), we'll get the same AppleEvent, to which we'll already have
  437. // added the relevant parameter. Instead, regenerate the AppleEvent every time.
  438. // There's probably a way to do this better; if any has any ideas, please let
  439. // me know.
  440. return appleEvent;
  441. }
  442. catch (IllegalAccessException iae) {
  443. browser = null;
  444. errorMessage = iae.getMessage();
  445. return browser;
  446. }
  447. catch (InstantiationException ie) {
  448. browser = null;
  449. errorMessage = ie.getMessage();
  450. return browser;
  451. }
  452. catch (InvocationTargetException ite) {
  453. browser = null;
  454. errorMessage = ite.getMessage();
  455. return browser;
  456. }
  457. case MRJ_2_1:
  458. File systemFolder;
  459. try {
  460. systemFolder = (File)findFolder.invoke(null, new Object[]{kSystemFolderType});
  461. }
  462. catch (IllegalArgumentException iare) {
  463. browser = null;
  464. errorMessage = iare.getMessage();
  465. return browser;
  466. }
  467. catch (IllegalAccessException iae) {
  468. browser = null;
  469. errorMessage = iae.getMessage();
  470. return browser;
  471. }
  472. catch (InvocationTargetException ite) {
  473. browser = null;
  474. errorMessage = ite.getTargetException().getClass() + ": " + ite.getTargetException().getMessage();
  475. return browser;
  476. }
  477. String[] systemFolderFiles = systemFolder.list();
  478. // Avoid a FilenameFilter because that can't be stopped mid-list
  479. for (int i = 0; i < systemFolderFiles.length; i++) {
  480. try {
  481. File file = new File(systemFolder, systemFolderFiles[i]);
  482. if (!file.isFile()) {
  483. continue;
  484. }
  485. // We're looking for a file with a creator code of 'MACS' and
  486. // a type of 'FNDR'. Only requiring the type results in non-Finder
  487. // applications being picked up on certain Mac OS 9 systems,
  488. // especially German ones, and sending a GURL event to those
  489. // applications results in a logout under Multiple Users.
  490. Object fileType = getFileType.invoke(null, new Object[]{file});
  491. if (FINDER_TYPE.equals(fileType.toString())) {
  492. Object fileCreator = getFileCreator.invoke(null, new Object[]{file});
  493. if (FINDER_CREATOR.equals(fileCreator.toString())) {
  494. browser = file.toString(); // Actually the Finder, but that's OK
  495. return browser;
  496. }
  497. }
  498. }
  499. catch (IllegalArgumentException iare) {
  500. browser = browser;
  501. errorMessage = iare.getMessage();
  502. return null;
  503. }
  504. catch (IllegalAccessException iae) {
  505. browser = null;
  506. errorMessage = iae.getMessage();
  507. return browser;
  508. }
  509. catch (InvocationTargetException ite) {
  510. browser = null;
  511. errorMessage = ite.getTargetException().getClass() + ": " + ite.getTargetException().getMessage();
  512. return browser;
  513. }
  514. }
  515. browser = null;
  516. break;
  517. case MRJ_3_0:
  518. case MRJ_3_1:
  519. browser = ""; // Return something non-null
  520. break;
  521. case WINDOWS_NT:
  522. browser = "cmd.exe";
  523. break;
  524. case WINDOWS_9x:
  525. browser = "command.com";
  526. break;
  527. case OTHER:
  528. default:
  529. browser = "netscape";
  530. break;
  531. }
  532. return browser;
  533. }
  534. /**
  535. * Attempts to open the default web browser to the given URL.
  536. *
  537. * @param url The URL to open
  538. * @throws IOException If the web browser could not be located or does not run
  539. */
  540. public static void openURL(String url) throws IOException {
  541. if (!loadedWithoutErrors) {
  542. throw new IOException("Exception in finding browser: " + errorMessage);
  543. }
  544. Object browser = locateBrowser();
  545. if (browser == null) {
  546. throw new IOException("Unable to locate browser: " + errorMessage);
  547. }
  548. switch (jvm) {
  549. case MRJ_2_0:
  550. Object aeDesc = null;
  551. try {
  552. aeDesc = aeDescConstructor.newInstance(new Object[]{url});
  553. putParameter.invoke(browser, new Object[]{keyDirectObject, aeDesc});
  554. sendNoReply.invoke(browser, new Object[]{});
  555. }
  556. catch (InvocationTargetException ite) {
  557. throw new IOException("InvocationTargetException while creating AEDesc: " + ite.getMessage());
  558. }
  559. catch (IllegalAccessException iae) {
  560. throw new IOException("IllegalAccessException while building AppleEvent: " + iae.getMessage());
  561. }
  562. catch (InstantiationException ie) {
  563. throw new IOException("InstantiationException while creating AEDesc: " + ie.getMessage());
  564. }
  565. finally {
  566. aeDesc = null; // Encourage it to get disposed if it was created
  567. browser = null; // Ditto
  568. }
  569. break;
  570. case MRJ_2_1:
  571. Runtime.getRuntime().exec(new String[]{(String)browser, url});
  572. break;
  573. case MRJ_3_0:
  574. int[] instance = new int[1];
  575. int result = ICStart(instance, 0);
  576. if (result == 0) {
  577. int[] selectionStart = new int[]{0};
  578. byte[] urlBytes = url.getBytes();
  579. int[] selectionEnd = new int[]{urlBytes.length};
  580. result = ICLaunchURL(instance[0], new byte[]{0}, urlBytes,
  581. urlBytes.length, selectionStart,
  582. selectionEnd);
  583. if (result == 0) {
  584. // Ignore the return value; the URL was launched successfully
  585. // regardless of what happens here.
  586. ICStop(instance);
  587. }
  588. else {
  589. throw new IOException("Unable to launch URL: " + result);
  590. }
  591. }
  592. else {
  593. throw new IOException("Unable to create an Internet Config instance: " + result);
  594. }
  595. break;
  596. case MRJ_3_1:
  597. try {
  598. openURL.invoke(null, new Object[]{url});
  599. }
  600. catch (InvocationTargetException ite) {
  601. throw new IOException("InvocationTargetException while calling openURL: " + ite.getMessage());
  602. }
  603. catch (IllegalAccessException iae) {
  604. throw new IOException("IllegalAccessException while calling openURL: " + iae.getMessage());
  605. }
  606. break;
  607. case WINDOWS_NT:
  608. case WINDOWS_9x:
  609. // Add quotes around the URL to allow ampersands and other special
  610. // characters to work.
  611. Process process = Runtime.getRuntime().exec(new String[]{(String)browser,
  612. FIRST_WINDOWS_PARAMETER,
  613. SECOND_WINDOWS_PARAMETER,
  614. THIRD_WINDOWS_PARAMETER,
  615. '"' + url + '"'});
  616. // This avoids a memory leak on some versions of Java on Windows.
  617. // That's hinted at in <http://developer.java.sun.com/developer/qow/archive/68/>.
  618. try {
  619. process.waitFor();
  620. process.exitValue();
  621. }
  622. catch (InterruptedException ie) {
  623. throw new IOException("InterruptedException while launching browser: " + ie.getMessage());
  624. }
  625. break;
  626. case OTHER:
  627. // Assume that we're on Unix and that Netscape is installed
  628. // First, attempt to open the URL in a currently running session of Netscape
  629. process = Runtime.getRuntime().exec(new String[]{(String)browser,
  630. NETSCAPE_REMOTE_PARAMETER,
  631. NETSCAPE_OPEN_PARAMETER_START +
  632. url +
  633. NETSCAPE_OPEN_PARAMETER_END});
  634. try {
  635. int exitCode = process.waitFor();
  636. if (exitCode != 0) { // if Netscape was not open
  637. Runtime.getRuntime().exec(new String[]{(String)browser, url});
  638. }
  639. }
  640. catch (InterruptedException ie) {
  641. throw new IOException("InterruptedException while launching browser: " + ie.getMessage());
  642. }
  643. break;
  644. default:
  645. // This should never occur, but if it does, we'll try the simplest thing possible
  646. Runtime.getRuntime().exec(new String[]{(String)browser, url});
  647. break;
  648. }
  649. }
  650. /**
  651. * Methods required for Mac OS X. The presence of native methods does not cause
  652. * any problems on other platforms.
  653. */
  654. private native static int ICStart(int[] instance, int signature);
  655. private native static int ICStop(int[] instance);
  656. private native static int ICLaunchURL(int instance, byte[] hint, byte[] data, int len,
  657. int[] selectionStart, int[] selectionEnd);
  658. }