blob: c56769804ef5c3e97ddcbc14accbc0955aead93f [file] [log] [blame]
/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.util;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
/**
* Provides a platform and JDK-independent method of launching a browser
* given a URI.
*
* <p>Portions derived from public-domain code at
* <pre>http://www.centerkey.com/java/browser/</pre>
*/
public class BrowserLauncher {
/**
* A browser launcher that uses JDK 1.6 Desktop.browse support.
*/
private static class Jdk16Launcher extends ReflectiveLauncher {
/**
* Create a Jdk16Launcher if supported.
*
* @throws UnsupportedOperationException if not supported
*/
public Jdk16Launcher() throws UnsupportedOperationException {
try {
Class<?> desktopClass = Class.forName("java.awt.Desktop");
browseMethod = desktopClass.getMethod("browse", URI.class);
Method factory = desktopClass.getMethod("getDesktop");
browseObject = factory.invoke(null);
return;
} catch (ClassNotFoundException e) {
// not on JDK 1.6, try other methods
} catch (NoSuchMethodException e) {
// not on JDK 1.6, try other methods
} catch (SecurityException e) {
// ignore, try other methods
} catch (IllegalArgumentException e) {
// ignore, try other methods
} catch (IllegalAccessException e) {
// ignore, try other methods
} catch (InvocationTargetException e) {
// ignore, try other methods
}
throw new UnsupportedOperationException("no JDK 1.6 Desktop.browse");
}
@Override
protected Object convertUrl(String url) throws URISyntaxException, MalformedURLException {
return new URL(url).toURI();
}
}
private interface Launcher {
void browse(String url) throws IOException, URISyntaxException;
}
/**
* Launch the default browser on Mac via FileManager openURL.
*/
private static class MacLauncher extends ReflectiveLauncher {
public MacLauncher() throws UnsupportedOperationException {
Throwable caught = null;
try {
Class<?> fileManager = Class.forName("com.apple.eio.FileManager");
browseMethod = fileManager.getMethod("openURL", String.class);
browseObject = null;
return;
} catch (SecurityException e) {
caught = e;
} catch (ClassNotFoundException e) {
caught = e;
} catch (NoSuchMethodException e) {
caught = e;
}
throw new UnsupportedOperationException("Can't get openURL", caught);
}
}
/**
* Interface for launching a URL in a browser, which uses reflection.
*
* <p>Subclass must set browseObject and browseMethod appropriately.
*/
private abstract static class ReflectiveLauncher implements Launcher {
protected Object browseObject;
protected Method browseMethod;
public void browse(String url) throws IOException, URISyntaxException {
Object arg = convertUrl(url);
Throwable caught = null;
try {
browseMethod.invoke(browseObject, arg);
return;
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof IOException) {
throw (IOException) cause;
}
caught = e;
} catch (IllegalAccessException e) {
caught = e;
}
throw new RuntimeException("Unexpected exception", caught);
}
/**
* Convert the URL into another form if required. The default
* implementation simply returns the unmodified string.
*
* @param url URL in string form
* @return the URL in the form needed for browseMethod
* @throws URISyntaxException
* @throws MalformedURLException
*/
protected Object convertUrl(String url) throws URISyntaxException,
MalformedURLException {
return url;
}
}
/**
* Launch a browser by searching for a browser executable on the path.
*/
private static class UnixExecBrowserLauncher implements Launcher {
private static final String[] browsers = {
"firefox", "opera", "konqueror", "chrome", "chromium", "epiphany",
"seamonkey", "mozilla", "netscape", "galeon", "kazehakase",
};
private String browserExecutable;
/**
* Creates a launcher by searching for a suitable browser executable.
* Assumes the presence of the "which" command.
*
* @throws UnsupportedOperationException if no suitable browser can be
* found.
*/
public UnixExecBrowserLauncher() throws UnsupportedOperationException {
for (String browser : browsers) {
try {
Process process = Runtime.getRuntime().exec(new String[] { "which",
browser});
if (process.waitFor() == 0) {
browserExecutable = browser;
return;
}
} catch (IOException e) {
// ignore, try next one
} catch (InterruptedException e) {
// ignore, try next one
}
}
throw new UnsupportedOperationException("no suitable browser found");
}
public void browse(String url) throws IOException {
Runtime.getRuntime().exec(new String[] { browserExecutable, url });
// TODO(jat): do we need to wait for it to exit and check exit status?
// That would be best for Firefox, but bad for some of the other browsers.
}
}
/**
* Launch the default browser on Windows via the URL protocol handler.
*/
private static class WindowsLauncher implements Launcher {
public void browse(String url) throws IOException {
Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
// TODO(jat): do we need to wait for it to exit and check exit status?
}
}
private static Launcher launcher;
/**
* Browse to a given URI.
*
* @param url
* @throws IOException
* @throws URISyntaxException
*/
public static void browse(String url) throws IOException, URISyntaxException {
if (launcher == null) {
findLauncher();
}
launcher.browse(url);
}
/**
* Main method so this can be run from the command line for testing.
*
* @param args URL to launch
* @throws URISyntaxException
* @throws IOException
*/
public static void main(String[] args) throws IOException,
URISyntaxException {
if (args.length == 0) {
System.err.println("Usage: BrowserLauncher url...");
System.exit(1);
}
for (String url : args) {
browse(url);
}
}
/**
* Initialize launcher to an appropriate one for the current platform/JDK.
*/
private static void findLauncher() {
try {
launcher = new Jdk16Launcher();
return;
} catch (UnsupportedOperationException e) {
// ignore and try other methods
}
String osName = System.getProperty("os.name");
if (osName.startsWith("Mac OS")) {
launcher = new MacLauncher();
} else if (osName.startsWith("Windows")) {
launcher = new WindowsLauncher();
} else {
launcher = new UnixExecBrowserLauncher();
// let UnsupportedOperationException escape to caller
}
}
}