Powered by SmartDoc
ENGLISHJAPANESE

Connecting prolog to Relational Database on Servlet

Why prolog and RDB?

We will conclude this part by showing how to combine prolog and relational databases in Java. One may ask why we should use both prolog and relational databases. We would reply to the question, for example, as follows:

The combination of prolog, RDB, and Java, may develop into an intelligent system with huge supply of data both from RDB and through Internet. We do not know yet how we can exploit the possibility, but the goal sounds attractive, especially to the ear of retired prolog programmers.

The demo story

We demonstrate how prolog works with RDB with an example. Recall the prolog program we examined to test the connection between prolog and Servlet. The program is reproduced below for readers' convenience:

test.pl
child_of( joe, ralf ).
child_of( mary, joe ).
child_of( steve, joe ).

descendent_of( X, Y ) :-
	child_of( X, Y ).
descendent_of( X, Y ) :-
	child_of( Z, Y ),
	descendent_of( X, Z ).

Of the definitions, the first three lines defining the child_of relation will vary, depending on the domain to which you apply the 'descendent_of' program. The relation is therefore a good candidate to encode in RDB. We remove the lines to slim down 'test.pl' program as follows:

test2.pl
descendent_of( X, Y ) :-
	child_of( X, Y ).
descendent_of( X, Y ) :-
	child_of( Z, Y ),
	descendent_of( X, Z ).

The items of information we have removed from the prolog program, test.pl, is obtainable from the database, mysqltest, we built in the previous section to teach you the very basics of MySQL:

The items in child_of table
mysql> use mysqltest;
Database changed

mysql> select * from child_of;
+-------+--------+
| child | parent |
+-------+--------+
| joe   | ralf   |
| mary  | joe    |
| steve | joe    |
+-------+--------+
3 rows in set (0.00 sec)

We will modify the demo system we presented in the beginning of this part, the system that calls prolog to return the name of parent given name of a person. We replace a database connection module for the prolog predicate specifying child_of relation.

Implementing the demo

Handling facts dynamically in prolog

To allow prolog to handle facts dynamically, we have to declare the predicate, child_of, to be dynamic. The program, test2.pl, must be therefore inserted the first line as below:

test2.pl with dynamic declaration
:- dynamic child_of/2.

descendent_of( X, Y ) :-
	child_of( X, Y ).
descendent_of( X, Y ) :-
	child_of( Z, Y ),
	descendent_of( X, Z ).

When you load the program, the definition of child_of is of course missing:

Loading test.pl
programs/proServ2/src> pl
Welcome to SWI-Prolog (Version 3.3.6)
Copyright (c) 1990-2000 University of Amsterdam. 
Copy policy: GPL-2 (see www.gnu.org)

For help, use ?- help(Topic). or ?- apropos(Word).

1 ?- [test2].
% test2 compiled 0.02 sec, 860 bytes

Yes
2 ?- listing.


descendent_of(A, B) :-
	child_of(A, B).
descendent_of(A, B) :-
	child_of(C, B),
	descendent_of(A, C).

Yes

With the declaration, you can insert the set of facts about child_of relation as follows:

Inserting the facts of child_of
3 ?- assert( child_of( joe, ralf ) ).

Yes
4 ?- assert( child_of( mary, joe ) ).

Yes
5 ?- assert( child_of( steve, joe ) ).

Yes
6 ?- listing.

child_of(joe, ralf).
child_of(mary, joe).
child_of(steve, joe).

descendent_of(A, B) :-
	child_of(A, B).
descendent_of(A, B) :-
	child_of(C, B),
	descendent_of(A, C).

Yes

With the facts of child_of, one can correctly calculate who is descendent of Ralf:

Inferring of the descendent of Ralf
7 ?- descendent_of( X, ralf ).

X = joe ;

X = mary ;

X = steve ;

No

After getting the result, you should not forget to clean up the data by executing abolish:

Cleaning up the facts
8 ?- abolish( child_of/2 ).

Yes
9 ?- listing.

descendent_of(A, B) :-
	child_of(A, B).
descendent_of(A, B) :-
	child_of(C, B),
	descendent_of(A, C).

Yes

Retrieving data from RDB

What we need to do to realize the demo story, is to do the same operation as presented in the previous section using Java. Basic idea is to combine the Java program to call prolog, e.g., Test.java presented in the first chapter of this part, and the other to get access to RDB, JDBCSample.java shown above. The whole program, which we call Test2.java, is shown below:

Test2.java
//*****************************************************************************/
//
// This program is drived from Test.java included in the JPL package under 
// 'demo/getting_started'. The original was implemented by Fred Dushin, 
// who developed JPL package. 
//
//*****************************************************************************/

import java.util.Hashtable;
import jpl.Atom;
import jpl.Variable;
import jpl.Term;
import jpl.Query;
import jpl.JPL;
import jpl.Compound;
import jpl.Util;
import jpl.Integer;
import java.io.*;
import java.sql.*;

public class Test2
{
    public static void
    main( String argv[] )
    {
        load_prog(); 
        assert_facts(); 
        test_4();
        abolish_facts();
    }

    static void abolish_facts() {
        Term pred = new Atom( "child_of" );
        Term num = new Integer( 2 );
        Term rem = new Compound( "/", Util.toTermArray( pred, num ) );

        Query query = 
                new Query( 
                          "abolish", 
                              rem );
        query.oneSolution();
    }

    static void
    assert_facts() {
        try {
            Class.forName( "org.gjt.mm.mysql.Driver" );
            String url = "jdbc:mysql://ks15e0f00/mysqltest?user=YourID&password=YourPassword";
            Connection con = DriverManager.getConnection( url );

            Statement select = con.createStatement();
            String sql = "select * from child_of";
            ResultSet res = select.executeQuery( sql );

            while( res.next() ) {
                Term child = new Atom( res.getString( "child" ) );
                Term parent = new Atom( res.getString( "parent" ) );
                Term pair = new Compound( "child_of", Util.toTermArray( child, parent ) );
                
                Query assert_query = 
                    new Query( 
                              "assert", 
                              pair );
                assert_query.oneSolution();
            }
            select.close();
            con.close();
        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }

    static void
    load_prog()
    {
        JPL.init();

        Term consult_arg[] = { 
            new Atom( "test2.pl" ) 
        };
        Query consult_query = 
            new Query( 
                "consult", 
                consult_arg );

        boolean consulted = consult_query.query();
        
        if ( !consulted ){
            System.err.println( "Consult failed" );
            System.exit( 1 );
        }
    }

    static void
    test_4()
    {
        Variable X = new Variable();
        Term args[] = { 
            X,
            new Atom( "ralf" )
        };
        Query query = 
            new Query( 
                "descendent_of", 
                args );

        System.out.println( "querying descendent_of( X, ralf )" );
        
        while ( query.hasMoreSolutions() ){
            java.util.Hashtable solution =
                query.nextSolution();
            System.out.println( "X = " + solution.get( X ) );
        }
    }
}
The explanation is in order.

Imported packages

We import 'java.util.Hashtable' to recieve the result from prolog. The packages under 'jpl' are part of JPL, the java interface to prolog. The last, 'java.sql.*', is to get access to the RDB.

Imported packages
import java.util.Hashtable;
import jpl.Atom;
import jpl.Variable;
import jpl.Term;
import jpl.Query;
import jpl.JPL;
import jpl.Compound;
import jpl.Util;
import jpl.Integer;
import java.io.*;
import java.sql.*;

Flow of control

The top level defines the control flow. The method, 'load_prog()', first loads the prolog program, 'test2.pl'. Another method, 'assert_facts()', establishes the connection to RDB and retrieves data. The method asserts each datum into prolog every time it retrieves a line of data. The method, 'test_4()', prints out the result in prolog. This method is as implemented by Fred Dushin, the author of JPL. We finally clean up the facts of prolog interpreter.

Control flow
load_prog(); 
assert_facts(); 
test_4();
abolish_facts();

Loading prolog program

We first laod the prolog program, 'test2.pl'. Nothing new to the method as we have already explained about the procedure.

Loading test2.pl
    static void
    load_prog()
    {
        JPL.init();

        Term consult_arg[] = { 
            new Atom( "test2.pl" ) 
        };
        Query consult_query = 
            new Query( 
                "consult", 
                consult_arg );

        boolean consulted = consult_query.query();
        
        if ( !consulted ){
            System.err.println( "Consult failed" );
            System.exit( 1 );
        }
    }

Porting data from RDB to prolog

The next thing we have to do is to establish the connection to MySQL. We have already gave an explanation on the procedure. There is nothing new in retrieving data, too. In 'while' loop, however, we insert the data into prolog through JPL. The actoin occurs each time the method reads out a line from RDB.

porting data from RDB into prolog
    static void
    assert_facts() {
        try {
            Class.forName( "org.gjt.mm.mysql.Driver" );
            String url = "jdbc:mysql://ks15e0f00/mysqltest?user='YourID'&password='YourPassword";
            Connection con = DriverManager.getConnection( url );

            Statement select = con.createStatement();
            String sql = "select * from child_of";
            ResultSet res = select.executeQuery( sql );

            while( res.next() ) {
                Term child = new Atom( res.getString( "child" ) );
                Term parent = new Atom( res.getString( "parent" ) );
                Term pair = new Compound( "child_of", Util.toTermArray( child, parent ) );
                
                Query assert_query = 
                    new Query( 
                              "assert", 
                              pair );
                assert_query.oneSolution();
            }
            select.close();
            con.close();
        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }

Executing the query to display the result

The method, 'test_4()', executes the query, descendent_of(X, ralf), in prolog and receives the result through JPL.

test_4
    static void
    test_4()
    {
        Variable X = new Variable();
        Term args[] = { 
            X,
            new Atom( "ralf" )
        };
        Query query = 
            new Query( 
                "descendent_of", 
                args );

        System.out.println( "querying descendent_of( X, ralf )" );
        
        while ( query.hasMoreSolutions() ){
            java.util.Hashtable solution =
                query.nextSolution();
            System.out.println( "X = " + solution.get( X ) );
        }
    }

Cleaning up

The last thing to do is to clean up the prolog facts, i.e., child_of/2, we inserted for the query. The point to note is that the expression, child_of/2, is in fact a compound term such as /(child_of, 2). No mystery, otherwise. You can skip the execution of this method because the inserted facts disappear anyway when Tomcat terminates.(1)

abolish_facts
    static void abolish_facts() {
        Term pred = new Atom( "child_of" );
        Term num = new Integer( 2 );
        Term rem = new Compound( "/", Util.toTermArray( pred, num ) );

        Query query = 
                new Query( 
                          "abolish", 
                              rem );
        query.oneSolution();
    }

Test-run

The program, Test2.class, works as follows when compiled. Do not forget to replace your userID and password for 'YourID' and 'YourPassword' in Test2.java.

Runing Test2.class
programs/proServ2/src> java Test2
% test2.pl compiled 0.02 sec, 784 bytes
querying descendent_of( X, ralf )
X = joe
X = mary
X = steve

  1. We come back to this point later. The trouble is that the method, JPL.halt(), causes Tomcat crash, thus we cannot terminate prolog within a Servlet. Fred Dushin, the developer of JPL, claims that this is due to the implementation of SWI-prolog.

Combining prolog and MySQL on Servlet

We have now everything at our hand to combine prolog and MySQL on Servlet. We will extend the sample Servlet, proServ, which we developed in the first chapter of this part. The Servlet was to return the name of a parent given a person name by calling prolog program. The modified version of the Servlet will return all the names of parents of the person.

The source code

We show you above all the source code. The reader who have read through the text in every corer should understand it without difficulties. The explanation comes in order.

prologServ2.java
//
//   for Servlet
//
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
// 
//   JPL package
//
import jpl.Atom;
import jpl.Variable;
import jpl.Term;
import jpl.Query;
import jpl.JPL;
import java.util.Hashtable;
import jpl.Compound;
import jpl.Util;
import jpl.Integer;
//
//   for SQL
//
import java.sql.*;

public class prologServ2 extends HttpServlet {
    public void doGet (
        HttpServletRequest req,
        HttpServletResponse res
        ) throws ServletException, IOException
    {
        res.setContentType( "text/html" );
        PrintWriter out = res.getWriter();
        out.println( "<html><head><title>Finding decendents of the person</title></head>" );
        out.println( "<body>Using SWI-prolog and MySQL<br>" );

        load_prog();    // Loading test2.pl
        assert_facts(); // Asserting facts into prolog

        Enumeration enum = req.getParameterNames();
        while( enum.hasMoreElements() ) {
            String name = (String)enum.nextElement();
            String value = req.getParameter( name );
            out.println( name + "=" + value + "<br>" );
            test4( value, out );
        }
        // JPL.halt(); this causes Tomcat shutdown
        abolish_facts();
        out.println( "</body></html>" );
        out.close();
    }
    
    public void doPost (
        HttpServletRequest req,
        HttpServletResponse res
        ) throws ServletException, IOException
    {
        doGet( req, res );
    }

    static void
    load_prog()
    {
        JPL.init();

        Term consult_arg[] = { 
            new Atom( "/WinApps/jakarta-tomcat/webapps/proServ2/WEB-INF/classes/test2.pl" )
        };
        Query consult_query = 
            new Query( 
                "consult", 
                consult_arg );

        boolean consulted = consult_query.query();
        
        if ( !consulted ){
            System.err.println( "Consult failed" );
            System.exit( 1 );
        }
    }

    static void
    assert_facts() {
        try {
            Class.forName( "org.gjt.mm.mysql.Driver" );
            String url = "jdbc:mysql://ks15e0f00/mysqltest?user=YourID&password=YourPassword";
            Connection con = DriverManager.getConnection( url );

            Statement select = con.createStatement();
            String sql = "select * from child_of";
            ResultSet res = select.executeQuery( sql );

            while( res.next() ) {
                Term child = new Atom( res.getString( "child" ) );
                Term parent = new Atom( res.getString( "parent" ) );
                Term pair = new Compound( "child_of", Util.toTermArray( child, parent ) );
                
                Query assert_query = 
                    new Query( 
                              "assert", 
                              pair );
                assert_query.oneSolution();
            }
            select.close();
            con.close();
        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }

    static void
    test4( String person, PrintWriter out )
    {
        Variable X = new Variable();
        Term args[] = { 
            X,
            new Atom( person )
        };
        Query query = 
            new Query( 
                "descendent_of", 
                args );

        out.println( "querying descendent_of( X, " + person + " ) <br>" );

        while ( query.hasMoreSolutions() ){
            java.util.Hashtable solution =
                query.nextSolution();
            out.println( "X = " + solution.get( X ) + "<br>" );
        }
    }

    static void abolish_facts() {
        Term pred = new Atom( "child_of" );
        Term num = new Integer( 2 );
        Term rem = new Compound( "/", Util.toTermArray( pred, num ) );

        Query query = 
                new Query( 
                          "abolish", 
                              rem );
        query.oneSolution();
    }
}

Imported packages

We import the packages for Servlet, JPL, and SQL.

Imported packages
//
//   for Servlet
//
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
// 
//   JPL package
//
import jpl.Atom;
import jpl.Variable;
import jpl.Term;
import jpl.Query;
import jpl.JPL;
import java.util.Hashtable;
import jpl.Compound;
import jpl.Util;
import jpl.Integer;
//
//   for SQL
//
import java.sql.*;

Sending header

We firstly send back the browser the header and the line to be printed out in the top.

Sending header, etc.
public class prologServ2 extends HttpServlet {
    public void doGet (
        HttpServletRequest req,
        HttpServletResponse res
        ) throws ServletException, IOException
    {
        res.setContentType( "text/html" );
        PrintWriter out = res.getWriter();
        out.println( "<html><head><title>Finding decendents of the person</title></head>" );
        out.println( "<body>Using SWI-prolog and MySQL<br>" );

Loading test2.pl and asserting the facts

The program, test2.pl, is then loaded, and the facts of child_of relation is imported from RDB to prolog. The methods, load_prog() and assert_facts(), are identical with the ones presented above.

Loading test2.pl and asserting the facts
    load_prog();    // Loading test2.pl
    assert_facts(); // Asserting facts into prolog

Displaying the names of parents

In the next step, the program receives the person's name to hand it to the method, test4. The method, test4, will evoke prolog to infer who are parents of the person and display the result.

Listing up the parents
    Enumeration enum = req.getParameterNames();
    while( enum.hasMoreElements() ) {
        String name = (String)enum.nextElement();
        String value = req.getParameter( name );
        out.println( name + "=" + value + "<br>" );
        test4( value, out );
    }

Closing up

When finished with displaying the names of parents, the program terminates by abolishing the inserted facts and closing the connection to the browser.

Closing up
    abolish_facts();
    out.println( "</body></html>" );
    out.close();
}

Evoking prolog and showing the result

The first half is to construct the query, descendent_of(X, 'ralf'). The last part is to print out each name of parent to the browser by executing the query in prolog.

test4
    static void
    test4( String person, PrintWriter out )
    {
        Variable X = new Variable();
        Term args[] = { 
            X,
            new Atom( person )
        };
        Query query = 
            new Query( 
                "descendent_of", 
                args );

        out.println( "querying descendent_of( X, " + person + " ) <br>" );

        while ( query.hasMoreSolutions() ){
            java.util.Hashtable solution =
                query.nextSolution();
            out.println( "X = " + solution.get( X ) + "<br>" );
        }
    }

Building the web application

We build the web application as 'proServ2'. The files are appended in appendix. Do not forget to add the following lines to 'conf/server.xml', before restarting Tomcat.

In conf/server.xml
    <Context path="/proServ2" docBase="webapps/proServ2" debug="0" reloadable="true" > 
    </Context>

Runing the proServ2 Servlet

You start Tomcat by executing 'startup.bat' under jakarta-tomcat/bin. Open the page, 'http://127.0.0.1:8080/proServ2/index.html' and enter 'ralf' as before in the place of Name. (図[Opening the input window]) Then click 'SUBMIT' button.

Opening the input window

You will then see the result displayed on the window as (図[Getting the result])

Getting the result