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; }