2.16 Sending Object References to Mathematica
Part 1 of this User Guide describes how to use J/Link to allow Mathematica code to launch a Java runtime, load Java classes and directly execute Java methods. What this means for you, the reader of Part 2, who is probably writing his or her own program to launch and communicate with the Mathematica kernel, is that you can have a very highlevel interaction with Mathematica. You can send your own objects to Mathematica and use them in Mathematica code, but you have to take a special step to enable this type of interaction.
Consider what happens if you have written a Java front end to the Mathematica kernel and a user of your program calls a Mathematica function that uses the "installable Java" features of J/Link and thus calls InstallJava in Mathematica. InstallJava launches a separate Java runtime and proceeds to direct all J/Link traffic to that Java runtime. The kernel is blissfully unconcerned whether the front end that is driving it is the notebook front end or your Java program—it does the same thing in each case. This is fine and it is what many J/Link programmers will want. You do not have to worry about doing anything special if some Mathematica code happens to invoke the "installable Java" features of J/Link, because a separate Java runtime will be used.
But what if you want to make use of the ability that J/Link gives Mathematica code to interact with Java objects? You might want to send Java object references to Mathematica and operate on them with Mathematica code. Mathematica "operates" on Java objects by calling into Java, so any callbacks for such objects must be directed to your Java runtime. A further detail of J/Link is that it only supports one active Java runtime for all installable Java uses. What this all adds up to is that if you want to pass references to your own objects into Mathematica, then you must call InstallJava and specify the link to your Java runtime, and you must do this before any function is executed that itself calls InstallJava. Actually, a number of steps need to be taken to enable J/Link callbacks into your Java environment, so J/Link includes a special method in the KernelLink interface, enableObjectReferences(), that takes care of everything for you.
public void enableObjectReferences() throws MathLinkException;
// For sending object references:
public void put(Object obj) throws MathLinkException;
public void putReference(Object obj) throws MathLinkException;
After calling enableObjectReferences(), you can use the KernelLink interface's put() or putReference() methods to send Java objects to Mathematica, and they will arrive as JavaObject expressions that can be used in Mathematica code as described throughout Part 1. Recall that the difference between the put() and putReference() methods is that put() sends objects that have meaningful "value" representations in Mathematica (like arrays and strings) by value, and all others by reference. The putReference() method sends everything as a reference. If you want to use enableObjectReferences(), call it early on in your program, before you call putReference(). It requires that the JLink.m file be present in the expected location, which means that J/Link must be installed in the standard way on the machine that is running the kernel.
Once you have called enableObjectReferences(), not only can you send Java objects to Mathematica, you can also read Java object references that Mathematica sends back to Java. The getObject() method is used for this purpose. If a valid JavaObject expression is waiting on the link, getObject() will return the object that it refers to.
public Object getObject() throws MathLinkException;
If you call enableObjectReferences() in your program, it is imperative that you do not try to write your own packet loop. Instead, you must use the KernelLink methods that encapsulate the reading and handling of packets until a result is received. These methods are waitForAnswer(), discardAnswer(), evaluateToInputForm(), evaluateToOutputForm(), evaluateToImage(), and evaluateToTypeset(). If you want to see all the incoming packets yourself, use a PacketListener object in conjunction with one of these methods. This is discussed in Section 2.8.6.
It is worthwhile to examine in more detail the question of why you would want to use enableObjectReferences(). Traditionally, MathLink programmers have worked with the C API, which limits the types of data that can be passed back and forth between C and Mathematica to Mathematica expressions. Since Mathematica expressions are not generally meaningful in a C program, this translates basically to numbers, strings, and arrays of these things. The native structures that are meaningful in your C or C++ program (structs, objects, functions, and so on) are not meaningful in Mathematica. As a result, programmers tend to use a simplistic oneway communication with Mathematica, decomposing the native data structures and objects into simple components like numbers and strings. Program logic and behavior is coded entirely in C or C++, with Mathematica used solely for mathematical computations.
In contrast, J/Link allows Java and Mathematica code to collaborate in a highlevel way. You can easily code algorithms and other program behavior in Mathematica if it is easier for you. As an example, say you are writing a Java servlet that needs to use the Mathematica kernel in some way. Your servlet's doGet() method will be called with HttpServletRequest and HttpServletResponse objects as arguments. One approach would be to extract the information you need out of these objects, package it up in some way for Mathematica, and send the desired computation for evaluation. But another approach would be simply to send the HttpServletRequest and HttpServletResponse objects themselves to Mathematica. You can then use the features and syntax described in Part 1 to code the behavior of the servlet in Mathematica, rather than in Java. Of course, these are just two extremes of a continuum. At one end you have the servlet behavior hardcoded into a compiled Java class file, and you make use of Mathematica in a limited way, using a very narrow pipeline (narrow in the logical sense, passing only simple things like numbers, strings, or arrays). At the other end of the continuum you have a completely generic servlet that does nothing but forward all the work into Mathematica. The behavior of the servlet is written completely in Mathematica. You can use this approach even if you do not need Mathematica as a mathematics engine—you might just find it easier to develop and debug your servlet logic in the Mathematica language. You can draw the line between Java and Mathematica anywhere you like along the continuum, doing whatever amount of work you prefer in each language.
In case you are wondering what such a generic servlet might look like, here is the doGet() method:
// ml.enableObjectReferences() must have been called prior, for example
// in the servlet's init method.
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
try {
ml.putFunction("EvaluatePacket", 1);
ml.putFunction("DoGet", 2);
// We could also use plain 'put' here, as these objects would be put
// by reference anyway.
ml.putReference(req);
ml.putReference(res);
ml.endPacket();
ml.discardAnswer();
} catch (MathLinkException e) {}
}
This would be accompanied by a Mathematica function DoGet that takes the two Java object arguments and implements the servlet behavior. The syntax is explained in Part 1 of this User Guide:
doGet[req_, resp_] :=
JavaBlock[
Module[{outStream},
outStream = resp@getOutputStream[];
outStream@print["<HTML> <BODY>"];
outStream@print["Hello World"];
outStream@print["</BODY> </HTML>"];
]
]
]
