001: /*
002: * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package java.awt;
027:
028: import java.io.File;
029: import java.io.IOException;
030: import java.net.URISyntaxException;
031: import java.net.URI;
032: import java.net.URL;
033: import java.net.MalformedURLException;
034: import java.awt.AWTPermission;
035: import java.awt.GraphicsEnvironment;
036: import java.awt.HeadlessException;
037: import java.awt.peer.DesktopPeer;
038: import sun.awt.SunToolkit;
039: import sun.awt.HeadlessToolkit;
040: import java.io.FilePermission;
041: import sun.security.util.SecurityConstants;
042:
043: /**
044: * The {@code Desktop} class allows a Java application to launch
045: * associated applications registered on the native desktop to handle
046: * a {@link java.net.URI} or a file.
047: *
048: * <p> Supported operations include:
049: * <ul>
050: * <li>launching the user-default browser to show a specified
051: * URI;</li>
052: * <li>launching the user-default mail client with an optional
053: * {@code mailto} URI;</li>
054: * <li>launching a registered application to open, edit or print a
055: * specified file.</li>
056: * </ul>
057: *
058: * <p> This class provides methods corresponding to these
059: * operations. The methods look for the associated application
060: * registered on the current platform, and launch it to handle a URI
061: * or file. If there is no associated application or the associated
062: * application fails to be launched, an exception is thrown.
063: *
064: * <p> An application is registered to a URI or file type; for
065: * example, the {@code "sxi"} file extension is typically registered
066: * to StarOffice. The mechanism of registering, accessing, and
067: * launching the associated application is platform-dependent.
068: *
069: * <p> Each operation is an action type represented by the {@link
070: * Desktop.Action} class.
071: *
072: * <p> Note: when some action is invoked and the associated
073: * application is executed, it will be executed on the same system as
074: * the one on which the Java application was launched.
075: *
076: * @since 1.6
077: * @author Armin Chen
078: * @author George Zhang
079: */
080: public class Desktop {
081:
082: /**
083: * Represents an action type. Each platform supports a different
084: * set of actions. You may use the {@link Desktop#isSupported}
085: * method to determine if the given action is supported by the
086: * current platform.
087: * @see java.awt.Desktop#isSupported(java.awt.Desktop.Action)
088: * @since 1.6
089: */
090: public static enum Action {
091: /**
092: * Represents an "open" action.
093: * @see Desktop#open(java.io.File)
094: */
095: OPEN,
096: /**
097: * Represents an "edit" action.
098: * @see Desktop#edit(java.io.File)
099: */
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())
143: throw new HeadlessException();
144: if (!Desktop.isDesktopSupported()) {
145: throw new UnsupportedOperationException(
146: "Desktop API is not "
147: + "supported on the current platform");
148: }
149:
150: sun.awt.AppContext context = sun.awt.AppContext.getAppContext();
151: Desktop desktop = (Desktop) context.get(Desktop.class);
152:
153: if (desktop == null) {
154: desktop = new Desktop();
155: context.put(Desktop.class, desktop);
156: }
157:
158: return desktop;
159: }
160:
161: /**
162: * Tests whether this class is supported on the current platform.
163: * If it's supported, use {@link #getDesktop()} to retrieve an
164: * instance.
165: *
166: * @return <code>true</code> if this class is supported on the
167: * current platform; <code>false</code> otherwise
168: * @see #getDesktop()
169: */
170: public static boolean isDesktopSupported() {
171: Toolkit defaultToolkit = Toolkit.getDefaultToolkit();
172: if (defaultToolkit instanceof SunToolkit) {
173: return ((SunToolkit) defaultToolkit).isDesktopSupported();
174: }
175: return false;
176: }
177:
178: /**
179: * Tests whether an action is supported on the current platform.
180: *
181: * <p>Even when the platform supports an action, a file or URI may
182: * not have a registered application for the action. For example,
183: * most of the platforms support the {@link Desktop.Action#OPEN}
184: * action. But for a specific file, there may not be an
185: * application registered to open it. In this case, {@link
186: * #isSupported} may return {@code true}, but the corresponding
187: * action method will throw an {@link IOException}.
188: *
189: * @param action the specified {@link Action}
190: * @return <code>true</code> if the specified action is supported on
191: * the current platform; <code>false</code> otherwise
192: * @see Desktop.Action
193: */
194: public boolean isSupported(Action action) {
195: return peer.isSupported(action);
196: }
197:
198: /**
199: * Checks if the file is a valid file and readable.
200: *
201: * @throws SecurityException If a security manager exists and its
202: * {@link SecurityManager#checkRead(java.lang.String)} method
203: * denies read access to the file
204: * @throws NullPointerException if file is null
205: * @throws IllegalArgumentException if file doesn't exist
206: */
207: private static void checkFileValidation(File file) {
208: if (file == null)
209: throw new NullPointerException("File must not be null");
210:
211: if (!file.exists()) {
212: throw new IllegalArgumentException("The file: "
213: + file.getPath() + " doesn't exist.");
214: }
215:
216: file.canRead();
217: }
218:
219: /**
220: * Checks if the action type is supported.
221: *
222: * @param actionType the action type in question
223: * @throws UnsupportedOperationException if the specified action type is not
224: * supported on the current platform
225: */
226: private void checkActionSupport(Action actionType) {
227: if (!isSupported(actionType)) {
228: throw new UnsupportedOperationException(
229: "The "
230: + actionType.name()
231: + " action is not supported on the current platform!");
232: }
233: }
234:
235: /**
236: * Calls to the security manager's <code>checkPermission</code> method with
237: * an <code>AWTPermission("showWindowWithoutWarningBanner")</code>
238: * permission.
239: */
240: private void checkAWTPermission() {
241: SecurityManager sm = System.getSecurityManager();
242: if (sm != null) {
243: sm.checkPermission(new AWTPermission(
244: "showWindowWithoutWarningBanner"));
245: }
246: }
247:
248: /**
249: * Launches the associated application to open the file.
250: *
251: * <p> If the specified file is a directory, the file manager of
252: * the current platform is launched to open it.
253: *
254: * @param file the file to be opened with the associated application
255: * @throws NullPointerException if {@code file} is {@code null}
256: * @throws IllegalArgumentException if the specified file doesn't
257: * exist
258: * @throws UnsupportedOperationException if the current platform
259: * does not support the {@link Desktop.Action#OPEN} action
260: * @throws IOException if the specified file has no associated
261: * application or the associated application fails to be launched
262: * @throws SecurityException if a security manager exists and its
263: * {@link java.lang.SecurityManager#checkRead(java.lang.String)}
264: * method denies read access to the file, or it denies the
265: * <code>AWTPermission("showWindowWithoutWarningBanner")</code>
266: * permission, or the calling thread is not allowed to create a
267: * subprocess
268: * @see java.awt.AWTPermission
269: */
270: public void open(File file) throws IOException {
271: checkAWTPermission();
272: checkExec();
273: checkActionSupport(Action.OPEN);
274: checkFileValidation(file);
275:
276: peer.open(file);
277: }
278:
279: /**
280: * Launches the associated editor application and opens a file for
281: * editing.
282: *
283: * @param file the file to be opened for editing
284: * @throws NullPointerException if the specified file is {@code null}
285: * @throws IllegalArgumentException if the specified file doesn't
286: * exist
287: * @throws UnsupportedOperationException if the current platform
288: * does not support the {@link Desktop.Action#EDIT} action
289: * @throws IOException if the specified file has no associated
290: * editor, or the associated application fails to be launched
291: * @throws SecurityException if a security manager exists and its
292: * {@link java.lang.SecurityManager#checkRead(java.lang.String)}
293: * method denies read access to the file, or {@link
294: * java.lang.SecurityManager#checkWrite(java.lang.String)} method
295: * denies write access to the file, or it denies the
296: * <code>AWTPermission("showWindowWithoutWarningBanner")</code>
297: * permission, or the calling thread is not allowed to create a
298: * subprocess
299: * @see java.awt.AWTPermission
300: */
301: public void edit(File file) throws IOException {
302: checkAWTPermission();
303: checkExec();
304: checkActionSupport(Action.EDIT);
305: file.canWrite();
306: checkFileValidation(file);
307:
308: peer.edit(file);
309: }
310:
311: /**
312: * Prints a file with the native desktop printing facility, using
313: * the associated application's print command.
314: *
315: * @param file the file to be printed
316: * @throws NullPointerException if the specified file is {@code
317: * null}
318: * @throws IllegalArgumentException if the specified file doesn't
319: * exist
320: * @throws UnsupportedOperationException if the current platform
321: * does not support the {@link Desktop.Action#PRINT} action
322: * @throws IOException if the specified file has no associated
323: * application that can be used to print it
324: * @throws SecurityException if a security manager exists and its
325: * {@link java.lang.SecurityManager#checkRead(java.lang.String)}
326: * method denies read access to the file, or its {@link
327: * java.lang.SecurityManager#checkPrintJobAccess()} method denies
328: * the permission to print the file, or the calling thread is not
329: * allowed to create a subprocess
330: */
331: public void print(File file) throws IOException {
332: checkExec();
333: SecurityManager sm = System.getSecurityManager();
334: if (sm != null) {
335: sm.checkPrintJobAccess();
336: }
337: checkActionSupport(Action.PRINT);
338: checkFileValidation(file);
339:
340: peer.print(file);
341: }
342:
343: /**
344: * Launches the default browser to display a {@code URI}.
345: * If the default browser is not able to handle the specified
346: * {@code URI}, the application registered for handling
347: * {@code URIs} of the specified type is invoked. The application
348: * is determined from the protocol and path of the {@code URI}, as
349: * defined by the {@code URI} class.
350: * <p>
351: * If the calling thread does not have the necessary permissions,
352: * and this is invoked from within an applet,
353: * {@code AppletContext.showDocument()} is used. Similarly, if the calling
354: * does not have the necessary permissions, and this is invoked from within
355: * a Java Web Started application, {@code BasicService.showDocument()}
356: * is used.
357: *
358: * @param uri the URI to be displayed in the user default browser
359: * @throws NullPointerException if {@code uri} is {@code null}
360: * @throws UnsupportedOperationException if the current platform
361: * does not support the {@link Desktop.Action#BROWSE} action
362: * @throws IOException if the user default browser is not found,
363: * or it fails to be launched, or the default handler application
364: * failed to be launched
365: * @throws SecurityException if a security manager exists and it
366: * denies the
367: * <code>AWTPermission("showWindowWithoutWarningBanner")</code>
368: * permission, or the calling thread is not allowed to create a
369: * subprocess; and not invoked from within an applet or Java Web Started
370: * application
371: * @throws IllegalArgumentException if the necessary permissions
372: * are not available and the URI can not be converted to a {@code URL}
373: * @see java.net.URI
374: * @see java.awt.AWTPermission
375: * @see java.applet.AppletContext
376: */
377: public void browse(URI uri) throws IOException {
378: SecurityException securityException = null;
379: try {
380: checkAWTPermission();
381: checkExec();
382: } catch (SecurityException e) {
383: securityException = e;
384: }
385: checkActionSupport(Action.BROWSE);
386: if (uri == null) {
387: throw new NullPointerException();
388: }
389: if (securityException == null) {
390: peer.browse(uri);
391: return;
392: }
393:
394: // Calling thread doesn't have necessary priviledges.
395: // Delegate to DesktopBrowse so that it can work in
396: // applet/webstart.
397: URL url = null;
398: try {
399: url = uri.toURL();
400: } catch (MalformedURLException e) {
401: throw new IllegalArgumentException(
402: "Unable to convert URI to URL", e);
403: }
404: sun.awt.DesktopBrowse db = sun.awt.DesktopBrowse.getInstance();
405: if (db == null) {
406: // Not in webstart/applet, throw the exception.
407: throw securityException;
408: }
409: db.browse(url);
410: }
411:
412: /**
413: * Launches the mail composing window of the user default mail
414: * client.
415: *
416: * @throws UnsupportedOperationException if the current platform
417: * does not support the {@link Desktop.Action#MAIL} action
418: * @throws IOException if the user default mail client is not
419: * found, or it fails to be launched
420: * @throws SecurityException if a security manager exists and it
421: * denies the
422: * <code>AWTPermission("showWindowWithoutWarningBanner")</code>
423: * permission, or the calling thread is not allowed to create a
424: * subprocess
425: * @see java.awt.AWTPermission
426: */
427: public void mail() throws IOException {
428: checkAWTPermission();
429: checkExec();
430: checkActionSupport(Action.MAIL);
431: URI mailtoURI = null;
432: try {
433: mailtoURI = new URI("mailto:?");
434: peer.mail(mailtoURI);
435: } catch (URISyntaxException e) {
436: // won't reach here.
437: }
438: }
439:
440: /**
441: * Launches the mail composing window of the user default mail
442: * client, filling the message fields specified by a {@code
443: * mailto:} URI.
444: *
445: * <p> A <code>mailto:</code> URI can specify message fields
446: * including <i>"to"</i>, <i>"cc"</i>, <i>"subject"</i>,
447: * <i>"body"</i>, etc. See <a
448: * href="http://www.ietf.org/rfc/rfc2368.txt">The mailto URL
449: * scheme (RFC 2368)</a> for the {@code mailto:} URI specification
450: * details.
451: *
452: * @param mailtoURI the specified {@code mailto:} URI
453: * @throws NullPointerException if the specified URI is {@code
454: * null}
455: * @throws IllegalArgumentException if the URI scheme is not
456: * <code>"mailto"</code>
457: * @throws UnsupportedOperationException if the current platform
458: * does not support the {@link Desktop.Action#MAIL} action
459: * @throws IOException if the user default mail client is not
460: * found or fails to be launched
461: * @throws SecurityException if a security manager exists and it
462: * denies the
463: * <code>AWTPermission("showWindowWithoutWarningBanner")</code>
464: * permission, or the calling thread is not allowed to create a
465: * subprocess
466: * @see java.net.URI
467: * @see java.awt.AWTPermission
468: */
469: public void mail(URI mailtoURI) throws IOException {
470: checkAWTPermission();
471: checkExec();
472: checkActionSupport(Action.MAIL);
473: if (mailtoURI == null)
474: throw new NullPointerException();
475:
476: if (!"mailto".equalsIgnoreCase(mailtoURI.getScheme())) {
477: throw new IllegalArgumentException(
478: "URI scheme is not \"mailto\"");
479: }
480:
481: peer.mail(mailtoURI);
482: }
483:
484: private void checkExec() throws SecurityException {
485: SecurityManager sm = System.getSecurityManager();
486: if (sm != null) {
487: sm.checkPermission(new FilePermission("<<ALL FILES>>",
488: SecurityConstants.FILE_EXECUTE_ACTION));
489: }
490: }
491: }
|