OCaml-Java: developing servlets


This page contains the information explaining how to develop Java servlets using OCaml-Java. The first sections describe how to compile and link servlets, as well as listeners. Then, a section explains how to deploy compiled servlets. Finally, the last section consists in a complete example of a simple HTTP servlet.

Compilation

Servlets and related listeners are Java classes that extend or implement a given Java type. However, when ocamljava is used to compile OCaml modules, its default behavior is to generate Java classes with only static methods, for the various OCaml functions. As such, ocamljava-compiled modules cannot be used as servlets by a servlet container. This is why a dedicated compilation mode, triggered by the -servlet command-line switch, is introduced.

The -servlet command-line switch informs the compiler that a proper Java class should be generated for latter use by a servlet container. The command-line switch takes a parameter used to indicate the kind of servlet or listener the user is interested in. Of course, the different servlet/listener kinds requires differents sets of functions to define their behaviour. The following table indicates the module type that a module should abide to in order to be successfully compiled.

Java type -servlet parameter OCaml module type
javax.servlet.GenericServlet generic JavaServlet.Generic
javax.servlet.http.HttpServlet http JavaServlet.HTTP
javax.servlet.ServletContextListener context-listener JavaServlet.ServletContextListener
javax.servlet.ServletContextAttributeListener context-attribute-listener JavaServlet.ServletContextAttributeListener
javax.servlet.http.HttpSessionListener session-listener JavaServlet.HTTPSessionListener
javax.servlet.http.HttpSessionActivationListener session-activation-listener JavaServlet.HTTPSessionActivationListener
javax.servlet.http.HttpSessionAttributeListener session-attribute-listener JavaServlet.HTTPSessionAttributeListener
javax.servlet.http.HttpSessionBindingListener session-binding-listener JavaServlet.HTTPSessionBindingListener
javax.servlet.http.HttpSessionIdListener session-id-listener JavaServlet.HTTPSessionIdListener

When an OCaml module M is compiled with the -servlet command-line switch, a class named MImpl is generated, in the package set by the -java-package command-line switch (defaulting to pack).

Link

When linking the application in order to produce an archive to be deployed to a servlet container, the developer has to use the -war command-line switch in order to requested the creation of an archive abiding the war structure. The -war command-line switch takes a parameter that is the path to a file to be included in the archive as its descriptor file (web.xml).

Deployment

The actual deployment depends on the actual servlet container used to run the web application. Any container able to handle war files running on an 1.7+ JVM can be used. Deployment has been successfully tested with Apache Tomcat and Jetty. Cloud facilities based on such containers can then be used to host the servlets (Heroku and CloudBees have been successfully used to host servlets).

Example

This example shows how to compile and link a simple servlet greeting a user whose name is passed to the servlet as a GET parameter. We first define the signature of our module in hello.mli; as it is an HTTP servlet, the file is simply:

include JavaServlet.HTTP

As we are only interested in answering to GET requests, we only redefine the do_get function (note that the type t of the module is used to store data in the servlet instance), leading to the following contents for hello.ml:

type t = unit
    
let init _ = ()
    
include JavaServlet.Default_HTTP
    
let print out s =
  Java.call "javax.servlet.ServletOutputStream.println(String)"
    out
    (JavaString.of_string s)
    
let get req s =
  Java.call "javax.servlet.ServletRequest.getParameter(String)"
    req
    (JavaString.of_string s)
  |> Java.wrap

let do_get _ _ req resp =
  Java.call "javax.servlet.http.HttpServletResponse.setContentType(_)"
    resp
    (JavaString.of_string "text/html");
  let out = Java.call "javax.servlet.http.HttpServletResponse.getOutputStream()" resp in
  print out "<html>";
  print out "  <body>";
  begin match get req "name" with
  | Some name ->
      already_seen := name :: !already_seen;
      print out (Printf.sprintf "    Hi %s!<br>" (JavaString.to_string name))
  | None ->
      print out "    Hi!<br>"
  end;
  print out "    <form action=\"hello\" method=\"get\">";
  print out "      <input type=\"text\" name=\"name\">";
  print out "      <input type=\"submit\" value=\"go\"><br>";
  print out "    </form>";
  print out "  </body>";
  print out "</html>"

let do_options _ _ _ resp =
  JavaServlet.options resp [`GET; `OPTIONS]

The code of the servlet heavily relies on the Java extensions that are presented here.

In order to deploy this code, a web.xml file is also necessary, with a contents akin to:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

  <display-name>Hello Application</display-name>
  <description>...</description>

  <servlet>
    <servlet-name>HelloImpl</servlet-name>
    <servlet-class>pack.HelloImpl</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>HelloImpl</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
    
</web-app>

The following commands are then used to build the archive:

ocamljava -c hello.mli
ocamljava -c -java-extensions hello.ml
ocamljava -o hello.war -war web.xml javalib.cmja hello.cmj

The hello.war file is finally ready to be deployed to a servlet container. For example, if copied to the webapps directory of an Apache Tomcat instance, the servlet can be tested at an URL such as http://localhost:8080/hello/hello?name=xyz.