1 /* 2 * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.awt; 27 28 import java.awt.peer.DesktopPeer; 29 import java.io.File; 30 import java.io.FilePermission; 31 import java.io.IOException; 32 import java.net.MalformedURLException; 33 import java.net.URI; 34 import java.net.URISyntaxException; 35 import java.net.URL; 36 37 import sun.awt.SunToolkit; 38 import sun.security.util.SecurityConstants; 39 40 /** 41 * The {@code Desktop} class allows a Java application to launch 42 * associated applications registered on the native desktop to handle 43 * a {@link java.net.URI} or a file. 44 * 45 * <p> Supported operations include: 46 * <ul> 47 * <li>launching the user-default browser to show a specified 48 * URI;</li> 49 * <li>launching the user-default mail client with an optional 50 * {@code mailto} URI;</li> 51 * <li>launching a registered application to open, edit or print a 52 * specified file.</li> 53 * </ul> 54 * 55 * <p> This class provides methods corresponding to these 56 * operations. The methods look for the associated application 57 * registered on the current platform, and launch it to handle a URI 58 * or file. If there is no associated application or the associated 59 * application fails to be launched, an exception is thrown. 60 * 61 * <p> An application is registered to a URI or file type; for 62 * example, the {@code "sxi"} file extension is typically registered 63 * to StarOffice. The mechanism of registering, accessing, and 64 * launching the associated application is platform-dependent. 65 * 66 * <p> Each operation is an action type represented by the {@link 67 * Desktop.Action} class. 68 * 69 * <p> Note: when some action is invoked and the associated 70 * application is executed, it will be executed on the same system as 71 * the one on which the Java application was launched. 72 * 73 * @since 1.6 74 * @author Armin Chen 75 * @author George Zhang 76 */ 77 public class Desktop { 78 79 /** 80 * Represents an action type. Each platform supports a different 81 * set of actions. You may use the {@link Desktop#isSupported} 82 * method to determine if the given action is supported by the 83 * current platform. 84 * @see java.awt.Desktop#isSupported(java.awt.Desktop.Action) 85 * @since 1.6 86 */ 87 public static enum Action { 88 /** 89 * Represents an "open" action. 90 * @see Desktop#open(java.io.File) 91 */ 92 OPEN, 93 /** 94 * Represents an "edit" action. 95 * @see Desktop#edit(java.io.File) 96 */ 97 EDIT, 98 /** 99 * Represents a "print" action. 100 * @see Desktop#print(java.io.File) 101 */ 102 PRINT, 103 /** 104 * Represents a "mail" action. 105 * @see Desktop#mail() 106 * @see Desktop#mail(java.net.URI) 107 */ 108 MAIL, 109 /** 110 * Represents a "browse" action. 111 * @see Desktop#browse(java.net.URI) 112 */ 113 BROWSE 114 }; 115 116 private DesktopPeer peer; 117 118 /** 119 * Suppresses default constructor for noninstantiability. 120 */ 121 private Desktop() { 122 Toolkit defaultToolkit = Toolkit.getDefaultToolkit(); 123 // same cast as in isDesktopSupported() 124 if (defaultToolkit instanceof SunToolkit) { 125 peer = ((SunToolkit) defaultToolkit).createDesktopPeer(this); 126 } 127 } 128 129 /** 130 * Returns the <code>Desktop</code> instance of the current 131 * browser context. On some platforms the Desktop API may not be 132 * supported; use the {@link #isDesktopSupported} method to 133 * determine if the current desktop is supported. 134 * @return the Desktop instance of the current browser context 135 * @throws HeadlessException if {@link 136 * GraphicsEnvironment#isHeadless()} returns {@code true} 137 * @throws UnsupportedOperationException if this class is not 138 * supported on the current platform 139 * @see #isDesktopSupported() 140 * @see java.awt.GraphicsEnvironment#isHeadless 141 */ 142 public static synchronized Desktop getDesktop(){ 143 if (GraphicsEnvironment.isHeadless()) throw new HeadlessException(); 144 if (!Desktop.isDesktopSupported()) { 145 throw new UnsupportedOperationException("Desktop API is not " + 146 "supported on the current platform"); 147 } 148 149 sun.awt.AppContext context = sun.awt.AppContext.getAppContext(); 150 Desktop desktop = (Desktop)context.get(Desktop.class); 151 152 if (desktop == null) { 153 desktop = new Desktop(); 154 context.put(Desktop.class, desktop); 155 } 156 157 return desktop; 158 } 159 160 /** 161 * Tests whether this class is supported on the current platform. 162 * If it's supported, use {@link #getDesktop()} to retrieve an 163 * instance. 164 * 165 * @return <code>true</code> if this class is supported on the 166 * current platform; <code>false</code> otherwise 167 * @see #getDesktop() 168 */ 169 public static boolean isDesktopSupported(){ 170 Toolkit defaultToolkit = Toolkit.getDefaultToolkit(); 171 if (defaultToolkit instanceof SunToolkit) { 172 return ((SunToolkit)defaultToolkit).isDesktopSupported(); 173 } 174 return false; 175 } 176 177 /** 178 * Tests whether an action is supported on the current platform. 179 * 180 * <p>Even when the platform supports an action, a file or URI may 181 * not have a registered application for the action. For example, 182 * most of the platforms support the {@link Desktop.Action#OPEN} 183 * action. But for a specific file, there may not be an 184 * application registered to open it. In this case, {@link 185 * #isSupported} may return {@code true}, but the corresponding 186 * action method will throw an {@link IOException}. 187 * 188 * @param action the specified {@link Action} 189 * @return <code>true</code> if the specified action is supported on 190 * the current platform; <code>false</code> otherwise 191 * @see Desktop.Action 192 */ 193 public boolean isSupported(Action action) { 194 return peer.isSupported(action); 195 } 196 197 /** 198 * Checks if the file is a valid file and readable. 199 * 200 * @throws SecurityException If a security manager exists and its 201 * {@link SecurityManager#checkRead(java.lang.String)} method 202 * denies read access to the file 203 * @throws NullPointerException if file is null 204 * @throws IllegalArgumentException if file doesn't exist 205 */ 206 private static void checkFileValidation(File file){ 207 if (file == null) throw new NullPointerException("File must not be null"); 208 209 if (!file.exists()) { 210 throw new IllegalArgumentException("The file: " 211 + file.getPath() + " doesn't exist."); 212 } 213 214 file.canRead(); 215 } 216 217 /** 218 * Checks if the action type is supported. 219 * 220 * @param actionType the action type in question 221 * @throws UnsupportedOperationException if the specified action type is not 222 * supported on the current platform 223 */ 224 private void checkActionSupport(Action actionType){ 225 if (!isSupported(actionType)) { 226 throw new UnsupportedOperationException("The " + actionType.name() 227 + " action is not supported on the current platform!"); 228 } 229 } 230 231 232 /** 233 * Calls to the security manager's <code>checkPermission</code> method with 234 * an <code>AWTPermission("showWindowWithoutWarningBanner")</code> 235 * permission. 236 */ 237 private void checkAWTPermission(){ 238 SecurityManager sm = System.getSecurityManager(); 239 if (sm != null) { 240 sm.checkPermission(new AWTPermission( 241 "showWindowWithoutWarningBanner")); 242 } 243 } 244 245 /** 246 * Launches the associated application to open the file. 247 * 248 * <p> If the specified file is a directory, the file manager of 249 * the current platform is launched to open it. 250 * 251 * @param file the file to be opened with the associated application 252 * @throws NullPointerException if {@code file} is {@code null} 253 * @throws IllegalArgumentException if the specified file doesn't 254 * exist 255 * @throws UnsupportedOperationException if the current platform 256 * does not support the {@link Desktop.Action#OPEN} action 257 * @throws IOException if the specified file has no associated 258 * application or the associated application fails to be launched 259 * @throws SecurityException if a security manager exists and its 260 * {@link java.lang.SecurityManager#checkRead(java.lang.String)} 261 * method denies read access to the file, or it denies the 262 * <code>AWTPermission("showWindowWithoutWarningBanner")</code> 263 * permission, or the calling thread is not allowed to create a 264 * subprocess 265 * @see java.awt.AWTPermission 266 */ 267 public void open(File file) throws IOException { 268 checkAWTPermission(); 269 checkExec(); 270 checkActionSupport(Action.OPEN); 271 checkFileValidation(file); 272 273 peer.open(file); 274 } 275 276 /** 277 * Launches the associated editor application and opens a file for 278 * editing. 279 * 280 * @param file the file to be opened for editing 281 * @throws NullPointerException if the specified file is {@code null} 282 * @throws IllegalArgumentException if the specified file doesn't 283 * exist 284 * @throws UnsupportedOperationException if the current platform 285 * does not support the {@link Desktop.Action#EDIT} action 286 * @throws IOException if the specified file has no associated 287 * editor, or the associated application fails to be launched 288 * @throws SecurityException if a security manager exists and its 289 * {@link java.lang.SecurityManager#checkRead(java.lang.String)} 290 * method denies read access to the file, or {@link 291 * java.lang.SecurityManager#checkWrite(java.lang.String)} method 292 * denies write access to the file, or it denies the 293 * <code>AWTPermission("showWindowWithoutWarningBanner")</code> 294 * permission, or the calling thread is not allowed to create a 295 * subprocess 296 * @see java.awt.AWTPermission 297 */ 298 public void edit(File file) throws IOException { 299 checkAWTPermission(); 300 checkExec(); 301 checkActionSupport(Action.EDIT); 302 file.canWrite(); 303 checkFileValidation(file); 304 305 peer.edit(file); 306 } 307 308 /** 309 * Prints a file with the native desktop printing facility, using 310 * the associated application's print command. 311 * 312 * @param file the file to be printed 313 * @throws NullPointerException if the specified file is {@code 314 * null} 315 * @throws IllegalArgumentException if the specified file doesn't 316 * exist 317 * @throws UnsupportedOperationException if the current platform 318 * does not support the {@link Desktop.Action#PRINT} action 319 * @throws IOException if the specified file has no associated 320 * application that can be used to print it 321 * @throws SecurityException if a security manager exists and its 322 * {@link java.lang.SecurityManager#checkRead(java.lang.String)} 323 * method denies read access to the file, or its {@link 324 * java.lang.SecurityManager#checkPrintJobAccess()} method denies 325 * the permission to print the file, or the calling thread is not 326 * allowed to create a subprocess 327 */ 328 public void print(File file) throws IOException { 329 checkExec(); 330 SecurityManager sm = System.getSecurityManager(); 331 if (sm != null) { 332 sm.checkPrintJobAccess(); 333 } 334 checkActionSupport(Action.PRINT); 335 checkFileValidation(file); 336 337 peer.print(file); 338 } 339 340 /** 341 * Launches the default browser to display a {@code URI}. 342 * If the default browser is not able to handle the specified 343 * {@code URI}, the application registered for handling 344 * {@code URIs} of the specified type is invoked. The application 345 * is determined from the protocol and path of the {@code URI}, as 346 * defined by the {@code URI} class. 347 * <p> 348 * If the calling thread does not have the necessary permissions, 349 * and this is invoked from within an applet, 350 * {@code AppletContext.showDocument()} is used. Similarly, if the calling 351 * does not have the necessary permissions, and this is invoked from within 352 * a Java Web Started application, {@code BasicService.showDocument()} 353 * is used. 354 * 355 * @param uri the URI to be displayed in the user default browser 356 * @throws NullPointerException if {@code uri} is {@code null} 357 * @throws UnsupportedOperationException if the current platform 358 * does not support the {@link Desktop.Action#BROWSE} action 359 * @throws IOException if the user default browser is not found, 360 * or it fails to be launched, or the default handler application 361 * failed to be launched 362 * @throws SecurityException if a security manager exists and it 363 * denies the 364 * <code>AWTPermission("showWindowWithoutWarningBanner")</code> 365 * permission, or the calling thread is not allowed to create a 366 * subprocess; and not invoked from within an applet or Java Web Started 367 * application 368 * @throws IllegalArgumentException if the necessary permissions 369 * are not available and the URI can not be converted to a {@code URL} 370 * @see java.net.URI 371 * @see java.awt.AWTPermission 372 * @see java.applet.AppletContext 373 */ 374 public void browse(URI uri) throws IOException { 375 SecurityException securityException = null; 376 try { 377 checkAWTPermission(); 378 checkExec(); 379 } catch (SecurityException e) { 380 securityException = e; 381 } 382 checkActionSupport(Action.BROWSE); 383 if (uri == null) { 384 throw new NullPointerException(); 385 } 386 if (securityException == null) { 387 peer.browse(uri); 388 return; 389 } 390 391 // Calling thread doesn't have necessary privileges. 392 // Delegate to DesktopBrowse so that it can work in 393 // applet/webstart. 394 URL url = null; 395 try { 396 url = uri.toURL(); 397 } catch (MalformedURLException e) { 398 throw new IllegalArgumentException("Unable to convert URI to URL", e); 399 } 400 sun.awt.DesktopBrowse db = sun.awt.DesktopBrowse.getInstance(); 401 if (db == null) { 402 // Not in webstart/applet, throw the exception. 403 throw securityException; 404 } 405 db.browse(url); 406 } 407 408 /** 409 * Launches the mail composing window of the user default mail 410 * client. 411 * 412 * @throws UnsupportedOperationException if the current platform 413 * does not support the {@link Desktop.Action#MAIL} action 414 * @throws IOException if the user default mail client is not 415 * found, or it fails to be launched 416 * @throws SecurityException if a security manager exists and it 417 * denies the 418 * <code>AWTPermission("showWindowWithoutWarningBanner")</code> 419 * permission, or the calling thread is not allowed to create a 420 * subprocess 421 * @see java.awt.AWTPermission 422 */ 423 public void mail() throws IOException { 424 checkAWTPermission(); 425 checkExec(); 426 checkActionSupport(Action.MAIL); 427 URI mailtoURI = null; 428 try{ 429 mailtoURI = new URI("mailto:?"); 430 peer.mail(mailtoURI); 431 } catch (URISyntaxException e){ 432 // won't reach here. 433 } 434 } 435 436 /** 437 * Launches the mail composing window of the user default mail 438 * client, filling the message fields specified by a {@code 439 * mailto:} URI. 440 * 441 * <p> A <code>mailto:</code> URI can specify message fields 442 * including <i>"to"</i>, <i>"cc"</i>, <i>"subject"</i>, 443 * <i>"body"</i>, etc. See <a 444 * href="http://www.ietf.org/rfc/rfc2368.txt">The mailto URL 445 * scheme (RFC 2368)</a> for the {@code mailto:} URI specification 446 * details. 447 * 448 * @param mailtoURI the specified {@code mailto:} URI 449 * @throws NullPointerException if the specified URI is {@code 450 * null} 451 * @throws IllegalArgumentException if the URI scheme is not 452 * <code>"mailto"</code> 453 * @throws UnsupportedOperationException if the current platform 454 * does not support the {@link Desktop.Action#MAIL} action 455 * @throws IOException if the user default mail client is not 456 * found or fails to be launched 457 * @throws SecurityException if a security manager exists and it 458 * denies the 459 * <code>AWTPermission("showWindowWithoutWarningBanner")</code> 460 * permission, or the calling thread is not allowed to create a 461 * subprocess 462 * @see java.net.URI 463 * @see java.awt.AWTPermission 464 */ 465 public void mail(URI mailtoURI) throws IOException { 466 checkAWTPermission(); 467 checkExec(); 468 checkActionSupport(Action.MAIL); 469 if (mailtoURI == null) throw new NullPointerException(); 470 471 if (!"mailto".equalsIgnoreCase(mailtoURI.getScheme())) { 472 throw new IllegalArgumentException("URI scheme is not \"mailto\""); 473 } 474 475 peer.mail(mailtoURI); 476 } 477 478 private void checkExec() throws SecurityException { 479 SecurityManager sm = System.getSecurityManager(); 480 if (sm != null) { 481 sm.checkPermission(new FilePermission("<<ALL FILES>>", 482 SecurityConstants.FILE_EXECUTE_ACTION)); 483 } 484 } 485 }