Java Class
Java is designed to be an object oriented language
for deploying secure, multithreaded network applications (both clients
and servers). Prolog is ideal for building intelligent components, expert
systems and logic-bases. In combination, Java and Prolog are an ideal pair
for delivering useful intelligent applications on the Internet. The Java
Class encapsulates the Amzi! Logic Server for use by Java applications
and applets. It includes:
-
A Java Class Package the encapsulates a Logic Server Engine
-
Methods that correspond to the Logic Server API Functions
-
Use of Java's exception handling for API errors, and
-
Java-like method interfaces.
In addition, you can extend the Java Class to allow Prolog to call methods
in your Java code.
Where to Learn
About Java
If you want to learn more about Java and Java Applets and Servlets,
see java.sun.com. You can also download Sun's Java Development Kit (JDK) from
there.
Installation
The Java Class and its samples are distributed for JDK
2.0 (1.2).
To use the Java Class, you must make these files accessible to the calling
environment:
- Amzi! Dynamic/Shared Library - The Amzi! Logic Server, amzi.dll
or libamzi.so, and the Amzi! Java interface, amzijni.dll, must be in your
PATH or otherwise accessible to the calling environment. These files are in
the amzi/\bin directory.
- Amzi! Java Classes - The amzi/ls directory structure, containing
the amzi.ls.* package must be accessible via your CLASSPATH. You can do this
by adding amzi/lsapis/java20 to your CLASSPATH or copying the amzi\ls subdirectory
structure to a directory already in your CLASSPATH.
Do not put any other
files in your amzi/ls directory as they will be included when you import amzi.ls.*;
and may cause naming conflicts.
Hello Prolog
To make sure the Amzi! Java interface is ready
to use, run the Amzi! Hello sample for Java. It is in the directory amzi5\samples\java\hello.
To build the Hello program from Java, first open a 'DOS' window and change
to the sample directory containing Hello.java. To compile it, type:
javac Hello.java
This will produce Hello.class which is simply run by typing:
java Hello
Overview
The Java Class is implemented using the Java Native
Interface (JNI). This allows methods used in a Java class to be implemented
in C/C++. In the case of Amzi!, the JNI is used to build a bridge to the
Logic Server API, which is the external interface on the Amzi! Prolog engine.
The bridge is necessary because many of the Logic Server API functions
have to be changed slightly to conform to Java parameter passing and return
conventions (such as accommodating the lack of pointer support in Java).
So, the Amzi! Java interface defines a Java class, LogicServer, whose methods
are implemented by calls to C++ functions in a dynamic/shared libary that, in
turn, calls the main Amzi! engine dynamic/shared library. This is illustrated
in Figure 1.
Figure 1
Working right to left in the diagram, amzi.dll is the Amzi! Logic Server (Prolog
engine). amzijni.dll/libamzijni.so is the interface library that implements
the Java versions of Logic Server API functions. Those, in turn are wrapped
in the Java classes, LogicServer and LSException.
There are a number of features of the Java class that were dictated
by the distinct characteristics of Java. These are detailed in the following
sections.
Object Oriented
The Amzi! Logic Server is implemented as an object oriented
program, so that each Logic Server is an object and the Logic Server API
functions are methods of that object. It is natural, therefor, to provide
object oriented interfaces to the Logic Server for object oriented languages
such as Java. (Amzi! is also available in a C++ and Delphi class.) The
Logic Server class can then be used as any other class in an object oriented
application, supporting, for example, subclassing, embedding and multiple
instances. This makes for an elegant approach to encapsulating Prolog services
in applications. (see "Objects and Logic-C++
Meets Prolog", PC AI May/Jun 95)
Figure 2 is an expanded architecture diagram that illustrates the package
and its classes.
Figure 2
Pointerless Methods
The biggest challenge in the design of the Amzi! Java
class was the requirement for pointerless methods. The creators of Java
wanted to keep their language simple, so they did not include support for
pointers, specifically in function parameter lists. For this reason the
Java methods had to be implemented so they return a single value.
A fundamental data type for the Prolog interface is a Prolog term. Internally,
a Prolog term is a pointer, but, since that pointer is not manipulated
by the application, it can be stored as an integer. For Java, it is stored
in a 64-bit integer so Java can support both 64- and 32-bit versions of
the Amzi! Prolog engine.
Many Logic Server API (LSAPI) functions return error codes, and use
argument pointers to pass values back and forth. The Java class, like other
Amzi! lsapis, is designed to use exception handling for errors, with
return values passed directly as return values, without using pointers.
There are a number of Java LSAPI functions that differ from the corresponding
base LSAPI because of lack of pointers. These are discussed in the following
sections.
Issuing Prolog
Queries
The base LSAPI functions that issue Prolog
queries return TRUE or FALSE, corresponding to Prolog success or failure,
and use a pointer to the calling Prolog term to pass back the term resulting
from the query. For example a function issuing the query 'available(com,
Port)' will return true or false plus the term representing the query with
the Port variable unified with the result.
For Java, the query LSAPI functions (Exec, ExecStr, Call, CallStr) return
the term (a long) directly, instead of a true false. If the query fails,
that is indicated by a zero (0) value returned. (Errors are indicated by
LSExceptions.)
String Conversion
The Java LSAPI automatically converts
the internal Unicode Java representation of character strings to and from
the Amzi! internal Unicode representation.
Exceptions
Instead of returning error codes, all the
LogicServer methods use Java's exception mechanism. The LSException class
is thrown when an error occurs. LSException contains a number of methods
for learning details about the exceptions.
Multi-Threaded
Java allows you to start multiple threads
in the same program, and Amzi! supports multiple simultaneous Prolog engines.
So each instance of the Java Logic Server class will contain its own Prolog
runtime environment.
Using the Java Class
To use the LogicServer class you import the amzi.ls
package into your Java program:
import amzi.ls.*;
From there you can either instantiate a new LogicServer object and invoke
its methods, or you can define a new class that extends the LogicServer
class adding new methods and variables.
LogicServer
and LSException Methods
The LogicServer class includes all the methods that
give the developer full control over the Prolog engine. These include methods
for:
The LSException class has no methods and is simply used to signal and catch
errors as described in the section on 'Exceptions.'
Logic Server Set-Up
These
methods provide the basic API services. They are used to initialize and
close down the Prolog environment. Most are similar to their base LSAPI
equivalents, with the exception of AddPred, which is used to defined extended
predicates.
For AddPred, the Java method that implements the predicate is defined
with two string parameters, giving the class and method names, and one
Object reference for the particular instance of the object that will be
used for the functions. (AddPred does not allow the use of static methods
for extended predicates.) See the samples for an example of its use.
public native void Init(String ININame) throws LSException;
public native void Init2(String INIParms) throws LSException;
public native void InitLSX(long Arg) throws LSException;
public native void AddLSX(String LSXName, long Arg) throws LSException;
public native void AddPred(String PredName, int Arity,
String Class, String Method, Object Obj) throws LSException;
public native void Load(String XPLName) throws LSException;
public native boolean Main() throws LSException;
public native void Reset() throws LSException;
public native void Close() throws LSException;
Calling Prolog
These
are the methods that actually call predicate in a Prolog module. The query
term can be represented as a string or a Prolog term. The methods return
the term representing the result, or the value 0 if the query fails.
public native long Exec(long Term) throws LSException;
public native long ExecStr(String Query) throws LSException;
public native long Call(long Term) throws LSException;
public native long CallStr(String Query) throws LSException;
public native boolean Redo() throws LSException;
public native void ClearCall() throws LSException;
Dynamic
Database
These
methods make it easy to assert and retract terms to and from Prolog's dynamic
database.
public native void Asserta(long Term) throws LSException;
public native void Assertz(long Term) throws LSException;
public native long Retract(long Term) throws LSException;
public native void AssertaStr(String TermStr) throws LSException;
public native void AssertzStr(String TermStr) throws LSException;
public native boolean RetractStr(String TermStr) throws LSException;
Converting Strings/Terms
These
methods convert strings to terms and terms to strings. The 'Q' version
create quoted strings when necessary for atoms and strings that require
delimiter symbols. They are necessary for those cases when you want to
use the resulting string in another query.
TermToStr is especially useful during development to display the results
of a Prolog query.
public native String TermToStr(long Term, int Len) throws LSException;
public native String TermToStrQ(long Term, int Len) throws LSException;
public native long StrToTerm(String TermStr) throws LSException;
public native int StrTermLen(long Term) throws LSException;
Making/Getting
Prolog Types
These
methods map Prolog types to Java types.
public native long MakeAtom(String AtomStr) throws LSException;
public native long MakeStr(String Str) throws LSException;
public native long MakeInt(int Num) throws LSException;
public native long MakeFloat(double Num) throws LSException;
public native int GetTermType(long Term) throws LSException;
public native String GetStrTerm(long Term) throws LSException;
public native int GetIntTerm(long Term) throws LSException;
public native double GetFloatTerm(long Term) throws LSException;
Manipulating
Structures
These
methods let you create and take apart terms that represent structures.
This is especially useful for retrieving arguments in a query. For example,
for the query 'sibling(mary, X)' GetStrArg can be used to retrieve the
second argument of the strucutre.
public native String GetFunctor(long Term) throws LSException;
public native int GetArity(long Term) throws LSException;
public native long MakeFA(String Functor, int Arity) throws LSException;
public native long GetArg(long Term, int Num) throws LSException;
public native String GetStrArg(long Term, int Num) throws LSException;
public native int GetIntArg(long Term, int Num) throws LSException;
public native double GetFloatArg(long Term, int Num) throws LSException;
public native long UnifyStrArg(long Term, int Num, String Str) throws LSException;
public native long UnifyIntArg(long Term, int Num, int Val) throws LSException;
public native long UnifyFloatArg(long Term, int Num, double Val) throws LSException;
public native int GetArgType(long Term, int Num) throws LSException;
public native int StrArgLen(long Term, int Num) throws LSException;
Manipulating Parameters
for Extended Predicates
These
methods let you get and set (unify) the parameters that are passed with
a Prolog extended predicate. The term parameter is used to distinguish
them from functor arguments, although in pure Prolog terminology the parameters
are simply the arguments of the functor of the extended predicate. For
example, if you implemented an extended predicate call get_url/1 in Java,
these predicates would let you unify a URL with the first parameter of
that predicate.
public native int GetParmType(int iarg) throws LSException;
public native long GetParm(int iarg) throws LSException;
public native String GetStrParm(int iarg) throws LSException;
public native int GetIntParm(int iarg) throws LSException;
public native double GetFloatParm(int iarg) throws LSException;
public native boolean UnifyParm(int iarg, long Term) throws LSException;
public native boolean UnifyStrParm(int iarg, String s) throws LSException;
public native boolean UnifyIntParm(int iarg, int i) throws LSException;
public native boolean UnifyFloatParm(int iarg, double f) throws LSException;
Manipulating Lists
These
methods let you create Prolog lists, add items to lists and retrieve items
from lists. The Get__Head family of functions can be used in loops to get
all the items in a list (in conjunction with GetTail).
public native long MakeList() throws LSException;
public native long PushList(long ListTerm, long Term) throws LSException;
public native long GetHead(long ListTerm) throws LSException;
public native String GetStrHead(long ListTerm) throws LSException;
public native int GetIntHead(long ListTerm) throws LSException;
public native double GetFloatHead(long ListTerm) throws LSException;
public native long GetTail(long ListTerm) throws LSException;
Error Handling
These methods are all implemented
as part of the LSException class, and let you get details about the exception.
public native int GetType();
public native int GetRC();
public native int GetLineno();
public native String GetMsg();
public native String GetReadFileName();
public native String GetReadBuffer();
public native String GetCallStack();
Miscellaneous
Methods
This method lets you
get the current version number.
public native String GetVersion() throws LSException;
Copyright ©1987-2000 Amzi! inc. All Rights Reserved.