Hello,
I did some more investigation into this problem. Here's
what I found.
The problem also occurred when using the Javasoft JDK.
I found that I could run the program successfully when
I ran the prolog server independently (not exec'ed from Java).
However, when I did so, I got the following warning
from Prolog
WARNING: Predicate foo/1 undefined in module jtopl
So, I had the Java program tell Prolog to use a foo module
:- module(foo, [foo/1]).
foo(0).
However, in this case the foo predicate was static and I
got exceptions when I tried to assert(foo(5)). So I made
the foo predicate dynamic by adding :- dynamic(foo/1). to the
module. This allowed the program to run successfully to completion
(because Prolog now never tried to write anything to stderr).
The real issue here is what happens to output that Prolog
sends to its stdout and stderr. Apparently what's happening in
the problem situations is that Prolog is blocked trying to
write to stderr, and hence cannot service the request that
Java has sent to it, and Java just sits waiting to
read a reply that will never come because Prolog is blocked
on output -- a classic deadlock situation.
So here's what I did to get things working:
In the PLConnection constructor, I added code to obtain
Prolog's stdout and stderr. Then I created a helper
class (called PrologOutputReader) that runs in a separate thread.
This helper periodically checks for data coming in from prolog on
those streams. When it sees that there is some, it reads it and
sends it to Java's System.out or System.err appropriately. This allows
Prolog to unblock and continue with its processing.
The code I have not written is not a 'general' solution because
the helper writes any output from prolog directly to System.in/err
without regard for what might be happening on the display at the
time -- hence the flow of output to the screen may sometimes
contain Prolog stdout/stderr mixed into whatever other output
the Java program might be doing at the time. There is also
the problem of how often this helper checks for incoming -- it
currently looks every 2 seconds. Also the code is was written
primarily with the goal of getting my example to work just well
enough to illustrate the problem/solution. I'm sure you'll want
to come up with a solution that is more suitable for general use.
I'll attach my modified portion of the PLConnection.java code, as
well as an abbreviated version of the example & helper class.
By the way, I was surprised to find that you can't reuse structure
safely because the constructors copy their args. For example, I
can't do:
String fooFunctor = "foo";
PLTerm[] fooArgs = new PLTerm[1];
fooArgs[0] = new PLInteger(555);
PLStructure foo = new PLStructure(fooFunctor, fooArgs);
and use the PLStructure foo for something, then do
fooArgs[0] = new PLInteger(777);
or
fooArgs[0] = new PLVariable(777);
and then use PLStructure foo again because the constructor for
PLStructure copies it's args upon construction.
Thanks for the Ciao system! Now that I've gotten things working,
I look forward to moving beyond my example code. (I may post
a silly question once and a while though - It's been about 8
years since I last used Prolog, and I'm a bit rusty!-))))
I was also wondering: is the mailing list is archived anywhere?
- Dan
////////////////////////////////////////////////////////////
Modified PLConnection.java Additions noted by the text 'Larner'
package CiaoJava;
import java.io.*;
import java.net.*;
import java.util.*;
/**
* Starts and handles a connection to a Prolog process
* via sockets.
* The PLConnection can be used in two ways, so the
* <code>CiaoJava</code> interface can work as a Java
* object server (using the constructor with no arguments),
* or as a connection to a Prolog query server.
* Working with a Prolog server using the Java side as a
* client, the Prolog goals can be launched using the <code>launchGoal</code>
* method with a <code>PLTerm</code> object representing a goal
* (those terms where the isRunnable() method returns true),
* or creating and using <code>PLGoal</code> objects.
*/
public class PLConnection {
/**
* Private fields.
*/
private Process plProc = null;
private boolean allowThreads = false;
private BufferedReader plIn;
private PrintWriter plOut;
private BufferedReader evIn;
private PrintWriter evOut;
private static final PLTerm DATA_SYNC = new PLAtom("data");
private static final PLTerm EVENT_SYNC = new PLAtom("event");
/** Larner add - prolog std out -- valid when this connection
* resulted from an exec call
*/
private BufferedReader m_prologStdOut = null;
/** Larner add - prolog std err -- valid when this connection
* resulted from an exec call
*/
private BufferedReader m_prologStdErr = null;
/** Larner add - accessor for prolog std out -- valid when this connection
* resulted from an exec call
*/
public BufferedReader getPrologStdOut() {
return m_prologStdOut;
}
/** Larner add - accessor for prolog std err -- valid when this connection
* resulted from an exec call
*/
public BufferedReader getPrologStdErr() {
return m_prologStdErr;
}
/** Larner add closes the connection - it's no longer usable
*/
public void close() {
try { plIn.close(); }
catch (Exception e) {
System.err.println("PLConnection: Problem closing plIn " + e);
}
try { evIn.close(); }
catch (Exception e) {
System.err.println("PLConnection: Problem closing evIn " + e);
}
plOut.close();
evOut.close();
}
/**
* Creates a PLConnection to use the Java-to-Prolog
* interface. Starts the Prolog server process and
* connects to it creating the sockets.
*
* @param where command used to start the Prolog server process.
*
* @exception IOException if there are I/O problems.
* @exception PLException if there are problems regarding the Prolog
* process.
*/
public PLConnection(String where) throws IOException, PLException {
Runtime rt = Runtime.getRuntime();
plProc = rt.exec(where);
// Larner add - get prolog std out
m_prologStdOut = new BufferedReader(new InputStreamReader(plProc.getInputStream()));
// Larner add - get prolog std err
m_prologStdErr = new BufferedReader(new InputStreamReader(plProc.getErrorStream()));
OutputStream pipeOut = plProc.getOutputStream();
PrintStream out = new PrintStream(pipeOut);
createSockets(out);
}
... Rest of PLConnection is unchanged from the Ciao distribution code.
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
abbreviated version of my example & its helper class
import CiaoJava.*;
import java.io.BufferedReader;
import java.io.IOException;
/** class for tesing Java-Ciao functionality
*/
public class CiaoTest {
/**
* The main entry point for the application.
*/
public static void main (String[] args) {
// make a new prolog server process
try {
if (args.length == 0) {
// use the default server launch line
sm_plServer = new PLConnection(sm_str_prologExec);
sm_plOutputReader = new PrologOutputReader(sm_plServer);
}
else if (args[0].equalsIgnoreCase("wait"))
// wait for a prolog to connect
sm_plServer = new PLConnection();
else { // treate args[0] as the launch line to use
sm_plServer = new PLConnection(args[0]);
sm_plOutputReader = new PrologOutputReader(sm_plServer);
}
} catch (Exception e) {
System.err.println("Problems starting java server: " + e);
e.printStackTrace();
System.exit(1);
}
// set up for prolog output to be read if we need to
if (sm_plOutputReader != null) {
Thread readerThread = new Thread(sm_plOutputReader, "PrologOutputReader");
readerThread.start();
}
tryFooCiao();
System.out.println("Main Ending");
// xxx With jdk 1.2.2 the prolog subprocess automatically
// terminates, but with the MS VM, it doesn't!
// We need to have the prolog engine terminate
// but there doesn't seem to be a way to make that happen
// with the standard Ciao PLConnection -- Larner added a close
// call to PLConnection which closes off its streams and lets the
// prolog process terminate.
sm_plServer.close();
// stop
System.exit(0);
}
// runs the foo example
public static void tryFooCiao() {
System.out.println("--- start tryFooCiao --- ");
PLGoal currentGoal = null;
boolean b_done = false;
try {
PLTerm solution;
// build up foo(555).
String fooFunctor = "foo";
PLTerm[] fooArgs = new PLTerm[1];
fooArgs[0] = new PLInteger(555);
PLStructure foo = new PLStructure(fooFunctor, fooArgs);
// build up asserta(foo(555)).
String assertaFunctor = "asserta";
PLTerm[] assertaArgs = new PLTerm[1];
assertaArgs[0] = foo;
PLStructure assertFoo = new PLStructure(assertaFunctor, assertaArgs);
// create a goal holding the asserta(foo(555)).
currentGoal = new PLGoal(sm_plServer, assertFoo);
System.out.println("Built " + currentGoal);
// if we tell prolog to use the foo module, then prolog doesn't
// try to write to stderr about the predicate being undefined
// if we leave these lines commented out, then Prolog will try
// to write to stderr, and the example will deadlock unless a
// PrologOutputReader is running to absorb Prolog's output
//
// if the foo module doesn't delcare :- dynamic(foo/1). then
// we'll get exceptions about foo being a static predicate
/////System.out.println("Doing currentGoal.useModule(\"c:/larner/ciaotest/foo.pl\")");
/////currentGoal.useModule("c:/larner/ciaotest/foo.pl");
// tell prolog asserta(foo(555)).
System.out.println("Doing currentGoal.query()");
currentGoal.query();
System.out.println("Doing currentGoal.nextSolution()");
solution = currentGoal.nextSolution();
System.out.println("Doing currentGoal.terminate()");
currentGoal.terminate();
System.out.println("solution=" + solution);
System.out.println("asserta(foo(555)) Completed\n");
// note: don't reuse previous structure (i.e. don't
// just set fooArgs[0] = new PLInteger(777); and reuse the structures
// built up earlier -- they still print out with the old values
PLTerm[] foo777Args = new PLTerm[1];
foo777Args[0] = new PLInteger(777);
PLStructure foo777 = new PLStructure(fooFunctor, foo777Args);
// build up asserta(foo(777)).
PLTerm[] asserta777Args = new PLTerm[1];
asserta777Args[0] = foo777;
PLStructure assert777Foo = new PLStructure(assertaFunctor, asserta777Args);
// create a goal holding the asserta(foo(777)).
currentGoal = new PLGoal(sm_plServer, assert777Foo);
System.out.println("Built " + currentGoal);
// tell prolog asserta(foo(777)).
System.out.println("Doing currentGoal.query()");
currentGoal.query();
System.out.println("Doing currentGoal.nextSolution()");
solution = currentGoal.nextSolution();
System.out.println("Doing currentGoal.terminate()");
currentGoal.terminate();
System.out.println("solution=" + solution);
System.out.println("asserta(foo(777)) Completed\n");
// now build up a foo query
PLVariable answer = new PLVariable();
PLTerm[] fooAnsArgs = new PLTerm[1];
fooAnsArgs[0] = answer;
PLStructure fooAns = new PLStructure(fooFunctor, fooAnsArgs);
// create a goal holding the asserta(foo(?)).
currentGoal = new PLGoal(sm_plServer, fooAns);
System.out.println("Built " + currentGoal);
System.out.println("Doing currentGoal.query()");
currentGoal.query();
System.out.println("Doing currentGoal.nextSolution()");
while ((solution = currentGoal.nextSolution()) != null) {
System.out.println("solution=" + solution);
System.out.println("answer = " + answer);
System.out.println("Doing currentGoal.nextSolution()");
}
System.err.println("No more solutions.");
b_done = true;
}
catch(Exception e) {
System.out.println("Exception " + e);
e.printStackTrace();
}
finally {
if (currentGoal != null && !b_done)
try {currentGoal.terminate();}
catch (Exception e2) {}
}
System.out.println("--- end tryFooCiao --- ");
}
/////////////////////////////////////////////////////////////
// fields
private static PrologOutputReader sm_plOutputReader = null;
// Prolog process connection.
private static PLConnection sm_plServer = null;
// string used to start the prolog process
private static String sm_str_prologExec =
"C:\\prolog\\ciao-1.7p30Win32\\Win32\\bin\\ciaoengine.exe -C -b c:/larner/ciaotest/plserver.cpx";
}
/** Helper class that reads anything that's come in from an exec'ed Prolog's
* stdout or stderr, and prints it out to Java's System.out and System.err
* xxx note that it prints out without any regard for anything else that
* might be printing out at the time
* xxx note assumes that 'lines' are written
*/
class PrologOutputReader implements Runnable {
public PrologOutputReader(PLConnection plServer) {
m_prologStdOut = plServer.getPrologStdOut();
m_prologStdErr = plServer.getPrologStdErr();
}
/** keeps looking for anything prolog has output to its stdout or stderr
*/
public void run() {
if ((m_prologStdOut == null) ||
(m_prologStdErr == null)) {
System.err.println("Warning PrologOutputReader: one or more null Prolog output streams");
return;
}
// used for getting chars
String str_line;
while (true) {
// sleep for a bit
try {
Thread.sleep(sm_i_msecReadPauseTime);
}
catch (InterruptedException e) {
continue; // keep looping
}
try {
// check for anything from prolog's stdout
if (m_prologStdOut.ready()) {
str_line = m_prologStdOut.readLine();
if (str_line == null) // at end of stream -- must be done
return;
System.out.println("\nPrologStdOut: " + str_line);
}
// check for anything from prolog's stderr
if (m_prologStdErr.ready()) {
str_line = m_prologStdErr.readLine();
if (str_line == null) // at end of stream -- must be done
return;
System.err.println("\nPrologStdErr: " + str_line);
}
}
catch (IOException e) {
System.err.println("PrologOutputReader: exception " + e);
return;
}
}
}
/** prolog std out */
private BufferedReader m_prologStdOut;
/** prolog std err*/
private BufferedReader m_prologStdErr;
/** how long a reader sleeps between checks
*/
public static int sm_i_msecReadPauseTime = 2000;
}