Go to the first, previous, next, last section, table of contents.


Mixing Java and Prolog

Jasper is a bi-directional interface between programs written in Java and programs written in Prolog. The Java-side of the interface constists of a Java package (jasper) containing classes representing the SICStus emulator. The Prolog part is designed as an extension to the foreign language interface, which means that foreign/3 declarations are used to map Java-methods on Prolog predicates and provide automatic conversion of arguments.

Jasper can be operated in two different ways, depending on which emulator is used as top-level application; the Java Runtime System or the SICStus Development System. When the Java Runtime System is used as a top-level application, the SICStus runtime kernel is loaded into the Java Runtime System. When the Java Runtime System is loaded into the SICStus Development System, it is loaded as any other foreign resource, i.e. by using load_foreign_resource/1. This distinction is not visible to the programmer, but it is useful to have a picture of how the interface works.

Note: Some of the information in this chapter is a recapitulation of the information in the chapter section Mixing C and Prolog. The intention is that this chapter should be possible to read fairly independently.

Prerequisites

The low-level interface uses the JNI (Java Native Interface) to call C functions from Java. The JNI is a native interface standard developed by Sun Microsystems. See section Resources. Since the interface uses the JNI, it is necessary that the Java VM which is to be used has support for JNI. Even though the JNI is intended as a standard, not all vendors support it. If your Java installation does not support the JNI, Sun's Java Runtime Environment provides a minimal execution enviroment for running Java code. It can be downloaded from

http://java.sun.com/products/jdk/1.1/jre/index.html

The rest of this chapter assumes that there is a Java installation with JNI support available.

Calling Java from Prolog

Java methods are called from Prolog much in the same way as C functions are called (see section Calling C from Prolog); by creating a foreign resource. When loaded, this resource installs a set of predicates which are mapped onto Java-methods and when invoked, converts the Prolog arguments to the corresponding Java-types before calling the Java method itself.

In fact, a foreign resource (as defined in section Foreign Resource and Conversion Declarations) is not language specific itself. The language is instead specified in the second argument to the foreign/3 fact and it is possible to mix foreign C functions with foreign Java methods.

How a foreign resource is created in general is described in detail in section Creating the Linked Foreign Resource. The following section(s) will focus on the Java-specific parts of foreign resources.

Static and Dynamic Linking

There is no support for static linking of foreign resources containing Java declarations, since Java implementations usually do not support static linking.

Declarating Java-methods

Java-methods are declared similarly to C-functions. There are two major differences. The first is how methods are identified. It is not enough to simply use an atom as the C interface does. Instead, a term method/3 is introduced:

method(+ClassName,+MethodName,+Flags)
Used as first argument to foreign/3 when declaring Java methods. The first argument is an atom containing the Fully Qualified Classname of the class. The second argument is the method name. The third argument is a list of flags. Possible flags are instance or static, indicating whether or not the method is static or non-static. Non-static methods must have an object-reference as their first argument. This is a reference to the object on which the method will be invoked. This term is then used to identify the method in the foreign_resource/2 predicate. So, to define a foreign resource exporting the non-static Java method getFactors in the class PrimeNumber in the package numbers, the method/3 term would look like
method('numbers/PrimeNumber','getFactors',[instance])

The syntax for foreign/3 is the basically the same as for C-functions:

foreign(+MethodIdentifier, java, +Predicate)
A hook predicate, specifies the Prolog interface to a Java method. MethodIdentifier is method/3 term as described above. Predicate specifies the name of the Prolog predicate that will be used to call MethodIdentifier. Predicate also specifies how the predicate arguments are to be translated into the corresponding Java arguments.

Conversions between Prolog Arguments and Java Types

The following table lists the possible values of arguments of the predicate specification to foreign/3. The value declares which conversion between corresponding Prolog argument and Java type will take place.

Note: The conversion declarations (composed of the declarators specified below) together with the method/3 term are used by the glue-code generator to create the method's type signature, i.e. a string which can uniquely identify a method within a class. This means that unlike the C interface, the conversion declarations for a Java method will affect the lookup of the method-name (in the C interface, only the function name is relevant). So, if a method is declared as foo(+integer), there must be a method which has the name foo and takes one argument of type int, or an argument which can be automatically converted to an int (a short, for example).

Prolog: +integer
Java: int
The argument should be a number. It is converted to a Java int.
Prolog: +byte
Java: byte
The argument should be a number. It is converted to a Java byte.
Prolog: +short
Java: short
The argument should be a number. It is converted to a Java short.
Prolog: +long
Java: long
The argument should be a number. It is converted to a Java long. Note: Since Java's long type is 64 bits wide and there is no standardized support for 64 bits integers in C, the value will be truncated. So, this declaration is really only useful in order to indicate which method should be used. For example:
class Bar
{
  void foo(int x)
    { ... }

  void foo(long x)
    { ... }
}
In order to be able to indicate that the latter of the foo methods should be called, a +long declaration must be used, even if the value itself will be truncated in the call.
Prolog: +float
Java: float
The argument should be a number. It is converted to a Java float.
Prolog: +double
Java: double
The argument should be a number. It is converted to a Java double.
Prolog: +term
Java: SPTerm
The argument can be any term. It is passed to Java as an object of the class SPTerm.
Prolog: +object(Class)
Java: SPTerm
The argument should be the Prolog representation of a Java object of class Class. Unless it is the first argument in a non-static method (in which case is it treated as the object on which the method should be invoked), it is passed to the Java method as an object of class Class.
Prolog: +atom
Java: SPTerm
The argument should be an atom. The Java method will be passed an object of class SPTerm.
Prolog: +boolean
Java: boolean
The argument should be an atom in {true,false}. The Java method will receive a boolean.
Prolog: +chars
Java: String
The argument should be a list of character codes. The Java method will receive an object of class String.
Prolog: +string
Java: String
The argument should be an atom. The Java method will receive an object of class String.
Prolog: -atom
Java: SPTerm
The argument should be an unbound variable. The Java method will receive an atom of class SPTerm which can be modified. The argument will be bound to the value of the atom when the method returns.
Prolog: -chars
Java: StringBuffer
The argument should be an unbound variable. The Java method will receive an object of type StringBuffer which can be modified. The argument will be bound to a list of the character codes of the StringBuffer object.
Prolog: -string
Java: StringBuffer
The argument should be an unbound variable. The Java method will receive an object of type StringBuffer which can be modified. The argument will be bound to an atom converted from the StringBuffer object.
Prolog: [-integer]
Java: int M()
The Java method should return an int. The value will be converted to a Prolog integer.
Prolog: [-byte]
Java: byte M()
The Java method should return a byte. The value will be converted to a Prolog integer.
Prolog: [-short]
Java: short M()
The Java method should return a short. The value will be converted to a Prolog integer.
Prolog: [-long]
Java: long M()
The Java method should return a long. The value will be converted and possibly truncated to a Prolog integer.
Prolog: [-float]
Java: float M()
The Java method should return a float. The value will be converted to a Prolog float.
Prolog: [-double]
Java: double M()
The Java method should return a double. The value will be converted to a Prolog float.
Prolog: [-term]
Java: SPTerm M()
The Java method should return an object of class SPTerm which will be converted to a Prolog term.
Prolog: [-object(Class)]
Java: SPTerm M()
The Java method should return an object of class Class which will be converted to the internal Prolog representation of the Java object.
Prolog: [-atom]
Java: SPTerm M()
The Java method should return an object of class SPTerm which will be converted to a Prolog atom.
Prolog: [-boolean]
Java: boolean M()
The Java should return a boolean. The value will be converted to a Prolog atom in {true,false}.
Prolog: [-chars]
Java: String M()
The Java method should return an object of class String which will be converted to a list of character codes.
Prolog: [-string]
Java: String M()
The Java method should return an object of class String which will be converted to an atom.

Calling Java from Prolog: An Example

The following is an simple, but complete example of how a Java method can be called from Prolog.

First, we must write the resource file. Let us call it `simple.pl'.

% File: simple.pl

:- module(simple, [simple/2]).

:- use_module(library(jasper)).
:- load_foreign_resource(simple).

foreign(method('Simple', 'simpleMethod', [static]), java,
        simple(+integer,[-integer])).

foreign_resource(simple,
                 [
                  method('Simple', 'simpleMethod', [static])
                 ]).

This file is the processed with the script splfr (see section Interface Predicates) to produce a foreign resource:

% splfr simple simple.pl
SICStus  3.7: Mon Mar  2 19:22:44 MET 1998
{/var/tmp/aaaa004Cd.c generated, 20 msec}

yes

Note that we do not specify any Java files to splfr as we would specify C files when building foreign resources for C code. This is because the C code can be compiled into the resource itself, while the Java code must be loaded at runtime into the JVM. This means that the resource will only contain the glue-code for calling the JVM, and no actual Java code. Hence, these resources are usually quite small.

Note also that the use of +dynamic is implicit and obligatory; resources with Java-calls must be dynamic.

Now, we need some Java code to call:

Simple.java:

public class Simple
{
  static int simpleMethod(int value)
  {
    return value*42;
  }
}

This Java code must now be compiled. Refer to the documentation of your Java implementation exactly how to do this. On Solaris, this might look like:

% javac Simple.java

Now we are ready to call the method simple/2 from inside SICStus.

% sicstus
SICStus  3.7: Mon Mar  2 19:22:44 MET 1998
| ?- compile(simple).
{compiling ...}
[...]
{compiled ... simple.pl in module simple, 160 msec 48640 bytes}

yes
| ?- simple(17,X).

X = 714 ? 

yes
| ?- 

What has happened is that the predicate simple/2 has been installed as a predicate defined in Java (this is not exactly true; the predicate is defined as a C-function which calls the Java method). When we load the simple module, we will first load the jasper module (and thereby the JVM) and then load the simple foreign resource, which defines the simple/2 predicate.

Calling Prolog from Java

Calling Prolog from Java is done by using the Java package jasper. This package contains a set of Java classes which can be used to create and manipulate terms, ask queries and request one or more solutions. The functionality provided by this set of classes is basically the same as the functionality provided by the C-Prolog interface (see section Mixing C and Prolog).

The usage is easiest described by an example. The following is a Java version of the train example. See section Train Example (connections).

import jasper.*;

public class Simple
{
  public static void main(String argv[]) {

  SICStus sp;
  SPPredicate pred;
  SPTerm from, to, way;
  SPQuery query;
  int i;
    
  try 
    {
      sp = new SICStus(argv,null);

      sp.load("train.ql");
    
      pred = new SPPredicate(sp, "connected", 4, "");
      to = new SPTerm(sp, "Orebro");
      from = new SPTerm(sp, "Stockholm");
      way = new SPTerm(sp).putVariable();

      query = sp.openQuery(pred, new SPTerm[] { from, to, way, way });
    
      while (query.nextSolution())
        {
          System.out.println(way.toString());
        }
    }
  catch ( Exception e )
    {
      e.printStackTrace();
    }
  }
}

This is how it works:

  1. First, we import all the jasper-classes. To find them, the class-path should be set to contain the path to the SICStus libraries ($SP_PATH/library). How this is done depends on the operating system and which Java implementation is used, but typically there is a environment variable CLASSPATH which contains the path. The path can often also be set on the commandline using an option such as `-classpath'. Refer to your Java documentation for more details.
  2. Before any predicates can be called, the SICStus emulator must be initialized. This is done by instantiating the SICStus class. NOTE: This class must only be instantiated once per Java process. Multiple SICStus-objects are not supported. Most methods take a reference to this object as their first argument. This is implicit in the rest of this chapter, unless otherwise stated.
  3. The next step is to load the Prolog code. This is done by the method load. Corresponds to SP_load() in the C-interface. See section Loading Prolog Code.
  4. Now, everything is set up to start making queries. In order to make a query, the actual query term must be created. This is done by creating an object of the SPPredicate class:
    SPPredicate pred = new SPPredicate(sp, "connected", 4, "");
    
  5. At this point, we have created a predicate object for the predicate connected/4. It is now time to create the arguments for the query. The arguments are placed in an array which is passed to a suitable method to make the query. The arguments consist of objects of the class SPTerm. For example, if we need two atoms and a variable for the query
    | ?- connected('Stockholm', 'Orebro', X, X).
    
    the following Java code will do it for us:
    to = new SPTerm(sp, "Orebro");
    from = new SPTerm(sp, "Stockholm");
    way = new SPTerm(sp).putVariable();
    
  6. Now it is time to make the query. As in the C-Prolog interface, there are three ways of making a query.
    query(pred, args)
    This method is useful if you are only interested in finding the first solution to a goal. In the case of connected/4 this is not the case; there are more than one solution.
    queryCutFail(pred, args)
    This method is useful if you are only interested in the side-effects of the query. As query() it only finds the first solution, and then it cuts away all other solutions and fails.
    openQuery(pred, args)
    This method is useful when you are interested in some or all solutions to the query. Since the connected/4 may give us multiple solution, this is what we will use.
    SPQuery query;
    
    query = sp.openQuery(pred, new SPTerm[] { from, to, way, way });
        
    while (query.nextSolution())
      System.out.println(way.toString());
    
    The openQuery method returns a reference to the query, an object of the SPQuery class. To obtain solutions, the method nextSolution() is called with no arguments. nextSolution returns true as long as there are more solutions, the example above will print the value of way until there are no more solutions.

Jasper Package Class Reference

Detailed documentation of the classes in the jasper package can be found at

http://www.sics.se/isl/sicstus/jasper/Package-jasper.html
Or follow this hyperlink.

This documentation will most probably be included in a future version of the manual.

Exception Handling

Exceptions are handled seamlessly between Java and Prolog. This means that exceptions can be thrown in Prolog and caught in Java and the other way around. For example, if a predicate called from Java raises an exception by raise_exception/1 and the predicate itself does not catch the exception, the Java-method which performed the query, queryCutFail() for example, will throw an exception containing the exception term. Symmetrically, a Java-exception thrown (and not caught) in a method called from Prolog will cause the corresponding predicate (simple/2 in the example above) to raise an exception containing the exception object (in the internal Prolog representation of a Java object).

Resources

There are almost infinitely many Java resources on the Internet. Here is a list of a few which are related to Jasper:


Go to the first, previous, next, last section, table of contents.