001: /*
002: * SSHTools - Java SSH2 API
003: *
004: * Copyright (C) 2002-2003 Lee David Painter and Contributors.
005: *
006: * Contributions made by:
007: *
008: * Brett Smith
009: * Richard Pernavas
010: * Erwin Bolwidt
011: *
012: * This program is free software; you can redistribute it and/or
013: * modify it under the terms of the GNU General Public License
014: * as published by the Free Software Foundation; either version 2
015: * of the License, or (at your option) any later version.
016: *
017: * This program is distributed in the hope that it will be useful,
018: * but WITHOUT ANY WARRANTY; without even the implied warranty of
019: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
020: * GNU General Public License for more details.
021: *
022: * You should have received a copy of the GNU General Public License
023: * along with this program; if not, write to the Free Software
024: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
025: */
026: package com.sshtools.common.util;
027:
028: import java.io.*;
029:
030: import java.lang.reflect.*;
031:
032: /**
033: *
034: *
035: * @author $author$
036: * @version $Revision: 1.14 $
037: */
038: public class BrowserLauncher {
039: private static int jvm;
040: private static Object browser;
041: private static boolean loadedWithoutErrors;
042: private static Class mrjFileUtilsClass;
043: private static Class mrjOSTypeClass;
044: private static Class macOSErrorClass;
045: private static Class aeDescClass;
046: private static Constructor aeTargetConstructor;
047: private static Constructor appleEventConstructor;
048: private static Constructor aeDescConstructor;
049: private static Method findFolder;
050: private static Method getFileType;
051: private static Method makeOSType;
052: private static Method putParameter;
053: private static Method sendNoReply;
054: private static Object kSystemFolderType;
055: private static Integer keyDirectObject;
056: private static Integer kAutoGenerateReturnID;
057: private static Integer kAnyTransactionID;
058: private static final int MRJ_2_0 = 0;
059: private static final int MRJ_2_1 = 1;
060: private static final int WINDOWS_NT = 2;
061: private static final int WINDOWS_9x = 3;
062: private static final int OTHER = -1;
063: private static final String FINDER_TYPE = "FNDR";
064: private static final String FINDER_CREATOR = "MACS";
065: private static final String GURL_EVENT = "GURL";
066: private static final String FIRST_WINDOWS_PARAMETER = "/c";
067: private static final String SECOND_WINDOWS_PARAMETER = "start";
068: private static final String NETSCAPE_OPEN_PARAMETER_START = " -remote 'openURL(";
069: private static final String NETSCAPE_OPEN_PARAMETER_END = ")'";
070: private static String errorMessage;
071:
072: static {
073: loadedWithoutErrors = true;
074:
075: String osName = System.getProperty("os.name");
076:
077: if ("Mac OS".equals(osName)) {
078: String mrjVersion = System.getProperty("mrj.version");
079: String majorMRJVersion = mrjVersion.substring(0, 3);
080:
081: try {
082: double version = Double.valueOf(majorMRJVersion)
083: .doubleValue();
084:
085: if (version == 2) {
086: jvm = MRJ_2_0;
087: } else if (version >= 2.1) {
088: // For the time being, assume that all post-2.0 versions of MRJ work the same
089: jvm = MRJ_2_1;
090: } else {
091: loadedWithoutErrors = false;
092: errorMessage = "Unsupported MRJ version: "
093: + version;
094: }
095: } catch (NumberFormatException nfe) {
096: loadedWithoutErrors = false;
097: errorMessage = "Invalid MRJ version: " + mrjVersion;
098: }
099: } else if (osName.startsWith("Windows")) {
100: if (osName.indexOf("9") != -1) {
101: jvm = WINDOWS_9x;
102: } else {
103: jvm = WINDOWS_NT;
104: }
105: } else {
106: jvm = OTHER;
107: }
108:
109: if (loadedWithoutErrors) { // if we haven't hit any errors yet
110: loadedWithoutErrors = loadClasses();
111: }
112: }
113:
114: private BrowserLauncher() {
115: }
116:
117: private static boolean loadClasses() {
118: switch (jvm) {
119: case MRJ_2_0:
120:
121: try {
122: Class aeTargetClass = Class
123: .forName("com.apple.MacOS.AETarget");
124: macOSErrorClass = Class
125: .forName("com.apple.MacOS.MacOSError");
126:
127: Class osUtilsClass = Class
128: .forName("com.apple.MacOS.OSUtils");
129: Class appleEventClass = Class
130: .forName("com.apple.MacOS.AppleEvent");
131: Class aeClass = Class.forName("com.apple.MacOS.ae");
132: aeDescClass = Class.forName("com.apple.MacOS.AEDesc");
133: aeTargetConstructor = aeTargetClass
134: .getDeclaredConstructor(new Class[] { int.class });
135: appleEventConstructor = appleEventClass
136: .getDeclaredConstructor(new Class[] {
137: int.class, int.class, aeTargetClass,
138: int.class, int.class });
139: aeDescConstructor = aeDescClass
140: .getDeclaredConstructor(new Class[] { String.class });
141: makeOSType = osUtilsClass.getDeclaredMethod(
142: "makeOSType", new Class[] { String.class });
143: putParameter = appleEventClass.getDeclaredMethod(
144: "putParameter", new Class[] { int.class,
145: aeDescClass });
146: sendNoReply = appleEventClass.getDeclaredMethod(
147: "sendNoReply", new Class[] {});
148:
149: Field keyDirectObjectField = aeClass
150: .getDeclaredField("keyDirectObject");
151: keyDirectObject = (Integer) keyDirectObjectField
152: .get(null);
153:
154: Field autoGenerateReturnIDField = appleEventClass
155: .getDeclaredField("kAutoGenerateReturnID");
156: kAutoGenerateReturnID = (Integer) autoGenerateReturnIDField
157: .get(null);
158:
159: Field anyTransactionIDField = appleEventClass
160: .getDeclaredField("kAnyTransactionID");
161: kAnyTransactionID = (Integer) anyTransactionIDField
162: .get(null);
163: } catch (ClassNotFoundException cnfe) {
164: errorMessage = cnfe.getMessage();
165:
166: return false;
167: } catch (NoSuchMethodException nsme) {
168: errorMessage = nsme.getMessage();
169:
170: return false;
171: } catch (NoSuchFieldException nsfe) {
172: errorMessage = nsfe.getMessage();
173:
174: return false;
175: } catch (IllegalAccessException iae) {
176: errorMessage = iae.getMessage();
177:
178: return false;
179: }
180:
181: break;
182:
183: case MRJ_2_1:
184:
185: try {
186: mrjFileUtilsClass = Class
187: .forName("com.apple.mrj.MRJFileUtils");
188: mrjOSTypeClass = Class
189: .forName("com.apple.mrj.MRJOSType");
190:
191: Field systemFolderField = mrjFileUtilsClass
192: .getDeclaredField("kSystemFolderType");
193: kSystemFolderType = systemFolderField.get(null);
194: findFolder = mrjFileUtilsClass.getDeclaredMethod(
195: "findFolder", new Class[] { mrjOSTypeClass });
196: getFileType = mrjFileUtilsClass.getDeclaredMethod(
197: "getFileType", new Class[] { File.class });
198: } catch (ClassNotFoundException cnfe) {
199: errorMessage = cnfe.getMessage();
200:
201: return false;
202: } catch (NoSuchFieldException nsfe) {
203: errorMessage = nsfe.getMessage();
204:
205: return false;
206: } catch (NoSuchMethodException nsme) {
207: errorMessage = nsme.getMessage();
208:
209: return false;
210: } catch (SecurityException se) {
211: errorMessage = se.getMessage();
212:
213: return false;
214: } catch (IllegalAccessException iae) {
215: errorMessage = iae.getMessage();
216:
217: return false;
218: }
219:
220: break;
221: }
222:
223: return true;
224: }
225:
226: private static Object locateBrowser() {
227: if (browser != null) {
228: return browser;
229: }
230:
231: switch (jvm) {
232: case MRJ_2_0:
233:
234: try {
235: Integer finderCreatorCode = (Integer) makeOSType
236: .invoke(null, new Object[] { FINDER_CREATOR });
237: Object aeTarget = aeTargetConstructor
238: .newInstance(new Object[] { finderCreatorCode });
239: Integer gurlType = (Integer) makeOSType.invoke(null,
240: new Object[] { GURL_EVENT });
241: Object appleEvent = appleEventConstructor
242: .newInstance(new Object[] { gurlType, gurlType,
243: aeTarget, kAutoGenerateReturnID,
244: kAnyTransactionID });
245:
246: // Don't set browser = appleEvent because then the next time we call
247: // locateBrowser(), we'll get the same AppleEvent, to which we'll already have
248: // added the relevant parameter. Instead, regenerate the AppleEvent every time.
249: // There's probably a way to do this better; if any has any ideas, please let
250: // me know.
251: return appleEvent;
252: } catch (IllegalAccessException iae) {
253: browser = null;
254: errorMessage = iae.getMessage();
255:
256: return browser;
257: } catch (InstantiationException ie) {
258: browser = null;
259: errorMessage = ie.getMessage();
260:
261: return browser;
262: } catch (InvocationTargetException ite) {
263: browser = null;
264: errorMessage = ite.getMessage();
265:
266: return browser;
267: }
268:
269: case MRJ_2_1:
270:
271: File systemFolder;
272:
273: try {
274: systemFolder = (File) findFolder.invoke(null,
275: new Object[] { kSystemFolderType });
276: } catch (IllegalArgumentException iare) {
277: browser = null;
278: errorMessage = iare.getMessage();
279:
280: return browser;
281: } catch (IllegalAccessException iae) {
282: browser = null;
283: errorMessage = iae.getMessage();
284:
285: return browser;
286: } catch (InvocationTargetException ite) {
287: browser = null;
288: errorMessage = ite.getTargetException().getClass()
289: + ": " + ite.getTargetException().getMessage();
290:
291: return browser;
292: }
293:
294: String[] systemFolderFiles = systemFolder.list();
295:
296: // Avoid a FilenameFilter because that can't be stopped mid-list
297: for (int i = 0; i < systemFolderFiles.length; i++) {
298: try {
299: File file = new File(systemFolder,
300: systemFolderFiles[i]);
301:
302: if (!file.isFile()) {
303: continue;
304: }
305:
306: Object fileType = getFileType.invoke(null,
307: new Object[] { file });
308:
309: if (FINDER_TYPE.equals(fileType.toString())) {
310: browser = file.toString(); // Actually the Finder, but that's OK
311:
312: return browser;
313: }
314: } catch (IllegalArgumentException iare) {
315: browser = browser;
316: errorMessage = iare.getMessage();
317:
318: return null;
319: } catch (IllegalAccessException iae) {
320: browser = null;
321: errorMessage = iae.getMessage();
322:
323: return browser;
324: } catch (InvocationTargetException ite) {
325: browser = null;
326: errorMessage = ite.getTargetException().getClass()
327: + ": "
328: + ite.getTargetException().getMessage();
329:
330: return browser;
331: }
332: }
333:
334: browser = null;
335:
336: break;
337:
338: case WINDOWS_NT:
339: browser = "cmd.exe";
340:
341: break;
342:
343: case WINDOWS_9x:
344: browser = "command.com";
345:
346: break;
347:
348: case OTHER:
349: default:
350:
351: //browser = "netscape"; surely mozilla is the thing these days
352: browser = "mozilla";
353:
354: break;
355: }
356:
357: return browser;
358: }
359:
360: /**
361: *
362: *
363: * @param url
364: *
365: * @throws IOException
366: */
367: public static void openURL(String url) throws IOException {
368: if (!loadedWithoutErrors) {
369: throw new IOException("Exception in finding browser: "
370: + errorMessage);
371: }
372:
373: Object browser = locateBrowser();
374:
375: if (browser == null) {
376: throw new IOException("Unable to locate browser: "
377: + errorMessage);
378: }
379:
380: switch (jvm) {
381: case MRJ_2_0:
382:
383: Object aeDesc = null;
384:
385: try {
386: aeDesc = aeDescConstructor
387: .newInstance(new Object[] { url });
388: putParameter.invoke(browser, new Object[] {
389: keyDirectObject, aeDesc });
390: sendNoReply.invoke(browser, new Object[] {});
391: } catch (InvocationTargetException ite) {
392: throw new IOException(
393: "InvocationTargetException while creating AEDesc: "
394: + ite.getMessage());
395: } catch (IllegalAccessException iae) {
396: throw new IOException(
397: "IllegalAccessException while building AppleEvent: "
398: + iae.getMessage());
399: } catch (InstantiationException ie) {
400: throw new IOException(
401: "InstantiationException while creating AEDesc: "
402: + ie.getMessage());
403: } finally {
404: aeDesc = null; // Encourage it to get disposed if it was created
405: browser = null; // Ditto
406: }
407:
408: break;
409:
410: case MRJ_2_1:
411: Runtime.getRuntime().exec(
412: new String[] { (String) browser, url });
413:
414: break;
415:
416: case WINDOWS_NT:
417: case WINDOWS_9x:
418: Runtime.getRuntime().exec(
419: new String[] { (String) browser,
420: FIRST_WINDOWS_PARAMETER,
421: SECOND_WINDOWS_PARAMETER, url });
422:
423: break;
424:
425: case OTHER:
426:
427: // Assume that we're on Unix and that Netscape is installed
428: // First, attempt to open the URL in a currently running session of Netscape
429: Process process = Runtime.getRuntime().exec(
430: (String) browser + NETSCAPE_OPEN_PARAMETER_START
431: + url + NETSCAPE_OPEN_PARAMETER_END);
432:
433: try {
434: int exitCode = process.waitFor();
435:
436: if (exitCode != 0) { // if Netscape was not open
437: Runtime.getRuntime().exec(
438: new String[] { (String) browser, url });
439: }
440: } catch (InterruptedException ie) {
441: throw new IOException(
442: "InterruptedException while launching browser: "
443: + ie.getMessage());
444: }
445:
446: break;
447:
448: default:
449:
450: // This should never occur, but if it does, we'll try the simplest thing possible
451: Runtime.getRuntime().exec(
452: new String[] { (String) browser, url });
453:
454: break;
455: }
456: }
457: }
|