Jan 4, 2011

Writin a Vaadin app using Clojure

First of all you need Clojure with leiningen environment. Vaadin jar file.

Setup the leiningen environment and create a new project.
lein new myapp

Then change the directry to myapp and there you can see some flies. Edit the file src/myapp/core.clj file to your vaadin app. After editing it will be like this.

(ns myapp.core)

(defn main [args]
  (proxy [com.vaadin.Application] []
    (init []
      (let [app this]
        (.setMainWindow this
          (doto (new com.vaadin.ui.Window "Test application")
            (.addComponent
              (new com.vaadin.ui.Label "Hello Vaadin/LISP user!"))
            (.addComponent
              (doto (new com.vaadin.ui.Button "button")
                (.addListener (proxy [com.vaadin.ui.Button$ClickListener] []
                                (buttonClick [event] (. (. app (getMainWindow)) (showNotification "test")))))))))))))

Note the (ns **** ).

Then edit the project.clj file.

(defproject myapp "1.0.0-SNAPSHOT"
  :description "FIXME: write"
  :dependencies [[org.clojure/clojure "1.2.0"]
                 [org.clojure/clojure-contrib "1.2.0"]
                 [com.vaadin/vaadin "6.4.8"]]
  :dev-dependencies [[uk.org.alienscience/leiningen-war "0.0.1"]]
  :aot [myapp.core])

In this you can see there is a   :dev-dependencies [[uk.org.alienscience/leiningen-war "0.0.1"]] and   :aot [myapp.core]. First one is a plugin which can createa WAR file. The second one is to create the servlet class from the clojure file.

Now we need to write the web.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
        <display-name>testVaadin</display-name>
        <context-param>
                <description>Vaadin production mode</description>
                <param-name>productionMode</param-name>
                <param-value>false</param-value>
        </context-param>
        <servlet>
                <servlet-name>Lisp</servlet-name>
                <servlet-class>org.thilina.Servlet</servlet-class>
                <init-param>
                        <param-name>script-name</param-name>
                        <param-value>myapp.core</param-value>
                </init-param>
                <init-param>
                        <param-name>package-name</param-name>
                        <param-value>myapp.core</param-value>
                </init-param>
                <init-param>
                        <param-name>function-name</param-name>
                        <param-value>main</param-value>
                </init-param>
        </servlet>
        <servlet-mapping>
                <servlet-name>Lisp</servlet-name>
                <url-pattern>/*</url-pattern>
        </servlet-mapping>
</web-app>

Vaadin http handling is little bit different so we neet to some additional task. You need to compile above java code and place those classes in the class directory.

package org.thilina;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import clojure.lang.RT;
import com.vaadin.Application;
import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;

public class Servlet extends AbstractApplicationServlet {

 @Override
 protected Class<? extends Application> getApplicationClass()
   throws ClassNotFoundException {
  return Application.class;
 }

 @Override
 protected Application getNewApplication(HttpServletRequest request) throws ServletException {
  try { //load script, with name provided as a servlet's parameter
   RT.load(getServletConfig().getInitParameter("script-name"), true);

   //run Lisp function
   return (Application)RT.var(getServletConfig().getInitParameter("package-name"),
                 getServletConfig().getInitParameter("function-name")).invoke(new String[0]);
  }
  catch (Exception e) {
   throw new ServletException(e);
  }
 }
}

You need clojure.jar and vaadin.jar in order to compile this java file.

Now you can compile the project and get a WAR file.
lein deps
lein compile
lein uberwar

Then put this *.war file to tomcat/webapps directory.


References :-

https://github.com/technomancy/leiningen
http://dev.vaadin.com/wiki/Articles/ClojureScripting

2 comments:

  1. It's actually possible to get rid of the Java class and consequently all of the init stuff in web.xml. Simply create a VaadinServlet class and compile it using gen-class:

    (ns myapp.VaadinServlet
    (:use [myapp.core :only (main)])
    (:gen-class
    :extends com.vaadin.terminal.gwt.server.AbstractApplicationServlet))
    (defn -getApplicationClass [_] (class com.vaadin.Application))
    (defn -getNewApplication [_ _] (main))

    Since the servlet calls main with no arguments now, change the signature of main to:

    (defn main []

    Also, change web.xml to:

    <web-app ...>
    <display-name>testVaadin</display-name>
    <context-param>
    <description>Vaadin production mode</description>
    <param-name>productionMode</param-name>
    <param-value>false</param-value>
    </context-param>
    <servlet>
    <servlet-name>Vaadin</servlet-name>
    <servlet-class>myapp.VaadinServlet</servlet-class>
    </servlet>
    <servlet-mapping>
    <servlet-name>Vaadin</servlet-name>
    <url-pattern>/*</url-pattern>
    </servlet-mapping>
    </web-app>

    Get the complete project at https://github.com/ulsa/cljvaadin.

    ReplyDelete
  2. Thanks for sharing such valuable information. Keep posting such great info for us, thanks.

    ReplyDelete

Your comments are always welcome ...