001/* Launch the user's default web browser. 002 003 Copyright (c) 2002-2018 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028package ptolemy.actor.gui; 029 030import java.awt.Desktop; 031import java.io.File; 032import java.io.IOException; 033import java.net.URI; 034import java.net.URL; 035 036import ptolemy.util.StringUtilities; 037 038/** 039 BrowserLauncher is a class that provides one static method, openURL, 040 which opens the default web browser for the current user of the system 041 to the given URL. It may support other protocols depending on the 042 system -- mailto, ftp, etc. -- but that has not been rigorously tested 043 and is not guaranteed to work. 044 045 @author Christopher Brooks 046 @version $Id$ 047 @since Ptolemy II 3.0 048 @Pt.ProposedRating Red (cxh) 049 @Pt.AcceptedRating Red (cxh) 050 */ 051public class BrowserLauncher { 052 /** Launch the browser on the first argument. If there is 053 * no first argument, then open http://ptolemy.eecs.berkeley.edu. 054 * Second and subsequent arguments are ignored. 055 * It is best if the first argument is an absolute URL 056 * as opposed to a relative URL. 057 * 058 * <p> For example, to open the user's default browser on 059 * http://www.eecs.berkeley.edu 060 * <pre> 061 * java -classpath $PTII ptolemy.actor.gui.BrowserLauncher http://www.eecs.berkeley.edu 062 * </pre> 063 * @param args An array of command line arguments. The first 064 * argument names a URL to be opened. If there is no first 065 * argument, then open http://ptolemy.eecs.berkeley.edu. Second 066 * and subsequent arguments are ignored. 067 * @exception Exception If there is a problem launching the browser. 068 */ 069 public static void main(String[] args) throws Exception { 070 if (args.length >= 1) { 071 // Ignore any arguments after the first one. 072 BrowserLauncher.openURL(args[0]); 073 } else { 074 BrowserLauncher.openURL("http://ptolemy.eecs.berkeley.edu"); 075 } 076 077 if (BrowserLauncher.delayExit) { 078 System.out.println("Delaying exit for 10 seconds because we" 079 + "may have copied a jar: file"); 080 081 try { 082 Thread.sleep(10000); 083 } catch (InterruptedException e) { 084 } 085 086 StringUtilities.exit(0); 087 } 088 } 089 090 /** Set to true if we copied a file out of a jar file so that the 091 * browser could display it. The reason we need this flag is 092 * that the system will delete the temporary file on exit, and 093 * after openURL() is called, this Java process will exit unless 094 * we delay. 095 */ 096 public static boolean delayExit = false; 097 098 /** 099 * Attempts to open the default web browser to the given URL. 100 * 101 * <p> We use the following strategy to find URLs that may be inside 102 * jar files: 103 * <br> If the string does not start with "http": see if it is a 104 * file. 105 * <br> If the file cannot be found, look it up in the classpath. 106 * <br> If the file can be found in the classpath then use the 107 * found file instead of the given URL. 108 * <br>If the file cannot be found in the classpath, then pass the 109 * original given URL to the browser. 110 * <p>If the ptolemy.ptII.browser property is set, then its value 111 * is used as the value of the browser. 112 * <br>To always use Internet Explorer, one might invoke Ptolemy 113 * with: 114 * <pre> 115 * java -classpath $PTII -Dptolemy.ptII.browser=c:\\Program\ Files\\Internet\ Explorer\\iexplore.exe ptolemy.vergil.VergilApplication 116 * </pre> 117 * <p>To always use Firefox: 118 * <pre> 119 * java -classpath $PTII -Dptolemy.ptII.browser=c:\\Program\ Files\\Mozilla\ Firefox\\firefox ptolemy.vergil.VergilApplication 120 * </pre> 121 * 122 * @param url The URL to open. 123 * It is best if the first argument is an absolute URL 124 * as opposed to a relative URL. 125 * @exception IOException If the web browser could not be located or 126 * does not run 127 */ 128 public static void openURL(String url) throws IOException { 129 if (!url.startsWith("http:") && !url.startsWith("https:")) { 130 // If the url does not start with http:, then look it up 131 // as a regular file and then possibly in the classpath. 132 File urlFile = null; 133 134 try { 135 urlFile = new File(url); 136 } catch (Exception ex) { 137 // Ignored, we try to fix this below. 138 urlFile = null; 139 } 140 141 if (urlFile == null || !urlFile.exists()) { 142 // The file could not be found, so try the search path mambo. 143 // We might be in the Swing Event thread, so 144 // Thread.currentThread().getContextClassLoader() 145 // .getResource(entry) probably will not work. 146 String refClassName = "ptolemy.kernel.util.NamedObj"; 147 148 try { 149 Class refClass = Class.forName(refClassName); 150 URL entryURL = refClass.getClassLoader().getResource(url); 151 152 if (entryURL != null && !url.startsWith("jar:")) { 153 System.out.println("BrowserLauncher: Could not " 154 + "find '" + url + "', but '" + entryURL 155 + "' was found."); 156 url = entryURL.toString(); 157 } else { 158 if (url.startsWith("jar:")) { 159 // If the URL begins with jar: then we are 160 // inside Web Start and we should get the 161 // resource, write it to a temporary file 162 // and pass that value to the browser 163 // Save the jar file as a temporary file 164 // in the default platform dependent 165 // directory with the same suffix as that 166 // of the jar URL 167 // FIXME: we should probably cache this 168 // copy somehow. 169 String old = url; 170 String temporaryURL = JNLPUtilities 171 .saveJarURLInClassPath(url); 172 173 if (temporaryURL != null) { 174 url = temporaryURL; 175 } else { 176 url = JNLPUtilities.saveJarURLAsTempFile(url, 177 "tmp", null, null); 178 delayExit = true; 179 } 180 181 System.out.println("BrowserLauncher: Could not " 182 + "find '" + old + "', but jar url'" + url 183 + "' was found."); 184 } 185 } 186 } catch (ClassNotFoundException ex) { 187 System.err.println("BrowserLauncher: Internal error, " 188 + " Could not find " + refClassName); 189 } 190 } 191 } 192 193 if (Desktop.isDesktopSupported()) { 194 Desktop desktop = Desktop.getDesktop(); 195 URI uri = null; 196 try { 197 uri = new URI(url); 198 } catch (Throwable throwable) { 199 ; 200 IOException exception = new IOException( 201 "Failed to convert url \"" + url + "\" to a uri."); 202 exception.initCause(throwable); 203 throw exception; 204 } 205 boolean invokeByHand = false; 206 try { 207 desktop.browse(uri); 208 return; 209 } catch (IOException ex) { 210 // System.out.println("BrowserLauncher: Failed to open " + uri + ": " + ex); 211 // ex.printStackTrace(); 212 File temporaryFile = JNLPUtilities 213 .getResourceSaveJarURLAsTempFile(uri.getPath()); 214 try { 215 desktop.browse(temporaryFile.toURI()); 216 return; 217 } catch (IOException ex2) { 218 System.out.println( 219 "BrowserLauncher: Failed to open " + temporaryFile 220 + ": " + ex2 + "\nAlso tried " + uri); 221 } 222 223 invokeByHand = true; 224 } catch (UnsupportedOperationException ex3) { 225 System.out.println("BrowserLauncher: UnsupportedOperation: " 226 + uri + ": " + ex3); 227 228 invokeByHand = true; 229 } 230 231 if (invokeByHand) { 232 String errorMessage = ""; 233 try { 234 // Under Linux, desktop.browse() may fail with: 235 236 // java.lang.UnsupportedOperationException: The 237 // BROWSE action is not supported on the current 238 // platform! 239 240 // So, we start up Firefox. 241 242 String browser = "firefox"; 243 String args[] = null; 244 String osName = System.getProperty("os.name"); 245 if (osName.startsWith("Windows")) { 246 browser = "cmd.exe"; 247 args = new String[] { browser, "/c", "start", "\"\"", 248 '"' + url + '"' }; 249 Process process = Runtime.getRuntime().exec(args); 250 errorMessage = "Command was: " + args[0] + " " + args[1] 251 + " " + args[2] + " " + args[3] + " " + args[4] 252 + ". " 253 + "Under Windows check that the file named by " 254 + url + " is executable."; 255 int exitCode = 0; 256 try { 257 exitCode = process.waitFor(); 258 process.exitValue(); 259 } catch (InterruptedException ie) { 260 throw new IOException("InterruptedException while " 261 + "launching " + browser + ": " + ie); 262 } 263 264 if (exitCode != 0) { 265 throw new IOException( 266 "Process exec'd by BrowserLauncher returned " 267 + exitCode + ". \nUrl was: " + url 268 + "\nBrowser was: " + errorMessage); 269 } 270 271 } else { 272 browser = "firefox"; 273 File macFirefox = new File( 274 "/Applications/Firefox.app/Contents/MacOS/firefox"); 275 if (macFirefox.exists()) { 276 browser = macFirefox.getCanonicalPath(); 277 } 278 args = new String[] { browser, "-remote", 279 "'openURL(" + url + ")'" }; 280 Process process = Runtime.getRuntime().exec(args); 281 282 errorMessage = "Command was: " + args[0] + " " + args[1] 283 + " " + args[2]; 284 285 try { 286 // If Firefox is not open then try invoking the browser. 287 if (process.waitFor() != 0) { 288 if (osName.startsWith("Mac OS X")) { 289 browser = "safari"; 290 } 291 292 Runtime.getRuntime() 293 .exec(new String[] { browser, url }); 294 } 295 } catch (InterruptedException ie) { 296 throw new IOException("InterruptedException while " 297 + "launching " + browser + ": " + ie); 298 } 299 return; 300 } 301 } catch (Throwable throwable) { 302 IOException exception = new IOException( 303 "Failed to open \"" + uri + "\". " + errorMessage); 304 exception.initCause(throwable); 305 throw exception; 306 } 307 308 } 309 } else { 310 throw new IOException( 311 "java.awt.Desktop is not supported on this platform, so we can't open \"" 312 + url + "\""); 313 } 314 315 } 316 317 /** 318 * This class should be never be instantiated; this just ensures so. 319 */ 320 private BrowserLauncher() { 321 } 322}