Finally, the Code



Ok, first we need to create a wrapper for our Scheme code- We can do this by creating the following Java file that lets us start Scheme and send commands to it. First, though, you'll want to restart your ssh session so the new profile settings get loaded. Then, type the following to change into the directory for the code, then start up emacs with a new file to create:

cd $WEBDIR/classes
emacs scheme_wrapper.java



Now add the following code to the file:
import java.io.*;
import sisc.*;
import sisc.data.*;
import sisc.ser.*;
import sisc.ser.SeekableInputStream;
import sisc.interpreter.*;

public class scheme_wrapper{
    Interpreter interpreter;
    scheme_wrapper()throws Exception{
        SeekableInputStream heap=new MemoryRandomAccessInputStream(getClass().getResourceAsStream("sisc.shp"));
        AppContext ctx = new AppContext();
        Context.register("main", ctx);
        interpreter=Context.enter("main");
        REPL.loadHeap(interpreter, heap);
    }
    String eval(String s) throws Exception{
        return interpreter.eval(s).toString();
    }
    String eval_into_string(String s) throws Exception{
        return ((SchemeString)interpreter.eval(s)).asString();
    }
    public static void main(String args[])throws Exception{
        scheme_wrapper s=new scheme_wrapper();
        System.out.println(s.eval(args[0]));
    }
}
Now, you can compile this file as follows:

javac scheme_wrapper.java

By creating the scheme_wrapper object, we initialize SISC Scheme. The eval and eval_into_string let us send commands to the REPL- The eval_into_string function is a convenience function that writes the output into a more convenient, but less efficient, string variable. The main function lets us run some tests from the command line. In order for the scheme interpreter to function, we also need to copy a valid sisc heap into our directory:

cp /root/sisc/sisc.shp $WEBDIR/classes

Try it out with the following test:

java scheme_wrapper "(+ 1 2)"
==> 3

Isn't that cool? Now let's hook our new wrapper into Tomcat with the following code in a file named scheme_server.java:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class scheme_server extends HttpServlet{
    scheme_wrapper sw;
    public void init()
        throws ServletException{
        try{
            sw=new scheme_wrapper();
            sw.eval("(load \""+getServletContext().getRealPath("WEB-INF/scheme_server.scm")+"\")");
        }
        catch(Exception e){throw new ServletException("Failed to initialize SISC: "+e.toString());}
    }
    public void doGet(HttpServletRequest request,HttpServletResponse response)throws IOException, ServletException{
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        try{
            out.println(sw.eval_into_string("(get)"));
        }
        catch (Exception e){
            out.println("error on get:" + e.toString());
        }
    }
}

Notice this code loads a scheme file within the init() function. (so it only happens once when Tomcat is initialized but not whenever someone loads a page) Notice that we made sure all exceptions get output one way or another so that we know what happened. Finally, notice that the doGet() function just calls the Scheme function get without any parameters- If we wanted to serve more than one page (perhaps a good idea) we'd have to feed the Scheme some more information from the request variable about which page to serve up or who to serve it up to.

Let's to compile all our Java at this point:

javac *.java

OK, now we need to create the scheme code to handle the get command, in the file scheme_server.scm in the $WEBDIR directory- first let's create the file:

cd $WEBDIR

emacs scheme_server.scm

...and here's the code inside the file:

(define (sexp->html html)
  (cond ((pair? html) (string-append (apply string-append "<" (symbol->string (car html)) ">" (map sexp->html (cdr html))) "</" (symbol->string (car html)) ">"))
        ((symbol? html) (string-append (symbol->string html) " "))
        (#t html)))

(define (get)
  (sexp->html '(html (title My First SISC/Tomcat/Debian App!) (body hello (b cruel) (i scheme unfriendly) World!))))

I also added in a cheesy, incomplete, but useful sexp->html converter that let's us write pretty xml in Scheme.


Now, from the intro page from the Tomcat server, go to the Tomcat Manager (An option on the left side), type the user name you created for Tomcat, stop and restart the root server, (labelled with a "/") go to the page "http://123.234.345.456:8180/servlet/scheme_server", and you should see the following:




The sweet smell of success! Note that this web application is a completely valid and self-contained java server app: If you run
jar cvf foo.war .
from the root directory of the web application, a java WAR file is created that can be loaded into any Java-enabled web server- Talk about easy deployment!

Now you've got a gzillion things to do to actually have something useful happen... I recommend you look at the calculator.scm and the more extensive servlet code that comes with SISC for more sophisticated possibilities. You may also want to look at the free download of On Lisp which tells you what the whole deal is with continuations and why Lisp/Scheme programmers go googoo over them. Happy Scheme hacking!

THE END