wiki:Articles/ClojureScripting

Clojure Scripting in Vaadin

Introduction

Since Vaadin runs (partially) server-side, it is theoritecally possible to implement GUI logic in any language that runs on Java VM as a scripting extension. In this article, I will provide an example and proof of concept implementation based on Clojure (Jython, JRuby, Beanshell, etc. -based implementations could probably be also possible using this technique).

Clojure is a Lisp dialect, which runs on Java VM. More information is available at  Clojure web site.

Vaadin/Grails integration is far more advanced example of scripting in Vaadin.

Implementation

This implementation serves only as an example and proof-of-concept rather than final, production-ready solution.

The implementation is based on:

  • simple servlet, which provides Vaadin-Clojure bridge
  • web.xml descriptor, parametrising above servlet
  • example clojure App, written entirely in Clojure

The servlet

The implementation is really straightforward:

package com.example.testvaadin;

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 Clojure4Vaadin 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);
		}
	}

}

web.xml

web.xml descriptor is as simple as servlet implemenation. It only provides mappings and parameters to the servlet and was generated using Vaadin Eclipse plugin.

<?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>com.example.testvaadin.Clojure4Vaadin</servlet-class>
		<init-param>
			<param-name>script-name</param-name>
			<param-value>test</param-value>
		</init-param>
		<init-param>
			<param-name>package-name</param-name>
			<param-value>test.tlp</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>

The application

Finally, the interactive Clojure application, which displays notification "test" when user clicks on a button labeled "button":

(ns test.tlp)
 
(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")))))))))))))

And that is it.