001/* 002 * $Id: WebBrowser.java,v 1.7 2011/03/24 16:06:35 davep Exp $ 003 * 004 * This file is part of McIDAS-V 005 * 006 * Copyright 2007-2011 007 * Space Science and Engineering Center (SSEC) 008 * University of Wisconsin - Madison 009 * 1225 W. Dayton Street, Madison, WI 53706, USA 010 * https://www.ssec.wisc.edu/mcidas 011 * 012 * All Rights Reserved 013 * 014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 015 * some McIDAS-V source code is based on IDV and VisAD source code. 016 * 017 * McIDAS-V is free software; you can redistribute it and/or modify 018 * it under the terms of the GNU Lesser Public License as published by 019 * the Free Software Foundation; either version 3 of the License, or 020 * (at your option) any later version. 021 * 022 * McIDAS-V is distributed in the hope that it will be useful, 023 * but WITHOUT ANY WARRANTY; without even the implied warranty of 024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 025 * GNU Lesser Public License for more details. 026 * 027 * You should have received a copy of the GNU Lesser Public License 028 * along with this program. If not, see http://www.gnu.org/licenses. 029 */ 030package edu.wisc.ssec.mcidasv.util; 031 032import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.list; 033 034import java.io.IOException; 035import java.lang.reflect.Method; 036import java.net.URI; 037import java.util.List; 038 039import javax.swing.JOptionPane; 040 041import ucar.unidata.util.LogUtil; 042 043import edu.wisc.ssec.mcidasv.McIDASV; 044 045public final class WebBrowser { 046 047 /** Probe Unix-like systems for these browsers, in this order. */ 048 private static final List<String> unixBrowsers = 049 list("firefox", "konqueror", "opera", "mozilla", "netscape"); 050 051 /** None shall instantiate WebBrowser!! */ 052 private WebBrowser() { } 053 054 /** 055 * Attempts to use the system default browser to visit {@code url}. Tries 056 * looking for and executing any browser specified by the IDV property 057 * {@literal "idv.browser.path"}. 058 * 059 * <p>If the property wasn't given or there 060 * was an error, try the new (as of Java 1.6) way of opening a browser. 061 * 062 * <p>If the previous attempts failed (or we're in 1.5), we finally try 063 * some more primitive measures. 064 * 065 * <p>Note: if you are trying to use this method with a 066 * {@link javax.swing.JTextPane} you may need to turn off editing via 067 * {@link javax.swing.JTextPane#setEditable(boolean)}. 068 * 069 * @param url URL to visit. 070 * 071 * @see #tryUserSpecifiedBrowser(String) 072 * @see #openNewStyle(String) 073 * @see #openOldStyle(String) 074 */ 075 public static void browse(final String url) { 076 // if the user has taken the trouble to explicitly provide the path to 077 // a web browser, we should probably use it. 078 if (tryUserSpecifiedBrowser(url)) 079 return; 080 081 // determine whether or not we can use the 1.6 classes 082 if (canAttemptNewStyle()) 083 if (openNewStyle(url)) 084 return; 085 086 // if not, use the hacky stuff. 087 openOldStyle(url); 088 } 089 090 /** 091 * Uses the new functionality in {@link java.awt.Desktop} to try opening 092 * the browser. Because McIDAS-V does not yet require Java 1.6, and 093 * {@code Desktop} was introduced in 1.6, we have to jump through some 094 * reflection hoops. 095 * 096 * @param url URL to visit. 097 * 098 * @return Either {@code true} if things look ok, {@code false} if there 099 * were problems. 100 */ 101 private static boolean openNewStyle(final String url) { 102 boolean retVal = true; 103 try { 104 Class<?> desktop = Class.forName("java.awt.Desktop"); 105 Method isDesktopSupported = desktop.getMethod("isDesktopSupported", (Class[])null); 106 Boolean b = (Boolean)isDesktopSupported.invoke(null, (Object[])null); 107 if (b.booleanValue()) { 108 final Object desktopInstance = desktop.getMethod("getDesktop", (Class[])null).invoke(null, (Object[])null); 109 Class<?> desktopAction = Class.forName("java.awt.Desktop$Action"); 110 Method isSupported = desktop.getMethod("isSupported", new Class[] { desktopAction }); 111 Object browseConst = desktopAction.getField("BROWSE").get(null); 112 b = (Boolean)isSupported.invoke(desktopInstance, new Object[] {browseConst}); 113 if (b.booleanValue()) { 114 final Method browse = desktop.getMethod("browse", new Class[]{ URI.class }); 115 browse.invoke(desktopInstance, new Object[] { new URI(url) }); 116 retVal = true; 117 } else { 118 retVal = false; 119 } 120 } else { 121 retVal = false; 122 } 123 } catch (ClassNotFoundException e) { 124 // JDK 5, ignore 125 retVal = false; 126 } catch (Exception e) { 127 retVal = false; 128 } 129 return retVal; 130 } 131 132 /** 133 * Uses {@link Runtime#exec(String)} to launch the user's preferred web 134 * browser. This method isn't really recommended unless you're stuck with 135 * Java 1.5. 136 * 137 * <p>Note that the browsers need to be somewhere in the PATH, as this 138 * method uses the {@code which} command (also needs to be in the PATH!). 139 * 140 * @param url URL to visit. 141 */ 142 private static void openOldStyle(final String url) { 143 try { 144 if (isWindows()) { 145 Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url); 146 } else if (isMac()) { 147 Runtime.getRuntime().exec("/usr/bin/open "+url); 148 } else { 149 for (String browser : unixBrowsers) { 150 if (Runtime.getRuntime().exec("which "+browser).waitFor() == 0) { 151 Runtime.getRuntime().exec(browser+" "+url); 152 return; 153 } 154 } 155 throw new IOException("Could not find a web browser to launch (tried "+unixBrowsers+")"); 156 } 157 } catch (Exception e) { 158 JOptionPane.showMessageDialog(null, "Problem running web browser:\n" + e.getLocalizedMessage()); 159 } 160 } 161 162 /** 163 * Attempts to launch the browser pointed at by 164 * the {@literal "idv.browser.path"} IDV property, if it has been set. 165 * 166 * @param url URL to open. 167 * 168 * @return Either {@code true} if the command-line was executed, {@code false} if 169 * either the command-line wasn't launched or {@literal "idv.browser.path"} 170 * was not set. 171 */ 172 private static boolean tryUserSpecifiedBrowser(final String url) { 173 McIDASV mcv = McIDASV.getStaticMcv(); 174 if (mcv != null) { 175 String browserPath = mcv.getProperty("idv.browser.path", (String)null); 176 if (browserPath != null && browserPath.trim().length() > 0) { 177 try { 178 Runtime.getRuntime().exec(browserPath+" "+url); 179 return true; 180 } catch (Exception e) { 181 LogUtil.logException("Executing browser: "+browserPath, e); 182 } 183 } 184 } 185 return false; 186 } 187 188 /** 189 * There's supposedly a bug lurking that can hang the JVM on Linux if 190 * {@code java.net.useSystemProxies} is enabled. Detect whether or not our 191 * configuration may trigger the bug. 192 * 193 * @return Either {@code true} if everything is ok, {@code false} 194 * otherwise. 195 */ 196 private static boolean canAttemptNewStyle() { 197 if (Boolean.getBoolean("java.net.useSystemProxies") && isUnix()) { 198 // remove this check if JDK's bug 6496491 is fixed or if we can 199 // assume ORBit >= 2.14.2 and gnome-vfs >= 2.16.1 200 return false; 201 } 202 return true; 203 } 204 205 /** 206 * @return Are we shiny, happy OS X users? 207 */ 208 private static boolean isMac() { 209 return System.getProperty("os.name", "").startsWith("Mac OS"); 210 } 211 212 /** 213 * @return Do we perhaps think that beards and suspenders are the height 214 * of fashion? 215 */ 216 private static boolean isUnix() { 217 return !isMac() && !isWindows(); 218 } 219 220 /** 221 * @return Are we running Windows?? 222 */ 223 private static boolean isWindows() { 224 return System.getProperty("os.name", "").startsWith("Windows"); 225 } 226 227 public static void main(String[] args) { 228 browse("http://www.haskell.org/"); // sassy! 229 } 230}