001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2015 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 */ 028package edu.wisc.ssec.mcidasv.util; 029 030import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.list; 031 032import java.awt.Desktop; 033import java.io.IOException; 034import java.net.URI; 035import java.net.URISyntaxException; 036import java.util.List; 037 038import javax.swing.JOptionPane; 039 040import ucar.unidata.util.LogUtil; 041 042import edu.wisc.ssec.mcidasv.McIDASV; 043 044/** 045 * A simple utility class for opening a web browser to a given link. 046 */ 047public final class WebBrowser { 048 049 /** Probe Unix-like systems for these browsers, in this order. */ 050 private static final List<String> unixBrowsers = 051 list("firefox", "chromium-browser", "google-chrome", "konqueror", 052 "opera", "mozilla", "netscape"); 053 054 /** Do not create instances of {@code WebBrowser}. */ 055 private WebBrowser() { } 056 057 /** 058 * Attempts to use the system default browser to visit {@code url}. Tries 059 * looking for and executing any browser specified by the IDV property 060 * {@literal "idv.browser.path"}. 061 * 062 * <p>If the property wasn't given or there 063 * was an error, try the new (as of Java 1.6) way of opening a browser. 064 * 065 * <p>If the previous attempts failed (or we're in 1.5), we finally try 066 * some more primitive measures. 067 * 068 * <p>Note: if you are trying to use this method with a 069 * {@link javax.swing.JTextPane} you may need to turn off editing via 070 * {@link javax.swing.JTextPane#setEditable(boolean)}. 071 * 072 * @param url URL to visit. 073 * 074 * @see #tryUserSpecifiedBrowser(String) 075 * @see #openNewStyle(String) 076 * @see #openOldStyle(String) 077 */ 078 public static void browse(final String url) { 079 // if the user has taken the trouble to explicitly provide the path to 080 // a web browser, we should probably use it. 081 if (tryUserSpecifiedBrowser(url)) { 082 return; 083 } 084 085 // determine whether or not we can use the 1.6 classes 086 if (canAttemptNewStyle()) { 087 if (openNewStyle(url)) { 088 return; 089 } 090 } 091 092 // if not, use the hacky stuff. 093 openOldStyle(url); 094 } 095 096 /** 097 * Use the functionality within {@link java.awt.Desktop} to try opening 098 * the user's preferred web browser. 099 * 100 * @param url URL to visit. 101 * 102 * @return Either {@code true} if things look ok, {@code false} if there 103 * were problems. 104 */ 105 private static boolean openNewStyle(final String url) { 106 boolean retVal = false; 107 if (Desktop.isDesktopSupported()) { 108 Desktop desktop = Desktop.getDesktop(); 109 if (desktop.isSupported(Desktop.Action.BROWSE)) { 110 try { 111 desktop.browse(new URI(url)); 112 // well... the assumption is that there was not a problem 113 retVal = true; 114 } catch (URISyntaxException e) { 115 LogUtil.logException("Bad syntax in URI: "+url, e); 116 } catch (IOException e) { 117 LogUtil.logException("Problem accessing URI: "+url, e); 118 } 119 } 120 } 121 return retVal; 122 } 123 124 /** 125 * Uses {@link Runtime#exec(String)} to launch the user's preferred web 126 * browser. This method isn't really recommended unless you're stuck with 127 * Java 1.5. 128 * 129 * <p>Note that the browsers need to be somewhere in the PATH, as this 130 * method uses the {@code which} command (also needs to be in the PATH!). 131 * 132 * @param url URL to visit. 133 */ 134 private static void openOldStyle(final String url) { 135 try { 136 if (isWindows()) { 137 Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url); 138 } else if (isMac()) { 139 Runtime.getRuntime().exec("/usr/bin/open "+url); 140 } else { 141 for (String browser : unixBrowsers) { 142 if (Runtime.getRuntime().exec("which "+browser).waitFor() == 0) { 143 Runtime.getRuntime().exec(browser+' '+url); 144 return; 145 } 146 } 147 throw new IOException("Could not find a web browser to launch (tried "+unixBrowsers+')'); 148 } 149 } catch (Exception e) { 150 JOptionPane.showMessageDialog(null, "Problem running web browser:\n" + e.getLocalizedMessage()); 151 } 152 } 153 154 /** 155 * Attempts to launch the browser pointed at by 156 * the {@literal "idv.browser.path"} IDV property, if it has been set. 157 * 158 * @param url URL to open. 159 * 160 * @return Either {@code true} if the command-line was executed, {@code false} if 161 * either the command-line wasn't launched or {@literal "idv.browser.path"} 162 * was not set. 163 */ 164 private static boolean tryUserSpecifiedBrowser(final String url) { 165 McIDASV mcv = McIDASV.getStaticMcv(); 166 boolean retVal = false; 167 if (mcv != null) { 168 String browserPath = mcv.getProperty("idv.browser.path", (String)null); 169 if (browserPath != null && !browserPath.trim().isEmpty()) { 170 try { 171 Runtime.getRuntime().exec(browserPath+' '+url); 172 retVal = true; 173 } catch (Exception e) { 174 LogUtil.logException("Executing browser: "+browserPath, e); 175 } 176 } 177 } 178 return retVal; 179 } 180 181 /** 182 * There's supposedly a bug lurking that can hang the JVM on Linux if 183 * {@code java.net.useSystemProxies} is enabled. Detect whether or not our 184 * configuration may trigger the bug. 185 * 186 * @return Either {@code true} if everything is ok, {@code false} 187 * otherwise. 188 */ 189 private static boolean canAttemptNewStyle() { 190 // remove this check if JDK's bug 6496491 is fixed or if we can 191 // assume ORBit >= 2.14.2 and gnome-vfs >= 2.16.1 192 return isUnix() && Boolean.valueOf(System.getProperty("java.net.useSystemProxies", "true")); 193 } 194 195 /** 196 * Test for whether or not the current platform is Mac OS X. 197 * 198 * @return Are we shiny, happy OS X users? 199 */ 200 private static boolean isMac() { 201 return System.getProperty("os.name", "").startsWith("Mac OS"); 202 } 203 204 /** 205 * Test for whether or not the current platform is some form of 206 * {@literal "unix"} (but not OS X!). 207 * 208 * @return Do we perhaps think that beards and suspenders are the height 209 * of fashion? 210 */ 211 private static boolean isUnix() { 212 return !isMac() && !isWindows(); 213 } 214 215 /** 216 * Test for whether or not the current platform is Windows. 217 * 218 * @return Are we running Windows?? 219 */ 220 private static boolean isWindows() { 221 return System.getProperty("os.name", "").startsWith("Windows"); 222 } 223 224 public static void main(String[] args) { 225 browse("http://www.haskell.org/"); // sassy! 226 } 227}