PageRenderTime 60ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/ptolemy/src/ptolemy/actor/gui/BrowserLauncher.java

https://github.com/Elblonko/kepler
Java | 977 lines | 517 code | 138 blank | 322 comment | 64 complexity | 7fb661e9314feba258c2acea36807d02 MD5 | raw file
Possible License(s): GPL-2.0
  1. /* Launch the user's default web browser.
  2. Copyright (c) 2002-2013 The Regents of the University of California.
  3. All rights reserved.
  4. Permission is hereby granted, without written agreement and without
  5. license or royalty fees, to use, copy, modify, and distribute this
  6. software and its documentation for any purpose, provided that the above
  7. copyright notice and the following two paragraphs appear in all copies
  8. of this software.
  9. IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
  10. FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
  11. ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
  12. THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
  13. SUCH DAMAGE.
  14. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  15. INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  16. MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
  17. PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
  18. CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
  19. ENHANCEMENTS, OR MODIFICATIONS.
  20. PT_COPYRIGHT_VERSION_2
  21. COPYRIGHTENDKEY
  22. */
  23. package ptolemy.actor.gui;
  24. import java.io.File;
  25. import java.io.IOException;
  26. import java.lang.reflect.Constructor;
  27. import java.lang.reflect.Field;
  28. import java.lang.reflect.InvocationTargetException;
  29. import java.lang.reflect.Method;
  30. import java.net.URL;
  31. import ptolemy.util.StringUtilities;
  32. /**
  33. BrowserLauncher is a class that provides one static method, openURL,
  34. which opens the default web browser for the current user of the system
  35. to the given URL. It may support other protocols depending on the
  36. system -- mailto, ftp, etc. -- but that has not been rigorously tested
  37. and is not guaranteed to work.
  38. <p> Yes, this is platform-specific code, and yes, it may rely on
  39. classes on certain platforms that are not part of the standard JDK.
  40. What we're trying to do, though, is to take something that's
  41. frequently desirable but inherently platform-specific -- opening a
  42. default browser -- and allow programmers (you, for example) to do so
  43. without worrying about dropping into native code or doing anything
  44. else similarly evil.</p>
  45. <p> Anyway, this code is completely in Java and will run on all JDK
  46. 1.1-compliant systems without modification or a need for additional
  47. libraries. All classes that are required on certain platforms to
  48. allow this to run are dynamically loaded at runtime via reflection
  49. and, if not found, will not cause this to do anything other than
  50. returning an error when opening the browser.</p>
  51. <p> There are certain system requirements for this class, as it's
  52. running through Runtime.exec(), which is Java's way of making a
  53. native system call. Currently, this requires that a Macintosh have a
  54. Finder which supports the GURL event, which is true for Mac OS 8.0
  55. and 8.1 systems that have the Internet Scripting AppleScript
  56. dictionary installed in the Scripting Additions folder in the
  57. Extensions folder (which is installed by default as far as I know
  58. under Mac OS 8.0 and 8.1), and for all Mac OS 8.5 and later systems.
  59. On Windows, it only runs under Win32 systems (Windows 95, 98, and NT
  60. 4.0, as well as later versions of all). On other systems, this drops
  61. back from the inherently platform-sensitive concept of a default
  62. browser and simply attempts to launch Firefox via a shell command.</p>
  63. <blockquote>
  64. <p> This code is Copyright 1999-2001 by Eric Albert (ejalbert@cs.stanford.edu)
  65. and may be redistributed or modified in any form without restrictions as
  66. long as the portion of this comment from this paragraph through the end
  67. of the comment is not removed. The author requests that he be notified
  68. of any application, applet, or other binary that makes use of this code,
  69. but that is more out of curiosity than anything and is not required. This
  70. software includes no warranty. The author is not responsible for any
  71. loss of data or functionality or any adverse or unexpected effects of
  72. using this software.</p>
  73. <p>
  74. Credits:
  75. <br>Steven Spencer, JavaWorld magazine (<a href="http://www.javaworld.com/javaworld/javatips/jw-javatip66.html">Java//Tip 66</a>)
  76. <br>Thanks also to Ron B. Yeh, Eric Shapiro, Ben Engber, Paul
  77. Teitlebaum, Andrea Cantatore, Larry Barowski, Trevor Bedzek, Frank
  78. Miedrich, and Ron Rabakukk
  79. </p>
  80. </blockquote>
  81. <p> On November 3, 2003, BrowserLauncher was downloaded from
  82. <a href="http://browserlauncher.sourceforge.net/">http://browserlauncher.sourceforge.net/</a>.
  83. That URL no longer exists, there is a replacement package which is LGPL'd.
  84. See <a href="http://web.archive.org/web/20031028032907/http://browserlauncher.sourceforge.net/"><code>http://web.archive.org/web/20031028032907/http://browserlauncher.sourceforge.net/</code></a> for the original web page.</p>
  85. @author Eric Albert (<a href="mailto:ejalbert@cs.stanford.edu">ejalbert@cs.stanford.edu</a>)
  86. @version $Id: BrowserLauncher.java 69510 2014-07-13 00:29:23Z cxh $, based on 1.4b1 (Released June 20, 2001)
  87. @since Ptolemy II 3.0
  88. @Pt.ProposedRating Red (cxh)
  89. @Pt.AcceptedRating Red (cxh)
  90. */
  91. public class BrowserLauncher {
  92. /** Launch the browser on the first argument. If there is
  93. * no first argument, then open http://ptolemy.eecs.berkeley.edu.
  94. * Second and subsequent arguments are ignored.
  95. * It is best if the first argument is an absolute URL
  96. * as opposed to a relative URL.
  97. *
  98. * <p> For example, to open the user's default browser on
  99. * http://www.eecs.berkeley.edu
  100. * <pre>
  101. * java -classpath $PTII ptolemy.actor.gui.BrowserLauncher http://www.eecs.berkeley.edu
  102. * </pre>
  103. * @param args An array of command line arguments. The first
  104. * argument names a URL to be opened. If there is no first
  105. * argument, then open http://ptolemy.eecs.berkeley.edu. Second
  106. * and subsequent arguments are ignored.
  107. * @exception Exception If there is a problem launching the browser.
  108. */
  109. public static void main(String[] args) throws Exception {
  110. if (args.length >= 1) {
  111. // Ignore any arguments after the first one.
  112. BrowserLauncher.openURL(args[0]);
  113. } else {
  114. BrowserLauncher.openURL("http://ptolemy.eecs.berkeley.edu");
  115. }
  116. if (BrowserLauncher.delayExit) {
  117. System.out.println("Delaying exit for 10 seconds because we"
  118. + "may have copied a jar: file");
  119. try {
  120. Thread.sleep(10000);
  121. } catch (InterruptedException e) {
  122. }
  123. StringUtilities.exit(0);
  124. }
  125. }
  126. /**
  127. * Attempts to open the default web browser to the given URL.
  128. *
  129. * <p> We use the following strategy to find URLs that may be inside
  130. * jar files:
  131. * <br> If the string does not start with "http": see if it is a
  132. * file.
  133. * <br> If the file cannot be found, look it up in the classpath.
  134. * <br> If the file can be found in the classpath then use the
  135. * found file instead of the given URL.
  136. * <br>If the file cannot be found in the classpath, then pass the
  137. * original given URL to the browser.
  138. * <p>If the ptolemy.ptII.browser property is set, then its value
  139. * is used as the value of the browser.
  140. * <br>To always use Internet Explorer, one might invoke Ptolemy
  141. * with:
  142. * <pre>
  143. * java -classpath $PTII -Dptolemy.ptII.browser=c:\\Program\ Files\\Internet\ Explorer\\iexplore.exe ptolemy.vergil.VergilApplication
  144. * </pre>
  145. * <p>To always use Firefox:
  146. * <pre>
  147. * java -classpath $PTII -Dptolemy.ptII.browser=c:\\Program\ Files\\Mozilla\ Firefox\\firefox ptolemy.vergil.VergilApplication
  148. * </pre>
  149. *
  150. * <p>To preserve your browser choice set the ptolemy.ptII.browser
  151. * property in <code>$PTII/lib/ptII.properties</code>. Note that
  152. * each time <code>$PTII/bin/configure</code> is run,
  153. * <code>$PTII/lib/ptII.properties.in</code> is read and
  154. * <code>$PTII/lib/ptII.properties</code> is overwritten, so you
  155. * may want to add your changes to
  156. co * <code>$PTII/lib/ptII.properties.in</code>.
  157. *
  158. * @param url The URL to open.
  159. * It is best if the first argument is an absolute URL
  160. * as opposed to a relative URL.
  161. * @exception IOException If the web browser could not be located or
  162. * does not run
  163. */
  164. public static void openURL(String url) throws IOException {
  165. if (!loadedWithoutErrors) {
  166. throw new IOException("Exception in finding browser: "
  167. + errorMessage);
  168. }
  169. if (!url.startsWith("http:") && !url.startsWith("https:")) {
  170. // If the url does not start with http:, then look it up
  171. // as a regular file and then possibly in the classpath.
  172. File urlFile = null;
  173. try {
  174. urlFile = new File(url);
  175. } catch (Exception ex) {
  176. // Ignored, we try to fix this below.
  177. urlFile = null;
  178. }
  179. if (urlFile == null || !urlFile.exists()) {
  180. // The file could not be found, so try the search path mambo.
  181. // We might be in the Swing Event thread, so
  182. // Thread.currentThread().getContextClassLoader()
  183. // .getResource(entry) probably will not work.
  184. String refClassName = "ptolemy.kernel.util.NamedObj";
  185. try {
  186. Class refClass = Class.forName(refClassName);
  187. URL entryURL = refClass.getClassLoader().getResource(url);
  188. if (entryURL != null && !url.startsWith("jar:")) {
  189. System.out.println("BrowserLauncher: Could not "
  190. + "find '" + url + "', but '" + entryURL
  191. + "' was found.");
  192. url = entryURL.toString();
  193. } else {
  194. if (url.startsWith("jar:")) {
  195. // If the URL begins with jar: then we are
  196. // inside Web Start and we should get the
  197. // resource, write it to a temporary file
  198. // and pass that value to the browser
  199. // Save the jar file as a temporary file
  200. // in the default platform dependent
  201. // directory with the same suffix as that
  202. // of the jar URL
  203. // FIXME: we should probably cache this
  204. // copy somehow.
  205. String old = url;
  206. String temporaryURL = JNLPUtilities
  207. .saveJarURLInClassPath(url);
  208. if (temporaryURL != null) {
  209. url = temporaryURL;
  210. } else {
  211. url = JNLPUtilities.saveJarURLAsTempFile(url,
  212. "tmp", null, null);
  213. delayExit = true;
  214. }
  215. System.out.println("BrowserLauncher: Could not "
  216. + "find '" + old + "', but jar url'" + url
  217. + "' was found.");
  218. }
  219. }
  220. } catch (ClassNotFoundException ex) {
  221. System.err.println("BrowserLauncher: Internal error, "
  222. + " Could not find " + refClassName);
  223. }
  224. }
  225. }
  226. if (!StringUtilities.getProperty("ptolemy.ptII.browser").equals("")) {
  227. Runtime.getRuntime()
  228. .exec(new String[] {
  229. "\""
  230. + StringUtilities
  231. .getProperty("ptolemy.ptII.browser")
  232. + "\"", url });
  233. return;
  234. }
  235. Object browser = locateBrowser();
  236. if (browser == null) {
  237. throw new IOException("Unable to locate browser: " + errorMessage);
  238. }
  239. String args[];
  240. switch (jvm) {
  241. case MRJ_2_0:
  242. errorMessage = "Command was a call to aeDescConstructor(" + url
  243. + ")";
  244. Object aeDesc = null;
  245. try {
  246. aeDesc = aeDescConstructor.newInstance(new Object[] { url });
  247. putParameter.invoke(browser, new Object[] { keyDirectObject,
  248. aeDesc });
  249. sendNoReply.invoke(browser, new Object[] {});
  250. } catch (InvocationTargetException ite) {
  251. throw new IOException("InvocationTargetException while "
  252. + "creating AEDesc: " + ite.getMessage());
  253. } catch (IllegalAccessException iae) {
  254. throw new IOException("IllegalAccessException while "
  255. + "building AppleEvent: " + iae.getMessage());
  256. } catch (InstantiationException ie) {
  257. throw new IOException("InstantiationException while "
  258. + "creating AEDesc: " + ie.getMessage());
  259. } finally {
  260. // Encourage it to get disposed if it was created
  261. aeDesc = null;
  262. // Ditto
  263. browser = null;
  264. }
  265. break;
  266. case MRJ_2_1:
  267. args = new String[] { (String) browser, url };
  268. errorMessage = "Command was: " + args[0] + " " + args[1];
  269. Runtime.getRuntime().exec(args);
  270. break;
  271. case MRJ_3_0:
  272. errorMessage = "Command was a call to ICLaunchURL(" + url + ")";
  273. int[] instance = new int[1];
  274. int result = ICStart(instance, 0);
  275. if (result == 0) {
  276. int[] selectionStart = new int[] { 0 };
  277. byte[] urlBytes = url.getBytes("UTF-8");
  278. int[] selectionEnd = new int[] { urlBytes.length };
  279. result = ICLaunchURL(instance[0], new byte[] { 0 }, urlBytes,
  280. urlBytes.length, selectionStart, selectionEnd);
  281. if (result == 0) {
  282. // Ignore the return value; the URL was launched
  283. // successfully regardless of what happens here.
  284. ICStop(instance);
  285. } else {
  286. throw new IOException("Unable to launch URL: " + result);
  287. }
  288. } else {
  289. throw new IOException("Unable to create an Internet "
  290. + "Config instance: " + result);
  291. }
  292. break;
  293. case MRJ_3_1:
  294. if (!url.startsWith("file:") && !url.startsWith("http:")
  295. && !url.startsWith("https:")) {
  296. // Needed by Web Start file:
  297. // ptII/ptolemy/domains/ct/demo/CartPendulum/CartPendulum.jnlp
  298. url = new File(url).toURI().toURL().toString();
  299. }
  300. errorMessage = "Command was a call to openURL(" + url + ")";
  301. try {
  302. openURL.invoke(null, new Object[] { url });
  303. } catch (InvocationTargetException ite) {
  304. throw new IOException("InvocationTargetException while "
  305. + "calling openURL() on " + url + " "
  306. + ite.getMessage());
  307. } catch (IllegalAccessException iae) {
  308. throw new IOException("IllegalAccessException while "
  309. + "calling openURL() on: " + url + " "
  310. + iae.getMessage());
  311. }
  312. break;
  313. case WINDOWS_NT:
  314. case WINDOWS_9x:
  315. // Add quotes around the URL to allow ampersands and other special
  316. // characters to work.
  317. args = new String[] { (String) browser, FIRST_WINDOWS_PARAMETER,
  318. SECOND_WINDOWS_PARAMETER, THIRD_WINDOWS_PARAMETER,
  319. '"' + url + '"' };
  320. Process process = Runtime.getRuntime().exec(args);
  321. errorMessage = "Command was: "
  322. + args[0]
  323. + " "
  324. + args[1]
  325. + " "
  326. + args[2]
  327. + " "
  328. + args[3]
  329. + " "
  330. + args[4]
  331. + "\nNote: Under Windows, make sure that the file named by "
  332. + "the url is executable.";
  333. // The return code returned by process.waitFor()
  334. // 0 usually indicates normal execution.
  335. int exitCode = 0;
  336. // This avoids a memory leak on some versions of Java on Windows.
  337. // That's hinted at in
  338. // <http://developer.java.sun.com/developer/qow/archive/68/>.
  339. try {
  340. exitCode = process.waitFor();
  341. process.exitValue();
  342. } catch (InterruptedException ie) {
  343. throw new IOException("InterruptedException while "
  344. + "launching browser: " + ie.getMessage());
  345. }
  346. if (exitCode != 0) {
  347. throw new IOException(
  348. "Process exec'd by BrowserLauncher returned "
  349. + exitCode + "." + "\n url was: " + url
  350. + "\n browser was: " + browser + "\n "
  351. + errorMessage);
  352. }
  353. break;
  354. case OTHER:
  355. // Assume that we're on Unix and that firefox is installed
  356. // First, attempt to open the URL in a currently running
  357. // session of Netscape
  358. args = new String[] {
  359. (String) browser,
  360. NETSCAPE_REMOTE_PARAMETER,
  361. NETSCAPE_OPEN_PARAMETER_START + url
  362. + NETSCAPE_OPEN_PARAMETER_END };
  363. process = Runtime.getRuntime().exec(args);
  364. errorMessage = "Command was: " + args[0] + " " + args[1] + " "
  365. + args[2];
  366. try {
  367. if (process.waitFor() != 0) { // if Netscape was not open
  368. Runtime.getRuntime().exec(
  369. new String[] { (String) browser, url });
  370. }
  371. } catch (InterruptedException ie) {
  372. throw new IOException("InterruptedException while "
  373. + "launching browser: " + ie.getMessage());
  374. }
  375. break;
  376. default:
  377. // This should never occur, but if it does, we'll try
  378. // the simplest thing possible
  379. Runtime.getRuntime().exec(
  380. new String[] { "\"" + (String) browser + "\"", url });
  381. break;
  382. }
  383. }
  384. /** Set to true if we copied a file out of a jar file so that
  385. * the browser could display it. The reason we need this flag
  386. * is that the system will delete the temporary file on exit,
  387. * and after openURL() is called, this Java process will exit
  388. * unless we delay.
  389. */
  390. public static boolean delayExit = false;
  391. /**
  392. * The Java virtual machine that we are running on. Actually, in
  393. * most cases we only care about the operating system, but some
  394. * operating systems require us to switch on the VM.
  395. */
  396. private static int jvm;
  397. /** The browser for the system */
  398. private static Object browser;
  399. /**
  400. * Caches whether any classes, methods, and fields that are not
  401. * part of the JDK and need to be dynamically loaded at runtime
  402. * loaded successfully.
  403. * <p> Note that if this is <code>false</code>,
  404. * <code>openURL()</code> will always return an IOException.
  405. */
  406. private static boolean loadedWithoutErrors;
  407. /** The com.apple.mrj.MRJFileUtils class */
  408. private static Class mrjFileUtilsClass;
  409. /** The com.apple.mrj.MRJOSType class */
  410. private static Class mrjOSTypeClass;
  411. /** The com.apple.MacOS.AEDesc class */
  412. private static Class aeDescClass;
  413. /** The <init>(int) method of com.apple.MacOS.AETarget */
  414. private static Constructor aeTargetConstructor;
  415. /** The <init>(int, int, int) method of com.apple.MacOS.AppleEvent */
  416. private static Constructor appleEventConstructor;
  417. /** The <init>(String) method of com.apple.MacOS.AEDesc */
  418. private static Constructor aeDescConstructor;
  419. /** The findFolder method of com.apple.mrj.MRJFileUtils */
  420. private static Method findFolder;
  421. /** The getFileCreator method of com.apple.mrj.MRJFileUtils */
  422. private static Method getFileCreator;
  423. /** The getFileType method of com.apple.mrj.MRJFileUtils */
  424. private static Method getFileType;
  425. /** The openURL method of com.apple.mrj.MRJFileUtils */
  426. private static Method openURL;
  427. /** The makeOSType method of com.apple.MacOS.OSUtils */
  428. private static Method makeOSType;
  429. /** The putParameter method of com.apple.MacOS.AppleEvent */
  430. private static Method putParameter;
  431. /** The sendNoReply method of com.apple.MacOS.AppleEvent */
  432. private static Method sendNoReply;
  433. /** Actually an MRJOSType pointing to the System Folder on a Macintosh */
  434. private static Object kSystemFolderType;
  435. /** The keyDirectObject AppleEvent parameter type */
  436. private static Integer keyDirectObject;
  437. /** The kAutoGenerateReturnID AppleEvent code */
  438. private static Integer kAutoGenerateReturnID;
  439. /** The kAnyTransactionID AppleEvent code */
  440. private static Integer kAnyTransactionID;
  441. // The linkage object required for JDirect 3 on Mac OS X.
  442. //private static Object linkage;
  443. /** The framework to reference on Mac OS X */
  444. //private static final String JDirect_MacOSX =
  445. //"/System/Library/Frameworks/Carbon.framework/Frameworks/"
  446. //+ "HIToolbox.framework/HIToolbox";
  447. /** JVM constant for MRJ 2.0 */
  448. private static final int MRJ_2_0 = 0;
  449. /** JVM constant for MRJ 2.1 or later */
  450. private static final int MRJ_2_1 = 1;
  451. /** JVM constant for Java on Mac OS X 10.0 (MRJ 3.0) */
  452. private static final int MRJ_3_0 = 3;
  453. /** JVM constant for MRJ 3.1 */
  454. private static final int MRJ_3_1 = 4;
  455. /** JVM constant for any Windows NT JVM */
  456. private static final int WINDOWS_NT = 5;
  457. /** JVM constant for any Windows 9x JVM */
  458. private static final int WINDOWS_9x = 6;
  459. /** JVM constant for any other platform */
  460. private static final int OTHER = -1;
  461. /**
  462. * The file type of the Finder on a Macintosh. Hardcoding
  463. * "Finder" would keep non-U.S. English systems from working
  464. * properly.
  465. */
  466. private static final String FINDER_TYPE = "FNDR";
  467. /**
  468. * The creator code of the Finder on a Macintosh, which is needed
  469. * to send AppleEvents to the application.
  470. */
  471. private static final String FINDER_CREATOR = "MACS";
  472. /** The name for the AppleEvent type corresponding to a GetURL event. */
  473. private static final String GURL_EVENT = "GURL";
  474. /**
  475. * The first parameter that needs to be passed into Runtime.exec()
  476. * to open the default web browser on Windows.
  477. */
  478. private static final String FIRST_WINDOWS_PARAMETER = "/c";
  479. /** The second parameter for Runtime.exec() on Windows. */
  480. private static final String SECOND_WINDOWS_PARAMETER = "start";
  481. /**
  482. * The third parameter for Runtime.exec() on Windows. This is a "title"
  483. * parameter that the command line expects. Setting this parameter allows
  484. * URLs containing spaces to work.
  485. */
  486. private static final String THIRD_WINDOWS_PARAMETER = "\"\"";
  487. /**
  488. * The shell parameters for Netscape or firefox that opens a given
  489. * URL in an already-open copy of Netscape or firefox on many
  490. * command-line systems.
  491. */
  492. private static final String NETSCAPE_REMOTE_PARAMETER = "-remote";
  493. private static final String NETSCAPE_OPEN_PARAMETER_START = "'openURL(";
  494. private static final String NETSCAPE_OPEN_PARAMETER_END = ")'";
  495. /**
  496. * The message from any exception thrown throughout the
  497. * initialization process.
  498. */
  499. private static String errorMessage;
  500. /**
  501. * An initialization block that determines the operating system
  502. * and loads the necessary runtime data.
  503. */
  504. static {
  505. loadedWithoutErrors = true;
  506. String osName = System.getProperty("os.name");
  507. if (osName.startsWith("Mac OS")) {
  508. String mrjVersion = System.getProperty("mrj.version");
  509. if (mrjVersion == null) {
  510. // Java build 1.7.0_04-ea-b11 does not have the mrj.version property set.
  511. jvm = MRJ_3_1;
  512. } else {
  513. String majorMRJVersion = mrjVersion.substring(0, 3);
  514. try {
  515. double version = Double.valueOf(majorMRJVersion)
  516. .doubleValue();
  517. if (version == 2) {
  518. jvm = MRJ_2_0;
  519. } else if (version >= 2.1 && version < 3) {
  520. // Assume that all 2.x versions of MRJ work the
  521. // same. MRJ 2.1 actually works via
  522. // Runtime.exec() and 2.2 supports that but has an
  523. // openURL() method as well that we currently
  524. // ignore.
  525. jvm = MRJ_2_1;
  526. } else if (version == 3.0) {
  527. jvm = MRJ_3_0;
  528. } else if (version >= 3.1) {
  529. // Assume that all 3.1 and later versions of MRJ
  530. // work the same.
  531. jvm = MRJ_3_1;
  532. } else {
  533. loadedWithoutErrors = false;
  534. errorMessage = "Unsupported MRJ version: " + version;
  535. }
  536. } catch (NumberFormatException nfe) {
  537. loadedWithoutErrors = false;
  538. errorMessage = "Invalid MRJ version: " + mrjVersion;
  539. }
  540. }
  541. } else if (osName.startsWith("Windows")) {
  542. if (osName.indexOf("9") != -1) {
  543. jvm = WINDOWS_9x;
  544. } else {
  545. jvm = WINDOWS_NT;
  546. }
  547. } else {
  548. jvm = OTHER;
  549. }
  550. if (loadedWithoutErrors) { // if we haven't hit any errors yet
  551. loadedWithoutErrors = loadClasses();
  552. }
  553. }
  554. /**
  555. * This class should be never be instantiated; this just ensures so.
  556. */
  557. private BrowserLauncher() {
  558. }
  559. /**
  560. * Methods required for Mac OS X. The presence of native methods
  561. * does not cause any problems on other platforms.
  562. */
  563. private native static int ICLaunchURL(int instance, byte[] hint,
  564. byte[] data, int len, int[] selectionStart, int[] selectionEnd);
  565. private native static int ICStart(int[] instance, int signature);
  566. private native static int ICStop(int[] instance);
  567. /**
  568. * Called by a static initializer to load any classes, fields, and
  569. * methods required at runtime to locate the user's web browser.
  570. * @return <code>true</code> if all initialization succeeded
  571. * <code>false</code> if any portion of the initialization failed
  572. */
  573. private static boolean loadClasses() {
  574. switch (jvm) {
  575. case MRJ_2_0:
  576. try {
  577. Class aeTargetClass = Class.forName("com.apple.MacOS.AETarget");
  578. Class osUtilsClass = Class.forName("com.apple.MacOS.OSUtils");
  579. Class appleEventClass = Class
  580. .forName("com.apple.MacOS.AppleEvent");
  581. Class aeClass = Class.forName("com.apple.MacOS.ae");
  582. aeDescClass = Class.forName("com.apple.MacOS.AEDesc");
  583. aeTargetConstructor = aeTargetClass
  584. .getDeclaredConstructor(new Class[] { int.class });
  585. appleEventConstructor = appleEventClass
  586. .getDeclaredConstructor(new Class[] { int.class,
  587. int.class, aeTargetClass, int.class, int.class });
  588. aeDescConstructor = aeDescClass
  589. .getDeclaredConstructor(new Class[] { String.class });
  590. makeOSType = osUtilsClass.getDeclaredMethod("makeOSType",
  591. new Class[] { String.class });
  592. putParameter = appleEventClass.getDeclaredMethod(
  593. "putParameter", new Class[] { int.class, aeDescClass });
  594. sendNoReply = appleEventClass.getDeclaredMethod("sendNoReply",
  595. new Class[] {});
  596. Field keyDirectObjectField = aeClass
  597. .getDeclaredField("keyDirectObject");
  598. keyDirectObject = (Integer) keyDirectObjectField.get(null);
  599. Field autoGenerateReturnIDField = appleEventClass
  600. .getDeclaredField("kAutoGenerateReturnID");
  601. kAutoGenerateReturnID = (Integer) autoGenerateReturnIDField
  602. .get(null);
  603. Field anyTransactionIDField = appleEventClass
  604. .getDeclaredField("kAnyTransactionID");
  605. kAnyTransactionID = (Integer) anyTransactionIDField.get(null);
  606. } catch (ClassNotFoundException cnfe) {
  607. errorMessage = cnfe.getMessage();
  608. return false;
  609. } catch (NoSuchMethodException nsme) {
  610. errorMessage = nsme.getMessage();
  611. return false;
  612. } catch (NoSuchFieldException nsfe) {
  613. errorMessage = nsfe.getMessage();
  614. return false;
  615. } catch (IllegalAccessException iae) {
  616. errorMessage = iae.getMessage();
  617. return false;
  618. }
  619. break;
  620. case MRJ_2_1:
  621. try {
  622. mrjFileUtilsClass = Class.forName("com.apple.mrj.MRJFileUtils");
  623. mrjOSTypeClass = Class.forName("com.apple.mrj.MRJOSType");
  624. Field systemFolderField = mrjFileUtilsClass
  625. .getDeclaredField("kSystemFolderType");
  626. kSystemFolderType = systemFolderField.get(null);
  627. findFolder = mrjFileUtilsClass.getDeclaredMethod("findFolder",
  628. new Class[] { mrjOSTypeClass });
  629. getFileCreator = mrjFileUtilsClass.getDeclaredMethod(
  630. "getFileCreator", new Class[] { File.class });
  631. getFileType = mrjFileUtilsClass.getDeclaredMethod(
  632. "getFileType", new Class[] { File.class });
  633. } catch (ClassNotFoundException cnfe) {
  634. errorMessage = cnfe.getMessage();
  635. return false;
  636. } catch (NoSuchFieldException nsfe) {
  637. errorMessage = nsfe.getMessage();
  638. return false;
  639. } catch (NoSuchMethodException nsme) {
  640. errorMessage = nsme.getMessage();
  641. return false;
  642. } catch (SecurityException se) {
  643. errorMessage = se.getMessage();
  644. return false;
  645. } catch (IllegalAccessException iae) {
  646. errorMessage = iae.getMessage();
  647. return false;
  648. }
  649. break;
  650. case MRJ_3_0:
  651. try {
  652. Class linker = Class.forName("com.apple.mrj.jdirect.Linker");
  653. Constructor constructor = linker
  654. .getConstructor(new Class[] { Class.class });
  655. /*linkage = */constructor
  656. .newInstance(new Object[] { BrowserLauncher.class });
  657. } catch (ClassNotFoundException cnfe) {
  658. errorMessage = cnfe.getMessage();
  659. return false;
  660. } catch (NoSuchMethodException nsme) {
  661. errorMessage = nsme.getMessage();
  662. return false;
  663. } catch (InvocationTargetException ite) {
  664. errorMessage = ite.getMessage();
  665. return false;
  666. } catch (InstantiationException ie) {
  667. errorMessage = ie.getMessage();
  668. return false;
  669. } catch (IllegalAccessException iae) {
  670. errorMessage = iae.getMessage();
  671. return false;
  672. }
  673. break;
  674. case MRJ_3_1:
  675. try {
  676. mrjFileUtilsClass = Class.forName("com.apple.mrj.MRJFileUtils");
  677. openURL = mrjFileUtilsClass.getDeclaredMethod("openURL",
  678. new Class[] { String.class });
  679. } catch (ClassNotFoundException cnfe) {
  680. errorMessage = cnfe.getMessage();
  681. return false;
  682. } catch (NoSuchMethodException nsme) {
  683. errorMessage = nsme.getMessage();
  684. return false;
  685. }
  686. break;
  687. default:
  688. break;
  689. }
  690. return true;
  691. }
  692. /**
  693. * Attempts to locate the default web browser on the local system.
  694. * Caches results so it only locates the browser once for each use
  695. * of this class per JVM instance.
  696. *
  697. * @return The browser for the system. Note that this may not be
  698. * what you would consider to be a standard web browser;
  699. * instead, it's the application that gets called to open the
  700. * default web browser. In some cases, this will be a non-String
  701. * object that provides the means of calling the default browser.
  702. *
  703. */
  704. private static Object locateBrowser() {
  705. if (browser != null) {
  706. return browser;
  707. }
  708. switch (jvm) {
  709. case MRJ_2_0:
  710. try {
  711. Integer finderCreatorCode = (Integer) makeOSType.invoke(null,
  712. new Object[] { FINDER_CREATOR });
  713. Object aeTarget = aeTargetConstructor
  714. .newInstance(new Object[] { finderCreatorCode });
  715. Integer gurlType = (Integer) makeOSType.invoke(null,
  716. new Object[] { GURL_EVENT });
  717. Object appleEvent = appleEventConstructor
  718. .newInstance(new Object[] { gurlType, gurlType,
  719. aeTarget, kAutoGenerateReturnID,
  720. kAnyTransactionID });
  721. // Don't set browser = appleEvent because then the
  722. // next time we call locateBrowser(), we'll get the
  723. // same AppleEvent, to which we'll already have added
  724. // the relevant parameter. Instead, regenerate the
  725. // AppleEvent every time. There's probably a way to
  726. // do this better; if any has any ideas, please let me
  727. // know.
  728. return appleEvent;
  729. } catch (IllegalAccessException iae) {
  730. browser = null;
  731. errorMessage = iae.getMessage();
  732. return browser;
  733. } catch (InstantiationException ie) {
  734. browser = null;
  735. errorMessage = ie.getMessage();
  736. return browser;
  737. } catch (InvocationTargetException ite) {
  738. browser = null;
  739. errorMessage = ite.getMessage();
  740. return browser;
  741. }
  742. case MRJ_2_1:
  743. File systemFolder;
  744. try {
  745. systemFolder = (File) findFolder.invoke(null,
  746. new Object[] { kSystemFolderType });
  747. } catch (IllegalArgumentException iare) {
  748. browser = null;
  749. errorMessage = iare.getMessage();
  750. return browser;
  751. } catch (IllegalAccessException iae) {
  752. browser = null;
  753. errorMessage = iae.getMessage();
  754. return browser;
  755. } catch (InvocationTargetException ite) {
  756. browser = null;
  757. errorMessage = ite.getTargetException().getClass() + ": "
  758. + ite.getTargetException().getMessage();
  759. return browser;
  760. }
  761. String[] systemFolderFiles = systemFolder.list();
  762. // Avoid a FilenameFilter because
  763. // that can't be stopped mid-list
  764. for (String systemFolderFile : systemFolderFiles) {
  765. try {
  766. File file = new File(systemFolder, systemFolderFile);
  767. if (!file.isFile()) {
  768. continue;
  769. }
  770. // We're looking for a file with a creator code of
  771. // 'MACS' and a type of 'FNDR'. Only requiring
  772. // the type results in non-Finder applications
  773. // being picked up on certain Mac OS 9 systems,
  774. // especially German ones, and sending a GURL
  775. // event to those applications results in a logout
  776. // under Multiple Users.
  777. Object fileType = getFileType.invoke(null,
  778. new Object[] { file });
  779. if (FINDER_TYPE.equals(fileType.toString())) {
  780. Object fileCreator = getFileCreator.invoke(null,
  781. new Object[] { file });
  782. if (FINDER_CREATOR.equals(fileCreator.toString())) {
  783. // Actually the Finder, but that's OK
  784. browser = file.toString();
  785. return browser;
  786. }
  787. }
  788. } catch (IllegalArgumentException iare) {
  789. errorMessage = iare.getMessage();
  790. return null;
  791. } catch (IllegalAccessException iae) {
  792. browser = null;
  793. errorMessage = iae.getMessage();
  794. return browser;
  795. } catch (InvocationTargetException ite) {
  796. browser = null;
  797. errorMessage = ite.getTargetException().getClass() + ": "
  798. + ite.getTargetException().getMessage();
  799. return browser;
  800. }
  801. }
  802. browser = null;
  803. break;
  804. case MRJ_3_0:
  805. case MRJ_3_1:
  806. browser = ""; // Return something non-null
  807. break;
  808. case WINDOWS_NT:
  809. browser = "cmd.exe";
  810. break;
  811. case WINDOWS_9x:
  812. browser = "command.com";
  813. break;
  814. case OTHER:
  815. default:
  816. browser = "firefox";
  817. break;
  818. }
  819. return browser;
  820. }
  821. }