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