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.
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.
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.
There is no support for static linking of foreign resources containing Java declarations, since Java implementations usually do not support static linking.
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)
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)
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.
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
int
.
Prolog: +byte
Java: byte
byte
.
Prolog: +short
Java: short
short
.
Prolog: +long
Java: long
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
float
.
Prolog: +double
Java: double
double
.
Prolog: +term
Java: SPTerm
SPTerm
.
Prolog: +object(Class)
Java: SPTerm
Class
.
Prolog: +atom
Java: SPTerm
SPTerm
.
Prolog: +boolean
Java: boolean
true
,false
}. The Java
method will receive a boolean
.
Prolog: +chars
Java: String
String
.
Prolog: +string
Java: String
String
.
Prolog: -atom
Java: SPTerm
SPTerm
which can be modified. The argument will
be bound to the value of the atom when the method returns.
Prolog: -chars
Java: StringBuffer
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
StringBuffer
which can be modified. The
argument will be bound to an atom converted from the StringBuffer
object.
Prolog: [-integer]
Java: int M()
int
. The value will be converted
to a Prolog integer.
Prolog: [-byte]
Java: byte M()
byte
. The value will be converted
to a Prolog integer.
Prolog: [-short]
Java: short M()
short
. The value will be converted
to a Prolog integer.
Prolog: [-long]
Java: long M()
long
. The value will be converted
and possibly truncated to a Prolog integer.
Prolog: [-float]
Java: float M()
float
. The value will be converted
to a Prolog float.
Prolog: [-double]
Java: double M()
double
. The value will be converted
to a Prolog float.
Prolog: [-term]
Java: SPTerm M()
SPTerm
which
will be converted to a Prolog term.
Prolog: [-object(Class)]
Java: SPTerm M()
Prolog: [-atom]
Java: SPTerm M()
SPTerm
which
will be converted to a Prolog atom.
Prolog: [-boolean]
Java: boolean M()
boolean
. The value will be converted to
a Prolog atom in {true
,false
}.
Prolog: [-chars]
Java: String M()
String
which
will be converted to a list of character codes.
Prolog: [-string]
Java: String M()
String
which
will be converted to an atom.
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 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:
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.
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.
load
. Corresponds to SP_load()
in the
C-interface. See section Loading Prolog Code.
SPPredicate
class:
SPPredicate pred = new SPPredicate(sp, "connected", 4, "");
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();
query(pred, args)
connected/4
this is not the
case; there are more than one solution.
queryCutFail(pred, args)
query()
it only finds the first solution, and
then it cuts away all other solutions and fails.
openQuery(pred, args)
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.
Detailed documentation of the classes in the jasper
package can
be found at
http://www.sics.se/isl/sicstus/jasper/Package-jasper.htmlOr follow this hyperlink.
This documentation will most probably be included in a future version of the manual.
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).
There are almost infinitely many Java resources on the Internet. Here is a list of a few which are related to Jasper:
library('jasper/examples')
See the file `README' for details on how to use the examples.
http://java.sun.com
http://www.yahoo.com/Computers_and_Internet/Programming_Languages/Java/
http://java.sun.com/products/jdk/1.1/docs/guide/jni/index.html
http://www1.acm.org:82/crossroads/xrds4-2/jni.html
Go to the first, previous, next, last section, table of contents.