Vlad Patryshev

JSP As Macro Language

The Problem

JSP is a very good tool for manipulating texts - it is format-independent, and it contains the full and beautiful functionality of Java, and it is easily parameterized. It would be great to use these features for text generation in an "industrial environment". See how easy it could be: e.g. we want to produce a "year-dependent" copyrigh notice; the following code could do it:

"Copyright (c) My Precious Company 1970-<%= new java.util.Date().getYear() + 1900 %>"

can produce something like

Copyright (c) My Precious Company 1970-2003

In real life, though, text generation, in most cases, depends on environment - typically, one has to specify target platform, code version, product name, and the like. These can be stored in a database, but, most frequently, are passed to other applications that process documents via environment variables. Now, how can one pass an environment variable to a JSP? The only conceivable way is to pass them through GET or POST parameters, so that the JSP could analyze them or just include into the generated text, e.g.:

"Copyright (c) <%= request.getParameter("COMPANY_NAME")%> 
    1970-<%= new java.util.Date().getYear() + 1900 %>. 
    <%= request.getParameter("WHAT_IS_RESERVED")%> rights reserved."

This JSP would produce something like when the correct arguments are passed:

Copyright (c) Evil Empire 1970-2003. All your base rights reserved.

There is one obstacle, though: JSP are supposed to work on the web servers, not on your desktop machine. What a pity, cannot we fix it? Yes, we can. To do this, we will have to launch a JSP server, Tomcat, for instance, on our local machine and pass it our JSP code for execution, together with parameters, intercepting the output.

The Program

Let us assume that there is a program that does just that: passes a JSP to Tomcat for execution. Its can be called using the following command line:

jsp.exe -. "// Copyright (c) My Precious Company 1970-<%= new java.util.Date().getYear() + 1900 %>" > MyFinalProgram.java

On Linux the following command would probably make more sense:

cat `jsp-linux -. "// Copyright (c) My Precious Company 1970-<%= new java.util.Date().getYear() + 1900 %>"` MyProgram.java
> MyFinalProgram.java

This way we could have the copyright notice inserted into the code, not bugging developers with orders to refresh the date every Christhmas.

Parameters, say, the values of environment variables, can be passed in the command line as well, like this:

cat `jsp-linux-. "//Copyright (c) <%= request.getParameter("COMPANY_NAME")%> 1970-<%= new java.util.Date().getYear()
+ 1900 %>. <%request.getParameter("WHAT_IS_RESERVED")%> rights reserved." -COMPANY_NAME "Evil Empire" -WHAT_IS_RESERVED
"All your base"` MyEvilProgram.java.jsp > MyEvilProgram.java

As you see, the JSP contents is just a special parameter named '.'.

Above we saw a shortcut method of passing parameters. An official method to pass parameters is the following:

--param <parameter name> --value <parameter value>

It is rarely convenient to pass the whole JSP contents in a command line; the contents is usually stored in a file. Other parameters may as well reside in a file - hence the third method of passing parameters:

--param <parameter name> --file <file name>

For instance, instead of writing a three-four-line "command line", we could write the following:

cat `jsp-linux -param . -file copyright.jsp -COMPANY_NAME $COMPANY_NAME -WHAT_IS_RESERVED "All your base"` MyEvilProgram.java >MyFinalEvilProgram.java

And, at last, the obvious solution to provide a JSP to execute is to pass it via standard input, like this:

cat `cat copyright.jsp | jsp-linux -COMPANY_NAME $COMPANY_NAME -WHAT_IS_RESERVED "All your base"` MyEvilProgram.java >MyFinalEvilProgram.java

One more form of passing parameters could probably be added: --param <parameter name> --http <url> - but I doubt whether it makes any practical sense.

Here is a real-world example of a real-world JSP being applied to the contents of another file:

jsp --param . --file sampletable.jsp --param data --file sampledata.txt > sampleresult.html

And here is one more example. This time we show that this can be used for conditional compilation (see Dan Savarese's "The Case for Conditional Compilation"). The sample code from that article can be rewritten, using JSP, like this:

import java.util.*;

/***
* Heap implements a binary heap stored as an array 
* that can be used as a priority queue or for
* performing a heap sort.
*/
public class Heap 
<% if (request.getParameter("HAS_JAVA_IO_SERIALIZABLE") != null) {%>
   implements java.io.Serializable
<%}%>
{
<% if (request.getParameter("HAS_JAVA_UTIL_COMPARATOR") != null) {%>
   public static interface Comparator {
      public int compare(Object o1, Object o2);
      public boolean equals(Object obj);
   }
<%}%>
....

The Trick

Well, how does it work then? When jsp.exe starts, it retrieves a zip file with Tomcat from its resources and installs Tomcat in a working directory - unless it is already installed. Then it launches Tomcat on a special port (8079). Inside Tomcat's web directory there is a very simple but powerful JSP program - Universal JSP. Its code looks like this:

<%
 /**
  *   Universal JSP
  */

 String value = (String)request.getParameter(".");
      
 if (value != null) {
    String filename = session.getId() + ".jsp";
    java.io.File sourcefile = new java.io.File("webapps/ROOT", filename);

    try {
      java.io.PrintWriter pw = new java.io.PrintWriter(
                               new java.io.FileOutputStream(sourcefile));

      pw.write(value);
      pw.close();
%><jsp:include page="<%=filename%>" flush="true"/><%

  } catch (Exception e) {
    out.print("***** Error: problems with file permissions for this jsp<br>" + 
              e + "<br>");
    e.printStackTrace(new java.io.PrintWriter(out));

  }
} else {
  %>;)<%
}%>

jsp.exe sends to Universal JSP a request that contains all the parameters and the body of the JSP that has to be executed. The normal output from Universal JSP is that of the JSP that we had just passed - and it is caught by jsp.exe and redirected to the standard output.

Download

Download source, Windows, Linux, binary jar. It is free.