ADC Home > Reference Library > Technical Notes > Legacy Documents > Java >
Important: This document is part of the Legacy section of the ADC Reference Library. This information should not be used for new development.
Current information on this Reference Library topic can be found here:
|
The Platform-Dependent Perils of
|
Microsoft Internet Explorer |
|
Netscape Navigator |
|
iCab |
|
SimpleText |
|
Finder |
|
The MRJToolkit library, provided as part of the MRJ SDK (including extensive documentation)
includes a findApplication
method that takes a creator code and returns
a File object that points to the app. For example:
import com.apple.mrj.*; ... File simpleTextApp = MRJFileUtils.findApplication(new MRJOSType("ttxt")); |
The third-party JConfig
library from Samizdat Productions, described above, has features that can locate
the user's preferred helper app for any type of URL, which is one of the common uses
of Runtime.exec
.
In MRJ 2.2, there is a new utility method MRJFileUtils.openURL()
in the MRJToolkit.
It works by passing the URL to the user's preferred helper application as configured
in the Internet control panel (e.g., Internet Config).
import com.apple.mrj.MRJFileUtils; ... MRJFileUtils.openURL("http://www.apple.com"); |
Note: |
The Mac OS uses a mechanism known as AppleEvents to send messages from one process to another. AppleEvents are much higher level than command-line arguments, and include a rich set of data types and tags. However, this means that MRJ can't take a set of arbitrary argument strings and send them to an application in any meaningful way. (Remember all the stuff about "platform-dependent" in the first section?)
MRJ tries to detect certain common types of command-line arguments and convert them into AppleEvents. In general, the two types of arguments MRJ knows about are file paths and URLs. Any argument containing a ":" character is interpreted as a URL, and anything else is interpreted as a file path. (These rules don't apply when you're launching another Java process, though. See the next section.)
Note: |
(If you're AppleEvent savvy, you may wish to know that file paths are sent in
a single 'odoc'
event, while URLs are sent in individual 'GURL'
events.)
There are a couple of things to keep in mind:
The Mac OS does not allow the same app to be launched multiple times, so if you
call Runtime.exec
on a running app, or call it multiple times on the
same app, the same instance of the app will receive each request and open each item.
The convention in web browsers, at least, is that they display most URLs in the active window, replacing its previous contents, rather than opening a new window. (They do open files in new windows.) That means that if you send a browser a URL argument it will effectively ignore any argument that came before it. (For instance, if you pass it two URLs, it will try to show the first one and then immediately the second one in the same window. If you pass a file and a URL, it will open a new window for the file, but then show the URL in that window.)
It turns out that one of the useful things you can do with Runtime.exec
is to launch a new Java process. This is useful if you need to give the code you're
launching complete independence of your current process. For instance, some Java
tools like javac
never reclaim memory or close files, and for practical
purposes must be run in a separate process that is cleaned up by the OS when it quits.
In MRJ 2.1 and 2.2, we valiantly added some special cases (some say "hacks")
to facilitate this. If the application/command named in the first argument does not
point to an existing file, and it does include the substring "java
"
(independent of case), then we assume you are trying to launch a Java process, just
as if you were invoking the JDK from a command line. MRJ builds an java application
on the fly and launches it.
Note: |
For example, you can invoke the Java compiler thusly:
String args = {"java", "sun.tools.javac.Main", ...javac argument list...}; Runtime.getRuntime().exec(args); |
Since we know the thing on the other end is a Java process, we allow a bit more general functionality:
main()
method of the app's main class, as
Strings.Process
object [see below] to access the Java process's
standard input / output / error streams.java
'The JDK 'java
' command takes a number of flag parameters. MRJ supports
a subset of these:
Flag |
Purpose |
MRJ |
MRJ 2.2 with |
---|---|---|---|
|
Prints help message & exits |
Supported |
Supported |
|
Supported |
Supported |
|
|
Sets system property |
Supported |
Supported |
|
Enable bytecode verification on remote classes |
Supported |
Supported |
|
Enable bytecode verification on all classes |
Supported |
Supported |
|
Disable bytecode verification |
Supported |
Supported |
|
Disable asynchronous garbage collection |
Ignored |
Supported |
|
Disable class GC |
Ignored |
Supported |
|
Set initial Java heap size |
Ignored |
Supported |
|
Set max Java heap size |
Ignored |
Supported |
|
Set max native stack size |
Ignored |
Warning |
|
Set max Java stack size |
Ignored |
Warning |
|
Report on garbage collection |
Ignored |
Supported |
|
Reports Java Native Interface activity |
Ignored |
Warning |
|
Reports class loading information |
Ignored |
Supported |
|
Reports class loading information |
Ignored |
Supported |
|
Prints help on nonstandard options & exits |
Ignored |
Warning |
|
Enable debugging |
Ignored |
Warning |
|
Enable method profiling |
Ignored |
Supported |
|
Enable instruction profiling |
Ignored |
Warning |
|
Enable heap profiling |
Ignored |
Warning |
|
Prints version & exits |
Error |
Warning |
|
Sets classpath |
Error |
Warning |
|
Sets classpath and main |
Error |
Warning |
|
Run via MRJAppBuilder app |
Ignored |
Required |
Future versions of MRJ may support more of these flags.
Note: |
Note: |
The Runtime.exec
method, if it succeeds, returns a Process
object. You can use this object to check the status of the process, force it to quit,
and access its input / output / error streams.
Most of these work as advertised, but the latter usage is problematic -- the Mac
OS has no notion of text streams attached to applications, so it's meaningless to
try to access them. MRJ just returns null
if you ask Process
for a stream
associated with a normal app.
The exception is Java processes, as described above. Since these are special processes
that just run Java code, they do have console I/O streams, and the Process
API allows
you to access them. This is implemented by using Sockets. Therefore, you must have
TCP/IP configured on the machine, or you will get an exception thrown when trying
to exec the java process.
Note: |
Future versions of MRJ may add an explicit command line option to independently control
whether or not the Process
object sets up sockets to the child process.
Here's some sample code by Levi Brown that demonstrates a cross-platform-savvy way to open a URL in a Web browser. It will attempt to use MRJ's openURL function, and upon failure will revert to presenting a file dialog to choose the browser to use, then launch the browser and open the specified URL:
import java.awt.Frame; import java.awt.FileDialog; import java.io.File; import java.io.IOException; import com.apple.mrj.MRJFileUtils; public class ExecTest extends Frame { public static void main(String[ ] args) { new ExecTest(); System.exit(0); } public ExecTest() { String url = "http://developer.apple.com/java/"; try { //Attempt to let MRJ do all the work for us. MRJFileUtils.openURL(url); //If this was successful, then we need not go on. return; } catch (IOException exc) { //This can occur if problems arise while attempting //to open the URL. } catch (NoSuchMethodError err) { //This can occur when earlier versions of MRJ are used which //do not support the openURL method. } catch (NoClassDefFoundError err) { //This can occur under runtime environments other than MRJ. } // If we make it here, MRJ was unsuccessful in opening // the URL, and we need to do it the hard way, using // Runtime.exec. String browserName; //Set up a FileDialog for the user to locate the //browser to use. FileDialog fileDialog = new java.awt.FileDialog(this); fileDialog.setMode(FileDialog.LOAD); fileDialog.setTitle("Choose the browser to use:"); fileDialog.setVisible(true); //Retrieve the path information from the dialog //and verify it. String resultPath = fileDialog.getDirectory(); String resultFile = fileDialog.getFile(); if(resultPath != null && resultPath.length()!= 0 && resultFile != null && resultFile.length() != 0) { File file = new File(resultPath + resultFile); if(file != null) { browserName = file.getPath(); try { //Launch the browser and pass it the desired URL Runtime.getRuntime().exec(new String[] {browserName, url}); } catch (IOException exc) { exc.printStackTrace(); } } } } } |
This example should be refined, if used in a real setting, to store the location
of the browser (browserName
) in a preference file, and only ask the
user if the cached browser could not be located. It should also make the prompt string
localizable.
Alternatively, as described above, you could locate a browser by its application signature, or use JConfig or BrowserLauncher to handle the whole open-the-URL process for you.
Technote 1134: The Preferences Problem
Acrobat version of this Note (76K). |
|