Calling Java from the Wolfram Language
Preamble
J/Link provides Wolfram Language users with the ability to interact with arbitrary Java classes directly from the Wolfram Language. You can create objects and call methods directly in the Wolfram Language. You do not need to write any Java code, or prepare in any way the Java classes you want to use. You also do not need to know anything about the Wolfram Symbolic Transfer Protocol (WSTP). In effect, all of Java becomes a transparent extension to the Wolfram Language, almost as if every existing and future Java class were written in the Wolfram Language itself.
This facility is called "installable Java" because it generalizes the ability that the Wolfram Language has always had to plug in extensions written in other languages through the Install function. You will see later how J/Link vastly simplifies this procedure for Java compared to languages like C or C++. In fact, J/Link makes the procedure go away completely, which is why Java becomes a transparent extension to the Wolfram Language.
Although Java is often referred to as an interpreted language, this is really a misnomer. To use Java you must write a complete program, compile it, and then execute it (some environments exist that let you interactively execute lines of Java code, but these are special tools, and similar tools exist for traditional languages like C). Wolfram Language users have the luxury of working in a true interpreted, interactive environment that lets them experiment with functions and build and test programs a line at a time. J/Link brings this same productive environment to Java programmers. You could say that the Wolfram Language becomes a scripting language for Java.
To Wolfram Language users, then, the "installable Java" feature of J/Link opens up the expanding universe of Java classes as an extension to the Wolfram Language; for Java users, it allows the extraordinarily powerful and versatile Wolfram Language environment to be used as a shell for interactively developing, experimenting with, and testing Java classes.
Loading the J/Link Package
Launching the Java Runtime
InstallJava
The next step is to launch the Java runtime and "install" it into the Wolfram Language. The function for this is InstallJava.
InstallJava[] | launch the Java runtime and prepare it for use from the Wolfram Language |
ReinstallJava[] | quit and restart the Java runtime if it is already running |
JavaLink[] | give the LinkObject that is being used to communicate with the Java runtime |
InstallJava can be called more than once in a session. On every call after the first, it does nothing. Thus, it is safe to call InstallJava in any program you write, without considering whether the user has already called it.
InstallJava creates a command line that is used to launch the Java runtime (typically called "java") and specify some initial arguments for it. In rare cases you will need to control what is on this command line, so InstallJava takes a number of options for this purpose. Most users will not need to use these options, and in fact you should avoid them. Programmers should not assume that they have the ability to control the launch of the Java runtime, as it might already be running. If for some reason you absolutely must apply options to control the launch of the Java runtime, use ReinstallJava instead of InstallJava.
ClassPath->None | use the default class path of your Java runtime |
ClassPath->"dirs" | use the specified directories and jar files |
CommandLine->"cmd" | use the specified command line to launch the Java runtime, instead of "java" |
Options for InstallJava.
Controlling the Command Used to Launch Java
An important option to InstallJava and ReinstallJava is CommandLine. This specifies the first part of the command line used to launch Java. One use for this option is if you have more than one Java runtime installed on your system, and you want to invoke a specific one.
By default, InstallJava will launch the Java runtime that is bundled with Mathematica 4.2 and later. If you have an earlier version of the Wolfram Language, the default command line that will be used is java on most systems. If the java executable is not on your system path, you can use InstallJava to point at it. Another use for this option is to specify arguments to Java that are not covered by other options. Here is an example that specifies verbose garbage collection and defines a property named foo to have the value bar.
Overriding the Class Path
The class path is the set of directories in which the Java runtime looks for classes. When you launch a Java program from your system's command line, the class path used by Java includes some default locations and any locations specified in the CLASSPATH environment variable, if it exists. If you use the -classpath command-line option to specify a set of locations, however, then the CLASSPATH environment variable is ignored. The ClassPath option to InstallJava and ReinstallJava works the same way. If you leave it at the default value, Automatic, then J/Link will include the contents of the CLASSPATH environment variable in its class search path. If you set it to None or a string, then the contents of CLASSPATH are not used. If you set it to be a string, use the same syntax that you would use for setting the CLASSPATH environment variable, which is different for Windows and Linux/OSX.
J/Link has its own mechanism for controlling the class search path that is very flexible. Not only does J/Link automatically search for classes in the Wolfram Language application directories, it also lets you dynamically add new search locations while the Java runtime is running. This means that using the ClassPath option to configure the class path when Java first launches is not very important. One setting for the ClassPath option that is sometimes useful is None, to prevent J/Link from finding any classes from the contents of CLASSPATH. You might want to do this if you had an experimental version of some class in a development directory and you wanted to make sure that J/Link used that version in preference to an older one that was present on your CLASSPATH. "The Java Class Path" presents a complete treatment of the subject of how J/Link searches for classes, and how to add locations to this search path.
Loading Classes
LoadJavaClass
LoadJavaClass["classname"] | load the specified class into Java and the Wolfram Language |
LoadClass["classname"] | deprecated name from earlier versions of J/Link; use LoadJavaClass instead |
To use a Java class in the Wolfram Language, it must first be loaded into the Java runtime and certain definitions must be set up in the Wolfram Language. This is accomplished with the LoadJavaClass function. LoadJavaClass takes a string specifying the fully qualified name of the class (i.e. the full hierarchical name with all the periods).
The return value is an expression with head JavaClass. This JavaClass expression can be used in many places in J/Link, so you might want to assign it to a variable as has been done here. Virtually everywhere in J/Link where a class needs to be specified as an argument, you can use a JavaClass expression, the fully qualified class name as a string, or an object of the class. Note that you cannot create a valid JavaClass expression by simply typing it in—it must be returned by LoadJavaClass.
When a class has been loaded, you can call static methods in the class, create objects of the class, and invoke methods and access fields of these objects. You can use any public constructors, methods, or fields of a class.
StaticsVisible->True | make static methods and fields accessible by just their names, not in a special context |
AllowShortContext->False | make static methods and fields accessible only in their fully qualified class context |
UseTypeChecking->False | suppress the type checking that is normally inserted in definitions for calls into Java |
Options for LoadJavaClass.
"The Java Class Path" discusses the details of how and where J/Link finds classes. J/Link will be able to find classes on the class path, in the special Java extensions directory, and in a set of extra directories that users can control even while J/Link is running.
When to Call LoadJavaClass
It is often the case that you do not need to explicitly load a class with LoadJavaClass. As described later, when you create a Java object with JavaNew, you can supply the class name as a string. If the class has not already been loaded, LoadJavaClass will be called internally by JavaNew. In fact, anytime a Java object is returned to the Wolfram Language its class is loaded automatically if necessary. This would seem to imply that there is little reason to use LoadJavaClass. There are a number of reasons why you would want or need to use LoadJavaClass explicitly:
- You need to call a static method of a class and you will not create, or have not yet created, an object of that class. A class must be loaded before any of its static methods can be called.
- You need to use one of the options to LoadJavaClass. When LoadJavaClass is called internally by JavaNew, it is called with the default option settings.
- You want to control where your users experience the initial delay associated with loading a class. Loading a class can take several seconds if it or one of its parent classes is very large (although it rarely takes that long). You might want to avoid a mysterious delay in a function that users expect to be very quick.
- You want to hang on to the JavaClass expression returned by LoadJavaClass to use it in other functions. Although all functions that take a JavaClass can also take a class name string, you might prefer to use a named JavaClass variable for readability purposes. It is also slightly faster than using a string, but this will not be perceptible unless you are using it many times in a loop.
The operation of loading a class in J/Link is only done once in a J/Link session (a session is the period between InstallJava and UninstallJava). You can call LoadJavaClass on a given class as many times as you want, and every call after the first one immediately returns the JavaClass expression without doing any work. This is important, as it means that you never have to worry whether a class has been loaded already—if you are not sure, call LoadJavaClass.
Developers writing code for a wide audience should always call LoadJavaClass on any classes they need in every function that needs them. It is not suitable to call LoadJavaClass in the body of your package code when it is read in, as the user may quit and restart the Java runtime (i.e. UninstallJava and InstallJava) after your package was read. To be safe, every user-level function that uses J/Link should call InstallJava and LoadJavaClass (if LoadJavaClass is necessary; see the following). Both calls execute very quickly if they are not needed.
As mentioned already, loading a class can take several seconds in some cases. When a class is loaded, all of its superclasses are loaded in succession, walking up the inheritance hierarchy. Because a given class is only actually loaded once, if you load another class that shares some of the same superclasses as a previously loaded class, these superclasses will not have to be loaded again. This means that loading the second class will be much quicker than the first if any of the shared superclasses were large. An example of this is loading classes in the java.awt package. The class java.awt.Component is very large, so the first time you load a class that inherits from it, say java.awt.Button, there will be a noticeable delay. Subsequent loading of other classes derived from Component will be much quicker.
Contexts and Visibility of Static Members
LoadJavaClass has two options that let you control the naming and visibility of static methods and fields. To understand these options, you need to understand the problems they help to solve. This explanation gets a bit ahead since how to call Java methods has not been discussed. When a class is loaded, definitions are created in the Wolfram Language that allow you to call methods and access fields of objects of that class. Static members are treated quite differently from nonstatic ones. None of these issues arise for nonstatic members, so only static members are discussed in this section. Say you have a class named com.foobar.MyClass that contains a static method named foo. When you load this class, a definition must be set up for foo so that it can be called by name, something like foo[args]. The question becomes: in what context do you want the symbol foo defined, and do you want this context to be visible (i.e. on $ContextPath)?
J/Link always creates a definition for foo in a context that mirrors its fully qualified classname: com`foobar`MyClass`foo. This is done to avoid conflicting with symbols named foo that might be present in other contexts. However, you might find it clumsy to have to call foo by typing the full context name every time, as in com`foobar`MyClass`foo[args]. The option AllowShortContext->True (this is the default setting) causes J/Link to also make definitions for foo accessible in a shortened context, one that consists of just the class name without the hierarchical package name prefix. In the example, this means that you could call foo as simply MyClass`foo[args]. If you need to avoid use of the short context because there is already a context of the same name in your Wolfram System session, you can use AllowShortContext->False. This forces all names to be put only in the "deep" context. Note that even with AllowShortContext->True, names for statics are also put into the deep context, so you can always use the deep context to refer to a symbol if you desire.
AllowShortContext, then, lets you control the context where the symbol names are defined. The other option, StaticsVisible, controls whether this context is made visible (put on $ContextPath) or not. The default is StaticsVisible->False, so you have to use a context name when referring to a symbol, as in MyClass`foo[args]. With StaticsVisible->True, MyClass` will be put on $ContextPath, so you could just write foo[args]. Having the default be True would be a bit dangerous—every time you load a class a potentially large number of names would suddenly be created and made visible in your Wolfram System session, opening up the possibility for all sorts of "shadowing" problems if symbols of the same names were already present. This problem is particularly acute with Java, because method and field names in Java typically begin with a lowercase letter, which is also the convention for user-defined symbols in the Wolfram Language. Some Java classes define static methods and fields with names like x, y, width, and so on, so shadowing errors are very likely to occur (see "Contexts" for a discussion of contexts and shadowing problems).
For these reasons StaticsVisible->True is recommended only for classes that you have written, or ones whose contents you are familiar with. In such cases, it can save you some typing, make your code more readable, and prevent the all-too-easy bug of forgetting to type the package prefix. A classic example would be implementing the venerable "addtwo" WSTP example program. In Java, it might look like this:
With the default StaticsVisible->False, you would have to call addtwo as AddTwo`addtwo[3,4]. Setting StaticsVisible->True lets you write the more obvious addtwo[3, 4].
Be reminded that these options are only for static methods and fields. As discussed later, nonstatics are handled in a way that makes context and visibility issues go away completely.
Inner Classes
Inner classes are public classes defined inside another public class. For example, the class javax.swing.Box has an inner class named Filler. When you refer to the Filler class in a Java program, you typically use the outer class name, followed by a period, then the inner class name.
You can use inner classes with J/Link, but you need to use the true internal name of the class, which has a $, not a period, separating the outer and inner class names.
If you look at the class files produced by the Java compiler, you will see these $-separated class names for inner classes.
Conversion of Types between Java and the Wolfram Language
Before you encounter the operations of creating Java objects and calling methods, you should examine the mapping of types between the Wolfram Language and Java. When a Java method returns a result to the Wolfram Language, the result is automatically converted into a Wolfram Language expression. For example, Java integer types (e.g. byte, short, int, and so on), are converted into Wolfram Language integers, and Java real number types (float, double) are converted into Wolfram Language reals. The following table shows the complete set of conversions. These conversions work both ways—for example, when a Wolfram Language integer is sent to a Java method that requires a byte value, the integer is automatically converted to a Java byte.
Java type | Wolfram Language type |
byte, char, short, int, long | Integer |
Byte, Character, Short, Integer, Long, BigInteger | |
Integer | |
float, double | Real |
Float, Double, BigDecimal | Real |
boolean | True or False |
String | String |
array | List |
controlled by user (see "Complex Numbers") | Complex |
Object | JavaObject |
Expr | any expression |
null | Null |
Corresponding types in Java and the Wolfram Language.
Java arrays are mapped to Wolfram Language lists of the appropriate depth. Thus, when you call a method that takes a double[], you might pass it {1.0,2.0,N[Pi],1.23}. Similarly, a method that returns a two-deep array of integers (i.e. int[][]) might return to the Wolfram Language the expression {{1,2,3},{5,3,1}}.
In most cases, J/Link will let you supply a Wolfram Language integer to a method that is typed to take a real type (float or double). Similarly, a method that takes a double[] could be passed a list of mixed integers and reals. The only times when you cannot do this are the rare cases where a method has two signatures that differ only in a real versus integer type at the same argument slot. For example, consider a class with these methods:
public void foo(byte b, Object obj);
public void foo(float f, Object obj);
public void bar(float f, Object obj);
J/Link would create two Wolfram Language definitions for the method foo—one that required an integer for the first argument and invoked the first signature, and one that required a real number for the first argument and invoked the second signature. The definition created for the method bar would accept an integer or a real for the first argument. In other words, J/Link will automatically convert integers to reals, except in cases where such conversion makes it ambiguous as to which signature of a given method to invoke. This is not strictly true, though, as J/Link does not try as hard as it possibly could to determine whether real versus integer ambiguity is a problem at every argument position. The presence of ambiguity at one position will cause J/Link to give up and require exact type matching at all argument positions. This is starting to sound confusing, but you will find that in most cases J/Link allows you to pass integers or lists with integers to methods that take reals or arrays of reals, respectively, as arguments. In cases where it does not, the call will fail with an error message, and you will have to use the Wolfram Language's N function to convert all integers to reals explicitly.
Creating Objects
To instantiate Java objects, use the JavaNew function. The first argument to JavaNew is the object's class, specified either as a JavaClass expression returned from LoadJavaClass or as a string giving the fully qualified class name (i.e. having the full package prefix with all the periods). If you wish to supply any arguments to the object's constructor, they follow as a sequence after the class.
JavaNew[cls,arg1,…] | construct a new object of the specified class and return it to the Wolfram Language |
JavaNew["classname",arg1,…] | construct a new object of the specified class and return it to the Wolfram Language |
The return value from JavaNew is a strange expression that looks like it has the head JavaObject, except that it is enclosed in angle brackets. The angle brackets are used to indicate that the form in which the expression is displayed is quite different from its internal representation. These expressions will be referred to as JavaObject expressions. JavaObject expressions are displayed in a way that shows their class name, but you should consider them opaque, meaning that you cannot pick them apart or peer into their insides. You can only use them in J/Link functions that take JavaObject expressions. For example, if obj is a JavaObject, you cannot use First[obj] to get its class name. Instead, there is a J/Link function, ClassName[obj], for this purpose.
JavaNew invokes a Java constructor appropriate for the types of the arguments being passed in, and then returns to the Wolfram Language what is, in effect, a reference to the object. That is how you should think of JavaObject expressions—as references to Java objects very much like object references in the Java language itself. What is returned to the Wolfram Language is not large no matter what type of object you are constructing. In particular, the object's data (that is, its fields) are not sent back to the Wolfram Language. The actual object remains on the Java side, and the Wolfram Language gets a reference to it.
Note that simply constructing a Frame does not cause it to appear. That requires a separate step (calling the frame's show or setVisible methods will work, but as you will see later, J/Link provides a special function, JavaShow, to make Java windows appear and come to the foreground).
JavaNew is not the only way to get a reference to a Java object in the Wolfram Language. Many methods and fields return objects, and when you call such a method, a JavaObject expression is created. Such objects can be used in the same way as ones you explicitly construct with JavaNew.
At this point, you may be wondering about things like reference counts and how objects returned to the Wolfram Language get cleaned up. These issues are discussed in "Object References in the Wolfram Language".
J/Link has two other functions for creating Java objects, called MakeJavaObject and MakeJavaExpr. These specialized functions are described in the section "MakeJavaObject and MakeJavaExpr".
Calling Methods and Accessing Fields
Syntax
The Wolfram Language syntax for calling Java methods and accessing fields is very similar to Java syntax. The following box compares the Wolfram Language and Java ways of calling constructors, methods, fields, static methods, and static fields. You can see that Wolfram Language programs that use Java are written in almost exactly the same way as Java programs, except the Wolfram Language uses [] instead of () for arguments, and the Wolfram Language uses @ instead of Java's . (dot) as the "member access" operator.
An exception is that for static methods, the Wolfram Language uses the context mark ` in place of Java's dot. This parallels Java usage also, as Java's use of the dot in this circumstance is really as a scope resolution operator (like :: in C++). Although the Wolfram Language does not use this terminology, its scope resolution operator is the context mark. Java's hierarchical package names map directly to the Wolfram Language's hierarchical contexts.
constructors | |
Java: | MyClass obj=new MyClass(args); |
Wolfram Language: | obj=JavaNew["MyClass",args]; |
methods | |
Java: | obj.methodName(args); |
Wolfram Language: | obj@methodName[args] |
fields | |
Java: | obj.fieldName=1; value=obj.fieldName; |
Wolfram Language: | obj@fieldName=1; value=obj@fieldName; |
static methods | |
Java: | MyClass.staticMethod(args); |
Wolfram Language: | MyClass`staticMethod[args]; |
static fields | |
Java: | MyClass.staticField=1; value=MyClass.staticField; |
Wolfram Language: | MyClass`staticField=1; value=MyClass`staticField; |
Java and Wolfram Language syntax comparison.
You may already be familiar with @ as a Wolfram Language operator for applying a function to an argument: f@x is equivalent to the more commonly used f[x]. J/Link does not usurp @ for some special operation—it is really just normal function application slightly disguised. This means that you do not have to use @ at all. The following are equivalent ways of invoking a method.
The first form preserves the natural mapping of Java's syntax to the Wolfram Language's, and it will be used exclusively in this tutorial.
When you call methods or fields and get results back, J/Link automatically converts arguments and results to and from their Wolfram Language representations according to the table in "Conversion of Types between Java and the Wolfram Language".
Method calls can be chained in the Wolfram Language just like in Java. For example, if meth1 returns a Java object, you could write in Java obj.meth1().meth2(). In the Wolfram Language, this becomes obj@meth1[]@meth2[]. Note that there is an apparent problem here: the Wolfram Language's @ operator groups to the right, whereas Java's dot groups to the left. In other words, obj.meth1().meth2() in Java is really (obj.meth1()).meth2() whereas obj@meth1[]@meth2[] in the Wolfram Language would normally be obj@(meth1[]@meth2[]). The word "normally" is used because J/Link automatically causes chained calls to group to the left like Java. It does this by defining rules for JavaObject expressions, not by altering the properties of the @ operator, so the global behavior of @ is not affected. This chaining behavior only applies to method calls, not fields. You cannot do the following.
You would have to split these up into two lines. For example, the second line above would become the following.
In Java, like other object-oriented languages, method and field names are scoped by the object on which they are called. In other words, when you write obj.meth(), Java knows that you are calling the method named meth that resides in the object's class, even though there may be other methods named meth in other classes. J/Link preserves this scoping for Wolfram Language symbols so that there is never a conflict with existing symbols of the same name. When you write obj@meth[], there is no conflict with any other symbols named meth in the system—the symbol meth used by the Wolfram Language in the evaluation of this call is the one set up by J/Link for this class. Here is an example using a field. First, you create a Point object.
The Point class has fields named x and y, which hold its coordinates. A user's session is also likely to have symbols named x or y in it, however. You set up a definition for x that will tell you when it is evaluated.
Now set a value for the field named x (this would be written as pt.x = 42 in Java).
You will notice that "gotcha" was not printed. There is no conflict between the symbol x in the Global` context that has the Print definition and the symbol x that is used during the evaluation of this line of code. J/Link protects the names of methods and fields on the right-hand side of @ so that they do not conflict with, or rely on, any definitions that might exist for these symbols in visible contexts. Here is a method example that demonstrates this issue differently.
Even though a new symbol show is being created here, the show that is used by J/Link is the one that resides down in the java`awt`Frame context, which has the necessary definitions set up for it.
In summary, for nonstatic methods and fields, you never have to worry about name conflicts and shadowing, no matter what context you are in or what the $ContextPath is at the moment. This is not true for static members, however. Static methods and fields are called by their full name, without an object reference, so there is no object out front to scope the name. Here is a simple example of a static method call that invokes the Java garbage collector. You need to call LoadJavaClass before you call a static method to make sure the class has been loaded.
The name scoping issue is not usually a problem with statics, because they are defined in their own contexts (Runtime` in this example). These contexts are usually not on $ContextPath, so you do not have to worry that there is a symbol of the same name in the Global` context or in a package that has been read. There is more discussion of this issue in the section on LoadJavaClass, because LoadJavaClass takes options that determine the contexts in which static methods are defined and whether or not they are put on $ContextPath. If there is already a context named Runtime` in your session, and it has its own symbol gc, you can always avoid a conflict by using the fully hierarchical context name that corresponds to the full class name for a static member.
Finally, just as in Java, you can call a static method on an object if you like. In this case, since there is an object out front, you get the name scoping. Here you call a static method of the Runtime class that returns the current Runtime object (you cannot create a Runtime object with JavaNew, as Runtime has no constructors). You then invoke the (static) method gc on the object, and you can use gc without any context prefix.
Underscores in Java Names
Java names can have characters in them that are not legal in Wolfram Language symbols. The only common one is the underscore. J/Link maps underscores in class, method, and field names to "U". Note that this mapping is only used where it is necessary—when names are used in symbolic form, not as strings. For example, assume you have a class named com.acme.My_Class. When you refer to this class name as a string, you use the underscore.
But when you call a static method in such a class, the hierarchical context name is symbolic, so you must convert the underscore to U.
The same rule applies to method and field names. Many Java field names have underscores in them, for example java.awt.Frame.TOP_ALIGNMENT. To refer to this method in code, use the U.
In cases where you supply a string, leave the underscore.
Getting Information about Classes and Objects
J/Link has some useful functions that show you the constructors, methods, and fields available for a given class or object.
Constructors[cls] | return a table of the public constructors and their arguments |
Constructors[obj] | constructors for this object's class |
Methods[cls] | return a table of the public methods and their arguments |
Methods[cls,"pat"] | show only methods whose names match the string pattern pat |
Methods[obj] | show methods for this object's class |
Fields[cls] | return a table of the public fields |
Fields[cls,"pat"] | show only fields whose names match the string pattern pat |
Fields[obj] | show fields for this object's class |
ClassName[cls] | return, as a string, the name of the class represented by cls |
ClassName[obj] | return, as a string, the name of this object's class |
GetClass[obj] | return the JavaClass representing this object's class |
ParentClass[obj] | return the JavaClass representing this object's parent class |
InstanceOf[obj,cls] | return True if this object is an instance of cls, False otherwise |
JavaObjectQ[expr] | return True if expr is a valid reference to a Java object, False otherwise |
Getting information about classes and objects.
You can give an object or a class to Constructors, Methods, and Fields. The class can be specified either by its full name as a string, or as a JavaClass expression:
The declarations returned by these functions have been simplified by removing the Java keywords public, final (removed only for methods, not fields), synchronized, native, volatile, and transient. The declarations will always be public, and the other modifiers are probably not relevant for use via J/Link.
Methods and Fields take one option, Inherited, which specifies whether to include members inherited from superclasses and interfaces or show only members declared in the class itself. The default is Inherited->True.
Inherited->False | show only members that are declared in the class itself, not inherited from superclasses or interfaces |
Option for Methods and Fields.
There are additional functions that give information about objects and classes. These functions are ClassName, GetClass, ParentClass, InstanceOf, and JavaObjectQ. They are self-explanatory, for the most part. The InstanceOf function mimics the Java language's instanceof operator. JavaObjectQ is useful for writing patterns that match only valid Java objects.
JavaObjectQ returns True if and only if its argument is a valid reference to a Java object or if it is the symbol Null, which maps to Java's null object.
Quitting or Restarting Java
When you are finished with using Java in a Wolfram System session, you can quit the Java runtime by calling UninstallJava[].
UninstallJava[] | quit the Java runtime |
ReinstallJava[] | restart the Java runtime |
In addition to quitting Java, UninstallJava clears out the many symbols and definitions created in the Wolfram Language when you load classes. All outstanding JavaObject expressions will become invalid when Java is quit. They will no longer satisfy JavaObjectQ, and they will show up as raw symbols like JLink`Objects`JavaObject12345678 instead of <<JavaObject[classname]>>.
Most users will have no reason to call UninstallJava. You should think of the Java runtime as an integral part of the Wolfram Language—start it up, and then just leave it running. All code that uses J/Link shares the same Java runtime, and there may be packages that you are using that make use of Java without your even knowing it. Shutting down Java might compromise their functionality. Developers writing packages should never call UninstallJava in their packages. You cannot assume that when your application is done with J/Link, your users are done with it as well.
About the only common reason to need to stop and restart Java is when you are actively developing Java classes that you want to call from the Wolfram Language. Once a class is loaded into the Java runtime, it cannot be unloaded. If you want to modify and recompile your class, you need to restart Java to reload the modified version. Even in this circumstance, though, you will not be calling UninstallJava. Instead, you will call ReinstallJava, which simply calls UninstallJava followed by InstallJava again.
Version Information
J/Link provides three symbols that supply version information. These symbols provide the same type of information as their counterparts in the Wolfram Language itself, except that they are in the JLink`Information` context, which is not on $ContextPath, so you must specify them by their full names.
JLink`Information`$Version | a string giving full version information |
JLink`Information`$VersionNumber | a real number giving the current version number |
JLink`Information`$ReleaseNumber | an integer giving the release number (the last digit in a full x.x.x version specification) |
ShowJavaConsole[] | the console window will show version information for the Java runtime and the J/Link Java component |
The ShowJavaConsole[] function, described in "The Java Console Window", will also display some useful version information. It shows the version of the Java runtime being used and the version of the portion of J/Link that is written in Java. The version of the J/Link Java component should match the version of the J/Link Wolfram Language component.
Controlling the Class Path: How J/Link Finds Classes
The Java Class Path
The class path tells the Java runtime, compiler, and other tools where to find third-party and user-defined classes—classes that are not Java "extensions" or part of the Java platform itself. The class path has always been a source of confusion among Java users and programmers.
Java can find classes that are part of the standard Java platform (so-called "bootstrap" classes), classes that use the so-called "extensions" mechanism, and classes on the class path, which is controlled by the CLASSPATH environment variable or by command-line options when Java is launched. J/Link can load and use any classes that the Java runtime can find through these normal mechanisms. In addition, J/Link can find classes, resources, and native libraries that are in a set of extra locations, beyond what is specified on the class path at startup. This set of extra locations can be added to while Java is running.
J/Link provides two ways to alter the search path Java uses to find classes. The first way is via the ClassPath option to ReinstallJava. The second way, which is superior to modifying the class path at startup, is to add new directories and jar files to the special set of extra locations that J/Link searches. These two methods will be described in the next two subsections.
Overriding the Startup Class Path
For a class to be accessible via the standard Java class path, one of the following must apply:
- It is a loose class file that is in an appropriately nested directory beneath a directory that is on the class path.
"Appropriately nested" means that the class file must be in a directory whose hierarchy mirrors the full package name of the class. For example, assume that the directory c:\MyClasses is on the class path. If you have a class that is not in a package (there is no package statement at the beginning of the code), its class file should be put directly into c:\MyClasses. If you have a class that is in the package com.acme.stuff, its class file would need to be in the directory c:\MyClasses\com\acme\stuff. Note that jar and zip files must be explicitly named on the class path—you cannot just toss them into a directory that is itself named on the class path. Directory issues are not relevant for jar and zip files, meaning that regardless of how hierarchically organized the classes inside a jar file are, you simply name the jar file itself on the class path and all the classes inside it can be found.
If you want to specify paths for classes that are not part of the standard Java platform or extensions, you can use the ClassPath option to ReinstallJava. The value that you supply for the ClassPath option is a string that names the desired directories and zip or jar files. This string is platform-dependent; the paths are specified in the native style for your platform, and the separator character is a colon on Linux and a semicolon on Windows. Here are typical specifications.
The default setting for ClassPath is Automatic, which means to use the value of the CLASSPATH environment variable. If you set ClassPath to something else, then J/Link will ignore the CLASSPATH environment variable—it will not be able to find those classes. In other words, if you use a ClassPath specification, you lose the CLASSPATH environment variable. This is similar to the behavior of the -classpath command-line option to the Java runtime and compiler, if you are familiar with those tools.
It is recommended that users avoid the ClassPath option. If you need the dynamic control that the ClassPath option provides, you should use the more powerful and convenient AddToClassPath mechanism, described in the next section. The most common reason for using the ClassPath option is if you want to specifically prevent the contents of the CLASSPATH environment variable from being used. To do this, set ClassPath->None.
Dynamically Modifying the Class Path
One thing that is inconvenient about the standard Java class path is that it cannot be changed after the Java runtime has been launched. J/Link has its own class loader that searches in a set of special locations beyond the standard Java class path. This gives J/Link an extremely powerful and flexible means of finding classes. To add locations to this extra set, use the AddToClassPath function.
AddToClassPath["location",…] | add the specified directories or jar files to J/Link's class search path |
Adding classes to the search path.
After Java has been started, you can call AddToClassPath whenever you wish, and it will take effect immediately. One convenient feature of this extra class search path is that if you add a directory, then any jar or zip files in that directory will be searched. This means that you do not have to name jar files individually, as you need to do with the standard Java class path. For loose class files, the nesting rules are the same as for the class path, meaning that if a class is in the package com.acme.stuff, and you called AddToClassPath["d:\\myClasses"], then you would need to put the class file into d:\MyClasses\com\acme\stuff.
Changes to the search path that you make with AddToClassPath only apply to the current Java session. If you quit and restart java, you will need to call AddToClassPath again.
In addition to the locations you add yourself with AddToClassPath, J/Link automatically includes any Java subdirectories of any directories in the standard Wolfram Language application locations ($UserBaseDirectory/AddOns/Applications, $BaseDirectory/AddOns/Applications, <Mathematica dir>/AddOns/Applications, and <Mathematica dir>/AddOns/ExtraPackages). This feature is designed to provide extremely easy deployment for developers who create applications for the Wolfram Language that use Java and J/Link for part of their implementation. This is described in "Deploying Applications that use J/Link" in more detail, but even casual Java programmers who are writing classes to use with J/Link can take advantage of it. Just create a subdirectory of AddOns/Applications, say MyStuff, create a Java subdirectory within it, and toss class or jar files into it. J/Link will be able to find and use them. Of course, loose class files have to be placed into an appropriately nested subdirectory of the Java directory, corresponding to their package names (if any), as described.
The AddToClassPath function was introduced in J/Link 2.0. Previous versions of J/Link had a variable called $ExtraClassPath that specified a list of extra locations. You could add to this list.
$ExtraClassPath was deprecated in J/Link 2.0, but it still works. One advantage of $ExtraClassPath over using AddToClassPath is that changes made to $ExtraClassPath persist across a restart of the Java runtime.
Examining the Class Path
The JavaClassPath function returns the set of directories and jar files in which J/Link will search for classes. This includes all locations added with AddToClassPath or $ExtraClassPath, as well as Java subdirectories of application directories in any of the standard Wolfram Language application locations. It does not display the jar files that make up the standard Java platform itself, or jar files in the Java extensions directory. Those classes can always be found by Java programs.
JavaClassPath[] | gives the complete set of directories and jar files in which J/Link will search for classes |
Inspecting the class search path.
Using J/Link’s Class Loader Directly
As stated earlier, J/Link uses its own class loader to allow it to find classes and other resources in a dynamic set of locations beyond the startup class path. Essentially all the classes that you load using J/Link that are not part of the Java platform itself will be loaded by this class loader. One consequence of this is that calling Java's Class.forName() method from the Wolfram Language will often not work.
The problem is that Class.forName() finds classes using a default class loader, not the J/Link class loader, and this default class loader does not know about the special directories in which J/Link looks for classes (in fact, it does not even know about the startup class path, because of details of how J/Link launches Java). If you are translating Java code into the Wolfram Language, or if you just want to get a Class object for a given class, watch out for this problem. The fix is to force J/Link's class loader to be used. One way to do this is to use the three-argument form of Class.forName(), which allows you to specify the class loader to be used.
An easier way is to use the static classFromName method of JLinkClassLoader.
You should think of this classFromName() method as being the replacement for Class.forName(). When you find yourself wanting to obtain a Class object from a class name given as a string, remember to use JLinkClassLoader.classFromName().
Class.forName() is not very commonly found in Java code. One reason it is used is when an object needs to be created, but its class was not known at compile time. For example, the class name might come from a preferences file or be determined programmatically in some other way. Often, the very next line creates an instance of the class.
// Java code
Class cls = Class.forName("SomeClassThatImplementsInterfaceX");
X obj = (X) cls.newInstance();
If you are translating code like this into a Wolfram Language program, this operation can be performed simply by calling JavaNew.
The point here is that for a very common usage of Class.forName(), you do not have to translate it line-by-line into the Wolfram Language—you can duplicate the functionality by calling JavaNew.
Performance Issues
Overhead of Calls to Java
The speed of Java programs is highly dependent on the Java runtime. On certain types of programs, for example, ones that spend most of their time in a tight number-crunching loop, the speed of Java can approach that of compiled, optimized C.
Java is a good choice for computationally intensive programs. Your mileage may vary, but do not rule out Java for any type of program before you have done some simple speed testing. For less demanding programs, where every ounce of speed is not necessary, the simplicity of using J/Link instead of programming traditional WSTP "installable" programs with C makes Java an obvious choice.
The speed issues with J/Link are not, for the most part, the speed of Java execution. Rather, the bottleneck is the rate at which you can perform calls into Java, which is itself limited mainly by the speed of WSTP and the processing that must be done in the Wolfram Language for each call into Java. The maximum rate of calls into Java is highly dependent on which operating system and which Java runtime you use. A fast Windows machine can perform more than 5000 Java method calls per second, and considerably more if they are static methods, which require less preprocessing in the Wolfram Language. On some operating systems the results will be less. You should keep in mind that there is a more or less fixed cost of a call into Java regardless of what the call does, and on slow machines this cost could be as much as .001 seconds. Many Java methods will execute in considerably less time than this, so the total time for the call is often dominated by the fixed turnaround time of a J/Link call, not the speed of Java itself.
For most uses, the overhead of a call into Java is not a concern, but if you have a loop that calls into Java 500,000 times, you will have a problem (unless your program takes so long that the J/Link cost is negligible, in which case you have an even bigger problem!). If your Wolfram Language program is structured in a way that requires a great many calls into Java, you may need to refactor it to do more on the Java side and thus reduce the number of times you need to cross the Java-Wolfram Language boundary. This will probably involve writing some Java code, which unfortunately defeats the J/Link premise of being able to use the Wolfram Language to script the functionality of an arbitrary Java program. There are uses of Java that just cannot be feasibly scripted in this way, and for these you will need to write more of the functionality in Java and less in the Wolfram Language.
Speeding Up Sending Large Arrays
You can send and receive arrays of most "primitive" Java types (e.g. byte, short, int, float, double) nearly as fast as in a C-language program. The set of types that can be passed quickly corresponds to the set of types for which the WSTP C API has single functions to put arrays. The Java types long (these are 64 bits), boolean, and String do not have fast WSTP functions, and so sending or receiving these types is much slower. Try to avoid using extremely large arrays of these types (say, more than 100,000 elements) if possible.
A setting that has a big effect on the speed of moving multidimensional arrays is the one used to control whether "ragged" arrays are allowed. As discussed in "Ragged Arrays", the default behavior of J/Link is to require that all arrays be fully rectangular. But Java does not require that arrays conform to this restriction, and if you want to send or receive ragged arrays, you can call AllowRaggedArrays[True] in your Wolfram System session. This causes J/Link to switch to a much slower method for reading and writing arrays. Avoid using this setting unless you need it, and switch it off as soon as you no longer require it.
When you load a class with a method that takes, say, an int[][], the definition in the Wolfram Language that J/Link creates for calling this method uses a pattern test that requires its argument to be a two-dimensional array of integers. If the array is quite large, say on the order of 500 by 500, this test can take a significant amount of time, probably similar to the time it takes to actually transfer the array to Java. If you want to avoid the time taken by this testing of array arguments, you can set the variable $RelaxedTypeChecking to True. If you do this, you are on your own to ensure that the arrays you send are of the right type and dimensionality. If you pass a bad array, you will get a WSTP error, but this will not cause any problems for J/Link (other than that the call will return $Failed).
You probably do not want to leave $RelaxedTypeChecking set to True for a long time, and if you are writing code for others to use you certainly do not want to alter its value in their session. $RelaxedTypeChecking is intended to be used in a Block construct, where it is given the value of True for a short period.
$RelaxedTypeChecking only has an effect for arrays, which are the only types for which the pattern test that J/Link creates is expensive relative to the actual call into Java.
Another optimization to speed up J/Link programs is to use ReturnAsJavaObject to avoid unnecessary passing of large arrays or strings back and forth between the Wolfram Language and Java. ReturnAsJavaObject is discussed in the section "ReturnAsJavaObject".
An Optimization Example
Next examine a simple example of steps you might take to improve the speed of a J/Link program. Java has a powerful DecimalFormat class you can use to format Wolfram Language numbers in a desired way for output to a file. Here you create a DecimalFormat object that will format numbers to exactly four decimal places.
To use the fmt object, you call its format() method, supplying the number you want formatted.
This returns a string with the requested format. Now suppose you want to use this ability to format a list of 20000 numbers before writing them to a file.
The Map call, which invokes the format method 40000 times, takes 46 seconds on a certain PC (this is wall clock time, not the result of the Timing function, which is not accurate for WSTP programs on most systems). Clearly this is not acceptable. As a first step, you try using MethodFunction because you are calling the same method many times.
Note that you use fmt as the first argument to MethodFunction. The first argument merely specifies the class; as with virtually all functions in J/Link that take a class specification, you can use an object of the class if you desire. The MethodFunction that is created can be used on any object of the DecimalFormat class, not just the fmt object.
Using methodFunc, this now takes 36 seconds. There is a slight speed improvement, much less than in earlier versions of J/Link. This means you are getting about 1100 calls per second, and it is still not fast enough to be useful. The only thing to do is to write your own Java method that takes an array of numbers, formats them all, and returns an array of strings. This will reduce the number of calls from the Wolfram Language into Java from 40000 down to one.
Here is the code for the trivial Java class necessary. Note that there is nothing about this code that suggests it will be called from the Wolfram Language via J/Link. This is exactly the same code you would write if you wanted to use this functionality within Java.
public class FormatArray {
public static String[] format(java.text.DecimalFormat fmt,double[] d) {
String[] result=new String[d.length];
for (int i = 0; i < d.length; i++)
result[i] = fmt.format(d[i]);
return result;
}
}
This new version takes less than 2 seconds.
Reference Counts and Memory Management
Object References in the Wolfram Language
The earlier treatment of JavaObject expressions avoided discussing deeper issues such as reference counts and uniqueness. Every time a Java object reference is returned to the Wolfram Language, either as a result of a method or field or an explicit call to JavaNew, J/Link looks to see if a reference to this object has been sent previously in this session. If not, it creates a JavaObject expression in the Wolfram Language and sets up a number of definitions for it. This is a comparatively time-consuming process. If this object has already been sent to the Wolfram Language, in most cases J/Link simply creates a JavaObject expression that is identical to the one created previously. This is a much faster operation.
There are some exceptions to this last rule, meaning that sometimes when an object is returned to the Wolfram Language a new and different JavaObject expression is created for it, even though this same object has previously been sent to the Wolfram Language. Specifically, any time an object's hashCode() value has changed since the last time it was seen in the Wolfram Language, the JavaObject expression created will be different. You do not really need to be concerned with the details of this, except to remember that SameQ is not a valid way to compare JavaObject expressions to decide whether they refer to the same object. You must use the SameObjectQ function.
SameObjectQ[obj1,obj2] | return True if the JavaObject expressions obj1 and obj2 refer to the same Java object, False otherwise |
Comparing JavaObject expressions.
The variable pt refers to a Java Point object. Now put it into a container so you can get it back out later.
Now change the value of one of its fields. For a Point object, changing the value of one of its fields changes its hashCode() value.
Now you compare the JavaObject expression given by pt and the JavaObject expression created when you ask for the first element of the Vector to be returned to the Wolfram Language. Even though these are both references to the same Java object, the JavaObject expressions are different.
Because you cannot use SameQ (===) to decide whether two object references in the Wolfram Language refer to the same Java object, J/Link provides a function, SameObjectQ, for this purpose.
You may be wondering why the SameObjectQ function is necessary. Why not just call an object's equals() method? It certainly gives the correct result for this example.
The problem with this technique is that equals() does not always compare object references. Any class is free to override equals() to provide any desired behavior for comparing two objects of that class. Some classes make equals() compare the "contents" of the objects, such as the String class, which uses it for string comparison. Java provides two distinct equality operations, the == operator and the equals() method. The == operator always compares references, returning true if and only if the references point to the same object, but equals() is often overridden for some other type of comparison. Because there is no method call in Java that mimics the behavior of the language's == operator as applied to object references, J/Link needs a SameObjectQ function that provides that behavior for Wolfram Language programmers.
In an unusual case where you need to compare object references for equality a very large number of times, the comparative slowness of SameObjectQ compared to SameQ could become an issue. The only thing that could cause two JavaObject expressions that refer to the exact same Java object to be not SameQ is if the hashCode() value of the object changed between the times that the two JavaObject expressions were created. If you know this has not happened, then you can safely use SameQ to test whether they refer to the same object.
ReleaseJavaObject
The Java language has a built-in facility called "garbage collection" for freeing up memory occupied by objects that are no longer in use by a program. Objects become eligible for garbage collection when no references to them exist anywhere, except perhaps in other objects that are also unreferenced. When an object is returned to the Wolfram Language, either as a result of a call to JavaNew or as the return value of a method call or field access, the J/Link code holds a special reference to the object on the Java side to ensure that it cannot be garbage-collected while it is in use by the Wolfram Language. If you know that you no longer need to use a given Java object in your Wolfram System session, you can explicitly tell J/Link to release its reference. The function that does this is ReleaseJavaObject. In addition to releasing the Wolfram Language-specific reference in Java, ReleaseJavaObject clears out internal definitions for the object that were created in the Wolfram Language. Any subsequent attempt to use this object in the Wolfram Language will fail.
Now tell Java that you no longer need to use this object from the Wolfram Language.
It is now an error to refer to frm.
ReleaseJavaObject[obj] | let Java know that you are done using obj in the Wolfram Language |
ReleaseObject[obj] | deprecated; replaced by ReleaseJavaObject in J/Link 2.0 |
JavaBlock[expr] | all novel Java objects returned to the Wolfram Language during the evaluation of expr will be released when expr finishes |
BeginJavaBlock[] | all novel Java objects returned to the Wolfram Language between now and the matching EndJavaBlock[] will be released |
EndJavaBlock[] | release all novel objects seen since the matching BeginJavaBlock[] |
LoadedJavaObjects[] | return a list of all objects that are in use in the Wolfram Language |
LoadedJavaClasses[] | return a list of all classes loaded into the Wolfram Language |
J/Link memory management functions.
Calling ReleaseJavaObject will not necessarily cause the object to be garbage-collected. It is quite possible that other references to it exist in Java. ReleaseJavaObject does not tell Java to throw the object away, only that it does not need to be kept around solely for the Wolfram Language's sake.
An important fact about the references that J/Link maintains for objects sent to the Wolfram Language is that only one reference is kept for each object, no matter how many times it is returned to the Wolfram Language. It is your responsibility to make sure that after you call ReleaseJavaObject, you never attempt to use that object through any reference that might exist to it in your Wolfram System session.
The add() method of the Frame class returns the object added, so b2 refers to the same object as b1.
If you call ReleaseJavaObject[b1], it is not the Wolfram Language symbol b1 that is affected, but the Java object that b1 refers to. Therefore, using b2 is also an error (or any other way to refer to this same Button object, such as %).
Calling ReleaseJavaObject is often not necessary in casual use. If you are not making heavy use of Java in your session then you will usually not need to be concerned about keeping track of what objects may or may not be needed anymore—you can just let them pile up. There are special times, though, when memory use in Java will be important, and you may need the extra control that ReleaseJavaObject provides.
JavaBlock
ReleaseJavaObject is provided mainly for developers who are writing code for others to use. Because you can never predict how your code will be used, developers should always be sure that their code cleans up any unnecessary references it creates. Probably the most useful function for this is JavaBlock.
JavaBlock automates the process of releasing objects encountered during the evaluation of an expression. Often, a Wolfram Language program will need to create some Java objects with JavaNew; operate with them, perhaps causing other objects to be returned to the Wolfram Language as the results of method calls; and finally return some result such as a number or string. Every Java object encountered by the Wolfram Language during this operation is needed only during the lifetime of the program, much like the local variables provided in the Wolfram Language by Block and Module, and in C, C++, Java, and many other languages by block scoping constructs (e.g., {}). JavaBlock allows you to mark a block of code as having the property that any new objects returned to the Wolfram Language during the evaluation are to be treated as temporary, and released when JavaBlock finishes.
It is important to note that the preceding sentence said "new objects". JavaBlock will not cause every object encountered during the evaluation to be released, only those that are being encountered for the first time. Objects that have already been seen by the Wolfram Language will not be affected. This means that you do not have to worry that JavaBlock will aggressively release an object that is not truly temporary to that evaluation.
It is not enough simply to call ReleaseJavaObject on every object you create with JavaNew, because many Java method calls return objects. You may not be interested in these return values, or you may never assign them to a named variable because they may be chained together with other calls (as in obj@returnsObject[]@foo[]), but you still need to release them. Using JavaBlock is an easy way to be sure that all novel objects are released when a block of code finishes.
JavaBlock[expr] returns whatever expr returns.
Many J/Link Wolfram Language programs will have the following structure.
It is very common to write a function that creates and manipulates a number of JavaObject expressions, and then returns one of them, the rest being temporary. To facilitate this, if the return value of a JavaBlock is a single JavaObject, it will not be released.
New in J/Link 2.1 is the KeepJavaObject function, which allows you to specify an object or sequence of objects that should not be released when the JavaBlock ends. Calling KeepJavaObject on a single object or sequence of objects means they will not be released when the first enclosing JavaBlock ends. If there is an outer enclosing JavaBlock, the objects will be freed when it ends, however, so if you want the objects to escape a nested set of JavaBlock expressions, you must call KeepJavaObject at each level. Alternatively, you can call KeepJavaObject[obj,Manual], where the Manual argument tells J/Link that the object should not be released by any enclosing JavaBlock expressions. The only way such an object will be released is if you manually call ReleaseJavaObject on it. Here is an example that uses KeepJavaObject to allow you to return a list of two objects without their being released.
BeginJavaBlock and EndJavaBlock can be used to provide the same functionality as JavaBlock across more than one evaluation. EndJavaBlock releases all novel Java objects returned to the Wolfram Language since the previous matching BeginJavaBlock. These functions are mainly of use during development, when you might want to set a mark in your session, do some work, and then release all novel objects returned to the Wolfram Language since that point. BeginJavaBlock and EndJavaBlock can be nested. Every BeginJavaBlock should have a matching EndJavaBlock, although it is not a serious error to forget to call EndJavaBlock, even if you have nested levels of them—you will only fail to release some objects.
LoadedJavaObjects and LoadedJavaClasses
LoadedJavaObjects[] returns a list of all Java objects that are currently referenced in the Wolfram Language. This includes all objects explicitly created with JavaNew and all those that were returned to the Wolfram Language as the result of a Java method call or field access. It does not include objects that have been released with ReleaseJavaObject or through JavaBlock. LoadedJavaObjects is intended mainly for debugging. It is very useful to call it before and after some function you are working on. If the list grows, your function leaks references, and you need to examine its use of JavaBlock and/or ReleaseJavaObject.
LoadedJavaClasses[] returns a list of JavaClass expressions representing all classes loaded into the Wolfram Language. Like LoadedJavaObjects, LoadedJavaClasses is intended mainly for debugging. Note that you do not have to determine if a class has already been loaded before you call LoadJavaClass. If the class has been loaded, LoadJavaClass does nothing but return the appropriate JavaClass expression.
Exceptions
How Exceptions Are Handled
J/Link handles Java exceptions automatically. If an uncaught exception is thrown during any call into Java, you will get a message in the Wolfram System. Here is an example that tries to format a real number as an integer.
If the exception is thrown before the method returns a result to the Wolfram Language, as in the example, the result of the call will be $Failed. As discussed later in "Manually Returning a Result to the Wolfram Language", it is possible to write your own methods that manually send a result to the Wolfram Language before they return. In such cases, if an exception is thrown after the result is sent to the Wolfram Language but before the method returns, you will get a warning message reporting the exception, but the result of the call will be unaffected.
If the Java code was compiled with debugging information included, the Wolfram System message you get as a result of an exception will show the full stack trace to the point where the exception occurred, with the exact line numbers in each file.
The JavaThrow Function
In some cases, you may want to cause an exception to be thrown in Java. This can be done with the JavaThrow function. JavaThrow is new in J/Link 2.0 and should be considered experimental. Its behavior might change in future versions.
JavaThrow[exceptionObj] | throw the given exception object in Java |
Throwing Java exceptions from the Wolfram Language.
You will only want to use JavaThrow in Wolfram Language code that is itself called from Java. It is quite common for J/Link programs written in the Wolfram Language to involve both calls from the Wolfram Language into Java and calls from Java back to the Wolfram Language. Such "callbacks" to the Wolfram Language are used extensively in Wolfram Language programs that create Java user interfaces, as described in detail later in the section "Creating Windows and Other User Interface Elements". For example, you can associate a Wolfram Language function to be called when the user clicks a Java button. This Wolfram Language function is called directly from Java, and you might want it to behave just like a Java method, including having the ability to throw Java exceptions.
An example of throwing an exception in a callback from a user interface action like clicking a button is not very realistic because there is typically nothing in Java to catch such exceptions; thus they are essentially ignored. A more meaningful example would be a program that involved a mix of Java and Wolfram Language code where, for flexibility and ease of development reasons, you have a Wolfram Language function being called to implement the "guts" of a Java method that can throw an exception. As a concrete example, say you are doing XML processing with Java and the Wolfram Language using the SAX (Simple API for XML) API. SAX processing is based on a set of handler methods that are called as certain events occur during parsing of the XML document. Each such method can throw a SAXException to indicate an error and halt the parsing. You want to implement these handler methods in Wolfram Language code, and thus you want a way to throw a SAXException from the Wolfram Language. Here is a hypothetical example of one such handler method, the startDocument() method, which is invoked by the SAX engine when document processing starts.
After a call to JavaThrow, the rest of the Wolfram Language function executes normally, but there is no result returned to Java.
Returning Objects "by Value" and "by Reference"
References and Values
J/Link provides a mapping between certain Wolfram Language expressions and their Java counterparts. What this means is that these Wolfram Language expressions are automatically converted to and from their Java counterparts as they are passed between the Wolfram Language and Java. For example, Java integer types (long, short, int, and so on) are converted to Wolfram Language integers and Java real types (float and double) are converted to Wolfram Language real numbers. Another mapping is that Java objects are converted to JavaObject expressions in the Wolfram Language. These JavaObject expressions are references to Java objects—they have no meaning in the Wolfram Language except as they are manipulated by J/Link. However, some Java objects are things that have meaningful values in the Wolfram Language, and these objects are by default converted to values. Examples of such objects are strings and arrays.
You could say, then, that Java objects are by default returned to the Wolfram Language "by reference", except for a few special cases. These special cases are strings, arrays, complex numbers (discussed later), BigDecimal and BigInteger (discussed later), and the "wrapper" classes (e.g., java.lang.Integer). You could say that these exceptional cases are returned "by value". The table in "Conversion of Types between Java and the Wolfram Language" shows how these special Java object types are mapped into Wolfram Language values.
In summary, every Java object that has a meaningful value representation in the Wolfram Language is converted into this value, simply because that is the most useful behavior. There are times, however, when you might want to override this default behavior. Probably the most common reason for doing this is to avoid unnecessary traffic of large expressions over WSTP.
ReturnAsJavaObject[expr] | a Java object returned by expr will be in the form of a reference |
ByRef[expr] | deprecated; replaced by ReturnAsJavaObject in J/Link 2.0 |
JavaObjectToExpression[obj] | give the value of the Java object obj as a Wolfram Language expression |
Val[obj] | deprecated; replaced by JavaObjectToExpression in J/Link 2.0 |
"By reference" and "by value" control.
ReturnAsJavaObject
Consider the case where you have a static method in class MyClass called arrayAbs() that takes an array of doubles and returns a new array where each element is the absolute value of the corresponding element in the argument array. The declaration of this method thus looks like double[] arrayAbs(double[] a). This is how you would call such a method from the Wolfram Language.
The example showed how you probably want the method to work: you pass a Wolfram Language list and get back a list. Now assume you have another method named arraySqrt() that acts like arrayAbs() except that it performs the sqrt() function instead of abs().
In this computation, the original list is sent over WSTP to Java and a Java array is created with these values. That array is passed as an argument to arrayAbs(), which itself creates and returns another array. This array is then sent back to the Wolfram Language via WSTP to create a list, which is then promptly sent back to Java as the argument for arraySqrt(). You can see that it was a waste of time to send the array data back to the Wolfram Language—you had a perfectly good array (the one returned by the arrayAbs() method) living on the Java side, ready to be passed to arraySqrt(), but instead you sent its contents back to the Wolfram Language only to have it immediately come back to Java again as a new array with the same values! For this example, the cost is negligible, but what if the array has 200,000 elements?
What is needed is a way to let the array data remain in Java and return only a reference to the array, not the actual data itself. This can be accomplished with the ReturnAsJavaObject function.
Note that the class name of the JavaObject is "[D", which, although a bit cryptic, is the actual Java class name of a one-dimensional array of doubles. Here is how the computation looks using ReturnAsJavaObject.
Earlier you saw arraySqrt() being called with an argument that was a Wolfram Language list of reals. Here it is being called with a reference to a Java object that is a one-dimensional array of doubles. All methods and fields that take an array can be called from the Wolfram Language with either a Wolfram Language list or a reference to a Java array of the appropriate type.
Strings are the other type for which ReturnAsJavaObject is useful. Like arrays, strings have the two properties that: (1) they are represented in Java as objects but also have a meaningful Wolfram Language value; and (2) they can be large, so it is useful to be able to avoid passing their data back and forth unnecessarily. As an example, say your class MyClass has a static method that appends to a string a digit taken from an external device that you are controlling from Java. It takes a string and returns a new one, so its signature is static String appendDigit(String s). You have a Wolfram Language variable named veryLongString that holds a long string, and you want to append to this string 100 times. This code will cause the string contents to make 100 round trips between the Wolfram Language and Java.
Using ReturnAsJavaObject lets the strings remain on the Java side, and thus it generates virtually no WSTP traffic.
This example is somewhat contrived, since repeatedly appending to a growing string is not a very efficient style of programming, but it illustrates the issues.
When the Do loop is executed, veryLongString gets assigned values that are not Wolfram Language strings, but JavaObject expressions that refer to strings residing in Java. That means that appendString() gets called with a Wolfram Language string the very first iteration, but with a JavaObject expression thereafter. As is the case with arrays, any Java method or field that takes a string can be called in the Wolfram Language either with a string or a JavaObject expression that refers to one. The veryLongString variable started out holding a string, but at the end of the loop it holds a JavaObject expression.
At some point, you probably want an actual Wolfram Language string, not this string object reference. How do you get the value back? You will visit this example again later when the JavaObjectToExpression function is introduced.
In summary, the ReturnAsJavaObject function causes methods and fields that return objects that would normally be converted into Wolfram Language values to return references instead. It is an optimization to avoid unnecessarily passing large amounts of data between the Wolfram Language and Java, and as such it will be useful primarily for very large arrays and strings. As with all optimizations, you should not concern yourself with ReturnAsJavaObject unless you have some code that is running at an unacceptable speed, or you know ahead of time that the code you are writing will benefit measurably from it. Objects of most Java classes have no meaningful "by value" representation in the Wolfram Language, and they are always returned "by reference". ReturnAsJavaObject will have no effect in these cases.
Finally, note that ReturnAsJavaObject has no effect on methods in which the Java programmer manually sends the result back to the Wolfram Language (this topic is discussed later in this User Guide). Manually returning a result bypasses the normal result-handling routines in J/Link, so there is no chance for the ReturnAsJavaObject request to be accommodated.
JavaObjectToExpression
In the previous section, you saw how the ReturnAsJavaObject function can be used to cause objects normally returned to the Wolfram Language by value to be returned by reference. It is necessary to have a function that does the reverse—takes a reference and converts it to its value representation. That function is JavaObjectToExpression.
Returning to the earlier appendString example, you used ReturnAsJavaObject to avoid costly passing of string data back and forth over WSTP. The result of this was that the veryLongString variable now held a JavaObject expression, not a literal Wolfram Language string. JavaObjectToExpression can be used to get the value of this string object as a Wolfram Language string.
The majority of Java objects have no meaningful value representation in the Wolfram Language. These objects can only be represented in the Wolfram Language as JavaObject expressions, and using JavaObjectToExpression on them has no effect.
The ReturnAsJavaObject function is not the only way to get a JavaObject expression for an object that is normally returned to the Wolfram Language as a value. The JavaNew function always returns a reference.
The next section introduces the MakeJavaObject function, which is easier than using JavaNew to construct Java objects out of Wolfram Language strings and arrays.
MakeJavaObject and MakeJavaExpr
Preamble
In addition to JavaNew, which calls a class constructor, J/Link provides two convenience functions for creating Java objects from Wolfram Language expressions. These functions are MakeJavaObject and MakeJavaExpr. Do not get them confused, despite their similar names. MakeJavaObject is a commonly used function for constructing objects of some special types. MakeJavaExpr is an advanced function that creates an object of J/Link's Expr class representing an arbitrary Wolfram Language expression.
MakeJavaObject
MakeJavaObject[val] | construct an object of the appropriate type to represent the Wolfram Language expression val (numbers, strings, lists, and so on) |
When you call a Java method from the Wolfram Language that takes, say, a Java String object, you can call it with a Wolfram Language string. The internals of J/Link will construct a Java string that has the same characters as the Wolfram Language string, and pass that string to the Java method. Sometimes, however, you want to pass a string to a method that is typed to take Object. You cannot call such a method from the Wolfram Language with a string as the argument because although J/Link recognizes that a Wolfram Language string corresponds to a Java string, it does not recognize that a Wolfram Language string corresponds to a Java Object. It does this deliberately, for the sake of imposing as much type safety as possible on calls into Java. For this example, assume that the class MyClass has a method with the following signature.
Assume also that theObj is an object of this class, created with JavaNew. Try to call foo with a literal string.
It fails for the reason given above. To call a Java method that is typed to take an Object with a string, you must first explicitly create a Java string object with the appropriate value. You can do this using JavaNew.
Now it works, because the argument is a JavaObject expression.
To avoid having to call JavaNew to create a Java string object, J/Link provides the MakeJavaObject function.
In the case of a string, MakeJavaObject just calls JavaNew for you. Of course, it would not be of much use if it could only construct String objects. The same issue arises with other Java objects that are direct representations of Wolfram Language values. This includes the "wrapper" classes like java.lang.Integer, java.lang.Boolean, and so on, and the array classes. If you want to call a Java method that takes a java.lang.Integer as an argument, you can call it from the Wolfram Language with a raw integer. But if you want to pass an integer to a method that is typed to take an Object, you must explicitly create an object of type java.lang.Integer—J/Link will not construct one automatically from an integer argument. It is simpler to call MakeJavaObject than JavaNew for this.
When given an integer argument, MakeJavaObject always constructs a java.lang.Integer, never a java.lang.Short, java.lang.Long, or other "integer" Java wrapper object. Similarly, if you call MakeJavaObject with a real number, it creates a java.lang.Double, never a java.lang.Float. If you require an object of one of these other types, you will have to call JavaNew explicitly.
MakeJavaObject also works for Boolean values.
If MakeJavaObject were only a shortcut for calling JavaNew, it would not be all that useful. It becomes indispensable, however, for creating objects of an array class. Recall that in Java, arrays are objects and they belong to a class. These classes have cryptic names, but if you know them you can create array objects with JavaNew. When creating array objects, the second argument to JavaNew is a list giving the length in each dimension. Here you create a 2×3 array of ints.
JavaNew lets you create array objects, but it does not let you supply initial values for the elements of the array. MakeJavaObject, on the other hand, takes a Wolfram Language list and converts it into a Java array object with the same values.
Thus, MakeJavaObject is particularly useful for creating array objects, because it lets you supply the initial values for the array elements, and it frees you from having to learn and remember the names of the Java array classes ([[I for a two-dimensional array of ints, [D for a one-dimensional array of doubles, and so on). MakeJavaObject can create arrays up to three dimensions deep of integers, doubles, strings, Booleans, and objects.
The JavaObjectToExpression function is discussed in the section "JavaObjectToExpression", and you can think of MakeJavaObject as being the inverse of JavaObjectToExpression. MakeJavaObject takes a Wolfram Language expression that has a corresponding Java object that can represent its value, and creates that object. It literally "makes it into a Java object". The JavaObjectToExpression function goes the other way—it takes a Java object that has a meaningful Wolfram Language representation and converts it into that expression. It will always be the case that, for any x that MakeJavaObject can operate on, the following applies.
Remember that MakeJavaObject is not a commonly used function. You do not need to explicitly construct Java objects from Wolfram Language strings, arrays, and so on, just to pass them to Java methods—J/Link does this automatically for you. But even though J/Link will create objects automatically from certain arguments in most circumstances, it will not do so when an argument is typed as a generic Object. Then you must create a JavaObject yourself, and MakeJavaObject is the easiest way to do this.
The code for the SetInternetProxy function discussed in the section "SetInternetProxy" provides a concrete example of using MakeJavaObject. To specify proxy information (for users behind firewalls), you need to set some system properties using the Properties class. This class is a subclass of Hashtable, so it has a method with the following signature.
You should always specify keys and values for Properties in the form of strings. Thus, you might try this from the Wolfram Language.
For this to work, you need to use MakeJavaObject to create Java String objects.
MakeJavaExpr
To understand the MakeJavaExpr function, you need to understand the motivation for J/Link's Expr class, which is discussed in detail in "Motivation for the Expr Class". Basically, an Expr is a Java object that can represent an arbitrary Wolfram Language expression. Its main use is as a convenience for Java programmers who want to examine and operate on Wolfram Language expressions in Java. Sometimes it is useful to have a way of creating Expr objects in the Wolfram Language instead of from Java. MakeJavaExpr is the function that fills this need.
MakeJavaExpr[expr] | construct an object of J/Link's Expr class that represents the Wolfram Language expression |
Note that if you are calling a Java method that is typed to take an Expr, then you do not have to call MakeJavaExpr to construct an Expr object. J/Link will automatically convert any expression you supply as the argument to an Expr object, as it does with other automatic conversions. Like MakeJavaObject, MakeJavaExpr is used in cases where you are calling a method that takes a generic Object, not an Expr, and therefore J/Link will not perform any automatic conversion for you. In such cases you need to explicitly create an Expr object out of some Wolfram Language expression. One reason you might want to do this is to store a Wolfram Language expression in Java for retrieval later.
Here is a simple example of MakeJavaExpr. This demonstrates a few methods from the Expr class, which has a number of Wolfram Language-like methods for examining, modifying, and extracting portions of expressions. Of course, this is a highly contrived example—if you wanted to know the length of an expression you would just call the Wolfram Language's Length[] function. The Expr methods demonstrated here are typically called from Java, not the Wolfram Language.
Note that Expr objects, like Wolfram Language expressions, are immutable. The above call to insert() did not modify e; instead, it returned a new Expr.
If you are having trouble understanding why you might want to use MakeJavaExpr in a Wolfram Language program, do not worry. It is an advanced function that few programmers will have any use for.
Creating Windows and Other User Interface Elements
Preamble
One of the most useful applications for J/Link is to write user interface elements for Wolfram Language programs. Examples of such elements would be a progress bar monitoring the completion of a computation, a window that displays an image or animation, a dialog box that prompts the user for input or helps them compose a proper call of an unfamiliar function, or a mini-application that leads the user through the steps of an analysis. These types of user interfaces are distinct from what you might write for a Java program that uses the Wolfram Language in the background in that they "pop up" when the user invokes some Wolfram Language code. They do not replace the notebook front end, they just augment it. In this way, they are like an extension of the palettes and other specialty notebook elements you can create in the front end.
The Wolfram Language with J/Link is an extremely powerful and productive environment for creating user interfaces. The complexity of user interface code is ideally suited to the interactive line-at-a-time nature of J/Link development. You can literally build, modify, and experiment with your user interface while it is running.
Anyone considering writing user interfaces for Wolfram Language programs should also look at GUIKit. GUIKit is built on top of J/Link, and provides an extremely high-level means of creating interfaces. Further discussion of GUIKit is beyond the scope of this manual, but be aware that GUIKit was specifically designed to provide an easier means of creating user interfaces than writing in "raw" J/Link, as described here.
Interactive and Non-Interactive Interfaces
To write Wolfram Language programs that create Java windows you need to understand important distinctions between several types of such user interfaces. These distinctions relate to how they interact with the Wolfram Language kernel.
At the highest level of categorization, there is a distinction between "interactive" and "non-interactive" interfaces. The interactiveness under consideration here is with the Wolfram Language kernel, not with the user. What we are calling non-interactive user interfaces have no need to communicate back to the Wolfram Language, although they typically are controlled by the Wolfram Language. Such interfaces often accept no user input at all—they are created, manipulated, and destroyed by Wolfram Language code. An example of this type is a window that shows a progress bar (a complete progress bar program is presented in "A Progress Bar"). A progress bar does not return a result to the Wolfram Language and it does not need to respond to user actions, at least not by interacting with the Wolfram Language. In other words, the window may go away when its close box is clicked (a user action), but this is not relevant to the Wolfram Language because it does not return a result or trigger a call back into the Wolfram Language. A progress bar is completely driven by a Wolfram Language program. The flow of information is in one direction only.
Such user interfaces typically have lifetimes that are encompassed by a single Wolfram Language program, as is the case with a progress bar. This is not required, however. Hosting an applet in its own window, as described in "Hosting Applets", is an example where the window lives on after the code that created it ends execution. The applet window is only dismissed when the user clicks in its close box. Again, though, the important property is that the applet does not need to interact with the Wolfram Language.
This type of user interface, which requires no interaction back with the Wolfram Language, poses no special issues that need to be discussed in this section. A program that creates, runs, and destroys such an interface is very much like a non-GUI Java computation that is accomplished with a series of calls into Java. It just happens to produce a visual effect. You can examine the progress bar code in "A Progress Bar" if you want to see a fully fleshed-out example.
The more common "interactive" type of user interface needs to communicate back to the Wolfram Language. This might be to return a result, like a typical modal input dialog, or to initiate a computation as a consequence of the user clicking a button. To understand the special problem this imposes, it is useful to examine some basic considerations about the kernel's "main loop", whereby it acquires input, evaluates it, and sends off any output.
When the Wolfram Language kernel is being used from the front end, it spends most of its life waiting for input to arrive on the WSTP link that it uses to communicate with the front end. This link is given by $ParentLink, and it is $ParentLink that has the kernel's "attention". When input arrives on $ParentLink, it is evaluated, any results are sent back on the link, and the kernel goes back to waiting for more input on $ParentLink. When J/Link is being used, the kernel has another link open, the one that connects to the Java runtime. When you execute some code that calls into Java, the kernel sends something to Java and then blocks waiting for the return value from Java. During this period when the kernel is waiting for a return value from Java, the Java link has the kernel's attention. It is only during this period of time that the kernel is paying attention to the Java link. A more general way of saying this is that the kernel is only listening for input arriving from Java when it has been specifically instructed to do so. The rest of the time it is listening only to $ParentLink, which is typically the notebook front end.
Consider what happens when the user clicks a button in your Java window and that button tries to execute some code that calls into the Wolfram Language. The Java side sends something to the Wolfram Language and then waits for the result, but the kernel will never get the request because it is not paying attention to the Java link. It is necessary to use some means to tell the kernel to look for input arriving on the Java link. J/Link provides three different ways to manage the kernel's attention to the Java link, and thereby control its readiness to accept requests for evaluations initiated by the Java side.
These three ways can be called "modal", "modeless", and "manual". In modal interaction, characterized by the use of the DoModal Wolfram Language function, the kernel is pointed at the Java link until the Java side releases it. The kernel is a complete slave to the Java side, and is unavailable for any other computations. In modeless interaction, characterized by the use of the ShareKernel Wolfram Language function, the kernel is kept in a state where it is receptive to evaluation requests arriving from either the notebook front end or Java, evenly sharing its attention between these two programs. Lastly, there is a manual mode, characterized by the use of the ServiceJava Wolfram Language function, which in some ways is intermediate between modal and modeless operation. Here, you manually instruct the kernel to allow single requests from Java while in the middle of running a larger program. The next few sections are devoted to further exploration of these types of user interfaces.
Before continuing, it is important to remember that all these issues about how to prepare the kernel for computations arriving from Java are only relevant for computations initiated in Java, typically by user actions like clicking a button. Calls from Java to the Wolfram Language that are part of a back-and-forth series of calls that involve a call from the Wolfram Language into Java are not a problem. Any time the Wolfram Language has called into Java, the Wolfram Language is actively listening for results arriving from Java. This may sound confusing, but that is mostly because it is only in a much later section that writing your own Java methods to be called from the Wolfram Language is discussed; such methods can call back to the Wolfram Language for computations before they return their result (typical examples are to print something in the notebook window or display a message). These are true callbacks into the Wolfram Language, and the Wolfram Language is always ready to handle them. In contrast, calls to the Wolfram Language that occur as the result of a user action in the Java side are, in effect, a surprise to the Wolfram Language, and it is not normally in a state where it is ready to accept them.
Modal versus Modeless Operation
A common type of user interface element is like a modal dialog: once it is displayed, the Wolfram Language program hangs waiting for the user to dismiss the window. Typically, this is because the window returns a result to the Wolfram Language, so it is not meaningful for the Wolfram Language to continue until the window is closed. An example of such a window is a simple input window that asks the user for some value, which it returns to the Wolfram Language when the OK button is clicked.
It is important to understand our slightly generalized use of the term "modal" to describe these windows. They may not be modal in the traditional sense that they must be dismissed before anything else can be done in the user interface. Rather, they are modal with respect to the Wolfram Language kernel—the kernel cannot do anything else until they are closed. A Java window that you create might not be modal with respect to other Java windows on the screen (i.e. a dialog might not have the isModal property set), but it ties up the kernel's attention until it is dismissed.
Another type of user interface element is like a modeless dialog: after it is displayed, the Wolfram Language program that created it will finish, leaving the window visible and usable while the user continues working in the notebook front end. This sounds a lot like the first type of user interface element described earlier, but these windows are distinguished by the fact that they can initiate interactions with the Wolfram Language while they are visible. An example would be a window that lets users load packages into the Wolfram Language by selecting them from a scrolling list. You write a J/Link program that creates this window, displays it, and returns. The window is left open and usable until the user clicks its close box. In the meantime, the user is free to continue working in the front end, going back to use this Java window whenever it is convenient.
Such a window is almost like another type of notebook or palette window in the front end. You can have any number of front end or Java windows open at once, and active, meaning that they can be used to initiate computations in the Wolfram Language. They are each their own little interface onto the same kernel. What is different about the Java window is that it is much more general than a notebook window, and, importantly, it lives in a different application layer than the front end. This last fact makes the Java window in effect a second front end, rather than an extension of the notebook front end. To accommodate such a second front end, the kernel must be kept in a special state that allows it to handle requests for evaluations arriving from either the notebook front end or Java.
Before presenting examples of how to implement modal and modeless windows, it is necessary to jump ahead a little bit and explain the main mechanism by which Java user interface elements can communicate with the Wolfram Language.
Handling Events with Wolfram Language Code: The "MathListener" Classes
User interface elements typically have active components like buttons, scrollbars, menus, and text fields that need to trigger some action when they are clicked. In the Java event model, components fire events in response to user actions, and other components indicate their interest in these events by registering as event listeners. In practice, though, components do not usually act as event listeners directly. Instead, the programmer writes an adapter class that implements the desired event-listener interface and calls certain methods in the component in response to various events. This avoids having to subclass the responding component just to have it act as an event listener. The only specialty code goes into the adapter class, allowing the components that fire and respond to events to be generic.
As an example, say you are writing a standard Java program and you have a button that you want to use to control the appearance of a text area. Clicking the button should toggle between black text on a white background and white text on a black background. Buttons fire ActionEvents when they are clicked, and a class that wants to receive notifications of clicks must implement the ActionListener interface, and register with the button by calling its addActionListener method. You would write a class, perhaps called MyActionAdapter, that implements ActionListener. In its actionPerformed() method, which is what will be called when the button is clicked, you would call the appropriate methods to set the foreground and background colors of the text area.
If you have ever used a Java GUI builder that lets you create an application by dropping components on a form and then wiring them together via events, the code that is being generated for you consists in large part of adapter classes that manage the logic of calling certain methods in the target objects when events are fired by the source objects.
What all this is leading up to is simply that the wiring of components in a GUI typically involves writing a lot of Java code in the form of classes that implement various event-listener interfaces. J/Link programmers want to write GUIs that use the standard Java event model, and they should not have to write Java code to do it. The solution is simple: J/Link provides a complete set of classes that implement the standard event-listener interfaces and whose actions are to call back into the Wolfram Language to execute user-defined code. This brings all the event-handling logic down into the Wolfram Language, where it can be scripted like every other part of the program.
Not only does this solution preserve the "pure Wolfram Language" property of even complex Java GUIs, it is vastly more flexible than writing a traditional application in Java. When you write in Java, or use a fancy drag-and-drop GUI builder, you hard-code the event logic. You have to decide at compile time what every click, scroll, and keystroke will do. But when you use J/Link, you decide how your program is wired together at run time. You can even change the behavior on the fly simply by typing a few lines of code.
J/Link provides implementations of all the standard AWT event-listener classes. These classes are named after the interfaces they implement, with "Math" prepended. Thus, the class that implements ActionListener is MathActionListener. (Perhaps these classes would be better named MathXXXAdapter.) The following table shows a summary of all the MathListener classes, the methods they implement, and the arguments they send to your Wolfram Language handler function.
class | methods | arguments to Wolfram Language handler |
MathActionListener | actionPerformed(ActionEvent e) | e, e.getActionCommand() (String) |
MathAdjustmentListener | adjustmentValueChanged( AdjustmentEvent e) | e, e.getAdjustmentType(),(Integer) e.getValue() (Integer) |
MathComponentListener | componentHidden(ComponentEvent e) componentShown(ComponentEvent e) componentResized(ComponentEvent e) componentMoved(ComponentEvent e) | e |
MathContainerListener | componentAdded(ContainerEvent e) componentRemoved(ContainerEvent e) | e |
MathFocusListener | focusGained(FocusEvent e) focusLost(FocusEvent e) | e |
MathItemListener | itemStateChanged(ItemEvent e) | e, e.getStateChange() (Integer) |
MathKeyListener | keyPressed(KeyEvent e) keyReleased(KeyEvent e) keyTyped(KeyEvent e) | e, e.getKeyChar(),(Integer) e.getKeyCode() (Integer) |
MathMouseListener | mouseClicked(MouseEvent e) mouseEntered(MouseEvent e) mouseExited(MouseEvent e) mousePressed(MouseEvent e) mouseReleased(MouseEvent e) | e, e.getX(), (Integer) e.getY(), (Integer) e.getClickCount() (Integer) |
MathMouseMotionListener | mouseMoved(MouseEvent e) mouseDragged(MouseEvent e) | e, e.getX(), (Integer) e.getY(), (Integer) e.getClickCount() (Integer) |
MathPropertyChangeListener | propertyChanged( PropertyChangeEvent e) | e |
MathTextListener | textValueChanged(TextEvent e) | e |
MathVetoableChangeListener | vetoableChange( PropertyChangeEvent e) | e(veto the change by returning False from your handler) |
MathWindowListener | windowOpened(WindowEvent e) windowClosed(WindowEvent e) windowClosing(WindowEvent e) windowActivated(WindowEvent e) windowDeactivated(WindowEvent e) windowIconified(WindowEvent e) windowDeiconified(WindowEvent e) | e |
Listener classes provided with J/Link.
As an example of how to read this table, take the MathKeyListener class. MathKeyListener implements the KeyListener interface, which contains the methods keyPressed(), keyReleased(), and keyTyped(). If you register a MathKeyListener object with a component that fires KeyEvents, then these three methods will be called in response to the key events they are named after. When any of these methods are called, they will call into the Wolfram Language and execute a user-defined function, passing it three arguments: the KeyEvent object itself, followed by two integers that are the results of the event object's getKeyChar() and getKeyCode() methods. All the MathListener classes pass your handler function the event object itself, and a few, like this one, pass additional integer arguments that are commonly needed values. This just saves you the overhead of having to call back into Java to get these additional values.
To specify the Wolfram Language function associated with any of the methods of a MathListener object, call the object's setHandler() method. setHandler() takes two strings, the first of which is the name of the event-handler method (e.g. "actionPerformed" or "keyPressed"), and the second of which is the Wolfram Language function that should be called in response. The Wolfram Language function can be a name, as in "myButtonFunction" or a pure function (specified as a string). The reason for supplying the name of the actual Java method in the listener interface is that many of the listeners have multiple methods. setHandler() returns True if the handler was set correctly and False otherwise (for example, if the method you named is not spelled correctly).
obj@setHandler["methodName","funcName"] | |
set the Wolfram Language function that will be called when the MathListener object obj's event-handler method methodName() is called. |
Assigning the Wolfram Language function that will be called in response to an event notification.
The use of these classes will become clear in the simple examples that follow for modal and modeless windows, and in the more fully worked-out examples in the sections "A Simple Modal Input Dialog" and "A Piano Keyboard".
You are not required to use the J/Link MathListener classes for creating calls into the Wolfram Language triggered by user actions. They are provided simply as a convenience. You could write your own classes to handle events and put calls into the Wolfram Language directly into their code. All the "MathListener" classes in J/Link are derived from an abstract base class called, appropriately, MathListener. The code in MathListener takes care of all of the details of interacting with the Wolfram Language, and it also provides the setHandler() methods that you use to associate events with Wolfram Language code. Users who want to write their own classes in MathListener style (for example, for one of the Swing-specific event-listener interfaces, which J/Link does not provide) are strongly encouraged to make their classes subclasses of MathListener to inherit all this functionality. You should examine the source code for one of the concrete classes derived from MathListener (MathActionListener is probably the simplest one) to see how it is written. You can use this as a starting point for your own implementation. If you do not make your class a subclass of MathListener, and instead choose to write your own event-handler code that calls into the Wolfram Language, you must read "Writing Your Own Event Handler Code".
Bringing Java Windows to the Foreground
If you are creating a Java window with a Wolfram Language program, you probably want that window to pop up in front of the notebook the user is working in, so that its presence becomes apparent. You might expect that the toFront() method of Java's Window class is what you would use for this, but this does not work on a Macintosh, and it works slightly differently on different Java runtimes on Windows. As a result of these differences, it is difficult to write a Wolfram Language program that behaves identically on all platforms and all Java virtual machines with respect to making Java windows visible in front of all other windows the user might see.
As a result of these unfortunate differences, J/Link provides a Wolfram Language function, JavaShow, which performs the proper steps on all configurations. You should use JavaShow[window] in place of window@setVisible[True], window@show[], or window@toFront[]. You will see JavaShow used in all the example programs. The argument to JavaShow must be a Java object that is an instance of a class that can represent a top-level window. Specifically, it must be of class java.awt.Window or a subclass. This includes the AWT Frame and Dialog windows, and also the Swing classes used for top-level windows (JFrame, JWindow, and JDialog).
JavaShow[windowObj] | make the specified Java window visible and bring it in front of all other windows, including notebook windows |
Bringing a Java window to the foreground.
Modal Windows
Here is an example of a simple "modal" window. The window contains a button and a text field. The text field starts out displaying the value 1, and each time the button is clicked the value is incremented. The com.wolfram.jlink.MathFrame class is used for the enclosing window. MathFrame is a simple extension to java.awt.Frame that calls dispose() on itself when its close box is clicked (the standard Frame class does nothing).
At this point, you should see a small frame window with a button on the left and a text field on the right. Now label the button and set the starting text for the field.
Now you want to add behavior to the button that causes it to increment the text field value. Buttons fire ActionEvents, so you need an instance of MathActionListener.
It must be registered with the button by calling addActionListener.
At this point, if you were to click the ++ button, the actionPerformed() method of your MathActionListener would be called (do not click the button yet!). You know from the MathListener table in the previous subsection that the actionPerformed() method will call a user-defined Wolfram Language function with two arguments: the ActionEvent object itself and the integer value that results from the event's getActionCommand() method.
You have not yet set the user-defined code to be called by the actionPerformed() method. That is done for all the MathListener classes with the setHandler() method. This method takes two strings, the first being the name of the method in the event-listener interface, and the second being the function you want called.
Now you need to define buttonFunc. It must be written to take two arguments, but in this example you are not interested in either argument.
You are still not quite ready to try the button. If you click the button now, the Java user interface thread will hang because it will call into the Wolfram Language trying to evaluate buttonFunc and wait for the result, but the result will never come because the kernel is not waiting for input to arrive on the Java link. What you need is a way to put the kernel into a state where it is continuously reading from the Java link. This is what makes the window "modal"—the kernel cannot do anything else until the window is closed. The function that implements this modal state is DoModal.
DoModal[] | put the kernel into a state where its attention is solely directed at the Java link |
EndModal[] | what the Java program must call to make the DoModal function return, ending the modal state |
Entering and exiting the modal state.
DoModal will not return until the Java program calls back into the Wolfram Language to evaluate EndModal[]. While DoModal is executing, the kernel is ready to handle callbacks from Java—for example, from MathListener objects. The way to get the Java side to call EndModal[] is typically to use a MathListener. For example, if your window had OK and Cancel buttons, these should dismiss the window, so you would create MathActionListener objects and register them with these two buttons. These MathActionListener objects would be set to call EndModal[] in their actionPerformed() methods.
DoModal returns whatever the block of code that calls EndModal[] returns. You would typically use this return value to determine how the window was closed—for example, whether it was the OK or Cancel button. You could then take appropriate action. See "A Simple Modal Input Dialog" for an example of using the return value of DoModal.
In the present example, the only way to close the window is by clicking its close box. Clicking the close box fires a windowClosing event, so you use a MathWindowListener to receive notifications.
Now you assign the Wolfram Language function to be called when the close box is clicked. All you need it to do is call EndModal[], so you can specify a pure function that ignores its arguments and does nothing but execute EndModal[].
The preceding few lines are a fine example of how to use a MathWindowListener to trigger a call to EndModal[] when a window's close box is clicked. You would use something similar to this, except with a MathActionListener, if you wanted to have an explicit Close button. In this example, though, there is an easier way. Mentioned earlier is that the MathFrame class is just a normal AWT Frame except that it calls dispose() on itself when its close box is clicked. Actually it has another useful property—it can also execute EndModal[] when its close box is clicked. Thus, if you use MathFrame as the top-level window class for your interfaces, you will not have to manually create a MathWindowListener to terminate the modal loop every time. To enable this behavior of MathFrame, you need to call its setModal method.
You must not call setModal if you are not using DoModal. This is because after setModal has been called, the MathFrame will try to call into the Wolfram Language when it is closed (to execute EndModal), and the Wolfram Language needs to be in a state where it is ready for calls originating in Java. The same issue exists for any MathListener you create yourself.
Now that everything is ready, you can enter the modal state and use the window.
When you are done playing with the window, click the close box in the frame, which will trigger a callback into the Wolfram Language that calls EndModal[]. DoModal then returns, and the kernel is ready to be used from the front end. DoModal[] returns Null if you click the close box of a MathFrame.
Here is how the entire example looks when packaged into a single program. (The code for SimpleModal is also available as SimpleModal.nb in the JLink/Examples/Part1 directory.)
Remember that DoModal will not return until the Java side calls EndModal. You have to be a little careful when you call DoModal that you have already established a way for the Java side to trigger a call to EndModal. As explained earlier, you will typically have done this by using a MathFrame as the frame window and calling its setModal method, or by creating and registering a MathListener of your own that will call EndModal in response to a user action (such as clicking an OK or Cancel button). Once DoModal has begun, the kernel is not responsive to the front end and thus it is too late to set anything up.
There is one subtlety you might notice in the code for SimpleModal that is not directly related to J/Link. In the line that calls buttonListener@setHandler, you pass the name of the button function not as the literal string "buttonFunc", but as ToString[buttonFunc]. This is because buttonFunc is a local name in a Module, and thus its real name is not buttonFunc, but something like buttonFunc$42. To make sure you capture its true run-time name, you call ToString on the symbolic name. You could avoid this by simply not making the name buttonFunc local to the Module, but the way you have done it automatically cleans up the definition for buttonFunc when the Module finishes.
MathFrame and MathJFrame
You encountered the MathFrame class in this section, which is a useful top-level window class for J/Link programmers because it has three special properties. You have already encountered two of them: it calls dispose() on itself when it is closed, and it has the setModal() method, which gives it easy support for use with DoModal. The third property is that it has an onClose() method that you can use to specify Wolfram Language code that will be executed when the window is closed. The onClose() method is used in the Palette example in "Sharing the Front End: Palette-Type Buttons". J/Link also has a MathJFrame class, which is a subclass of the Swing JFrame class, and it also has these three special properties. Programmers who want to create interfaces with Swing components instead of AWT ones can use MathJFrame as their top-level window class.
Modeless Windows: Sharing the Kernel with Java
The previous subsection demonstrated how to write J/Link programs that display Java windows and then how to use the DoModal function to cause the kernel to wait until the window is closed. During the time that DoModal is running, the kernel is able to receive and process requests for computations that originate from the Java side. The word "modal" is used in this context to refer to the fact that the kernel is busy servicing the Java link, and thus the notebook front end cannot use the kernel until DoModal returns.
This arrangement works fine for many types of Java windows, and it is required for those that return a result to the Wolfram Language, because the kernel cannot sensibly proceed until the window is dismissed. Unfortunately, it is too restrictive for a large class of user interface elements. Consider trying to duplicate the general concept of a front end palette window in Java. You want to have a window of buttons that, when clicked, cause some computation to occur in the Wolfram Language. Like a front end palette window, you want this window to be created and remain visible and active indefinitely. It would not be of much use if every time you wanted to click one of the buttons you had first to execute DoModal[] (and you would also have to arrange for each button to call EndModal[] as part of the computation it triggers). You want to be able to go back and forth between notebook windows in the front end and our Java window without needing manually to switch the kernel into and out of some special state each time.
What is needed is a way for the kernel to automatically pay attention to input arriving from the Java link in addition to the notebook front end link. What you really have here are two front ends vying for the kernel's attention. J/Link solves this problem by introducing a simple way in which the kernel can be put into a state where it is simultaneously listening for input on any number of links. The function that accomplishes this is ShareKernel.
Important Note: In Mathematica 5.1 and later, the kernel is always shared with Java. This means that the functions ShareKernel and UnshareKernel are not necessary and, in fact, do nothing at all. If you are writing a program that only needs to run in Mathematica 5.1 and later, you never need to call ShareKernel or UnshareKernel (ShareFrontEnd and UnshareFrontEnd are still useful, however). If your programs need to work on all versions of the Wolfram Language, then you will need to use ShareKernel and UnshareKernel as described next.
ShareKernel[] | begin sharing the kernel with Java |
ShareKernel[link] | begin sharing the kernel with link |
UnshareKernel[id] | unregister the request for sharing (that is, the call to ShareKernel) that returned id; kernel sharing will not be turned off unless no other requests are outstanding |
UnshareKernel[link] | end sharing of the kernel with link |
UnshareKernel[] | end sharing of the kernel with Java |
KernelSharedQ[] | True if the kernel is currently being shared; False otherwise |
SharingLinks[] | a list of the links currently sharing the kernel |
ShareKernel takes a LinkObject as an argument and initiates sharing of the kernel between that link and the current $ParentLink (typically, the notebook front end). If you call ShareKernel with no arguments, it assumes you mean the link to Java. Most users will call it with no arguments.
Note that while the kernel is being shared, the input prompt has "(sharing)" prepended to it. The string that is prepended is specified by the SharingPrompt option to ShareKernel.
Sharing is transparent to the user. Other than the changed input prompt, there is nothing to suggest that anything different is going on. Input sent from either the front end or a Java program to the kernel will be evaluated and the result sent back to the program that sent the input. Each link is the kernel's $ParentLink during the time that the kernel is computing input that arrived from that link. In other words, ShareKernel takes care of shuffling the $ParentLink value back and forth between links as input arrives on each.
It is safe to call ShareKernel if the kernel is already being shared. This means that programs you write can call it without your having to worry that a user might already have initiated sharing. When you are finished with the need to share the kernel with Java, you can call UnshareKernel. This restores the kernel to its normal mode of operation, paying attention only to the front end.
When called with no arguments, UnshareKernel shuts down sharing. This is not a desirable thing in most cases, because it might be that some other Java-based program is running that requires sharing. If you are writing code for others to use, you certainly cannot shut down sharing on your users just because your code is done with it. To solve this problem, ShareKernel returns a token (it is just an integer, but you should not be concerned with its representation) that reflects a request for sharing functionality. In other words, calling ShareKernel registers a request for sharing, turns it on if it is not on already, and returns a token that represents that particular request. When you call UnshareKernel, you pass it the token to "unregister" that particular request for sharing. Only if there are no other outstanding requests will sharing actually be turned off.
A quirk of ShareKernel is that you cannot call ShareKernel and UnshareKernel in the same cell. Doing so will cause the kernel to hang. Of course, there is no reason to ever do this, as kernel sharing is only relevant when it spans multiple evaluations (more precisely, the evaluation of multiple cells). There would be no point to turning sharing on and off within the scope of a single computation.
An example of a nontrivial user interface that uses ShareKernel is presented in "Real-Time Algebra: A Mini-Application".
Sharing the Front End
One goal of J/Link was to have Java user interface elements be as close as possible to first-class citizens of the notebook front end environment, in the way that notebooks and palettes are. The ability to share the kernel mimics one important aspect of this citizenship, hiding the fact that the Java runtime is a separate program and the kernel is normally only waiting for input from the front end.
There is one more important thing that palettes can do that would be nice to do from Java, and that is interact with the front end. You can create a palette button that, when clicked, evaluates the code Print["hello"]. You can do this easily with J/Link also, but with one big difference: when you click the palette button, hello appears in the active notebook, but when you click the Java button, the "hello" gets sent back to the Java program (which is, after all, the kernel's $ParentLink at that moment). Even if you persuaded the kernel to write the TextPacket that contains "hello" to the front end link instead of the Java link, nothing useful would happen because the front end is not paying attention to the kernel link when the front end is not waiting for the result of a computation. Poking some output at the front end while it is idle simply will not work.
J/Link provides the ShareFrontEnd function as the solution to this problem. ShareFrontEnd[] causes Print output and graphics generated by a Java user-interface element to appear in the front end. It also lets the Java side call Wolfram Language functions that manipulate elements of notebooks and have them work properly in the front end (for example, NotebookRead, NotebookWrite, SelectionEvaluate, and so on). While sharing is on, the front end behaves normally, and you can continue to use it for editing, calculations, or whatever. The sharing is transparent.
ShareFrontEnd[] | begin sharing the front end with Java |
UnshareFrontEnd[id] | unregister the request for sharing (that is, the call to ShareFrontEnd) that returned id; front end sharing will not be turned off unless no other requests are outstanding |
UnshareFrontEnd[] | end sharing of the front end with Java |
FrontEndSharedQ[] | True if the front end is currently being shared with Java; False otherwise |
Sharing the notebook front end.
ShareFrontEnd currently does not work with a remote kernel; the same machine must be running the kernel and the front end.
ShareFrontEnd is as close as you currently can come to having Java user interfaces hosted directly by the notebook front end itself, as if they were special types of notebook windows. This type of tight integration might be possible in the future.
Note that Print output, graphics, and messages generated by a modal Java window will appear in the front end without needing to call ShareFrontEnd. This is because $ParentLink remains the front end link during DoModal (these "side effect" packets always get sent to $ParentLink), and also because the front end is able to handle various packets arriving from the kernel because the front end is in the middle of a computation—it is waiting for the result of the code that called DoModal. ShareFrontEnd is a way to restore a feature that was lost when you gained the ability to create modeless interfaces via ShareKernel. That is how to think of ShareFrontEnd—as a step beyond ShareKernel that allows side-effect output generated by computations triggered in Java to appear in the notebook front end. ShareFrontEnd is particularly useful when developing code that needs to use ShareKernel, even if the code does not need the extra functionality of ShareFrontEnd. This is because Wolfram System error messages generated by computations triggered by Java events get lost with ShareKernel. The messages will show up in the front end if front end sharing is turned on.
When you are done with the need to share the front end, call UnshareFrontEnd. Like the ShareKernel/UnshareKernel pair of functions, ShareFrontEnd returns a token that you should pass to UnshareFrontEnd to unregister the request for front end sharing. Only when all calls to ShareFrontEnd have been unregistered by calls to UnshareFrontEnd will front end sharing be turned off. You can force front end sharing to be shut down immediately by calling UnshareFrontEnd with no arguments, but although this is convenient when you are developing code of your own, it should never be called in code that is intended for others to use. Just because your code is done with front end sharing does not mean that your users are done with it. Instead, save the token returned from ShareFrontEnd and pass it to UnshareFrontEnd.
ShareFrontEnd requires that the kernel be shared, so it calls ShareKernel internally. Calling UnshareKernel with no arguments forces kernel sharing to stop immediately, and this turns off front end sharing as well. Thus, you can use UnshareKernel[] as a quick shortcut to immediately shut down all sharing.
An example of some simple palette-type buttons that use ShareFrontEnd is presented in "Sharing the Front End: Palette-Type Buttons".
An important use for ShareFrontEnd is to allow a popup Java user interface to display graphics containing typeset expressions. When the kernel is asked to produce a graphic containing typeset expressions, say a plot with PlotLabel->Sqrt[z], it crunches out PostScript for the plot itself, but when it comes time to produce PostScript for the typeset label, it cannot do this. Instead, it sends a special request back to the front end, asking it for the PostScript representation. Because dealing with typeset expressions is a skill possessed only by the notebook front end, when any other interface is driving the kernel, the interface must be careful to instruct the kernel not to attempt to typeset anything in a graphic (ShareKernel handles this automatically for you). This works fine, but you lose the ability to get pictures of typeset expressions in your Java interface.
ShareFrontEnd does two things to overcome this limitation: it fools the kernel into thinking that the Java runtime is a notebook front end and, therefore, capable of handling the special "convert to PostScript" requests; and it gives Java the ability to make good on this promise by forwarding the requests to the front end. "GraphicsDlg: Graphics and Typeset Output in a Window" describes an example of a Java dialog box that displays typeset expressions using ShareFrontEnd.
Summary of Modal and Modeless Operation
The previous discussion of modal and modeless operation, ShareKernel, and ShareFrontEnd may have seemed complex. In fact, the principles and uses of these techniques are simple. This will become clear upon seeing some more examples. Many of the example programs in "Example Programs" use ShareKernel or ShareFrontEnd. The important thing is to understand the capabilities they provide so that you can begin to see how to use them in your own programs.
If you want your user-interface element (typically a window) to tie up the kernel until the user dismisses it, then you will use the setModal/DoModal/EndModal suite. Because the internal workings of the modal state are simpler than the modeless state, you should use this style unless your program needs the features of a modeless window. You will always want to use this type of window if you need to return a result to a running Wolfram Language program, such as if you are creating a dialog box into which the user will enter values and then click OK. "A Simple Modal Input Dialog" gives an example of this type of dialog.
If you want your window to remain visible and active while the user returns to work in the front end, you must run your window in a "modeless" fashion. This requires calling ShareKernel to put the kernel into a state where it is simultaneously receptive to input arriving from either the notebook front end or Java. At this point the kernel is dividing its attention between two independent and essentially equivalent front ends. One drawback (or feature, depending on your point of view) of this state is that all side-effect output like Print output, messages, or plots triggered by Java code is sent to Java instead of the front end (and the standard Java MathListener classes just throw all this output away). Thus, you could not create a button that prints something in a notebook window when it is clicked, like you can with a palette button in the front end. If you want to give your Java program the ability to interact with the front end the way that notebook and palette windows themselves can, you must instead use ShareFrontEnd, which you can think of as an extension to ShareKernel.
A very common mistake is to create a Java window, wire up a MathListener class that calls back to the Wolfram Language on some event, and then trigger the event before you have called DoModal or ShareKernel. This will cause the Java user interface thread to hang. A symptom that the UI thread is hanging is that the controls in your Java window are visually unresponsive (for example, buttons will not appear to depress when you click them). If you do inadvertently get into this state, you can just call ShareKernel to allow the queued-up call(s) from Java to proceed.
"Manual" Interfaces: The ServiceJava Function
In addition to the modal and modeless types of interfaces just discussed, there is another type that in some ways is intermediate. Consider the following scenario. You want to create a Wolfram Language program that puts up a Java window and displays something in it that changes over the course of the program. So far, this sounds like an example of a "non-interactive" interface, which was discussed at the beginning of this section, the progress bar example being a classic case. Now, though, you want to add some interactivity to the window, meaning that you want user actions in the window to trigger calls into the Wolfram Language. Keeping with the progress bar example, say you want to add an Abort button that stops the program. How do you manage to get the kernel's attention directed at the Java side so that Java events can trigger calls to the Wolfram Language?
The modal type of interface will not work, because in the modal state the kernel is executing DoModal, not your computation—the kernel is doing nothing but paying attention to Java. The modeless type of interface will not work either, because the modeless technique causes the kernel to pay attention to the front end and Java alternately, letting each perform a full computation in turn. There is no sharing within the context of a single computation.
The obvious answer is the there needs to be a function that allows the kernel to service a single computation arriving from Java, if there is one waiting. That function is ServiceJava. Calling ServiceJava in a program will cause the kernel to accept one request for a computation from the Java side. It performs the computation and then returns control to your program. If there is no request waiting, ServiceJava returns immediately.
Here is some pseudocode showing the structure of a program that displays a progress bar with an Abort button and periodically calls ServiceJava to handle user clicks on that button, stopping the computation if requested.
... create progress bar ...
progressBar@addActionListener[
JavaNew["com.wolfram.jlink.MathActionListener", "(userCancelled = True)&"]
];
JavaShow[progressBar];
While[i < 100 && !userCancelled,
... compute one iteration ...
... update progress bar ...
ServiceJava[];
i++
];
... destroy progress bar ...
You might recognize that ServiceJava is closely related to DoModal, and although this is not the actual implementation, you can think of DoModal as being written in terms of ServiceJava.
(* Not the actual implementation of DoModal, but the principle is correct. *)
DoModal[] :=
While[!endModal,
ServiceJava[]
]
Seen in this way, DoModal is a special case of the use of ServiceJava, where the Wolfram Language is doing nothing but servicing requests from Java. Sometimes you need something else to be going on in the Wolfram Language, but still need to be able to handle requests arriving from Java. That is when you call ServiceJava yourself. Like DoModal, there is no shifting of $ParentLink when ServiceJava is called. Thus, side-effect output like graphics, messages, and Print output triggered by Java computations appear in the notebook, just as if they were hard-coded into the Wolfram Language program that called ServiceJava.
The BouncingBalls example program presented in "BouncingBalls: Drawing in a Window" uses ServiceJava.
Using a GUI Builder
The preceding discussion on modal and modeless interfaces featured examples that were created entirely with Wolfram Language code. For complex user interfaces, you might find it more convenient to lay out your windows and wire up events with a drag-and-drop GUI builder like the ones present in most commercial Java development environments. You are free to write as much or as little of the code for your interface in native Java as you like. If you want events in your GUI to trigger calls into the Wolfram Language, then you can use any of the MathListener classes from Java code just as they are used from Wolfram Language code. Alternatively, you could write your own Java code that calls into the Wolfram Language at appropriate times. See the section "Writing Your Own Installable Java Classes" for information about how to write Java code that calls back into the Wolfram Language. "GraphicsDlg: Graphics and Typeset Output in a Window" gives a simple example of a dialog box that was created with a GUI builder and is then invoked and controlled by Wolfram Language code.
Drawing and Displaying Wolfram Language Images in Java Windows
The MathCanvas and MathGraphicsJPanel classes
J/Link makes it easy to draw into Java windows from the Wolfram Language, and also display Wolfram Language graphics and typeset expressions. The MathCanvas and MathGraphicsJPanel classes are provided for this purpose. You can use these classes in pure Java programs that use the Wolfram Language kernel, as described in "Writing Java Programs That Use the Wolfram Language", but it is also handy for Java windows that are created and scripted from the Wolfram Language. Note that the MathGraphicsJPanel class is new in J/Link 2.0.
MathCanvas is a subclass of the AWT Canvas class, and MathGraphicsJPanel is a subclass of the Swing JPanel class. In terms of their special added Wolfram Language graphics capabilities, they are identical. These classes provide two ways to supply the image to be displayed. The first way is by providing a fragment of Wolfram Language code whose output will be displayed. The output can either be a graphics object, or a nongraphics expression that will be typeset. This makes it trivial to display Wolfram Language graphics or typeset expressions in a Java window. The second way to control the display is to provide a Java Image object that will be painted. This Image will typically be created by Wolfram Language code, such as code that creates a bitmap out of raw Wolfram Language data, or code that draws something using calls to Java's graphics routines.
Because MathCanvas and MathGraphicsJPanel are Java classes and can be used from Java programs as well as Wolfram Language programs, there is full JavaDoc format documentation for them in the JLink/Documentation/JavaDoc directory. You can browse that documentation for more details.
Showing Wolfram Language Graphics and Typeset Expressions
Here is a simple example of displaying a window that shows a Wolfram Language plot. This example uses MathCanvas, but the relevant parts would look the same if you used MathGraphicsJPanel. You will be using this window throughout this section, so do not close it if you are evaluating the code as you read this section.
As you can see, it is as simple as calling the canvas' setMathCommand() method. The argument to setMathCommand() is a string giving the code to be evaluated. This code must return a graphics expression, not just cause one to be produced. For example, setMathCommand["Plot[x,{x,0,1}];"] will not work because the trailing semicolon causes the expression to evaluate to Null. The image is automatically rendered at the correct size, and centered in the canvas if the actual image size produced by the Wolfram Language does not completely fill the requested area (as is often the case with typeset output).
Calling setMathCommand() again resets the image.
If the plotting command depends on variables in your Wolfram System session, you can call recompute() to cause the graphic to be recomputed and rendered. For example, this displays a slow animation in the window.
Because you supply the expression as a string, remember to escape any quote marks inside the string with a backslash.
A MathCanvas can also display typeset expressions. The default behavior of MathCanvas is to expect that the expression supplied in setMathCommand() will evaluate to a graphics object, which should be rendered. To get it instead to typeset the return value, call the setImageType() method, supplying the constant TYPESET.
To switch back to displaying graphics, call mathCanvas@setImageType[MathCanvas`GRAPHICS]. The default format for typeset output is StandardForm. To switch to TraditionalForm, use the setUsesTraditionalForm() method. You call recompute() here because changing the output type does not force the image to be redrawn.
Graphics are rendered using the Wolfram Language's Display command, which is fast and does not require the notebook front end to be running. For higher quality, though, particularly for 3D graphics, an alternative method is available that uses the front end for rendering services. You can switch to using this technique by calling the setUsesFE() method.
You might want to compare the resulting plot with setUsesFE[True] and setUsesFE[False].
An important point about using the front end for rendering is that when the computation to produce the image is performed, the front end must be in a state where it is receptive to requests for services from the kernel. There are two times when this is the case: either a cell in the front end is currently evaluating (as will be the case when you are calling setMathCommand() or recompute() from a Wolfram Language program), or ShareFrontEnd has been called. Looking at it from the other direction, the only time it will not work is if ShareKernel is in use, but not ShareFrontEnd, and the computation is triggered by an event in Java. The rule is that if you want to involve the front end for rendering, and you want to call setMathCommand() or recompute() from Java in response to a user action in a modeless interface, you need to use ShareFrontEnd; ShareKernel is not enough. Modal and modeless interfaces and ShareFrontEnd are discussed in the section "Creating Windows and Other User Interface Elements".
Drawing Using Java's Graphics Functions
You saw that the setMathCommand() method of the MathCanvas and MathGraphicsJPanel classes lets you supply a Wolfram Language expression whose output is to be displayed. You can also use a MathCanvas or MathGraphicsJPanel to display a Java Image by using the setImage() method instead of setMathCommand().
Now look at a simple example of drawing into a Java window from the Wolfram Language. You will continue to use the same window and MathCanvas you have been working with. If this program used a MathGraphicsJPanel instead, the portions of the code related to drawing would look exactly the same. To draw into the MathCanvas, you create an offscreen image of the same dimensions, get a graphics context for drawing onto it, draw, and then use the setImage() method of MathCanvas to cause the offscreen image to be displayed. Drawing into an offscreen image and then blitting it to the screen is a standard technique for flicker-free drawing.
Programs that want to draw manually into a Java window from the Wolfram Language will generally all have this same structure. It takes just a few more lines of code to turn our MathCanvas into a scribble program. Here is the complete program (this code is also provided as the file Scribble.nb in the JLink/Examples/Part1 directory).
Run the program, then click and drag the mouse to draw in the window. Close the window to end the program and the Scribble function will return the list of points drawn.
If you examine the list of points returned, you will see that they are based on Java's coordinate system, which has (0, 0) in the upper left. If you want to plot the points in a Wolfram Language graphic, you have to invert the values. This is demonstrated in the Scribble.nb example notebook.
There is one new MathCanvas method demonstrated in this program, repaintNow(). In a computation-intensive program like this, where events are being fired on the user interface thread very quickly, and the handlers for these events take a nontrivial amount of time to execute, Java will sometimes delay repainting the window. The drawing becomes very chunky, with no visual effect for a while and then suddenly all the lines drawn in the last few seconds will appear. Even calling the standard repaint() method after every new point will not ensure that the window is updated in a timely manner. To solve this problem, the repaintNow() method is provided, which forces an immediate redraw of the canvas. If your program relies on smooth visual feedback from user events that fire rapidly, you should call repaintNow() also, even if it does not seem necessary on your system. There can be very significant differences between different platforms and different Java runtimes on the responsiveness of the screen updating mechanism.
The ability to draw in response to events in a MathCanvas or MathGraphicsJPanel opens up the possibility for some impressive interactive demonstrations, tutorials, and so on. Two of the larger example programs provided draw into a MathCanvas from the Wolfram Language: BouncingBalls (in the section "BouncingBalls: Drawing in a Window") and Spirograph (in the section "Spirograph").
Bitmaps
You have seen how to draw into a MathCanvas or MathGraphicsJPanel by using an offscreen image. Another type of image that you can create with Wolfram Language code and display using setImage() is a bitmap. In this example you will create an indexed-color bitmap out of Wolfram Language data and display it. You will use an 8-bit color table, meaning that every data point in the image will be treated as an index into a 256-element list of colors. You could use a larger color table if desired.
You closed the frame window in the Scribble example, so you must first create a new frame and canvas for the bitmap.
Here is the color table. It is an array of {r,g,b} triplets, with each color component being in the range 0…255. In this example, colors with low indices are mostly blue, and ones with high indices are mostly red.
The data is a 400×400 matrix of integers in the range 0…255 (because they are indices into the 256-element color table). In a real application, this data might be read from a file or computed in some more sophisticated way. If the range of numbers in the data did not span 0…255, you would have to scale it into that range, or a larger range if you wanted to use a deeper color table.
Here you create the Java objects that represent the color model and bitmap. You can read the standard Java documentation on these classes for more information.
Now create an Image out of the bitmap and display it.
The Java Console Window
J/Link provides a convenient means to display the Java "console" window. Any output written to the standard System.out and System.err streams will be directed to this window. If you are calling Java code that writes diagnostic information to System.out or System.err, then you can see this output while your program runs. Like most J/Link features, the console window can be used easily from either the Wolfram Language or Java programs (its use from Java code is described in "Writing Java Programs That Use the Wolfram Language"). To use it from the Wolfram Language, call the ShowJavaConsole function.
ShowJavaConsole[] | display the Java console window and begin capturing output written to System.out and System.err |
ShowJavaConsole["stream"] | display the Java console window and begin capturing output written to the specified stream, which should be "stdout" for System.out or "stderr" for System.err |
ShowJavaConsole[None] | stop all capturing of output |
Capturing of output only begins when you call ShowJavaConsole, so when the window first appears it will not have any content that might have been previously written to System.out or System.err. You will also note that the J/Link console window displays version information about the J/Link Java component and the Java runtime itself. Calling ShowJavaConsole when the window is already open will cause it to come to the foreground.
To demonstrate, you can write some output from the Wolfram Language. If you executed the ShowJavaConsole[] given earlier, then you will see "Hello from Java" printed in the window.
Although it is convenient to demonstrate writing to the window using Wolfram Language code like this, this is typically done from Java code instead. Actually, there is one common circumstance where it is quite useful to use the Java console window for diagnostic output written from Wolfram Language code. This is the case where you have a "modeless" Java user interface (as described in the section "Creating Windows and Other User Interface Elements") and you have not used the ShareFrontEnd function. Recall that in this circumstance, output from calls to Print in the Wolfram Language will not appear in the notebook front end. If you write to System.out instead, as in the example, then you will always be able to see the output. You might want to do this in other circumstances just to avoid cluttering up your notebook with debugging output.
Using JavaBeans
JavaBeans is Java's component architecture. Beans are reusable components that can be manipulated visually in a builder tool. At the code level, a Bean is essentially just a normal Java class that conforms to a particular design pattern with respect to how its methods are named and how it supports events and persistence.
JavaBeans has not been mentioned up to this point because there really is not anything special to be said. Beans are just Java classes, and they can be used and called like any other classes. It is probably the case that many Java classes you use from the Wolfram Language will be Beans, whether they advertise themselves to be or not. This is especially true for user interface components.
Beans are typically designed to be used in a visual builder tool, where the programmer is not writing code and calling named methods directly. Instead, a Bean exposes "properties" to the builder tool, which can be examined and set using a property editor window. In a typical simple example, a Bean might have methods named setColor and getColor, and by virtue of this it would be said to have a property named "color". A property editor would have a line showing the name "color" and an edit field where you could type in a color. It might even have a fancy editor that puts up a color picker window to let you visually select a desired color.
For the purposes of a visual builder tool or other type of automated manipulation, Beans try to hide the low-level details of actual method names. If you want to call methods in a Bean class from Wolfram Language code, you call them by name in the usual way, without any consideration of the "Beanness" of the class.
Note that it would be quite possible to add Wolfram Language functions to J/Link that would provide explicit support for Bean properties. For example, a function BeanSetProperty could be written that would take a Bean object, a property name as a string, and the value to set the property to. The following is currently required.
Instead, you could write the following.
The BeanSetProperty function lets you write code that manipulates nebulous things called properties instead of calling specific methods in the Bean class. If you do not see any particular advantage in the BeanSetProperty style, then you know why there is no special Bean support along these lines in J/Link. The advantages of working with properties versus directly calling methods accrues only when you are using a builder tool and not actually writing code by hand.
If you are interested, here are simplistic implementations of BeanSetProperty and BeanGetProperty.
To make use of events that a JavaBean fires, you can use one of the standard MathListener classes, as described in the section "Creating Windows and Other User Interface Elements". JavaBeans often fire PropertyChangeEvents, and you can arrange for Wolfram Language code to be executed in response to these events by using a MathPropertyChangeListener or a MathVetoableChangeListener.
Hosting Applets
J/Link gives you the ability to run most applets in their own window directly from the Wolfram Language. Although this may seem immensely useful, given the vast number of applets that have been created, most applets do not export any useful public methods. They are generally standalone pieces of functionality, and thus they benefit little from the scriptability that J/Link provides. Still, there are many applets that may be useful to launch from a Wolfram Language program.
Note that this section is not about writing applets that use the Wolfram Language kernel. That topic is covered in "Writing Applets".
AppletViewer["applet class"] | run the named applet class in its own window; the default width and height are 300 pixels |
AppletViewer["applet class",params] | run the named applet class in its own window, supplying it the given parameters, which is a list of "name=value" specifications like those used in an HTML page |
J/Link includes an AppletViewer function for running applets. This function takes care of all the steps of creating the applet instance, providing a frame window to hold it, and starting it running. The first argument to AppletViewer is the fully qualified name of the applet class. The second argument is an optional list of parameters in "name=value" format, corresponding to the parameters supplied to an applet in an HTML page that hosts it. For example, here is the <applet> tag in a web page that hosts an applet.
You would call AppletViewer as follows.
You will typically supply at least "WIDTH=" and "HEIGHT=" specifications to control the width and height of the applet window. If you do not specify these parameters, the default width and height are 300 pixels.
An excellent example of an applet that is useful to Wolfram Language users is LiveGraphics3D, written by Martin Kraus. LiveGraphics3D is an interactive viewer for Wolfram Language 3D graphics. It gives you the ability to rotate and zoom images, view them in stereo, and more. If you want to try the following example, you will need to get the LiveGraphics3D materials, available from http://wwwvis.informatik.uni-stuttgart.de/~kraus/LiveGraphics3D. Make sure you put live.jar onto your CLASSPATH before trying that example, or use the AddToClassPath feature of J/Link to make it available.
First, load the PolyhedronOperations` Package and create the graphic to display. The LiveGraphics3D documentation gives a more general-purpose function for turning a Wolfram Language graphics expression into appropriate input for the LiveGraphics3D applet but, for many examples, using ToString, InputForm, and N is sufficient.
You specify the image to be displayed via the INPUT parameter, which takes a string giving the InputForm representation of the graphic.
The Live applet has a number of keyboard and mouse controls for manipulating the image. You can read about them in the LiveGraphics3D documentation. Try Alt+S to switch into a stereo view.
When you are done with an applet, just click the window's close box.
If the applet needs to refer to other files, you should be aware that AppletViewer sets the document base to be the directory specified by the "user.dir" Java system property. This will normally be the Wolfram Language's current directory (given by Directory[]) at the time that InstallJava was called.
Most applets expose no public methods useful for controlling from the Wolfram Language, so there is nothing to do but start them up with AppletViewer and then let the user close the window when they are finished. The Live applet is an exception—it provides a full set of methods to allow the view point, spin, and so on to be modified by Wolfram Language code. These methods are in the Live class, so to call them you need an instance of the Live class. The way you used AppletViewer earlier does not give us any instance of the applet class. The construction and destruction of the applet instance was hidden within the internals of AppletViewer. You can also call AppletViewer with an instance of an applet class instead of just the class name. This lets you manage the lifetime of the applet instance.
Now you can call methods on the applet instance. See the LiveGraphics3D documentation for the full set of methods. This scriptability opens up lots of possibilities, such as programming "flyby" views of objects, or creating buttons that jump the image into certain orientations or spins.
When you are done, you call ReleaseJavaObject to release the applet instance. This can be done before or after the applet window is closed.
Periodical Tasks
The section "Creating Windows and Other User Interface Elements" described the ShareKernel function and how it allows Java and the notebook front end to share the kernel's attention. A side benefit of this functionality is that it becomes easy to provide a means whereby users can schedule arbitrary Wolfram Language programs to run at periodical intervals during a session. Say you have a source that provides continuously updated financial data and you want to have some variables in the Wolfram Language constantly reflect the current values. You have written a program that goes out and reads from the source to get the information, but you have to manually run this program all the time while you are working. A better solution would be to set up a periodical task that pulls the data from the source and sets the variables every 15 seconds.
AddPeriodical[expr,secs] | cause expr to be evaluated every secs seconds while the kernel is idle |
RemovePeriodical[id] | stop scheduling of the periodical represented by id |
Periodical[id] | return a list {HoldForm[expr],secs} showing the expression and time interval associated with the periodical represented by id |
Periodicals[] | return a list of the id numbers of all currently scheduled periodicals |
SetPeriodicalInterval[id] | reset the periodical interval for the periodical task represented by id |
$ThisPeriodical | holds the id of the currently executing periodical task |
You can set up such a task with the AddPeriodical function.
AddPeriodical returns an integer ID number that you must use to identify the task—for example, when it comes time to stop scheduling it by calling RemovePeriodical. AddPeriodical relies on kernel sharing, so it calls ShareKernel if it has not already been called. There is no limit on the number of periodicals that can be established.
After scheduling that task, updateFinancialData[] will be executed every 15 seconds while the kernel is idle. Note that periodical tasks are run only when the kernel is not busy—they do not interrupt other evaluations. If the kernel is in the middle of another evaluation when the allotted 15 seconds elapse, the task will wait to be executed until immediately after the computation finishes. Any such delayed periodicals are guaranteed to be executed as soon as the kernel finishes with the current computation. They cannot be indefinitely delayed if the user is busy with numerous computations in the front end or in Java. The converse to these facts is also true—if a periodical is executing when the user evaluates a cell in the front end, the evaluation will not be able to start until all periodicals finish, but it is guaranteed to start immediately thereafter.
To remove a single periodical task, use RemovePeriodical, supplying the ID number of the periodical as the argument. To remove all periodical tasks, use RemovePeriodical[Periodicals[]]. Periodical tasks are all removed if you call UnshareKernel[] with no arguments, which turns off all kernel sharing. You would then need to use AddPeriodical again to reestablish periodical tasks.
You can reset the scheduling interval for a periodical task by calling SetPeriodicalInterval, which is new in J/Link 2.0. This line makes the financial data periodical execute every 10 seconds, instead of 15 as shown earlier.
Sometimes you might want to change the interval for a periodical task or remove it entirely from within the code of the task itself. $ThisPeriodical is a variable that holds the ID of the currently executing periodical task. It will only have a value during the execution of a periodical task. You use $ThisPeriodical from within your periodical task to obtain its ID so that you can call RemovePeriodical or SetPeriodicalInterval.
Periodical tasks do not necessarily have anything to do with Java, nor do they need to use Java. Technically, Java does not even need to be running. However, because Java is used by the internals of ShareKernel to yield the CPU, if Java is not running then setting a periodical task will cause the kernel to keep the CPU continuously busy. Periodical task functionality is included in J/Link because it is a simple extension to ShareKernel, and it does have some nice uses in association with Java.
A final note about periodical tasks is that they do not cause output to appear in the front end. Look at this attempt.
The programmer expects to get hello printed in his notebook every 10 seconds, but nothing happens. During the time when periodicals are executed, $ParentLink is not assigned to the front end (or Java). Results or side effects like Print output, messages, or graphics vanish into the ether.
Before proceeding, clean up the periodical tasks you created.
Some Special Number Classes
Preamble
There is a set of special number-related classes in Java that J/Link maps to their Wolfram Language numeric representation. Like strings and arrays, objects of these number classes have an important property: although they are objects in Java, they have a meaningful "by value" representation in the Wolfram Language, so it is convenient for J/Link to automatically convert them to numbers as they are returned from Java to the Wolfram Language, and back to objects as they are sent from the Wolfram Language to Java.
These classes are the so-called "wrapper" classes that represent primitive types (Byte, Integer, Long, Double, and so on), BigDecimal and BigInteger, and any class used to represent complex numbers. The treatment of these classes is described in this section.
The "Wrapper" Classes: Integer, Float, Boolean, and Others
Java has a set of so-called "wrapper" classes that represent primitive types. These classes are Byte, Character, Short, Integer, Long, Float, Double, and Boolean. The wrapper classes hold single values of their respective primitive types, and are necessary to allow everything in Java to be represented as a subclass of Object. This lets various utility methods and data structures that deal with objects handle primitive types in a straightforward way. It is also necessary for Java's reflection capabilities.
If you have a Java method that returns one of these objects, it will arrive in the Wolfram Language as an integer (for Byte, Character, Short, Integer, and Long), real number (for Float and Double), or the symbols True or False (for Boolean). Likewise, a Java method that takes one of these objects as an argument can be called from the Wolfram Language with the appropriate raw Wolfram Language value. The same rules hold true for arrays of these objects, which are mapped to lists of values.
In the unlikely event that you want to defeat these automatic "pass by value" semantics, you can use the ReturnAsJavaObject and JavaObjectToExpression functions, discussed in "References and Values".
Complex Numbers
You have seen that Java number types (e.g. byte, int, double) are returned to the Wolfram Language as integers and reals, and integers and reals are converted to the appropriate types when sent as arguments to Java. What about complex numbers? It would be nice to have a Java class representing complex numbers that mapped directly to the Wolfram Language's Complex type, so that automatic conversions would occur as they were passed back and forth between the Wolfram Language and Java. Java does not have a standard class for complex numbers, so J/Link lets you name the class that you want to participate in this mapping.
SetComplexClass["classname"] | set the class to be mapped to complex numbers in the Wolfram Language |
GetComplexClass[] | return the class currently used for complex numbers |
Setting the class for complex numbers.
You can use any class you like as long as it has the following properties:
1. A public constructor that takes two doubles (the real and imaginary parts, in that order)
2. Methods that return the real and imaginary parts, having the following signatures
Say that you are doing some computations with complex numbers in Java, and you want to interact with these methods from the Wolfram Language. You like to use the complex number class available from netlib. This class is named ORG.netlib.math.complex.Complex and is available at http://www.netlib.org/java/. You use the SetComplexClass function to specify the name of the class.
Now any method or field that takes an argument of type ORG.netlib.math.complex.Complex will accept a Wolfram Language complex number, and any object of class ORG.netlib.math.complex.Complex returned from a method or field will automatically be converted into a complex number in the Wolfram Language. The same holds true for arrays of complex numbers.
Note that you must call SetComplexClass before you load any classes that use complex numbers, not merely before you call any methods of the class.
BigInteger and BigDecimal
Java has standard classes for arbitrary-precision floating-point numbers and arbitrary-precision integers. These classes are java.math.BigDecimal and java.math.BigInteger, respectively. Because the Wolfram Language effortlessly handles such "bignums", J/Link maps BigInteger to Wolfram Language integers and BigDecimal to Wolfram Language reals. What this means is that any Java method or field that takes, say, a BigInteger can be called from the Wolfram Language by passing an integer. Likewise, any method or field that returns a BigDecimal will have the value returned to the Wolfram Language as a real number.
Ragged Arrays
Java allows arrays that are deeper than one dimension to be "ragged", or non-rectangular, meaning that they do not have the same length at every position at the same level. For example, {{1,2,3},{4,5},{6,7,8}} is a ragged two-dimensional array. J/Link allows you to send and receive ragged arrays, but it is not the default behavior. The reason for this is simply efficiency—the WSTP library has functions that allow very efficient transfer of rectangular arrays of most primitive types (e.g. byte, int, double, and so on), whereas ragged ones have to be picked apart tediously with a series of individual calls to get every piece. This all happens deep inside J/Link, so you do not have to be concerned with the mechanics of array passing, but it has a huge impact on speed. To maximize speed, J/Link assumes that arrays of primitive types are rectangular. You can toggle back and forth between allowing and rejecting ragged arrays by calling the AllowRaggedArrays function with either True or False.
AllowRaggedArrays[True] | allow ragged (i.e. nonrectangular) arrays to be sent to Java |
With AllowRaggedArrays[True], sending of arrays deeper than one dimension is greatly slowed. Here is an example of array behavior and how it is affected. Assume the class Testing has the following method, which takes a two-dimensional array of ints and simply returns it.
Look what happens if you call it with a ragged array.
An error occurs because the Wolfram Language definition for the Testing`intArrayIdentity() function requires that its argument be a two-dimensional rectangular array of integers. The call never even gets out of the Wolfram Language.
Here you turn on support for ragged arrays, and the call works. This requires modifications in both the Wolfram Language-side type checking on method arguments and the Java-side array-reading routines.
It is a good idea to turn off support for ragged arrays as soon as you no longer need it, since it slows arrays down so much.
Implementing a Java Interface with Wolfram Language Code
You have seen how J/Link lets you write programs that use existing Java classes. You have also seen how you can wire up the behavior of a Java user interface via callbacks to the Wolfram Language via the MathListener classes. You can think of any of these MathListener classes, such as MathActionListener, as a class that "proxies" its behavior to arbitrary user-defined Wolfram Language code. It is as if you have a Java class that has its implementation written in the Wolfram Language. This functionality is extremely useful because it greatly extends the set of programs you can write purely in the Wolfram Language, without resorting to writing your own Java classes.
ImplementJavaInterface["interfaceName",{"methName"->"mathFunc",…}] | |
create an instance of a Java class that implements the named Java interface by calling back to the Wolfram Language according to the given mappings of Java methods to Wolfram Language functions |
Implementing a Java interface entirely in the Wolfram Language.
It would be nice to be able to take this behavior and generalize it, so that you could take any Java interface and implement its methods via callbacks to Wolfram Language functions, and do it all without having to write any Java code. The ImplementJavaInterface function, new in J/Link 2.0, lets you do precisely that. This function is easier to understand with a concrete example. Say you are writing a Wolfram Language program that uses J/Link to display a Java window with a Swing menu, and you want to script the behavior of the menu in the Wolfram Language. The Swing JMenu class fires events to registered MenuListeners, so what you need is a class that implements MenuListener by calling into the Wolfram Language. A quick glance at the section on MathListeners reveals that J/Link does not provide a MathMenuListener class for you. You could choose to write your own implementation of such a class, and in fact this would be very easy, even trivial, since you would make it a subclass of MathListener and inherit virtually all the functionality you would need. For the sake of this discussion, assume that you choose not to do that, perhaps because you do not know Java or you do not want to deal with all the extra steps required for that solution. Instead, you can use ImplementJavaInterface to create such a Java class with a single line of Wolfram Language code.
The first argument to ImplementJavaInterface is the Java interface or list of interfaces you want to implement. The second argument is a list of rules that associate the name of a Java method from one of the interfaces with the name of a Wolfram Language function to call to implement that method. The Wolfram Language function will be called with the same arguments that the Java method takes. What ImplementJavaInterface returns is a Java object of a newly created class that implements the named interface(s). You use it just like any JavaObject obtained by calling JavaNew or through any other means. It is just as if you had written your own Java class that implemented the named interface by calling the associated Wolfram Language functions, and then called JavaNew to create an instance of that class.
It is not necessary to associate every method in the interface with a Wolfram Language function. Any Java methods you leave out of your list of mappings will be given a default Java implementation that returns null. If this is not an appropriate return value for the method (e.g. if the method returns an int) and the method gets called at some point an exception will be thrown. Generally, this exception will propagate to the top of the Java call stack and be ignored, but it is recommended that you implement all the methods in the Java interface.
The ImplementJavaInterface function makes use of the "dynamic proxy" capability introduced in Java 1.3. It will not work in Java versions earlier than 1.3. All Java runtimes bundled with Mathematica 4.2 and later are at Version 1.3 or later. If you have Mathematica 4.0 or 4.1, the ImplementJavaInterface function is another reason to make sure you have an up-to-date Java runtime for your system.
At first glance, the ImplementJavaInterface function might seem to give us the capability to write arbitrary Java classes in the Wolfram Language, and to some extent that is true. One important thing you cannot do is extend, or subclass, an existing Java class. You also cannot add methods that do not exist in the interface you are implementing. Event-handler classes are a good example of the type of classes for which this facility is useful. You might think that the MathListener classes are rendered obsolete by ImplementJavaInterface, and it is true that their functionality can be duplicated with it. The MathListener classes are still useful for Java versions earlier than 1.3, but most importantly, they are useful for writing pure Java programs that call the Wolfram Language. Using a class implemented in the Wolfram Language via ImplementJavaInterface in a Java program that calls the Wolfram Language would be possible, but quite cumbersome. If you want a dual-purpose class that is as easy to use from the Wolfram Language as from Java, you should write your own subclass of MathListener. One poor reason for choosing to use ImplementJavaInterface instead of writing a custom Java class is that you are worried about complicating your application by requiring it to include its own Java classes in addition to Wolfram Language code. As explained in "Deploying Applications That Use J/Link", it is extremely easy to include supporting Java classes in your application. Your users will not require any extra installation steps nor will they need to modify the Java class path.
Writing Your Own Installable Java Classes
Preamble
The previous sections have shown how to load and use existing Java classes. This gives Wolfram Language programmers immediate access to the entire universe of Java classes. Sometimes, though, existing Java classes are not enough, and you need to write your own.
J/Link essentially obliterates the boundary between Java and the Wolfram Language, letting you pass expressions of any type back and forth and use Java objects in the Wolfram Language in a meaningful way. This means that when writing your own Java classes to call from the Wolfram Language, you usually do not need to do anything special. You write the code in exactly the same way as you would if you wanted to use the class only from Java. (One important exception to this rule is that because it is comparatively slow to call into Java from the Wolfram Language, you might need to design your classes in a way that will not require an excessive number of method calls from the Wolfram Language to get the job done. This issue is discussed in detail in "Overhead of Calls to Java".)
In some cases, you might want to exert more direct control over the interaction with the Wolfram Language. For example, you might want a method to return something different to the Wolfram Language than what the method itself returns. Or you might want the method not to just return something, but also trigger a side effect in the Wolfram Language—for example, printing something or displaying a message under certain conditions. You can even have an extended "dialog" with the Wolfram Language before your method returns, perhaps invoking multiple computations in the Wolfram Language and reading their results. You might also want to write a class of the MathListener type that calls into the Wolfram Language as the result of some event triggered in Java.
If you do not want to do any of these things, then you can happily ignore this section. The whole point of J/Link is to make unnecessary the need to be concerned about the interaction with the Wolfram Language through WSTP. Most programmers who want to write Java classes to be used from the Wolfram Language will just write Java classes, period, without thinking about the Wolfram Language or J/Link. Those programmers who want more control, or want to know more about the possibilities available with J/Link, read on.
The issues discussed in this section require some knowledge of WSTP programming (or, more precisely, J/Link programming using the Java methods that use WSTP), which is discussed in detail in "Writing Java Programs That Use the Wolfram Language". The fact that you meet some of these methods and issues here is a consequence of the false but useful dichotomy, noted in the "Introduction", between using WSTP to write "installable" functions to be called from the Wolfram Language and using WSTP to write front ends for the Wolfram Language. WSTP is always used in the same way, it is just that virtually all of it is handled for you in the installable case. This section is about how to go beyond this default behavior, so you will be making direct J/Link calls to read and write to the link. Thus you will encounter concepts, classes, and methods in this section that are not explained until "Writing Java Programs That Use the Wolfram Language".
Some of the discussion in this section will compare and contrast the process of writing an installable program in C. This is designed to help experienced WSTP programmers understand how J/Link works, and also to convince you that J/Link is a superior solution to using C, C++, or FORTRAN.
Installable Functions—The Old Way
Writing a so-called "installable" or "template" program in C requires a number of steps. If you have a file foo.c that contains a function foo, to call it from the Wolfram Language you must first write a template (.tm) file that contains a template entry describing how you want foo to be called from the Wolfram Language, what types of arguments it takes, and what it returns. You then pass this .tm file through a tool called mprep, which writes a file of C code that manages some, possibly all, of the WSTP-related aspects of the program. You also need to write a simple main routine, which is always the same. You then compile all of these files, resulting in an executable for just one platform.
Two big drawbacks of this method are that you need to write a template entry for every single function you want to call (imagine doing that for a whole function library), and the compiled program is not portable to other platforms. The biggest drawback, however, is that there is no automatic support for anything but the simplest types. If you want to do something as basic as returning a list of integers, you need to write the WSTP calls to do that yourself. And forget about object-oriented programming, as there is no way to pass "objects" to the Wolfram Language.
Installable Functions in Java
J/Link makes all those steps go away. As you have seen all throughout this tutorial, you can literally call any method in any class, without any preparation.
It is only in cases where the default behavior of calling a method and receiving its result is not enough that you need to write specialty Java code. The rest of this section will examine some of the special techniques that can be used.
Setting Up Definitions in the Wolfram Language When Your Class Is Loaded
Template entries in .tm files required by installable WSTP programs written in C have two features that might appear to be lost in J/Link. The first feature is the ability to specify arbitrary Wolfram Language code to be evaluated when the program is first "installed". This is done by using the :Evaluate: line in a template entry. The second feature is the ability to specify the way in which the function is to be called from the Wolfram Language, including the name of the Wolfram Language function that maps to the C function, its argument sequence, how those arguments are mapped to the ones provided to the C function, and possibly some processing to be done on them before they are sent. This information is specified in the :Pattern: and :Arguments: lines of a template entry.
These two features are related to each other, because they both rely on the ability to specify Wolfram Language code that is loaded when an external program is installed. J/Link gives you this ability and more, through two special methods called onLoadClass() and onUnloadClass(). When a class is loaded into the Wolfram Language, either directly through LoadJavaClass or indirectly by calling JavaNew, it is examined to see if it has a method with the following signature.
If such a method is present, it will be called after all the method and field definitions for the class are set up in the Wolfram Language. Because a class can only be loaded once in a Java session, this method will only be called once in the lifetime of a single Java runtime, although it may be called more than once in the lifetime of a single Wolfram Language kernel (because the user can repeatedly launch and quit the Java runtime). The KernelLink that is provided as an argument to this method is of course the link back to the Wolfram Language.
A typical use for this feature would be to define the text for an error message issued by one of the methods in the class. Here is an example.
public static void onLoadClass(KernelLink ml) throws MathLinkException {
ml.evaluate("MyClass::sun = \"The foo() method can only be called on Sunday.\"");
ml.discardAnswer();
}
Note that this method throws MathLinkException. Your onLoadClass() method can throw any exceptions you like (a MathLinkException would be typical). This will not interfere with the matching of the expected signature for onLoadClass(). If an exception is thrown during onLoadClass, it will be handled gracefully, meaning that the normal operation of LoadJavaClass will not be affected. The only exception to this rule is if your code throws an exception while it is interacting with the link to the kernel, and more specifically, in the period between the time that it sends a computation to the kernel and the time that it begins to read the result. In other words, exceptions you throw will not break the LoadJavaClass mechanism, but it is up to you to make sure that you do not damage the link's state by starting something you do not finish.
Another reason to use onLoadClass() would be if you wanted to create a Wolfram Language function for users to call that "wrapped" a static method call, providing it with a preferred name or argument sequence. If you have a class named MyClass with the method public static void myMethod(double[a]), the definition that will be automatically created for it in the Wolfram Language will require that its argument be a list of real numbers or integers. Say you want to add a definition named MyMethod, having the traditional Wolfram Language capitalization, and you also want this function automatically to use N on its argument so that it will work for anything that will evaluate to a list of numbers, such as {Pi, 2Pi, 3Pi}. Here is how you would set up such an additional definition.
public static void onLoadClass(KernelLink ml) throws MathLinkException {
ml.evaluate("MyMethod[x_] := myMethod[N[x]]");
ml.discardAnswer();
}
In other words, if you are not happy with the interface to the class that will automatically be created in the Wolfram Language, you can use onLoadClass() to set up the desired definitions without changing the Java interface.
The Wolfram Language context that will be current when onLoadClass() is called is the context in which all the class's static methods and fields are defined. That is why in the preceding example the definition was made for MyMethod and not MyClass`MyMethod. This is important since you cannot know the correct context in your Java code because it is determined by the user via the AllowShortContext option to LoadJavaClass.
It is generally not a good idea to use onLoadClass() to send a lot of code to the Wolfram Language. This will make the behavior of your class hard for people to understand because the Wolfram Language code is hidden, and also inflexible since you would have to recompile it to make changes to the embedded Wolfram Language code. If you have a lot of code that needs to accompany a Java class, it is better to put that code into a Wolfram Language package file that you or your users load. That is, rather than having users load a class that dumps a lot of code into the Wolfram Language, you should have your users load a Wolfram Language package that loads your class. This will provide the greatest flexibility for future changes and maintenance.
Finally, there is no reason why your onLoadClass() method needs to restrict itself to making J/Link calls. You could perform operations specific to the Java side, for example, writing some debugging information to the Java console window, opening a file for writing, or whatever else you desire.
Similar to the handling of the onLoadClass() method, the onUnloadClass() method is called when a class is unloaded. Every loaded class is unloaded automatically by UninstallJava right before it quits the Java runtime. You can use onUnloadClass() to remove definitions created by onLoadClass(), or perform any other clean-up you would like. The signature of onUnloadClass() must be the following, although it can throw any exceptions.
Note that the meaning of loading and unloading classes here refers to being loaded by the Wolfram Language with LoadJavaClass either directly or indirectly. It does not refer to the loading and unloading of classes internally by the Java runtime. Class loading by the Java runtime occurs when the class is first used, which may have occurred long before LoadJavaClass was called from the Wolfram Language.
Manually Returning a Result to the Wolfram Language
The default behavior of a Java method called from the Wolfram Language is to return to the Wolfram Language exactly what the method itself returns. There are times, however, when you want to return something else. For example, you might want to return an integer in some circumstances, and a symbol in others. Or you might want a method to return one thing when it is being called from Java, and return something different to the Wolfram Language. In these cases, you will need to manually send a result to the Wolfram Language before the method returns.
Say you are writing a file-reading class that you want to call from the Wolfram Language. Because you want almost the identical behavior to the standard class java.io.FileInputStream, your class will be a subclass of it. The only changes you want to make are to provide some more Wolfram Language-like behavior. One example is that you want the read method to return not -1 when it reaches the end of the file, but rather the symbol EndOfFile, which is what the Wolfram Language's built-in file-reading functions return.
import java.io.*;
import com.wolfram.jlink.*;
public class MyFileReader extends FileInputStream {
<<constructors, other methods deleted>>
public int read() {
int i = super.read();
if (i == -1) {
KernelLink link = StdLink.getLink();
if (link != null) {
link.beginManual();
try {
link.putSymbol("EndOfFile");
} catch (MathLinkException e) {}
}
}
return i;
}
}
If the file has reached the end, i will be -1, and you want to manually return something to the Wolfram Language. The first thing you need to do is get a KernelLink object that can be used to communicate with the Wolfram Language. This is obtained by calling the static method StdLink.getLink(). If you have written installable WSTP programs in C, you will recognize the choice of names here. A C program has a global variable named stdlink that holds the link back to the Wolfram Language. J/Link has a StdLink class that has a few methods related to this link object.
The first thing you do is check whether getLink() returns null. It will never be null if the method is being called from the Wolfram Language, so you can use this test to determine whether the method is being called from the Wolfram Language or as part of a normal Java program. In this way, you can have a method that can be used from Java in the usual way when a Wolfram Language kernel is nowhere in sight. The getLink() call works no matter if the method is called directly from the Wolfram Language, or indirectly as part of a chain of methods triggered by a call from the Wolfram Language.
Once you have verified that a link back to the kernel exists, the first thing to do is inform J/Link that you will be sending the result back to the Wolfram Language yourself, so it should not try automatically to send the method's return value. This is accomplished by calling the beginManual() method on the KernelLink object.
You must call beginManual() before you send any part of a result back to the Wolfram Language. If you fail to do this, the link will get out of sync and the next J/Link call you make from the Wolfram Language will probably hang. It is safe to call beginManual() more than once, so you do not have to worry that your method might be called from another method that has already called beginManual().
Returning to the example program, the next thing after beginManual() is to make the required "put"-type calls to send the result back to the Wolfram Language (in this case, just a single putSymbol()). As always, these calls can throw a MathLinkException, so you need to wrap them in a try/catch block. The catch handler is empty, since there really is not anything to do in the unlikely event of a WSTP error. The internal J/Link code that wraps all method calls will handle the cleanup and recovery from any WSTP error that might have occurred calling putSymbol(). You do not need to do anything for MathLinkExceptions that occur while you are putting a result manually. The method call will return $Failed to the Wolfram Language automatically.
Installable programs written in C can also manually send results back. This is indicated by using the Manual keyword in the function's template entry. Thus for C programs the manual/automatic decision must be made at compile time, whereas with J/Link it is a runtime switch. You can have it both ways with J/Link—a normal automatic return in some circumstances and a manual return in others, as the preceding example demonstrates.
Requesting Evaluations by the Wolfram Language
So far, you have seen only cases where a Java method has a very simple interaction with the Wolfram Language. It is called and returns a result, either automatically or manually. There are many circumstances, however, where you might want to have a more complex interaction with the Wolfram Language. You might want a message to appear in the Wolfram System, or some Print output, or you might want to have the Wolfram Language evaluate something and return the answer to you. This is a completely separate issue from what you want to return to the Wolfram Language at the end of your method—you can request evaluations from the body of a method whether it returns its final result manually or not.
In some sense, when you perform this type of interaction with the Wolfram Language you are turning the tables on the Wolfram Language, reversing the "master" and "slave" roles for a moment. When the Wolfram Language calls into Java, the Java code is acting as the slave, performing a computation and returning control to the Wolfram Language. In the middle of a Java method, however, you can call back into the Wolfram Language, temporarily turning it into a computational server for the Java side. Thus you would expect to encounter essentially all the same issues that are discussed in "Writing Java Programs That Use the Wolfram Language", and you would need to understand the full J/Link Java-side API.
The full treatment of the MathLink and KernelLink interfaces is presented in "Writing Java Programs That Use the Wolfram Language". This section discusses a few special methods in KernelLink that are specifically for use by "installed" methods. You have already seen one, the beginManual() method. Now you will treat the message(), print(), and evaluate() methods.
The tasks of issuing a Wolfram System message from a Java method and triggering some Print output are so commonly done that the KernelLink interface has special methods for these operations. The method message() performs all the steps of issuing a Wolfram System message. It comes in two signatures.
The first form is for when you just have a single string argument to be slotted into the message text, and the second form is for if the message text needs two or more arguments. You can pass null as the second argument if the message text needs no arguments.
The print() method performs all the steps necessary to invoke the Wolfram Language's Print function.
Here is an example method that uses both. Assume that the following messages are defined in the Wolfram System (this could be from loading a package or during this class's onLoadClass() method).
public static double foo(double x, double y) {
KernelLink link = StdLink.getLink();
if (link != null) {
link.print("inside foo");
if (x < 0)
link.message("Foo::arg", "first");
if (y < 0)
link.message("Foo::arg", "second");
}
return Math.sqrt(x) * Math.sqrt(y);
}
Note that print() and message() send the required code to the Wolfram Language and also read the result from the link (it will always be the symbol Null). They do not throw MathLinkException so you do not have to wrap them in try/catch blocks.
Here is what happens when you call foo().
Note that you automatically get Indeterminate returned to the Wolfram Language when a floating-point result from Java is NaN ("Not-a-Number").
The methods print() and message() are convenience functions for two special cases of the more general notion of sending intermediate evaluations to the Wolfram Language before your method returns a result. The general means of doing this is to wrap whatever you send to the Wolfram Language in EvaluatePacket, which is a signal to the kernel that this is not the final result, but rather something that it should evaluate and send the result back to Java. You can explicitly send the EvaluatePacket head, or you can use one of the methods in KernelLink that use EvaluatePacket for you. These methods are as follows.
These methods are discussed in "Writing Java Programs That Use the Wolfram Language" (actually, they also come in several more flavors with other argument sequences). Here is a simple example.
public static double foo(double x, double y) {
KernelLink link = StdLink.getLink();
if (link != null) {
try {
link.evaluate("2+2");
// Wait for, and then read, the answer.
link.waitForAnswer();
int sum1 = link.getInteger();
// evaluateToOutputForm makes the result come back as a
// string formatted in OutputForm, and all in one step
// (no waitForAnswer call needed).
String s = link.evaluateToOutputForm("3+3");
int sum2 = Integer.parseInt(s);
// If you want, put the whole evaluation piece by piece,
// including the EvaluatePacket head.
link.putFunction("EvaluatePacket");
link.putFunction("Plus", 2);
link.put(4);
link.put(4);
link.waitForAnswer();
int sum3 = link.getInteger();
} catch (MathLinkException e) {
// The only type of WSTP error we are likely to get
// is from a "get" function when what we are trying to
// get is not the type of expression that is waiting. We
// just clear the error state, throw away the packet we
// are reading, and let the method finish normally.
link.clearError();
link.newPacket();
}
}
return Math.sqrt(x) * Math.sqrt(y);
}
Throwing Exceptions
Any exceptions that your method throws will be handled gracefully by J/Link, resulting in the printing of a message in the Wolfram System describing the exception. This was discussed in "How Exceptions Are Handled". If you are sending computations to the Wolfram Language as described in the previous section, you need to make sure that an exception does not interrupt your code unexpectedly. In other words, if you start a transaction with the Wolfram Language, make sure you complete it or you will leave the link out of sync and future calls to Java will probably hang.
Making a Method Interruptible
If you are writing a method that may take a while to complete, you should consider making it interruptible from the Wolfram Language. In C WSTP programs, a global variable named WSAbort is provided for this purpose. In J/Link programs, you call the wasInterrupted() method in the KernelLink interface.
Here is an example method that performs a long computation, checking every 100 iterations whether the user tried to abort it (using the Abort Evaluation command in the Evaluation menu).
public int foo() {
KernelLink link = StdLink.getLink();
for (int i = 0; i < 10000, i++) {
... perform one step ...
if (i % 100 == 0 && link.wasInterrupted())
return 0; // Return value will not be seen by Mathematica.
}
return 42;
}
This method returns 0 if it detects an attempt by the user to abort, but this value will never be seen by the Wolfram Language. This is because J/Link causes a method or constructor call that is aborted to return Abort[], whether or not you detect the abort in your code. Therefore, if you detect an abort and want to honor the user's request, just return some value right away. When J/Link returns Abort[], the user's entire computation is aborted, just as if the Abort[] were embedded in Wolfram Language code. This means that you do not have to be concerned with any details of propagating the abort back to the Wolfram Language—all you have to do is return prematurely if you detect an abort request, and the rest is handled for you.
Sometimes you might want a Java method to detect an abort and do something other than cause the entire Wolfram Language computation to abort. For example, you might want a loop to stop and return its results up to that point. Note that this is not generally recommended. Users expect a program to abort and return $Aborted when they issue an abort request. In some cases, however, especially if the code is not intended for use by a large community, you might find it useful to use an abort as a "message" to communicate some information to your Java code instead of just having the computation aborted. This idea is similar to the Wolfram Language's CheckAbort function, which allows you to detect an abort and absorb it so that it does not propagate further and abort the entire computation. To "absorb" the abort in your Java code so that J/Link does not return Abort[], simply call the clearInterrupt() method.
public int foo() {
KernelLink link = StdLink.getLink();
for (int i = 0; i < 10000, i++) {
... perform one step ...
if (i % 100 == 0 && link.wasInterrupted()) {
link.clearInterrupt();
return resultSoFar; // This is the value that will be returned to Mathematica
}
}
...
return 42;
}
Writing Your Own Event Handler Code
"Handling Events with the Wolfram Languge Code: The "MathListener" Classes" introduced the topic of triggering calls into the Wolfram Language as a response to events fired in Java, such as clicking a button. A set of classes derived from MathListener is provided by J/Link for this purpose. You are not required to use the provided MathListener classes, of course. You can write your own classes to handle events and put calls into the Wolfram Language directly into their code. All the event handler classes in J/Link are derived from the abstract base class MathListener, which takes care of all the details of interacting with the Wolfram Language, and also provides the setHandler() methods that you use to associate events with Wolfram Language code. Users who want to write their own MathListener-style classes (for example, for one of the Swing-specific event listener interfaces, which J/Link does not provide) are strongly encouraged to make their classes subclasses of MathListener to inherit all this functionality. You should examine the source code for MathListener, and also one of the concrete classes derived from it (MathActionListener is probably the simplest one) to see how it is written. You can use this as a starting point for your own implementation.
There is a new feature of J/Link 2.0 that should be pointed out in this context. This is the ImplementJavaInterface Wolfram Language function, which lets you implement any Java interface entirely in Wolfram Language code. ImplementJavaInterface is described in more detail in "Implementing a Java Interface with Wolfram Language Code", but a common use for it would be to create event-handler classes that implement a "Listener"-type interface for which J/Link does not have a built-in MathListener. This is discussed in more detail in "Implementing a Java Interface with Wolfram Language Code", and if you choose this technique, then you do not have to worry about any of the issues in this section because they are handled for you.
If you are going to write a Java class, and you choose not to derive your class from MathListener, there are two very important rules that must be adhered to when writing event-handler code that calls into the Wolfram Language. To be more precise, these rules apply whenever you are writing code that needs to call into the Wolfram Language at a point when the Wolfram Language is not currently calling into Java. That may sound confusing, but it is really very simple. "Requesting Evaluations by the Wolfram Language" showed how to request evaluations by the Wolfram Language from within a Java method. In this case, the Wolfram Language has called your Java method, and while the Wolfram Language is waiting for the result, your code calls back to perform some computation. This works fine as described in that earlier section, because at the point the code calls back into the Wolfram Language, the Wolfram Language is in the middle of a call to Java. This is a true "callback"—the Wolfram Language has called Java, and during the handling of this call, Java calls back to the Wolfram Language. In contrast, consider the case where some Java code executes in response to a button click. When the button click event fires, the Wolfram Language is probably not in the middle of a call to Java.
Special considerations are necessary in the latter case because there are two threads in the Java runtime that are using WSTP links. The first one is created and used by the internals of J/Link to handle standard calls into Java originating in the Wolfram Language, as described throughout this tutorial. The second one is the Java user interface thread (sometimes called the AWT thread), which is the one on which your event handler code will be called. You need to make sure that your use of the link back to the kernel on the user interface thread does not interfere with J/Link's internal thread.
The following code shows an idealized version of the actionPerformed() method in the MathActionListener class. The actual code in MathActionListener is different, because this work is farmed out to the parent class, MathListener, but this example shows the correct flow of operations. This is the code that is executed when the associated object's action occurs (like a button click).
public void actionPerformed(ActionEvent e) {
KernelLink ml = StdLink.getLink();
StdLink.requestTransaction();
synchronized (ml) {
try {
// Send the code to perform the user's requested operation.
ml.putFunction("EvaluatePacket", 1);
... code to put rest of expression to evaluate goes here ...
ml.endPacket();
ml.discardAnswer();
} catch (MathLinkException exc) {
...
}
}
}
The first rule to note in this code is that the complete transaction with the Wolfram Language, which includes sending the code to evaluate and completely reading the result, is wrapped in a synchronized(ml) block. This is how you ensure that the user interface thread has exclusive access to the link for the entire transaction. The second rule is that the synchronized(ml) statement must be preceded by a call to StdLink.requestTransaction(). This call will block until the kernel is at a point where it is ready to accommodate evaluations originating in Java. The call must occur before the synchronized(ml) block begins, and once you call it you must make sure that you send something to the Wolfram Language. In other words, when requestTransaction() returns, the kernel will be blocking in an attempt to read from the Java link. The kernel will be stuck in this state until you send it something, so you must protect against a Java exception being thrown after you call requestTransaction() but before you send anything. Typically you will do this simply by calling requestTransaction() immediately before the synchronized(ml) block begins and you start sending something.
It was just said that StdLink.requestTransaction() will block until the kernel is ready to accept evaluations originating in Java. To be specific, it will block until one of the following conditions occurs:
- The Wolfram Language executes DoModal
- Kernel sharing has been turned on via ShareKernel or ShareFrontEnd, and the kernel is not busy with another computation
- Java is not being used from the Wolfram Language (InstallJava has not been called)
These conditions should make sense given the discussion about creating user interface elements in the section "Creating Windows and Other User Interface Elements". DoModal, ShareKernel, and ServiceJava are the three ways in which you direct the kernel's attention to the Java link so that it can detect incoming request for computations.
If you make the common mistake of inadvertently triggering a call to the Wolfram Language from Java before you have called DoModal or ShareKernel, the Java user interface thread will hang. This can be easily remedied by calling DoModal, ShareKernel, or ServiceJava afterward (ServiceJava may need to be called more than once, if more than one event callback is queued up).
If the rule about when it is necessary to use StdLink.requestTransaction() and synchronized(ml) is confusing, you will be happy to learn that it is fine to use these constructs in any code that calls the Wolfram Language. In code that does not need them, they are pointless, but harmless, and will not cause the calling thread to block. If you are writing a Java method that needs to call the Wolfram Language and there is any chance that it might be called from the user interface thread, add the StdLink.requestTransaction() and synchronized(ml).
Debugging Your Java Classes
You can use your favorite debugger to debug Java code that is called from the Wolfram Language. The only issue is that you typically have to launch a Java program inside the debugger to do this. The Java program that you need to launch is the one that is normally launched for you when you call InstallJava. The class that contains J/Link's main() method is com.wolfram.jlink.Install. Thus, the command line to start J/Link that is executed internally by InstallJava is typically as follows.
There may be additions or modifications to this depending on the options to InstallJava, and also some extra WSTP-specific arguments are tacked on at the end. To use a debugger, you just have to launch Java with the appropriate command-line arguments that allow you to establish the link to the Wolfram Language manually.
If you use a development environment that has an integrated debugger, then the debugger probably has a setting for the main class to use (the class whose main() method will be invoked) and a setting for command-line arguments. For example, in WebGain Visual Café, you can set these values in the Project panel of the Project/Options dialog. Set the main class to be com.wolfram.jlink.Install, and the arguments to be something like the following.
Then start the debugging session. You should see the J/Link copyright notice printed and then Java will wait for the Wolfram Language to connect. To do this, go to your Wolfram System session, make sure the JLink.m package has been read in, and execute the command.
This works because ReinstallJava can take a LinkObject as its argument, in which case it will not try to launch Java itself. This allows you to manually establish the WSTP connection between Java and the Wolfram Language, then feed that link to ReinstallJava and let it do the rest of the work of preparing the Wolfram Language and Java sides for interacting with each other.
If you like to use a command-line debugger like jdb, you can do the following.
C:\>jdb
Initializing jdb...
> run com.wolfram.jlink.Install -linkmode listen -linkname foo
running ...
main[1] J/Link (tm)
Copyright (C) 1999-2018, Wolfram Research, Inc. All Rights Reserved.
www.wolfram.com
Version 4.9.1
Current thread "main" died. Execution continuing...
>
The message about the main thread dying is normal. Now jdb is ready for commands. First, though, you have to execute in your Wolfram System session the LinkConnect and ReinstallJava lines shown earlier.
Deploying Applications That Use J/Link
This section discusses some issues relevant to developers who are creating add-ons for the Wolfram Language that use J/Link.
J/Link uses its own custom class loader that allows it to find classes in a set of locations beyond the startup class path. As described in "Dynamically Modifying the Class Path", users can grow this set of extra locations to search for classes by calling the AddToClassPath function. One of the motivations for having a custom class loader was to make it easy for application developers to distribute applications that have parts of their implementation in Java. If you structure your application directory properly, your users will be able to install it simply by copying it into any standard location for Wolfram Language applications. J/Link will be able to find your Java classes immediately, without users having to perform any classpath-related operations or even restart Java.
If your Wolfram Language application uses J/Link and includes its own Java components, you should create a Java subdirectory in your application directory. You can place any jar files that your application needs into this Java subdirectory. If you have loose class files (not bundled into a jar file), they should go into an appropriately nested subdirectory of the Java directory. "Appropriately nested" means that if your class is in the Java package com.somecompany.math, then its class file goes into the com/somecompany/math subdirectory of the Java directory. If the class is not in any package, it can go directly into the Java directory. J/Link can also find native libraries and resources your application needs. Native libraries must be in a subdirectory of your Java/Libraries directory that is named after the $SystemID of the platform on which it is installed. Here is an example directory structure for an application that uses J/Link.
MyApp/
... other files and directories used by the application ...
Java/
MyAppClasses.jar
MyImage.gif
Libraries/
Windows/
MyNativeLibrary.dll
MacOSX-x86-64/
libMyNativeLibrary.jnilib
Linux/
libMyNativeLibrary.so
... and so on for other platforms
Your application directory must be placed into one of the standard locations for Wolfram Language applications. These locations are listed as follows. In this notation, $InstallationDirectory/AddOns/Applications means "The AddOns/Applications subdirectory of the directory whose value is given by the Wolfram Language variable $InstallationDirectory".
$UserAddOnsDirectory/Applications
Coding Tips
Here are a few tips on producing high-quality applications. These suggestions are guided by mistakes that developers frequently make.
Call InstallJava in the body of a function or functions, not when your package is read in. It is best to avoid side effects during the reading of a package. Users expect reading in a package to be fast and to do nothing but load definitions. If you launch Java at this time, and it fails, it could cause a mysterious hang in the loading process. It is better to call InstallJava in the code of one or more of your functions. You probably do not need to call InstallJava in every single function that uses Java. Most applications have a few "major" functions that users are likely to use almost exclusively, or at least at the start of their session. If your application does not have this property, then provide an initialization function that your users must call first, and call InstallJava inside it.
Call InstallJava with no arguments. You cannot know what options your users need for Java on their systems, so do not override what they may have set up. It is the user's responsibility to make sure that they call SetOptions to customize the options for InstallJava as necessary. Typically this would be done in their init.m file.
Make sure you use JavaBlock and/or ReleaseJavaObject to avoid leaking object references. You cannot know how others will use your code, so you need to be careful to avoid cluttering up their sessions with a potentially large number of useless objects. Sometimes you need to create an object that persists beyond the lifetime of a single Wolfram Language function, like a viewer window. In such cases, use a MathFrame or MathJFrame as your top-level window and use its onClose() method to specify Wolfram Language code that releases all outstanding objects and unregisters kernel or front end sharing you may have used. If this is not possible, provide a cleanup function that users can call manually. Use LoadedJavaObjects to look at the list of objects referenced in the Wolfram Language before and after your functions run; it should not grow in length.
If you use ShareKernel or ShareFrontEnd, make sure you save the return values from these functions and pass them as arguments to UnshareKernel and UnshareFrontEnd. Do not call UnshareFrontEnd or UnshareKernel with no arguments, as this will shut down sharing even if other applications are using it.
Do not assume that the Java runtime will not be restarted during the lifetime of your application. Although users are strongly discouraged from calling UninstallJava or ReinstallJava, it happens. It is unavoidable that some applications will fail if the Java runtime is shut down at an inopportune time (e.g. when they have a Java window displayed), but there are steps you can take to increase the robustness of your application in the face of Java shutdowns and restarts. One step was already given as the first tip listed—call InstallJava at the start of your "major" functions. Another step is to avoid caching JavaClass or JavaObject expressions unnecessarily, as these will become invalid if Java restarts. An example of this is calling InstallJava and then LoadJavaClass and JavaNew several times when your package file is read in, and storing the results in private variables for the lifetime of your package. This is problematic if Java is restarted. Never store JavaClass expressions—call LoadJavaClass whenever there is any doubt about whether a class has been loaded into the current Java runtime. Calling LoadJavaClass is very inexpensive if the class has already been loaded. If you have a JavaObject that is very expensive to create and therefore you feel it necessary to cache it over a long period of time in a user's session, consider using the following idiom to test whether it is still valid whenever it is used. The JavaObjectQ test will fail if Java has been shut down or restarted since the object was last created, so you can then restart Java and create and store a new instance of the object.
Do not call UninstallJava or ReinstallJava in your application. You need to coexist politely with other applications that may be using Java. Do not assume that when your package is done with Java, the user is done with it as well. Only users should ever call UninstallJava, and they should probably never call it either. There is no cost to leaving Java running. Likewise, users will rarely call ReinstallJava unless they are doing active Java development and need to reload modified versions of their classes.
Example Programs
Introduction
This section will work through some example programs. These examples are intended to demonstrate a wide variety of techniques and subtleties. Discussions include some nuances in the implementations and touch on most of the major issues in J/Link programming.
This will take a relatively rigorous approach, and in particular it will be careful to avoid leaking references. As discussed in the section "JavaBlock", JavaBlock and ReleaseJavaObject are the tools in this fight, but if you find yourself becoming the least bit confused about the subject, just ignore it completely. For many casual, personal uses of J/Link, you can forget about memory management issues, and just let Java objects pile up.
J/Link includes a number of notebooks with sample programs, including most of the programs developed in this section. These notebooks can be found in the <Mathematica dir>/SystemFiles/Links/JLink/Examples/Part1 directory.
A Beep Function
Here is a very simple example that generates a system alert just like the Wolfram Language Beep function.
You may notice a short delay the first time JavaBeep[] is executed. This is due to the LoadJavaClass call, which only takes measurable time the first time it is called for any given class.
This is a perfectly good beep function, and many users will not need to go beyond this. If you are writing code for others to use, however, you will probably want to embellish this code a little bit. Here is a more professional version of the same function.
Note that the first thing you do is call InstallJava. It is a good habit to call InstallJava in functions that use J/Link, at least if you are writing code for others to use. If InstallJava has already been called, subsequent calls will do nothing and return very quickly. The whole program is wrapped in JavaBlock. As discussed in the section "JavaBlock", JavaBlock automates the process of releasing references to objects returned to the Wolfram Language. The getDefaultToolkit() method returns a Toolkit object, so you want to release the JavaObject that gets created in the Wolfram Language. The getDefaultToolkit() method returns a reference to the same Toolkit object every time it is called, so even if you do not call JavaBlock, you will only "leak" one object in an entire session. You could also write Beep using an explicit call to ReleaseJavaObject.
The advantage to using JavaBlock is that you do not have to think about what, if any, methods might return objects, and you do not have to assign them to variables.
Formatting Dates
Here is an example of a computation performed in Java. Java provides a number of powerful date- and calendar-oriented classes. Say you want to create a nicely formatted string showing the time and date. In this first step you create a new Java Date object representing the current date and time.
Next you load the DateFormat class and create a formatter capable of formatting dates.
Now you call the format() method, passing the Date object as its argument.
There are many different ways in which dates and times can be formatted, including respecting a user's locale. Java also has a useful number-formatting class, an example of which was given in "An Optimization Example".
A Progress Bar
A simple example of a popup user interface for a Wolfram Language program is a progress bar. This is an example of a "non-interactive" user interface, as defined in "Interactive and Non-Interactive Interfaces", because it does not need to call back to the Wolfram Language or return a result to the Wolfram Language. The implementation uses the Swing user interface classes, because Swing has a built-in class for progress bars. (You cannot run this example unless you have Swing installed. It comes as a standard part of Java 1.2 or later, but you can get it separately for Java 1.1.x. Most Java development tools that are still at Version 1.1.x come with Swing.) The complete code for this example is also provided in the file ProgressBar.nb in the JLink/Examples/Part1 directory.
The code is commented to point out the general structure. There are several classes and methods used in this code that may be unfamiliar to you. Just keep in mind that this is completely standard Java code translated into the Wolfram Language using the J/Link conventions. It is line-for-line identical to a Java program that does the same thing.
This code is presented as a complete program, but this does not suggest that it should be developed that way. The interactive nature of J/Link lets you tinker with Java objects a line at a time, experimenting until you get things just how you want them. Of course, this is how Wolfram Language programs are typically written, and J/Link lets you do the same with Java objects and methods.
You can create a function ShowProgressBar that prepares and displays a progress bar dialog. The bar will be used to show percentage completion of a computation. You can supply the initial percent completed or use the default value of zero. ShowProgressBar returns the JProgressBar object because the bar needs to be updated later by calling setValue(). Note that because you return the bar object from the JavaBlock, it is not released like all other new Java objects created within this JavaBlock. This is a new behavior of JavaBlock in J/Link 2.0. If what is returned from a JavaBlock is precisely a single Java object (and not, for example, a list of objects), then this object is not released. JavaBlock is discussed in the section "JavaBlock".
You also need a function to close the progress dialog and clean up after it. Only two things need to be done. First, the dispose() method must be called on the top-level frame window that contains the bar. Second, if you want to avoid leaking object references, you need to call ReleaseJavaObject on the bar object because it is the only object reference that escaped the JavaBlock in ShowProgressBar. You need to call dispose() on the JFrame object you created in ShowProgressBar, but you did not save a reference to it. The SwingUtilities class has a handy method windowForComponent() that will retrieve this frame, given the bar object.
The bar dialog has a close box in it, so a user can dismiss it prematurely if desired. This would take care of disposing the dialog, but you would still need to release the bar object. DestroyProgressBar (and the bar's setValue() method) is safe to call whether or not the user closed the dialog.
Here is how you would use the progress bar in a computation. The call to ShowProgressBar displays the bar dialog and returns a reference to the bar object. Then, while the computation is running, you periodically call the setValue() method to update the bar's appearance. When the computation is done, you call DestroyProgressBar.
An easy way to test whether your code leaks object references is to call LoadedJavaObjects[] before and after the computation. If the list of objects gets longer, then you have forgotten to use ReleaseJavaObject or improperly used JavaBlock.
It can take several seconds to load all the Swing classes used in this example. This means that the first time ShowProgressBar is called, there will be a significant delay. You could avoid this delay by using LoadJavaClass ahead of time to explicitly load the classes that appear in JavaNew statements.
The dialog appears onscreen with its upper left at the coordinates (400, 400). It is left as an exercise to the reader to make it centered on the screen. (Hint: the java.awt.Toolkit class has a getScreenSize() method).
Finally, because the progress bar uses the Swing classes, you can play with the look-and-feel options that Swing provides. Specifically, you can change the theme at runtime. The progress bar window is not very complicated, so it changes very little in going from one look-and-feel theme to another, but this demonstrates how to do it. The effect is much more dramatic for more complex windows.
First, create a new progress bar window.
Now load some classes from which you need to call static methods.
The default look and feel is the "metal" theme. You can change it to the native style look for your platform as follows (it helps to be able to see the window when doing this).
A Simple Modal Input Dialog
You saw one example of a simple modal dialog in "Modal Windows". Presented here is another one—a basic dialog that prompts the user to enter an angle, with a choice of whether it is being specified in degrees or radians. This will demonstrate a dialog that returns a value to a running Wolfram Language program when it is dismissed, much like the Wolfram Language's built-in Input function, which requests a string from the user before returning. Dialogs like this one are not "modal" in the traditional sense that they must be closed before other Java windows can be used, but rather they are modal with respect to the kernel, which is kept busy until they are dismissed (that is, until DoModal[] returns). The section "Creating Windows and Other User Interface Elements" discusses modal and modeless Java windows in detail.
The code is rather straightforward and warrants little in the way of commentary. In creating the window and the controls within it, it exactly mirrors the Java code you would use if you were writing the program in Java. One technique it demonstrates is determining whether the OK or Cancel button was clicked to dismiss the dialog. This is done by having the MathActionListener objects assigned to the two buttons return different things in addition to calling EndModal[]. Recall that DoModal[] returns whatever the code that calls EndModal[] returns, so here you have the OK button execute (EndModal[]; True)&, a pure function that ignores its arguments, calls EndModal[], and returns True, whereas the Cancel button executes (EndModal[]; False)&. Thus, DoModal[] returns True if the OK button was clicked, or False if the Cancel button was clicked. It will return Null if the window's close box was clicked (this behavior comes from the MathFrame itself).
It may take several seconds to display the dialog the first time GetAngle[] is called. This is due to the one-time cost of loading the several large AWT classes required. Subsequent invocations of GetAngle[] will be much quicker.
The complete code for this example is also provided in the file ModalInputDialog.nb in the JLink/Examples/Part1 directory.
A File Chooser Dialog Box
A useful feature for Wolfram Language programs is to be able to produce a file chooser dialog, such as the typical Open or Save dialog boxes. You could use such a dialog box to prompt a user for an input file or a file into which to write data. This is easily accomplished in a cross-platform way with Java, specifically with the JFileChooser class in the standard Swing library. The code for such a dialog box is provided in the file FileChooserDialog.nb in the JLink/Examples/Part1 directory.
Mathematica 4.0 introduced a new "experimental" function called FileBrowse[] that displays a file browser in the front end. Although this function is usable, it has several shortcomings compared to the Java technique presented next. One of the limitations is that it requires that the front end be in use. Another is that it is not customizable, so you always get a Save file as: dialog box and the concomitant behavior, which is not appropriate for an Open-type dialog box. The JFileChooser class used here allows very sophisticated customization, including setting the initial directory, masking out files based on their names or properties, controlling the title and text on the various buttons, supplying functions to validate the choice before the dialog box is allowed to be dismissed, allowing for multiple file selection, and allowing directories to be selected instead of files.
Although this example is a short program, the code has some unfortunate complexity (meaning "ugliness") in it related to making this special type of dialog window come to the foreground on all platforms. For this reason, the code is not presented here. Instead, some topics in the program code will be mentioned; you can read the full code and its associated comments in the example file if you are interested in the implementation details.
The FileChooserDialog function takes three string arguments. The first is the title of the dialog box (for example, Select a data file to import), the second is the text to appear on what is essentially the OK button (typically this will be Open or Save), and the third is the directory in which to start. You can also supply no arguments and get a default Open dialog box that starts in the kernel's current directory.
Although this is a "modal" dialog box, there is no need to use DoModal, because the showDialog() method will not return until the user dismisses the dialog box. Recall that DoModal is a way to force the Wolfram Language to stall until the dialog box or other window is dismissed. Here, you get this behavior for free from showDialog(). The other thing that DoModal does is put the kernel into a loop where it is ready to receive input from Java, so you can script some of the functionality of the dialog via callbacks to the Wolfram Language. The file chooser dialog box does not need to use the Wolfram Language in any way until it returns the selected file, so you have no need for this other aspect that DoModal provides.
A second point of interest is in the name of the constant that showDialog() returns to indicate that the user clicked the Save or Open button instead of the Cancel button. The name of this constant in Java is JFileChooser.APPROVE_OPTION. Java names map to Wolfram Language symbols, so they must be translated if they contain characters that are not legal in the Wolfram Language symbols, such as the underscore. Underscores are converted to a "U" when they appear in symbols, so the Wolfram Language name of this constant is JFileChooser`APPROVEUOPTION. See "Underscores in Java Names" for more information.
Sharing the Front End: Palette-Type Buttons
As discussed in the section "Creating Windows and Other User Interface Elements", one of the goals of J/Link is to allow Java user interface elements to be as close as possible to first-class members of the notebook front end environment in the way notebook and palette windows are. One of the ways this is accomplished is with the ShareKernel function, which allows Java windows to share the kernel's attention with notebook windows. Such Java windows are referred to as "modeless", not in the traditional sense of allowing other Java windows to remain active, but modeless with respect to the kernel, meaning that the kernel is not kept busy while they are open.
Beyond the ability to have Java windows share the kernel with the front end, it would be nice to allow actions in Java to cause effects in notebook windows, such as printing something, displaying a graph, or any of the notebook-manipulation commands like NotebookApply, NotebookPrint, SelectionEvaluate, SelectionMove, and so on. A good example of this is palette buttons. A palette button can cause the current selection to be replaced by something else and the resulting expression to be evaluated in place.
The ShareFrontEnd function lets actions in Java modeless windows trigger events in a notebook window just like can be done from palette buttons or Wolfram Language code you evaluate manually in a notebook. Remember that you automatically get the ability to interact with the front end when you use a modal dialog (i.e. when DoModal is running). When Java is being run in a modal way, the kernel's $ParentLink always points at the front end, so all side effect outputs get sent to the front end automatically. A modal window would not be acceptable for the palette example here because the palette needs to be an unobtrusive enhancement to the Wolfram Language environment—it cannot lock up the kernel while it is alive. ShareKernel allows Java windows to call the Wolfram Language without tying up the kernel, and ShareFrontEnd is an extension to ShareKernel (it calls ShareKernel internally) that allows such "modeless" Java windows to interact with the front end. ShareFrontEnd is discussed in more detail in "Sharing the Front End".
In the PrintButton example that follows, a simple palette-type button is developed in Java that prints its label at the current cursor position in the active notebook. Because of current limitations with ShareFrontEnd, this example will not work with a remote kernel; the same machine must be running the kernel and the front end.
Now invoke the PrintButton function to create and display the palette. Click the button to see the button's label (foo in this example) inserted at the current cursor location. When you are done, click the window's close box.
The code is mostly straightforward. As usual, you use the MathFrame class for the frame window because it closes and disposes of itself when its close box is clicked. You create a MathActionListener that calls buttonFunc and you assign it to the button. From the table in the section "Handling Events with Wolfram Language Code: The "MathListener" Classes", you know that buttonFunc will be called with two arguments, the first of which is the ActionEvent object. From this object you can obtain the button that was clicked and then its label, which you insert at the current cursor location using the standard NotebookApply function. One subtlety is that you need to specify SelectedNotebook[] as the target for notebook operations like NotebookApply, NotebookWrite, NotebookPrint, and so on, which take a notebook as an argument. Because of implementation details of ShareFrontEnd, the notebook given by EvaluationNotebook[] is not the correct target (after all, there is no evaluation currently in progress in the front end when the button is clicked).
The important thing to note in PrintButton is the use of ShareFrontEnd and UnshareFrontEnd. As discussed earlier, ShareFrontEnd puts Java into a state where it forwards everything other than the result of a computation to the front end, and puts the front end into a state where it is able to receive it. This is why the Print output triggered by clicking the Java button, which would normally be sent to Java (and just discarded there), appears in the front end. Front end sharing (and also kernel sharing) should be turned off when they are no longer needed, but if you are writing code for others to use you cannot just blindly shut sharing down—the user could have other Java windows open that need sharing. To handle this issue, ShareFrontEnd (and ShareKernel) works on a register/unregister principle. Every time you call ShareFrontEnd, it returns a token that represents a request for front end sharing. If front end sharing is not on, it will be turned on. When a program no longer needs front end sharing, it should call UnshareFrontEnd, passing the token from ShareFrontEnd as the argument. Only when all requests for sharing have been unregistered in this way will sharing actually be turned off.
The onClose() method of the MathFrame class lets you specify Wolfram Language code to be executed when the frame is closed. This code is executed after all event listeners have been notified, so it is a safe place to turn off sharing. In the onClose() code, you call UnshareFrontEnd with the token returned by ShareFrontEnd. Using the onClose() method in this way allows us to avoid writing a cleanup function that users would have to call manually after they were finished with the palette. It is not a problem to leave front end sharing turned on, but it is desirable to have your program alter the user's session as little as possible.
Now expand this example to include more buttons that perform different operations. The complete code for this example is provided in the file Palette.nb in the JLink/Examples/Part1 directory.
The first thing you do is separate the code that manages the frame containing the buttons from the code that produces a button. In this way you will have a reusable palette frame that can hold any number of different buttons. The ShowPalette function here takes a list of buttons, arranges them vertically in a frame window, calls ShareFrontEnd, and displays the frame in front of the user's notebook window.
Note that you do not return anything from the ShowPalette function—specifically, you do not return the frame object itself. This is because you do not need to refer to the frame ever again. It is destroyed automatically when its close box is clicked (remember, this is a feature of the MathFrame class). Because you do not need to keep references to any of the Java objects you create, the entire body of ShowPalette can be wrapped in JavaBlock.
Now create a reusable PaletteButton function that creates a button. You have to pass in only two things: the label text you want on the button and the function (as a string) you want to have invoked when the button is clicked. This is sufficient to allow completely arbitrary button behavior, as the entire functionality of the button is tied up in the button function you pass in as the second argument.
You will use the PaletteButton function to create four buttons. The first is just the print button just defined, the behavior of which is specified by printButtonFunc.
The second will duplicate the functionality of the buttons in the standard AlgebraicManipulation front end palette. These buttons wrap a function (e.g. Expand) around the current selection and evaluate the resulting expression in place. Here is how you create the button and define the button function for that operation.
The third button will create a plot. All you have to do is call a plotting function—the work of directing the graphics output to a new cell in the frontmost notebook is handled internally by J/Link as a result of having front end sharing turned on via ShareFrontEnd.
The final button demonstrates another method for causing text to be inserted at the current cursor location. The first example of this, printButtonFunc, uses NotebookApply. You can also just call Print—as with graphics, Print output is automatically routed to the frontmost notebook window by J/Link when front end sharing is on. This quick-and-easy Print method works fine for many situations when you want something to appear in a notebook window, but using NotebookApply is a more rigorous technique. You will see some differences in the effects of these two buttons if you put the insertion point into a StandardForm cell and try them.
Now you are finally ready to create the palette and show it.
In closing, it must be noted that although this example has demonstrated some useful techniques, it is not a particularly valuable way to use ShareFrontEnd. In creating a simple palette of buttons, you have done nothing that the front end cannot do all by itself. The real uses you will find for ShareFrontEnd will presumably involve aspects that cannot be duplicated within the front end, such as more sophisticated dialog boxes or other user interface elements.
Real-Time Algebra: A Mini-Application
This example will put together everything you have learned about modal and modeless Java user interfaces. You will implement the same "mini-application" (essentially just a dialog box) in both modal and modeless flavors. The application is inspired by the classic WSTP example program RealTimeAlgebra, originally written for the NeXT computer by Theodore Gray and then done in HyperCard by Doug Stein and John Bonadies. The original RealTimeAlgebra provides an input window into which the user types an expression that depends on certain parameters, an output window that displays the result of the computation, and some sliders that are used to vary the values of the parameters. The output window updates as the sliders are moved, hence the name RealTimeAlgebra. Our implementation of RealTimeAlgebra will be very simplistic, with only a single slider to modify the value of one parameter.
The complete code for this example is provided in the file RealTimeAlgebra.nb in the JLink/Examples/Part1 directory.
Here is the function that creates and displays the window.
The sliderFunc function is called by the MathAdjustmentListener whenever the slider's position changes. It gets the text in the inputText box, evaluates it in an environment where a has the value of the slider position (the range for this is 0…20, as established in the JavaNew call that creates the slider), and puts the resulting string into the outText box. It then calls ReleaseJavaObject to release the first argument, which is the AdjustmentEvent object itself. This is the only object passed in as an argument (the other two arguments are integers). If you are wondering how you determine the argument sequence for sliderFunc, you get it from the MathListener table in the section "Handling Events with Mathematica Code: The "MathListener" Classes". Note that you need to refer by name to the input and output text boxes in sliderFunc, so you cannot make their names local variables in the Module of CreateWindow, and of course they cannot be created inside that function's JavaBlock.
There is one interesting thing in the code that deserves a remark. Look at the lines where you add the three components to the frame. What is the ReturnAsJavaObject doing there? The method being called here is in the Frame class, and has the following signature.
The second argument, constraints, is typed only as Object. The value you pass in depends on the layout manager in use, but typically it is a string, as is the case here (BorderLayout`NORTH, for example, is just the string "NORTH"). The problem is that J/Link creates a definition for this signature of add that expects a JavaObject for the second argument, and Wolfram Language strings do not satisfy JavaObjectQ, although they are converted to Java string objects when sent. This means that you can only pass strings to methods that expect an argument of type String. In the rare cases where a Java method is typed to take an Object and you want to pass a string from the Wolfram Language, you must first create a Java String object with the value you want, and pass that object instead of the raw Wolfram Language string. You have encountered this issue several times before, and you have used MakeJavaObject as the trick to get the raw string turned into a reference to a Java String object. MakeJavaObject[BorderLayout`NORTH] would work fine here, but it is instructive to use a different technique (it also saves a call into Java). BorderLayout`NORTH calls into Java to get the value of the BorderLayout.NORTH static field, but in the process of returning this string object to the Wolfram Language, it gets converted to a raw Wolfram Language string. You need the object reference, not the raw string, so you wrap the access in ReturnAsJavaObject, which causes the string, which is normally returned by value, to be returned in the form of a reference.
Getting back to the RealTimeAlgebra dialog box, you are now ready to run it as a modal window. You write a special modal version that uses CreateWindow internally.
Note that the whole function is wrapped in JavaBlock. This is an easy way to make sure that all object references created in the Wolfram Language while the dialog is running are treated as temporary and released when DoModal finishes. This saves you having to properly use JavaBlock and ReleaseJavaObject in all the handler functions used for your MathListener objects (you will notice that these calls are absent from the sliderFunc function).
Now run the dialog. The RealTimeAlgebraModal function will not return until you close the RealTimeAlgebra window, which is what you mean when you call this a "modal" interface.
It may take several seconds before the window appears the first time. As always, this is the one-time cost of loading all the necessary classes. Play around by dragging the slider, and try changing the text in the input box, for example, to N[Pi,2a].
Recall that while the Wolfram Language is evaluating DoModal[], any Print output, messages, graphics, or any other output or commands other than the result of computations triggered from Java will be sent to the front end. To see this in action, try putting Print[a] in the input text box (you will want to arrange windows on your screen so that you can see the notebook window while you are dragging the slider). Next, try Plot[Sin[a x],{x,0,4 Pi}].
Quit RealTimeAlgebra by clicking the window's close box. In addition to closing and disposing of the window, this causes EndModal[] to be executed in the Wolfram Language, which then causes DoModal to return. The disposing of the window is due to using the MathFrame class for the window, and executing EndModal[] is the result of calling the setModal() method of MathFrame, as discussed in "Modal Windows".
Now implement RealTimeAlgebra as a modeless window. The CreateWindow function can be used unmodified. The only difference is how you make the Wolfram Language able to service the computations triggered by dragging the slider. For a modal window, you use DoModal to force the Wolfram Language to pay attention exclusively to the Java link. The drawback to this is that you cannot use the kernel from the notebook front end until DoModal ends. To allow the notebook front end and Java to share the kernel's attention, you use ShareKernel.
RealTimeAlgebraModeless returns immediately after the window is displayed, leaving the front end and the RealTimeAlgebra window able to use the kernel for computations.
You still need a little bit of polish on the modeless version, however. First, to avoid leaking object references, you must change sliderFunc. With the modal version, you did not bother to use JavaBlock or ReleaseJavaObject in sliderFunc because you had DoModal wrapped in JavaBlock. Every call to sliderFunc, or any other MathListener handler function, occurs entirely within the scope of DoModal, so you can handle all object releasing at this level. With a modeless interface, you no longer have a single function call that spans the lifetime of the window. Thus, you put memory-management functions in our handler functions. Here is the new sliderFunc.
The JavaBlock here is unnecessary because the code it wraps creates no new object references. Out of habit, though, you wrap these handlers in JavaBlock. You need to explicitly call ReleaseJavaObject on evt, which is the AdjustmentEvent object, because its reference is created in the Wolfram Language before sliderFunc is entered, so it will not be released by the JavaBlock. The type and scrollPos arguments are integers, not objects.
Try setting the input text to Print[a]. Notice that nothing appears in the front end when you move the slider, in contrast to the modal case. With a modeless interface, the Java link is the kernel's $ParentLink during the times when the kernel is servicing a request initiated from the Java side. Thus, the output from Print and graphics goes to Java, not the notebook front end. (The Java side ignores this output, in case you are wondering.) To get this output sent to the front end instead, use ShareFrontEnd.
Now if you set the input text to, say, Print[a] or Plot[a x,{x,0,a}], you will see the text and graphics appearing in the front end.
When you are finished, quit RealTimeAlgebra by clicking its close box. Then turn off front end sharing that was turned on in the previous input.
A modal interface is simpler than a modeless one in terms of how it uses the Wolfram Language, and is therefore the preferred method unless you specifically need the modeless attribute. ShareKernel and ShareFrontEnd are complex functions that put the kernel into an unusual state. They work fine, but do not use them unnecessarily.
GraphicsDlg: Graphics and Typeset Output in a Window
It is useful to be able to display Wolfram Language graphics and typeset expressions in your Java user interface, and this is easy to do using J/Link's MathCanvas class. This example demonstrates a simple dialog box that allows the user to type in a Wolfram Language expression and see the output in the form of a picture. If the expression is a plotting or other graphics function, the resulting image is displayed. If the expression is not a graphic, then it is typeset in TraditionalForm and displayed as a picture. The example is first presented in modal form and then in modeless form using ShareKernel and ShareFrontEnd.
This example also demonstrates a trivial example of using Java code that was created by a drag-and-drop GUI builder of the type present in most Java development environments. For layout of simple windows, it is easy enough to do everything from the Wolfram Language. This method was chosen for all the examples in this tutorial, writing no Java code and instead scripting the creation and layout of controls in windows with Wolfram Language calls into Java. This has the advantage of not requiring any Java classes to be written and compiled. For more complex windows, however, you will probably find it much easier to create the controls, arrange them in position, set their properties in a GUI builder, and let it generate Java code for you. You might also want to write some additional Java code by hand.
If you choose this route, the question becomes, "How do I connect the Java code thus generated with the Wolfram Language?" Any public fields or methods can be called directly from the Wolfram Language, but your GUI builder may not have made public all the ones you need to use. You could make these fields and methods public or add some new public methods that expose them. The latter approach is probably preferable since it does not involve modifying the code that the GUI builder wrote, which could confuse the builder or cause it to overwrite your changes in future modifications.
The complete code for this example is provided in the JLink/Examples/Part1/GraphicsDlg directory. Some of the code is in Java.
This example uses the GUI builder in the WebGain Visual Café Java development environment. The builder was used to create a frame window with three controls. The frame window was made to be a subclass of MathFrame because you want to inherit the setModal() method. In the top left is an AWT TextArea that serves as the input box for the expression. To its right is an Evaluate button. Occupying the rest of the window is a MathCanvas.
Up to this point, no code has been written by hand at all—everything has been done automatically as components were dropped into the frame and their properties set. All that is left to do is to wire up the button so that when it is clicked the input text is taken and supplied as to the MathCanvas via its setMathCommand() method. You could write that code in Java, using Visual Café's Interaction Wizard to wire up this event (similar facilities exist in other Java GUI builders). You would have to write some Java code by hand, as the code's logic is more complex than can be handled by graphical tools for creating event handlers.
Rather than doing that, move to the Wolfram Language to script the rest of the behavior because it is easier and more flexible. You will need to access the TextArea, Button, and MathCanvas objects from the Wolfram Language, but the GUI builder made these nonpublic fields of the frame class. Thus, you need to add three public methods that return these objects to the frame class.
public Button getEvalButton() {return evalButton;}
public TextArea getInputTextArea() {return inputTextArea;}
public MathCanvas getMathCanvas() {return mathCanvas;}
That is all you need to do to the Java code created by the GUI builder.
The GUI builder created a subclass of MathFrame that is named GraphicsDlg. It also gave it a main() method that does nothing but create an instance of the frame and make it visible. You will not bother with the main() method, choosing instead to do those two steps manually, since you need a reference to the frame.
Needed before the code is run is a demonstration of one more feature of J/Link—the ability to add directories to the class search path dynamically. You need to load the Java classes for this example, but they are not on the Java class path. With J/Link, you can add the directory in which the classes reside to the search path by calling AddToClassPath. This will work exactly as written in Mathematica 4.2 and later. You will need to modify the path if you have an earlier version of the Wolfram Language.
Here is the first implementation of the Wolfram Language code to create and run the graphics dialog. This runs the dialog in a modal loop.
As mentioned in the section "Creating Windows and Other User Interface Elements" only the notebook front end can perform the feat of taking a typeset (i.e. "box") expression and creating a graphical representation of it. Thus, the MathCanvas can render typeset expressions provided that it has a front end available to farm out the chore of creating the appropriate representation. The front end is used to run this example, but it is really because you are running the Java dialog "modally" that everything works the way it does. All the while the dialog is up, the front end is waiting for a result from a computation (DoModal[]), and therefore it is receptive to requests from the kernel for various services. As far as the front end is concerned, the code for DoModal invoked the request for typesetting, even though it was actually triggered by clicking a Java button.
What if you are not happy with the restriction of running the dialog modally? Now you want to have the dialog remain open and active while not interfering with normal use of the kernel from the front end. As discussed in "Modal Windows" and "Real-Time Algebra: A Mini-Application", you get a lot of useful behavior regarding the front end for free when you run your Java user interface modally. One of these features is that the front end is kept receptive to the various sorts of requests the kernel can send to it (such as for typesetting services). You know you can run a Java user interface in a "modeless" way by using ShareKernel, but then you give up the ability to have the kernel use the front end during computations initiated by actions in Java. Luckily, the ShareFrontEnd function exists to restore these features for modeless windows.
Re-implement the graphics dialog in modeless form.
The code shown is exactly the same as DoGraphicsDialogModal except for the last few lines. You call ShareFrontEnd here instead of setModal and DoModal. That is the only difference—the rest of the code (including btnFunc) is exactly the same. Notice also that you use the onClose() method of MathCanvas to execute code that unregisters the request for front end sharing when the window is closed.
Run the modeless version. Note how you can continue to perform computations in the front end while the window is active.
This new version functions exactly like the modeless version except that it does not leave the front end hanging in the middle of a computation. It is interesting to contrast what happens if you turn off front end sharing (but you need to leave kernel sharing on or the Java dialog will break completely). You can do this by replacing ShareFrontEnd and UnshareFrontEnd in DoGraphicsDialogModeless with ShareKernel and UnshareKernel. Now if you use the dialog you will find that it fails to render typeset expressions, producing just a blank window, but it still renders graphics normally (unless they have some typeset elements in them, such as a plot label). All the functionality is kept intact except for the ability of the kernel to make use of the front end for typesetting services.
BouncingBalls: Drawing in a Window
This example demonstrates drawing in Java windows using the Java graphics API directly from the Wolfram Language. It also demonstrates the use of the ServiceJava function to periodically allow event handler callbacks into the Wolfram Language from Java. The issues surrounding ServiceJava and how it compares to DoModal and ShareKernel are discussed in greater detail in "'Manual' Interfaces: The ServiceJava Function".
The full code is a little too long to include here in its entirety, but it is available in the sample file BouncingBalls.nb in the JLink/Examples/Part1 directory. Here is an excerpt that demonstrates the use of ServiceJava.
...
mwl = JavaNew["com.wolfram.jlink.MathWindowListener"];
mwl@setHandler["windowClosing", "(keepOn = False)&"];
mathCanvas@addWindowListener[mwl];
keepOn = True;
While[keepOn,
g@setColor[bkgndColor];
g@fillRect[0, 0, 300, 300];
drawBall[g, #]& /@ balls;
mathCanvas@setImage[offscreen];
balls = recomputePosition /@ balls;
ServiceJava[]
];
...
A MathWindowListener is used to set keepOn=False when the window is closed, which will cause the loop to terminate. While the window is up, mouse clicks will cause new balls to be created, appended to the balls list, and set in motion. This is done with a MathMouseListener (not shown in the code). Thus, the Wolfram Language needs to be able to handle calls originating from user actions in Java. As discussed in the section "Creating Windows and Other User Interface Elements", there are three ways to enable the Wolfram Language to do this: DoModal (modal interfaces), ShareKernel or ShareFrontEnd (modeless interfaces), and ServiceJava (manual interfaces). A modal loop via DoModal would not be appropriate here because the kernel needs to be computing something at the same time it is servicing calls from Java (it is computing the new positions of the balls and drawing them). ShareKernel would not help because that is a way to give Java access to the kernel between computations triggered from the front end, not during such computations.
You need to periodically point the kernel's attention at Java to service requests if any are pending, then let the kernel get back to its other work. The function that does this is ServiceJava, and the code above is typical in that it has a loop that calls ServiceJava every time through. The calls from Java that ServiceJava will handle are the ones from mouse clicks to create new balls and when the window is closed.
Spirograph
This example is just a little fun to create an interesting, nontrivial application—an implementation of a simple Spirograph-type drawing program. It is run as a modal window, and it demonstrates drawing into a Java window from the Wolfram Language, along with a number of MathListener objects for various event callbacks. It uses the Java Graphics2D API, so it will not run on systems that have only a Java 1.1.x runtime.
The code for this example can be found in the file Spirograph.nb in the JLink/Examples/Part1 directory.
One of the things you will notice is that on a reasonably fast machine, the speed is perfectly acceptable. There is nothing to suggest that the entire functionality of the application is scripted from the Wolfram Language. It is very responsive despite the fact that a large number of callbacks to the Wolfram Language are triggered. For example, the cursor is changed as you float the mouse over various regions of the window (it changes to a resize cursor in some places), so there is a constant flow of callbacks to the Wolfram Language as you move the mouse. This example demonstrates the feasibility of writing a sophisticated application entirely in the Wolfram Language.
This application was written in the Wolfram Language, but it could also have been written entirely in Java, or a combination of Java and the Wolfram Language. An advantage of doing it in the Wolfram Language is that you generally can be much more productive. Spirograph would have taken at least twice as long to write in Java. It is invaluable to be able to write and test the program a line at a time, and to debug and modify it while it is running. Even if you intend to eventually port the code to pure Java, it can be very useful to begin writing it in the Wolfram Language, just to take advantage of the scripting mode of development.
Modal programs like this are best developed using ShareFrontEnd, then made modal only when they are complete. Making it modeless while it is being developed is necessary to be able to build and debug it interactively, because while it is running you can continue to use the front end to modify the code, make new definitions, add debugging statements, and so on. Using ShareFrontEnd instead of ShareKernel for modeless operation lets Wolfram System error and warning messages generated by event callbacks, and Print statement inserted for debugging, show up in the notebook window. Only when everything is working as desired do you add the DoModal[] call to turn it into a modal window.
A Piano Keyboard
With the inclusion of the Java Sound API in Java 1.3 and later, it becomes possible to write Java programs that do sophisticated things with sound, such as playing MIDI instruments. The Piano.nb example in the JLink/Examples/Part1 directory displays a keyboard and lets you play it by clicking the mouse. A popup menu at the top lists the available MIDI instruments. This example was created precisely because it is so far outside the limitations of traditional Wolfram Language programming. Using J/Link, you can actually write a short and completely portable program, entirely in the Wolfram Language, that displays a MIDI keyboard and lets you play it! With just a little more work, the code could be modified to record a sequence played and then return it to the Wolfram Language, where you could manipulate it by transposing, altering the tempo, and so on.