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.
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
).
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
).
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).
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
.