PolyJsp

### What is PolyJsp?

PolyJsp is an extensible JSP implementation designed to support multiple scripting languages and multiple JSP versions.

Completely based on XML and XSL, PolyJsp currently supports Java, Javascript and WebL as scripting languages. Support is provided for the latest JSP spec (0.92), with some exceptions documented below.

PolyJsp is a free, open source product. Developers are encouraged to contribute to its development and enhancement.

Comments and suggestions can be sent to Ricardo Rocha.

#### Background

PolyJsp is a direct descendant of ESP (EcmaScript Pages), a servlet used for authoring dynamic web pages in EcmaScript.

What started as a "simple" effort to make ESP JSP-compliant eventually led to a polyglot, extensible implementation.

ESP users are encouraged to upgrade to PolyJsp as its EcmaScript processor is backwards-compatible with ESP and no further enhancements to this product will be developed once PolyJsp is released.

#### Objectives

PolyJsp aims at:

- Coping with version incompatibilities introduced by the evolution of the JSP standard.
- Delivering JSP's promise of supporting multiple scripting languages

The first goal is relevant because most of current JSP implementations are based on the aging 0.91 spec and, despite their established code base, their syntaxes may be rendered obsolete as JSP continues to evolve.

Multiple language support, on the other hand, is desirable to leverage potential authors' previous familiarity with "legacy" languages. It is also called for to support web publishing of dynamic content generated by specialized applications written in field-specific languages.

In the case of Javascript, in particular, HTML authors will certainly benefit from using a single language for both server and client side scripting.

Note: throughout this document the names JavaScript and EcmaScript are used interchangeably

#### Features

Thanks to PolyJsp's extensible design, new languages and versions can be easily added in a mostly declarative way.

In principle, any language for which a Java interpreter exists can be added to PolyJsp's repertoire. There are currently over 60 such languages ranging from the classic and simple to the experimental and sophisticated. For more information on language availability, see Robert Tolksdorf's Programming Languages for the Virtual Java Machine.

Languages for which there is no Java interpreter could be supported by means of simple inter-process communication (e.g. stdin/stdout, CORBA-based IPC). Another option is the JDBC-based execution of statement blocks written in procedural extensions to SQL, such as Oracle's PL/SQL.

New language processors can be easily added as specialized Java classes or as XML declarative language definitions "scripted" with Javascript. The scripted approach has been tested successfully to provide support for the Java language itself as well as for (Dartmouth) Basic.

Adding a new version is even easier as it just entails writing a declarative definition of JSP version tags (a small XML file) and one or more language-specific templates (XSL stylesheets optionally adorned with code generation Javascript extensions)

Version tag syntax files are dynamically loaded by the parser at runtime on a per-script basis; thus, PolyJsp can support multiple versions simultaneously.

Source code generators are implemented as XSL stylesheets that transform the DOM tree produced by the JSP parser into an equivalent program written in the target scripting language.

#### Current Status

In its current alpha version, PolyJsp supports Java, Javascript and WebL, as well as the JSP 0.92 spec.

JSP directives (<%@%>), declarations (<SCRIPT RUNAT="server">), scriptlets (<%%>) and expressions (<%=%>) are fully supported.

Support for the <BEAN> and associated tags is currently under development together with the runtime library that provides bean management and introspection.

PolyJsp introduces a few (optional) extensions to JSP:

- A <%@ version="..." > directive
  Useful to instruct the parser to use a version other than the default (currently 0.92)
- A <%@ contenttype="..."> directive
  Useful for cases in which the expected output MIME type is other than text/html
- A <header name="..." value="...">
  Useful to set response headers. Example: set expires to control browser chaching

For the Java language, PolyJsp provides complete servlet generation, cataloging and compilation as well as automatic reloading. Currently, the built-in Sun compiler is used.

For Javascript, PolyJsp uses FESI and provides persistent source code generation, automatic reloading and evaluator pooling to speed up execution. Despite their interpreted nature, Javascript pages perform at speeds comparable to their Java counterparts.

For WebL, PolyJsp provides persistent source code generation and automatic reloading. Like in the Javascript case, WebL pages perform at speeds comparable to Java.

### How does PolyJsp work?

The major PolyJsp components are:

- Extensible Parser, Written in JavaCC
- Language Processor. An interface implemented either procedurally or declaratively
- Source Code Generator. Implemented as an XSL stylesheet processor
- Script Executive. Implemented as a Java servlet and a script cache
- Runtime Environment. Java library providing generic support for JSP constructs

These components are integrated within the following flow of execution:

1. A JSP script request is received by the servlet executive

2. The request URL is translated to a file reference locally accessible to the servlet engine

3. The resulting file name is looked up as a JSP script in a script cache. If found (and current), control is passed to its associated object program; otherwise, the JSP parser is invoked to process it

4. Based on the JSP script's version, the parser translates it into an equivalent, language-independent DOM tree representation (XML)

5. Based on the JSP script language and version, an XSL stylesheet is selected and applied to the DOM tree produced by the parser. The resulting source program is stored in a stage directory where program files are kept for subsequent PolyJsp instantiations

6. The compile handler for the script language, if provided, is invoked to compile the source program and generate an object program. This file is also stored in the stage directory

7. The load handler for the script language is invoked to load the corresponding source/object program into memory and place it in the script cache

8. The last modification dates of the original JSP script and its associated source/object program file are compared. If the JSP script is more recent than the program, it is reparsed, recompiled and reloaded. During this process, the unload handler for the script language, if provided, is invoked so that the object program is invalidated and removed from memory. The corresponding script cache entry, if it existed, is refreshed

9. The execute handler for the script language is invoked on the object program with the servlet's request and response objects as arguments

10. Program output (which may be buffered) is sent to the browser

During this process, error conditions are trapped and reported to the user either through a script-specified error page or a servlet-generated HTML error message. Output buffering may be used to avoid garbling program output and servlet error pages.

#### Extensible Parser

As a general rule, version-specific JSP tags (such as <USEBEAN> or <DISPLAY>) are not built into the parser's lexical productions; rather, they are made special by means of a dynamically loaded XML version tag file. This is the esence of the version independence mechanism.

JSP directives (<%@ ... %>), scriptlets (<% ... %>) and expressions (<%= ... %>) are directly recognized by the parser.

Also recognized by the parser is the <SCRIPT RUNAT="server"> tag. This is a special case because the <SCRIPT> tag may be legitimately present in the client-side code as well.

Finally, NCSA directives (such as <!--# include>) are also recognized by the parser, though this may be disabled to allow for the underlying web server itself to process them.

JSP version selection is performed at runtime and on a per-script basis. Therefore, the same PolyJsp instance can support multiple versions simultaneously.

A PolyJsp propietary directive tag (<%@ version="..." >) has been added to allow scripts to specify a particular version. In absence of explicit version selection, the current implementation defaults to 0.92, though this can be overriden by setting the polyjsp.default.version system property.

In the same vein, and according to the spec, the language directive defaults to java; again, this can be changed by setting the polyjsp.default.language system property.

Depending on version selection, the parser dynamically loads the corresponding tag definition xml file. For version 0.92 this file contains: ``

           <tags>
             <tag name="comment" empty="true" body="true"/>
             <tag name="declaration" empty="false" body="false"/>
             <tag name="errorpage" empty="true" body="false"/>
             <tag name="import" empty="true" body="false"/>
             <tag name="language" empty="true" body="false"/>
             <tag name="boilerplate" empty="true" body="true"/>
             <tag name="display" empty="true" body="true"/>
             <tag name="excludeif" empty="false" body="true"/>
             <tag name="expression" empty="true" body="true"/>
             <tag name="include" empty="true" body="true"/>
             <tag name="includeif" empty="false" body="true"/>
             <tag name="loop" empty="false" body="true"/>
             <tag name="scriptlet" empty="true" body="true"/>
             <tag name="setfromrequest" empty="true" body="true"/>
             <tag name="setoncreate" empty="true" body="true"/>
             <tag name="usebean" empty="false" body="true"/>
             <!-- Non-standard tags added by PolyJsp -->
             <tag name="contenttype" empty="true" body="false"/>
             <tag name="header" empty="true" body="false"/>
           </tags>

where boolean attribute usage is:

Attribute | Meaning
--- | ---
empty | This is an empty tag, not containing nested subtags or text
body | This tag is not a directive or declaration, must be made part of the generated source program

Beyond this clasification (needed only to control parsing recursion and tree generation), the parser does not attempt to interpret the actual contents of JSP tags.

All remaining JSP script content (typically HTML tags and plain text) is treated as boilerplate and inserted verbatim into the generated DOM tree body.

For details about the structure and contents of JSP DOM trees, please see the JSP 0.92 DTD.

Version DTD's are used used to validate JSP tag attribute values as well as tag and boilerplate nesting during DOM tree construction.

#### Scripting Language Definition

Scripting languages are defined in an XML file loaded at startup. For Java and Javascript this file contains: ``

          <languages>
            <language
              name="java"
              source-extension="java"
              object-extension="class"
              processor="org.plenix.jsp.language.java.JavaProcessor"
            >
              <version name="0.92" stylesheet="java-0.92.xsl"/>
            </language>
            <language
              name="ecmascript|javascript"
              source-extension="es"
              processor="org.plenix.jsp.language.ecmascript.ESProcessor"
            >
              <version name="0.92" stylesheet="ecmascript-0.92.xsl"/>
              <version name="0.91" stylesheet="ecmascript-0.91.xsl"/>
              <param name="extension" value="FESI.Extensions.BasicIO,FESI.Extensions.FileAccess,FESI.Extensions.Database"/>
            </language>
          </languages>

where a processor is a Java class implementing the LanguageProcessor interface.

Alternatively, language processors may be defined embedding EcmaScript handlers in the <language> tag as follows: ``

          <language name="..." source-extension="..." object-extension="..." />
    	...
            <init-script>
    	  // Init logic
    	  ...
            </init-script>

            <compile-script>
    	  function compileProgram(baseName, path) { ... }
            </compile-script>

            <load-script>
    	  function loadProgram(baseName, path) { ...; return objectProgram }
            </load-script>

            <unload-script>
    	  function unloadProgram(objectProgram) { ... }
            </unload-script>

            <execute-script>
    	  function executeProgram(objectProgram, request, response) { ... }
            </execute-script>
          </language>

Function names and signatures must be specified as shown above. However, only the loadProgram and executeProgram functions are required.

For an example of a scripted language processor, please see the simplified scripted Java language definition.

#### Source Code Generation

Source code is generated by applying XSL stylesheets to DOM trees built by the parser. A separate XSL stylesheet is needed for each language/version pair.

For an example of a code generation XSL stylesheet, please see the simplified EcmaScript 0.92 stylesheet.

Thus, given the following input JSP script: ``

          <%@ version="0.92" %>
          <%@ language="javascript" %>
          <%@ contenttype="text/html" %>

          <header name="expires" value="Tues, 01 Jan 1980 00:00:00 GMT">

          <%@ import="util.es" %>

          <SCRIPT RUNAT="server">
            var accessCount = 0;
          </SCRIPT>

          <%
            var today = new Date();
          %>

          <HTML>
          <HEAD>
          <TITLE>A PolyJsp Demo</TITLE>
          </HEAD>
          <BODY BGCOLOR="#ffffff">
          <H1><FONT COLOR="navy">A PolyJsp Demo</FONT></H1>
          <P>
          This page has been accessed <%= ++accessCount %> times so far.
          <P>
          Generated by PolyJsp on <%= today %>
          </BODY>
          </HTML>

PolyJsp's parser will produce a DOM tree equivalent to the following XML document: ``

          <jsp-script name="sample.jsp">
            <version>0.92</version>
            <language>javascript</language>
            <import>util.es</import>
            <contenttype>text/html</contenttype>
            <header name="expires" value="Tues, 01 Jan 1980 00:00:00 GMT"/>
            <declaration><![CDATA[
    	  var accessCount = 0;
    	]]></declaration>
            <body>
              <scriptlet><![CDATA[
                var today = new Date();
              ]]></scriptlet>
              <boilerplate><![CDATA[\n\n<HTML><HEAD><TITLE>A PolyJsp Demo</TITLE></HEAD><BODY BGCOLOR=\"#ffffff\">\n]]></boilerplate>
              <boilerplate><![CDATA[<H1><FONT COLOR=\"navy\">A PolyJsp Demo</FONT></H1><P>\n]]></boilerplate>
              <boilerplate><![CDATA[This page has been accessed \n]]></boilerplate>
              <expression><![CDATA[++accessCount]]></expression>
              <boilerplate><![CDATA[ times so far.\n]]></boilerplate>
              <boilerplate><![CDATA[<P>\n]]></boilerplate>
              <boilerplate><![CDATA[Generated by PolyJsp on \n]]></boilerplate>
              <expression><![CDATA[today]]></expression>
              <boilerplate><![CDATA[</BODY></HTML>\n]]></boilerplate>
            </body>
          </jsp-script>

When the above mentioned EcmaScript 0.92 stylesheet is applied to this DOM tree, the following Javascript source file will be generated: ``

          // User imports
          load("util.es");

          // Declarations
          var accessCount = 0;

          function service(servletrequest, servletresponse) {
            var headerCount = 0;
            var headers = new Array();

            var errorPage = null;
            var contentType = "text/html";

            var input = servletrequest.getReader();
            var output = servletresponse.getWriter();

            servletresponse.setContentType(contentType);

    	headers[headerCount++] = new Header("expires", "Tues, 01 Jan 1980 00:00:00 GMT");

            for (var i = 0; i < headers.length; i++) {
              servletresponse.setHeader(headers[i].name, headers[i].value);
            }

            // User code
            var today = new Date();
            output.print("\n\n<HTML><HEAD><TITLE>A PolyJsp Demo</TITLE></HEAD><BODY BGCOLOR=\"#ffffff\">\n");
            output.print("<H1><FONT COLOR=\"navy\">A PolyJsp Demo</FONT></H1><P>\n");
            output.print("This page has been accessed \n");
            output.print(++accessCount);
            output.print(" times so far.\n");
            output.print("<P>\n");
            output.print("Generated by PolyJsp on \n");
            output.print(today);
            output.print("</BODY></HTML>\n");
          }

### Download

The adventurous may download the current alpha version in polyjsp.tgz (962k). Please note that, for now, this is only a (functional) daily snapshot.

The first beta version will be made available shortly with complete Javadoc documentation.

PolyJsp requires the following additional tools:

- IBM's Xml4j XML processor
- Keith Visco's Xslp XSL processor
- Jean-Marc Lugrin's FESI EcmaScript interpreter
- Optionally, Compaq's WebL interpreter

These libraries are included in the polyjsp.tgz distribution file. Note that, currently, PolyJsp requires the specific versions of these products that have been included in the distribution. Work is under way to upgrade PolyJsp to the latest version of the XML and XSL processors.

### Installation

To install PolyJsp you must have:

- A functional, configured web server
- A functional, configured servlet engine
- A 1.1 JDK that includes the sun.tools.javac compiler

PolyJsp requires an installation directory that must be accessible to your servlet engine. This structure is contained in the polyjsp.tgz distribution file with the following subdirectories:

- config. This directory contains all xml and xsl resource files used by PolyJsp. Your servlet engine process must have read access to this directory.
- stage. This directory will hold generated source and object programs. Your servlet engine process must have read/write access to this directory.
- lib. This directory contains the polyjsp.jar class library.

To configure PolyJsp in your web server/servlet engine environment follow these steps:

1. Extract polyjsp.tgz to a location accessible to your servlet engine
2. Add library polyjsp.jar to your CLASSPATH and/or servlet engine class search path
3. Add libraries xml4j.jar, xslp.jar and fesi.jar to your CLASSPATH and/or servlet engine class search path
4. Register class org.plenix.jsp.JspServlet with your servlet engine
5. Add the following init parameters for servlet org.plenix.jsp.JspServlet:

   Parameter | Required? | Default Value | Contents
   --- | --- | --- | ---
   configurationDirectory | Yes |  | Directory containing PolyJsp XML/XSL resource files. This is normally subdirectory config under your PolyJsp installation directory
   stageDirectory | Yes |  | Directory to place generated source and object files. This is normally subdirectory stage under your PolyJsp installation directory
   defaultLanguage | No | java | Language to be used when JSP scripts do not explicitly specify a language
   defaultVersion | No | 0.92 | Version to be used when JSP scripts do not explicitly specify a version

6. Associate an extension with servlet org.plenix.jsp.JspServlet. Typically, but not necessarily, this extension is .jsp.

### Related Free/Open-Source Projects

- ASPy. Non-JSP development tool for building dynamic web pages in Python.
- ESP. Non-JSP development tool for building dynamic web pages in EcmaScript.
- GNUJSP. Free implementation of JSP 0.91 for Java
- Resin. JSP implementation for (compiled!!) Javascript
- SJSP. A JSP 0.92 implementation for Java that uses compile-time introspection
- zJSP. Free implementation of JSP 0.91 for Java

### Credits

Special thanks to Peter Saitz (Optical Arts) for his invaluable quality control and implacable bug-hunting.

Special thanks to Alfonzo Forgione (AF Advanced Systems) for his sponsorship and continued support to PolyJsp.

Thanks to Centralogic for hosting the Plenix site. Thanks to all ESP users whose feedback and encouragement made PolyJsp possible.

Thanks, too, to all developers behind the free/open-source products used to develop PolyJsp:

- Jean-Marc Lugrin's FESI
- Keith Visco's Xslp
- Xml4j
- Javacc
- Jserv
- Apache
- Linux