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