001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2025 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 https://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 org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040 041import ucar.unidata.util.LogUtil; 042 043import edu.wisc.ssec.mcidasv.McIDASV; 044 045/** 046 * A simple utility class for opening a web browser to a given link. 047 */ 048public final class WebBrowser { 049 050 /** Logging object. */ 051 private static final Logger logger = 052 LoggerFactory.getLogger(WebBrowser.class); 053 054 /** Probe Unix-like systems for these browsers, in this order. */ 055 private static final List<String> unixBrowsers = 056 list("firefox", "chromium-browser", "google-chrome", "konqueror", 057 "opera", "mozilla", "netscape"); 058 059 /** 060 * {@code IOException} formatting string used when all browsing methods 061 * have failed. 062 */ 063 private static final String ALL_METHODS_ERRMSG = "Could not open '%s'"; 064 065 /** 066 * {@code IOException} formatting string used by 067 * {@link #openOldStyle(String)} when no browsers could be identified on 068 * the system. 069 */ 070 private static final String NO_BROWSER_ERRMSG = 071 "Could not find a web browser to launch (tried %s)"; 072 073 /** Message displayed to the user when all browsing methods have failed. */ 074 private static final String THINGS_DUN_BROKE_ERRMSG = 075 "All three approaches for opening a browser " + 076 "failed!\nPlease consider sending a support request via\n" + 077 "the button below.\n"; 078 079 /** Do not create instances of {@code WebBrowser}. */ 080 private WebBrowser() { } 081 082 /** 083 * Attempts to use the system default browser to visit {@code url}. Tries 084 * looking for and executing any browser specified by the IDV property 085 * {@literal "idv.browser.path"}. 086 * 087 * <p>If the property wasn't given or there 088 * was an error, try the new (as of Java 1.6) way of opening a browser. 089 * 090 * <p>If the previous attempts failed (or we're in 1.5), we finally try 091 * some more primitive measures. 092 * 093 * <p>Note: if you are trying to use this method with a 094 * {@link javax.swing.JTextPane} you may need to turn off editing via 095 * {@link javax.swing.JTextPane#setEditable(boolean)}. 096 * 097 * @param url URL to visit. 098 * 099 * @see #tryUserSpecifiedBrowser(String) 100 * @see #openNewStyle(String) 101 * @see #openOldStyle(String) 102 */ 103 public static void browse(final String url) { 104 // if the user has taken the trouble to explicitly provide the path to 105 // a web browser, we should probably prefer it. 106 if (tryUserSpecifiedBrowser(url)) { 107 return; 108 } 109 110 // try using the JDK-supported approach 111 if (openNewStyle(url)) { 112 return; 113 } 114 115 // if not, use the hacky stuff. 116 try { 117 openOldStyle(url); 118 } catch (Exception e) { 119 logger.warn(String.format(ALL_METHODS_ERRMSG, url), e); 120 IOException uhoh = 121 new IOException(String.format(ALL_METHODS_ERRMSG, url)); 122 LogUtil.logException(THINGS_DUN_BROKE_ERRMSG, uhoh); 123 } 124 } 125 126 /** 127 * Test whether or not a given URL should be opened in a web browser. 128 * 129 * @param url URL to test. Cannot be {@code null}. 130 * 131 * @return {@code true} if {@code url} begins with either 132 * {@literal "http:"} or {@literal "https:"}. 133 */ 134 public static boolean useBrowserForUrl(final String url) { 135 String lowercase = url.toLowerCase(); 136 return lowercase.startsWith("http:") || lowercase.startsWith("https:"); 137 } 138 139 /** 140 * Use the functionality within {@link java.awt.Desktop} to try opening 141 * the user's preferred web browser. 142 * 143 * @param url URL to visit. 144 * 145 * @return Either {@code true} if things look ok, {@code false} if there 146 * were problems. 147 */ 148 private static boolean openNewStyle(final String url) { 149 boolean retVal = false; 150 if (Desktop.isDesktopSupported()) { 151 Desktop desktop = Desktop.getDesktop(); 152 if (desktop.isSupported(Desktop.Action.BROWSE)) { 153 try { 154 desktop.browse(new URI(url)); 155 // well... the assumption is that there was not a problem 156 retVal = true; 157 } catch (URISyntaxException e) { 158 logger.warn("Bad syntax in URI: "+url, e); 159 } catch (IOException e) { 160 logger.warn("Problem accessing URI: "+url, e); 161 } 162 } 163 } 164 return retVal; 165 } 166 167 /** 168 * Uses {@link Runtime#exec(String)} to launch the user's preferred web 169 * browser. This method isn't really recommended unless you're stuck with 170 * Java 1.5. 171 * 172 * <p>Note that the browsers need to be somewhere in the PATH, as this 173 * method uses the {@code which} command (also needs to be in the PATH!). 174 * 175 * @param url URL to visit. 176 */ 177 private static void openOldStyle(final String url) { 178 try { 179 if (isWindows()) { 180 Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url); 181 } else if (isMac()) { 182 Runtime.getRuntime().exec("/usr/bin/open "+url); 183 } else { 184 for (String browser : unixBrowsers) { 185 if (Runtime.getRuntime().exec("which "+browser).waitFor() == 0) { 186 Runtime.getRuntime().exec(browser+' '+url); 187 return; 188 } 189 } 190 String msg = String.format(NO_BROWSER_ERRMSG, unixBrowsers); 191 throw new IOException(msg); 192 } 193 } catch (Exception e) { 194 logger.warn("Could not open URL '"+url+'\'', e); 195 } 196 } 197 198 /** 199 * Attempts to launch the browser pointed at by 200 * the {@literal "idv.browser.path"} IDV property, if it has been set. 201 * 202 * @param url URL to open. 203 * 204 * @return Either {@code true} if the command-line was executed, 205 * {@code false} if either the command-line wasn't launched or 206 * {@literal "idv.browser.path"} was not set. 207 */ 208 private static boolean tryUserSpecifiedBrowser(final String url) { 209 McIDASV mcv = McIDASV.getStaticMcv(); 210 boolean retVal = false; 211 if (mcv != null) { 212 String browserPath = mcv.getProperty("idv.browser.path", null); 213 if ((browserPath != null) && !browserPath.trim().isEmpty()) { 214 try { 215 Runtime.getRuntime().exec(browserPath+' '+url); 216 retVal = true; 217 } catch (Exception e) { 218 logger.warn("Could not execute '"+browserPath+'\'', e); 219 } 220 } 221 } 222 return retVal; 223 } 224 225 /** 226 * Test for whether or not the current platform is Mac OS X. 227 * 228 * @return Are we shiny, happy OS X users? 229 */ 230 private static boolean isMac() { 231 return System.getProperty("os.name", "").startsWith("Mac OS"); 232 } 233 234 /** 235 * Test for whether or not the current platform is some form of 236 * {@literal "unix"} (but not OS X!). 237 * 238 * @return Do we perhaps think that beards and suspenders are the height 239 * of fashion? 240 */ 241 private static boolean isUnix() { 242 return !isMac() && !isWindows(); 243 } 244 245 /** 246 * Test for whether or not the current platform is Windows. 247 * 248 * @return Are we running Windows?? 249 */ 250 private static boolean isWindows() { 251 return System.getProperty("os.name", "").startsWith("Windows"); 252 } 253 254 public static void main(String[] args) { 255 browse("http://www.rust-lang.org/"); // sassy! 256 } 257}