Subject: CGI with SWI-Prolog

From: Lukas Faulstich <[email-masked]>
Date: Mon, 3 Apr 2000 11:41:23 +0200 (MET DST)

Lionel Ains writes:
 > Hello,
 > 
 > I am trying to use SWI-Prolog as a CGI script.
 > To get the post parameters, I would like to read from the
 > stdin without Prolog printing ':|'.
 > I found out that this was possible by trying to get the
 > input when not on an empty line.
 > How could I do that in any cases (when the cursor is at the
 > begining of the line)?

	% switch off the prompt: 
	prompt(_, '') 

 > Is there a non-blocking way to detect the when nothing is
 > sent to the user input (script called without parameter)?
 > Are there available librairies to handle the URL
 > encoding/decoding?

it is easier to use Java Servlets 
<http://jserv.javasoft.com/index.html>
to let them do this job. This has the
advantage that you can maintain a stateful prolog process that handles
multiple http requests. While there is the jpl library for calling
prolog from within java
<http://blackcat.cat.syr.edu/~fadushin/software/jpl/>, I found it easier
to use standard input/output for communication. You might have a look at
the attached Java/Prolog source code. 

Hope that helps- 

Lukas

-- 
Lukas C. Faulstich		        [email-masked] 
Institut fuer Informatik	        +49 (30) 838 - 75 123
Freie Universitaet Berlin	        +49 (30) 838 - 75 109 (fax)
Takustr. 9			        http://www.inf.fu-berlin.de/~faulstic
D-14195 Berlin, Germany	                PGP key: see my homepage

/* 
 *	 $Id: servlet.pl,v 1.9 1999/11/14 21:31:46 faulstic Exp $
 * 
 * (c) Lukas Faulstich <[email-masked]>  
 *
 * Description: backend for servlet
 *
 */

:- module(servlet, [
	servlet_loop/0 
    ]). 

servlet_loop:- 
	% redirect user_error to current output
	current_output(CurrentOut), 
	dup_stream(CurrentOut, user_error), 
	% switch off the prompt: 
	prompt(_, ''), 
	% start the loop
	next. 

next:- 
	read_action(Action, Bindings), 
	handle(Action, Bindings). 

handle(http_request(Parameters), _):- !, 
	http_request(Parameters), next. 
handle(syntax_error, _):- !, 
	error('Syntax Error', []). 
handle(welcome, _):- !, welcome, next. 
handle(next, _):- !, fail. 
handle(halt,_):- !, 
	halt. 
handle(end_of_file,_):- !, 
	halt. 
handle(done,_):- !. 
handle(Action,_):- 
	error('Don\'t understand ~w', [Action]), 
	next. 

http_request(Attributes):- 
	member(halt=true, Attributes), !, 
	base_url(BaseURL), 
	format('<html><head><title>Halt</title></head><body><h1>Halt</h1>The system has been halted. </body></html>~n~n', []),
	halt. 
http_request(Attributes):- 
http_request(Attributes):- 
	decode_attributes(Attributes, DecodedAttributes)
	% use DecodedAttributes = [Attr1=Val1,...,Attrn=Valn] 
        % to generate HTML page and 
	% print it to standard output, e.g.: 
	format('<html><head><title>Attributes</title></head><body><h1>Attributes:</h1>~w</body></html>~n~n', [DecodedAttributes]),

decode_attributes([], []). 
decode_attributes([A=V|AVs], [A=T|ATs]):- 
	(atom_to_term(V,T,_) -> true; T=V), 
	decode_attributes(AVs, ATs). 

error(Message, Args):-
	format('<html><head><title>Error</title></head>~n<body><h1>Error:</h1>'), 
	format(Message, Args), 
	format('</body></html>~n~n'). 

remake:- 
	format('<html><head><title>Make</title></head><body><h1>Make:</h1><hr><pre>'), 
	make, 
	format('</pre><hr></body></html>~n~n'). 

welcome:- http_request([]). 

read_action(Action, Bindings):- 
	read_line_term(Action, Bindings), !. 
read_action(end_of_file, []). 

read_line_term(Term, Bindings):- 
	read_line(Line), 
	(atom_to_term(Line, Term, Bindings) -> true;
	Term= syntax_error, Bindings=[]). 

read_line(Line):- 
	get0(Char), 
	Char \= -1, 
	read_line_chars(Char, Chars, []), 
	name(Line, Chars). 

read_line_chars(10) --> !, []. 
read_line_chars(-1) --> !, []. 
read_line_chars(C) --> [C], { get0(N) }, read_line_chars(N). 

//  Frontend Servlet for SWI Prolog (servlet.pl)

import javax.servlet.*;
import javax.servlet.http.*;

import java.io.*;
import java.util.*;

public final class PrologServlet extends HttpServlet {

Process backEnd= null; 

PrintWriter backEndInput= null; 

BufferedReader backEndResults= null;  

PrintWriter logWriter= null; 

public String getServletInfo() {
        return "Frontend for SWI Prolog (servlet.pl)";
    }

public void init(ServletConfig config)
  throws ServletException
    {
      String logFileName; 
 
      super.init(config);

      initBackEnd(); 

      if ((logFileName = getInitParameter("logfile")) == null) { 
	//      throw new UnavailableException (this, "No log file given!");
      } else {
	try { 
	  logWriter= new PrintWriter(new FileOutputStream(logFileName), true); 
	} catch (IOException e) { 
	  throw new UnavailableException (this, "Cannot open log file " + logFileName + "!");
	}
      }
    }

public void initBackEnd() 
throws ServletException { 

    String tmpdir=getInitParameter("tmpdir"); 
    
    if (tmpdir == null) { tmpdir = "/tmp"; }

    String set_tmpdir="TMPDIR="+(new File(tmpdir)).getAbsolutePath();

    String servletName=getInitParameter("servletname"); 
  
    String[] envp; 

    if (servletName==null) {
	String[] tmp_envp= {set_tmpdir}; 
	envp= tmp_envp; 
    } else {
	String[] tmp_envp= {set_tmpdir, "SERVLET_NAME="+servletName}; 
	envp= tmp_envp; 
    }

    String backEndCall=null; 

    if ((backEndCall = getInitParameter("backend")) == null) { 
	backEndCall = "HyperView"; 
      }

    try { 
	backEnd = Runtime.getRuntime().exec(backEndCall, envp);

	backEndInput = 
	    new PrintWriter(backEnd.getOutputStream(), true); 
	
	backEndResults = 
	    new BufferedReader(new InputStreamReader(backEnd.getInputStream())); 
    } catch(IOException e) { 
	throw new UnavailableException (this, "Cannot initialize back end!");
    }
    
}

public void doPost(HttpServletRequest req, HttpServletResponse res)  
  throws ServletException {
  doGet(req, res); 
}

public void doGet(HttpServletRequest req, HttpServletResponse res) 
  throws ServletException {
  
  String action; 

  boolean result; 
  // Set Content Type:
  res.setContentType("text/html");
  
  // get output stream to client
  PrintWriter servletOut = null;
  try {
    servletOut = res.getWriter();
  } catch (IOException e) {
    log("Failed to establish output stream to client");
    return;
  }
   
  if(!forwardRequest(req, servletOut)){ 
      log("Pipe failed. Trying to restart backend...");
      closeBackEnd(); 
      initBackEnd();
      log("... backend restarted."); 
      if (!forwardRequest(req, servletOut)){
	  log("Pipe failed."); 
	  servletOut.println("<html><head><title>Fatal Error</title></head><body><h1>Fatal Error</h1>Failed to (re)start the HyperView Server.</body></html>");
      }
      
  }
  servletOut.close();
}

public boolean forwardRequest(HttpServletRequest req, PrintWriter servletOut){ 
  backEndInput.print("http_request(");
  printParameters(req, backEndInput); 
  backEndInput.println(").");
  log("Request sent."); 
  
  return pipePage(backEndResults, servletOut);    
}

public void printParameters(HttpServletRequest req, PrintWriter out){
  int j=0;
  Enumeration e = req.getParameterNames();
  log("Parameters:"); 
  out.print('['); 
  while (e.hasMoreElements()) {
    String name = (String)e.nextElement();
    String vals[] = (String []) req.getParameterValues(name);
    if (vals != null) {
      for (int i = 0; i<vals.length; i++) { 
	if (j > 0) { out.print(','); }
	out.print(name + "="); 
	log(name + "= \"" + vals[i] + "\"");  
	printAtom(out, vals[i]);
	j++; 
      }
    }
  }
  out.print(']');   
}

public void printAtom(PrintWriter out, String atom) {
  int i, l=atom.length(); 
  char c; 
  out.print('\''); 
  for (i=0; i<l; i++) {
    c= atom.charAt(i); 
    if (c == '\n') { out.print("\\n"); }
    else if (c == '\'') { out.print("\\'"); }
    else if (c == '\\') { out.print("\\\\"); }
    else { out.print(c); }
  }
  out.print('\''); 
}

public boolean pipePage(BufferedReader input, PrintWriter output) {  
  String thisLine= null; 

  try {
    while ((thisLine = input.readLine()) != null) {
      output.println(thisLine);
      if (thisLine.length() == 0) { 
	return true; 
      }
    }	
  } catch (IOException e) { 
    log("IOException" + e);
  }
  return false; 
}

public void destroy() {
  closeBackEnd(); 
  //  log("destroyed."); 
  if (logWriter != null) { logWriter.close(); } 
}

public void closeBackEnd() { 
  if (backEnd != null) {
    try { 
      backEndInput.println("halt."); 
      backEndInput.close(); 
      backEndResults.close();
    } catch (IOException e) { } 
    backEnd.destroy(); 
  }  
} 

public void log(String message) { 
  if (logWriter != null) {
    logWriter.println("frontend: "+ message); 
  }
}

}