J/Link Programming

Todd Gayley
Wolfram Research

Introduction

What is J/Link?

J/Link is a toolkit that integrates Mathematica and Java. You can think of it as a MathLink Developer Kit for Java, but it really is much more than that.

J/Link can be downloaded from http://www.wolfram.com/solutions/mathlink/jlink. It also ships with Mathematica, as part of the MathLink Developer Kit.

J/Link and a Java runtime will be bundled (preinstalled) with Mathematica starting in version 4.2. This means that developers can rely on J/Link being present and functioning on users' systems with no extra effort on the users' part.

J/Link lets you call Java from Mathematica in a completely transparent way.

J/Link lets you call Mathematica from Java using an API that is similar to the C MathLink API, but at a much higher level.

J/Link comes with extensive documentation and the complete source code.

Why You Want to Use It

Because J/Link lets you load arbitrary Java classes into Mathematica, create objects, and call methods, it makes all of Java an immediate extension to the Mathematica environment. There is a staggering amount of software development going on in Java. Having access to all that work directly in Mathematica is a tremendous benefit.

If you want to write a program that uses Mathematica for computations, the J/Link API is much easier to use than the MathLink C API, but with no loss in power or expressiveness.

When You Want to Use It

Any time you find yourself asking the question "Can Mathematica do X", and the answer appears to be no, ask "Can Java do X". The answer is probably yes, and if so, then you can do it in Mathematica.

	Examples:
		Can Mathematica import file format XYZ?
		Can Mathematica read and write directly to TCP sockets?
		Can Mathematica control external hardware?
		Can Mathematica talk to my database?

Any time you want to write a program that calls on the services of Mathematica.

Why Java?

Java is a general-purpose programming language. It achieved popularity through its association with the Internet, particularly in the form of special small programs called applets that can be embedded in web pages.

Java is not restricted to applets running in browsers. It is used for all sorts of programs, and has become very popular for server-side uses.

Java is portable, meaning that Java programs, like Mathematica programs, can run unmodified on any platform.

Java is available on virtually all platforms, from handhelds to supercomputers.

By one measure (job ads that mention computer languages), Java has become the world’s most popular programming language.

Calling Java from Mathematica

Quick Example: Formatting Numbers

<<JLink`

InstallJava[]

fmt = JavaNew["java.text.DecimalFormat", "#.0000"]

The @ symbol is used like the Java . (dot). This means "call the fmt object's format method."

fmt@format[12.34]

Building a GUI

frm = JavaNew["java.awt.Frame"];

frm@setSize[300, 200];

JavaShow[frm]

Make some room on your screen so the window is visible next to the notebook as you type.

LoadClass["java.awt.Color"];

frm@setBackground[Color`blue]

Now add a button.

btn = JavaNew["java.awt.Button"];
frm@add[btn];
btn@setBounds[100, 80, 90, 25];

btn@setLabel["Change Color"]

Now we want to wire up the button so that clicking it changes the color of the window.

Buttons fire ActionEvents when they are clicked. We want a class that implements the ActionListener interface via callbacks to Mathematica. J/Link comes with implementations of the AWT event listener classes that call back into Mathematica.

listener = JavaNew["com.wolfram.jlink.MathActionListener"]

The ActionListener interface has one method, actionPerformed(). Here is how we assign a Mathematica function to be called when the listener's actionPerformed() method is called:

listener@setHandler["actionPerformed", "buttonFunc"]

The J/Link user guide has a table that shows what arguments your Mathematica handler functions are passed for each method in the various "MathListener" classes. From this table, we see that ur handler function will get two arguments: the event itself and the integer action command. we are not interested in either:

buttonFunc[e_, _] :=
	JavaBlock[
		frm@setBackground[JavaNew["java.awt.Color",
			Random[Integer, {0,255}],
			Random[Integer, {0,255}],
			Random[Integer, {0,255}]]
		];
		ReleaseObject[e]
	]

btn@addActionListener[listener]

ShareKernel[]

frm@dispose[]

UnshareKernel[]

Calling Mathematica from Java

Overview of the J/Link API

The J/Link API consist of several layers.

At the lowest layer is the MathLink interface. This interface more or less corresponds to the C MathLink API, albeit simplified.

The next layer up is the KernelLink interface. It extends MathLink to include a higher-level interface appropriate for when the other side of the link is a Mathematica kernel. KernelLink has a number of very high-level methods that can perform a computation and get a result in a single step. It also knows how to get graphics and typeset results.

At the top layer are some user-interface components that encapsulate a great deal of functionality. Two examples are the MathCanvas class and the MathSessionPane class.

A Simple Program

import com.wolfram.jlink.*;

public class SampleProgram {

	public static void main(String[] argv) {

		KernelLink ml = null;

		try {
			ml = MathLinkFactory.createKernelLink(argv);
		} catch (MathLinkException e) {
			System.out.println("Fatal error opening link: " + e.getMessage());
			return;
		}

		try {
			// Get rid of the initial InputNamePacket the kernel will send
			// when it is launched.
			ml.discardAnswer();

			ml.evaluate("<<MyPackage.m");
			ml.discardAnswer();

			ml.evaluate("2+2");
			ml.waitForAnswer();
			int result = ml.getInteger();
			System.out.println("2 + 2 = " + result);

			ml.putFunction("EvaluatePacket", 1);
			ml.putFunction("Plus", 2);
			ml.put(3);
			ml.put(3);
			ml.endPacket();
			ml.waitForAnswer();
			result = ml.getInteger();
			System.out.println("3 + 3 = " + result);

			String strResult = ml.evaluateToOutputForm("4+4", 0);
			System.out.println("4 + 4 = " + strResult);

		} catch (MathLinkException e) {
			System.out.println("MathLinkException occurred: " + e.getMessage());
		} finally {
			ml.close();
		}
	}
}

Graphics

Graphics functions

J/Link has the following functions for getting graphics output in Java programs:

byte[] evaluateToImage(String s, int width, int height);
byte[] evaluateToImage(Expr e, int width, int height);

byte[] evaluateToImage(String s, int width, int height, int dpi, boolean useFE);
byte[] evaluateToImage(Expr e, int width, int height, int dpi, boolean useFE);

byte[] evaluateToTypeset(String s, int width, boolean useStdForm);
byte[] evaluateToTypeset(Expr e, int width, boolean useStdForm);

MathCanvas

J/Link has an even higher-level way to get Mathematica graphics and typeset output into your Java programs: the MathCanvas class.

import com.wolfram.jlink.*;
import java.awt.*;
import java.awt.event.*;

public class GraphicsApp extends Frame {
    
	static GraphicsApp app;
	static KernelLink ml;
	
	MathCanvas mathCanvas;
	TextArea inputTextArea;
	Button evalButton;
	Checkbox useFEButton;
	Checkbox graphicsButton;
	Checkbox typesetButton;

	public static void main(String[] argv) {

		try {
			String[] mlArgs = {"-linkmode", "launch", "-linkname", argv[0]};
			ml = MathLinkFactory.createKernelLink(mlArgs);
			ml.discardAnswer();
		} catch (MathLinkException e) {
			System.out.println("An error occurred connecting to the kernel.");
			if (ml != null)
				ml.close();
			return;
		}
		app = new GraphicsApp();
	}
    
	public GraphicsApp() {
		
		setLayout(null);
		setTitle("Graphics App");
		mathCanvas = new MathCanvas(ml);
		add(mathCanvas);
		mathCanvas.setBackground(Color.white);
		inputTextArea = new TextArea("", 2, 40, TextArea.SCROLLBARS_VERTICAL_ONLY);
		add(inputTextArea);
		evalButton = new Button("Evaluate");
		add(evalButton);
		evalButton.addActionListener(new BnAdptr());
		useFEButton = new Checkbox("Use front end", false);
		CheckboxGroup cg = new CheckboxGroup();
		graphicsButton = new Checkbox("Show graphics output", true, cg);
		typesetButton = new Checkbox("Show typeset result", false, cg);
		add(useFEButton);
		add(graphicsButton);
		add(typesetButton);
		
		setSize(300, 400);
		setLocation(100,100);
		mathCanvas.setBounds(10, 25, 280, 240);
		inputTextArea.setBounds(10, 270, 210, 60);
		evalButton.setBounds(230, 290, 60, 30);
		graphicsButton.setBounds(20, 340, 160, 20);
		typesetButton.setBounds(20, 365, 160, 20);
		useFEButton.setBounds(180, 340, 120, 20);
		
		addWindowListener(new WnAdptr());
		setBackground(Color.lightGray);
		setResizable(false);
		
		setVisible(true);
		toFront();
	}
    
	class BnAdptr implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			mathCanvas.setImageType(graphicsButton.getState() ? MathCanvas.GRAPHICS : MathCanvas.TYPESET);
			mathCanvas.setUsesFE(useFEButton.getState());
			mathCanvas.setMathCommand(inputTextArea.getText());
		}
	}
	
	class WnAdptr extends WindowAdapter {
		public void windowClosing(WindowEvent event) {
			if (ml != null) {
				ml.evaluateToInputForm("CloseFrontEnd[]", 0);
				ml.close();
			}
			dispose();
			System.exit(0);
		}
	}
}