Scripting with servlets [groovy - using jsr223] - part VI (Sun Java System WebServer 7.0)

Scripting with servlets [using groovy]  This time using the JDK6 and JSR223 Scripting interface

About the language

groovy is a language much like ruby, except that it's only implementation is over jvm. Its claim to
fame is that it integrates tightly with the jvm and the java libraries. (It does not provide any libraries
other than what the java itself provides in its standard libraries). However the language itself is quite
powerful and supports most of the higher order programming constructs.

(From a moderate hight, the languages like Groovy, Ruby, Javascript, Jython, Sleep all look
very similar. You can take what you have learned in one language and expect almost similar tools and
syntactical constructs to be available in the other languages too.  If you are interested in languages that
really have a very different look and feel, go for either Functional languages -- like Haskell  or for things
really different, go for Concatenative languages like Joy. Unfortunately for us while Haskell has a jvm
implementation called Jaskell, The language Joy does not seem to have one. Some others like Factor
did have a jvm implementation but it seems to have moved to native land now.)

Existing Implementations

Groovy has groovlets

 

In this entry, the discussion is on creating a generic JSR223 compatible servlet that is able to load any
scripting api - (jsr223) aware language. To that end Our servlet will either try to figure out the language
used from the scripting language handler extension, or will rely on the user supplying a 'language' as an
initialization parameter.

We will try to make use of the 'Invocable' interface if it is provided by the language implementation
to invoke the individual method handlers. However if the language does not provide the Invocable
interface will fall back to just evaluating the scripts with relevant variables bound to the context.

One more detail here is that, unlike the other entries, Closures are not used for binding the variables
instead, We evaluate the script handler first with the 'httpservlet' variable bound to instance of our
java servlet, and let it define the function names that can be called later as the entries in our symbol table.

The printEngines() method is provided for debugging support. In case the you find that the scripts does not
evaluate, call the printEngines() and monitor the stdout. Perhaps the engine is not getting registered properly.

 You can make use of the JSR223Servlet implemented here with any of the other languages that support the
java scripting API. The only need is to implement the servlet handler part in the language of your choice and
make sure that you redirect the requests to the servlet initialized with the correct 'language' parameter.

External Components

We make use of the Groovy Scripting libraries implemented by Sundararajan and Mike available here
They have quite a few languages already made aware of the scripting interface now.
 

JSR223Servlet

The ScriptServlet developed in the previous entry is used here as the parent class.
 

package com.sun.servlet;

import java.io.\*;
import java.util.\*;
import java.util.regex.\*;

import javax.servlet.\*;
import javax.servlet.http.\*;

import javax.script.\*;

public class JSR223Servlet extends ScriptServlet {

    private static Pattern FILENAME = Pattern.compile("\^(.\*).([\^.]+)$");
    protected ScriptEngineManager _sem = new ScriptEngineManager();
    protected ScriptEngine _eng = null;

    protected ScriptEngine getScriptEngine(String filename)
        throws Exception {
        // see if the user has some preference?
        String lang = getServletConfig().getInitParameter("language");
        if (lang != null) {
            ScriptEngine se = _sem.getEngineByName(lang);
            if (se!= null)
                return se;
        }

        // or figure out the engine to use by extension.
        Matcher m = FILENAME.matcher(filename);
        if (m.matches()) {
            String ext = m.group(1);
            ScriptEngine se = _sem.getEngineByExtension(ext);
            if (se != null)
                return se;
        }

        // we failed miserably. :(
        throw new Exception("Unable to figure out a script engine to use.");
    }

    public void initialize(String handler, Object code)
        throws Exception {
        List<ScriptEngineFactory> list = _sem.getEngineFactories(); // seems needed for init.
        _eng = getScriptEngine(handler);
        // we want to bind the variable httpservlet for initialization rather than
        // call a method.
        _eng.getContext().setAttribute("httpservlet", this, ScriptContext.ENGINE_SCOPE);
        _eng.eval((String)code);
    }

    public void eval(Object fn, HttpServletRequest request, HttpServletResponse response) {
        try {
            if (_eng instanceof Invocable) {
                Invocable inv = (Invocable)_eng;
                inv.invokeFunction((String)fn, this, request, response);
            } else {
                _eng.getContext().setAttribute("httpservlet", this, ScriptContext.ENGINE_SCOPE);
                _eng.getContext().setAttribute("request", request, ScriptContext.ENGINE_SCOPE);
                _eng.getContext().setAttribute("response", response, ScriptContext.ENGINE_SCOPE);
                _eng.eval((String)fn);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void printEngines() {
        List<ScriptEngineFactory> list = _sem.getEngineFactories();
        System.out.println("Supported Script Engines");
        for (ScriptEngineFactory factory: list) {
            // Obtains the full name of the ScriptEngine.
            String name = factory.getEngineName();
            String version = factory.getEngineVersion();
            // Returns the name of the scripting language
            // supported by this ScriptEngine
            String language = factory.getLanguageName();
            String languageVersion = factory.getLanguageVersion();
            System.out.printf("Name: %s (%s) : Language: %s v. %s \\n",
                    name, version, language, languageVersion);
            // Get a list of aliases
            List<String> engNames = factory.getNames();
            for(String e: engNames) {
                System.out.printf("\\tEngine Alias: %s\\n", e);
            }
        }
    }


 

Providing the handler

Jvm Bindings.

The java objects are accessed in the same way as that of native java in groovy. 

ie:  System.out.println("mystring") is written as System.out.println "mystring" - with or with out the parens.

docroot/WEB-INF/code/groovy.groovy

def do_service (httpservlet, request, response) {
    out = response.getWriter()
    response.setContentType("text/html")
    try {
        spath = request.getServletPath()
        filename = httpservlet.getServletConfig().getServletContext().getRealPath(spath)
        GroovyShell shell = new GroovyShell()
        shell.setVariable("request", request);
        shell.setVariable("response", response);
        shell.setVariable("httpservlet", httpservlet);
        out.println shell.evaluate(httpservlet.read(filename).toString(), filename)
    } catch (e) {
        out.println "<html><body><b>Servlet Error ( "+ e.getMessage() + "</b><xmp> "
        e.printStackTrace(out);
        out.println " </xmp></body></html>"
    }
}

httpservlet.add('get', "do_service")
httpservlet.add('post', "do_service")


Here we are taking another route than the previous entries. We define do_service as a function that takes
two arguments, and set the function name in the symbol table (rather than the closures that we used in previous
entries or the entire script as was the case in jacl.) Since the httpservlet is defined when the complete
script is evaluated the first time, this variable is available for the function too.

The JSR223Servlet takes care of calling the functions with necessary arguments.

An example groovy script that can get executed:

/docroot/hello.groovy

pre = "<html> <head><title>abc</title></head> <body> <h1> Hello at "
date = new Date()
from = " from "
path = request.getServletPath()
post = "</h1>  </body> </html>"
pre + date + from + path + post

Build  Steps.

          The complete groovy-webapp can be downloaded here. Extract the contents to a directory called 'groovy'
inside samples in your installation (samples/java/webapps/groovy). It has to be in that directory to make use
of the common.xml during ant build.

Important 

This will work only with JDK6. So make sure that when you install your Sun Java System WebServer, you choose
to install it with a pre-installed JDK6 rather than the bundled JDK. Even if you have installed your
Sun Java System WebServer with bundled JDK(5), you can still change the java.home variable to point to jdk6
in server.xml and use this sample.

Your extracted directory will look like this.

|cd groovy
|find .
./docs
./docs/index.html
./src
./src/build.xml
./src/JSR223Servlet.java
./src/docroot
./src/docroot/WEB-INF
./src/docroot/WEB-INF/lib
./src/docroot/WEB-INF/lib/groovy-1.0.jar
./src/docroot/WEB-INF/lib/groovy-engine.jar
./src/docroot/WEB-INF/lib/asm-2.2.jar
./src/docroot/WEB-INF/lib/asm-analysis-2.2.jar
./src/docroot/WEB-INF/lib/asm-attrs-2.2.jar
./src/docroot/WEB-INF/lib/asm-tree-2.2.jar
./src/docroot/WEB-INF/lib/asm-util-2.2.jar
./src/docroot/WEB-INF/lib/antlr-2.7.5.jar
./src/docroot/WEB-INF/lib/LICENSE.TXT
./src/docroot/WEB-INF/web.xml
./src/docroot/WEB-INF/sun-web.xml
./src/docroot/WEB-INF/code
./src/docroot/WEB-INF/code/groovy.groovy
./src/docroot/index.html
./src/docroot/hello.groovy
./src/ScriptServlet.java
./deploy.tcl

Please note that there are a number of jar files required for this to work. You need the groovy-engine
supplied by https://scripting.dev.java.net/ , the asm antlr and groovy jars supplied by the groovy project.

The groovy-webapp.war will be created in the groovy directory when you run ant from inside
the src. This war file can be deployed on the webserver using the wadm.

wadm  -u admin -f deploy.tcl

Once the deployment goes through, you will be able to access the js file using the url

http://yourserver:port/groovy/hello.groovy 


Comments:

Post a Comment:
  • HTML Syntax: NOT allowed
About

blue

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today