Skip to main content

Dynamic Interaction with Your Web Application

September 23, 2005

{cs.r.title}









Contents
Unveiling the Mystery:
Introducing the HookServlet
Installing the HookServlet
Running the HookServlet
   Example: Getting the System's Properties
   Example: Listing the .jars to Start Up Tomcat
Conclusions
Resources

Imagine you are working on a web application. A collection of
servlets, HTML pages, classes, .jars, and other resources is now
shaping into a fully complete application running on a web server.
But something is just not right. Perhaps you are trying to
investigate why certain forms seem to submit correctly but the
database is not updating, or perhaps a generated web page reports
that the server is in a state you would bet it cannot be in. Whatever
the problem, you know you could gather a better understanding if
only you could have access to the running servlet and check the
current state of a few objects. Perhaps you could even temporarily
fix it while you're at it.

In this article I will show you the code of a simple servlet.
This servlet accepts just one attribute via the POST method. An
equally simple HTML page consisting of a text area and a submit
button is written to interact with it. Yet despite the simplicity
of these two components, what we will have is a powerful tool to
interactively analyze the state of any web application.

Unveiling the Mystery: Introducing the HookServlet

The servlet we want to write needs to be able to hook into any
resource provided by the web server and allow the user to inspect
any part of it. To be able to gather the required information, it
might require flow-control constructs and loops. This leads to one
solution: to make the servlet able to execute a script sent by the
client (i.e., the browser). The script will have not only the
ability to access any server resource, but by manipulating host
objects representing a HTTP request and response, it will be
capable of communicating back to the client.

There are several scripting languages for Java that would be up
to the job, and in this article we will be using "http://www.mozilla.org/rhino">Rhino. Of course, if you are a
big fan of any of the other many scripting languages for Java, it would
not be too hard to port the servlet to an equivalent implementation
in Jython, Groovy, or similar.

Rhino is a popular open source JavaScript engine written in
Java. Using its API, it is possible with a few lines of code to
create a JavaScript interpreter able to evaluate scripts like
this:


// Script n.1
// printing the current time on the standard output stream
java.lang.System.out.println(new java.util.Date());

// script n.2
// writing a log file
var FileWriter = Packages.java.io.FileWriter;
var fw = new FileWriter("log.txt");
fw.write("hello from rhino");
fw.flush();
fw.close();

Since JavaScript is used so extensively in HTML pages to
manipulate DOM objects, people tend to get confused when it is used
in another environment. However, the language itself is
platform-neutral. The Rhino implementation gains access to any
class by defining two top-level variables named
Packages and java. The properties of the
variable Packages are all of the top-level Java packages,
such as java, org, and com.
The variable java, instead, is just a handy shortcut for
Packages.java.

Let's have a look at how we can integrate the Rhino
engine into our servlet.

import java.io.PrintWriter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.EcmaError;

public class HookServlet extends HttpServlet
{
  public void doPost(HttpServletRequest httpRequest, 
    HttpServletResponse httpResponse) throws IOException
  {
    PrintWriter out=httpResponse.getWriter();
    httpResponse.setContentType ("text/html");      
    httpResponse.setHeader("Cache-Control","no-cache");   
    String code=httpRequest.getParameter("serverjavascript");
    try
    {
      Context context= Context.enter();
      Scriptable scope=context.initStandardObjects(null);
      Scriptable jsArgs1=Context.toObject(out,         scope); 
      Scriptable jsArgs2=Context.toObject(httpRequest, scope); 
      Scriptable jsArgs3=Context.toObject(httpResponse,scope); 
      scope.put("out",         scope,jsArgs1);
      scope.put("httpRequest", scope,jsArgs2);
      scope.put("httpResponse",scope,jsArgs3);
      context.evaluateString(scope, 
                             code, 
                             "JAVASCRIPT-CODE", 
                             1, null);
      
      // flushes and closes the output stream
      out.flush(); 
      out.close(); 
    }
    catch(EcmaError e)
    {
      ByteArrayOutputStream baos = 
         new ByteArrayOutputStream();
      PrintWriter pw = new PrintWriter(baos);
      e.printStackTrace(pw);
      pw.flush();
      out.println("<html><body><pre>");
      out.println("Exception caused by serverside"+
                    " script execution:\n");
      out.println(new String(baos.toByteArray())); 
      out.println("</pre></body></html>".getBytes()); 
      out.flush();
      out.close();
    }
    finally 
    {
      Context.exit();   
    }
  } 
}

The doPost method is overridden to process the HTML
form posting. In it, the following actions are performed:

  • From the httpRequest object, we retrieve the
    serverjavascript parameter.
  • A Rhino Context is initialized.
    Context is an environment for the script to run
    in.
  • The variables out, httpRequest,
    and httpResponse are made available to the script.
  • The script is executed.

The exception handling ensures that the stack trace of any
exception caused by the script is passed back to the client.

To interact with the HookServlet on the client side,
all we have to do is to create an HTML page with a
textarea to host the script, and a button to post it.
Figure 1 shows what it looks like:

height="629" alt=
"The HTML page that interacts with the HookServlet" />
Figure 1. The HTML page that interacts with the
HookServlet

Installing the HookServlet

Deploying the HookServlet in your servlet container
or web server is probably no more work than deploying any other
servlet, and can be easily achieved by packaging the various parts
into a WAR file. However, to use the servlet to interact with your
application, you probably want to install an instance of the
HookServlet into your existing WAR file. In this way,
the HookServlet will share the same class-loader as
your web application and will have access to the same classes and
resources.

The rules to remember are:

  • The client-side files (the HTML file hookpage.html and the
    image hook.jpg) are stored in the top-level directory.
  • The HookServlet is stored in the WEB-INF/classes
    directory.
  • The Rhino library (consisting of two files: js.jar and
    xbeans.jar) is added in the WEB-INF/lib
    directory.

In the deployment descriptor web.xml, we just add a few
lines to enable the servlet.

  ...
  <servlet>
     <servlet-name>HookServlet</servlet-name>
     <servlet-class>HookServlet</servlet-class>
  </servlet>
  <servlet-mapping>
     <servlet-name>HookServlet</servlet-name>
     <url-pattern>/hook</url-pattern>
  </servlet-mapping>
  ...

The full path to access the HookServlet will now
depend on the name of your WAR file and the web server's URL. For
example, if we are running Apache Tomcat on the standard 8080 port
and the WAR file is named webapplication.war, the path to the
servlet would be:

http://localhost:8080/webapplication/hook

And the path to the HTML page would be:

http://localhost:8080/webapplication/hookpage.html

Running the HookServlet

Once you have the HookServlet installed in your web
server or servlet container, you can start exploring the resources,
libraries, and API by writing simple programs.

In this section, I will provide two short examples. I will
deliberately keep them uncomplicated to avoid generating any
confusion, confident that you can see beyond their simplicity and
get the gist of this technique.

Example: Getting the System Properties

The first example shows how to get the get System properties
with which your web server was started. This is particularly useful
when a certain behavior of the system is dependant on a System
property that might not have been properly set.

// importing.
var System = java.lang.System;
var Arrays = java.util.Arrays;

// getting the properties and ordering them.
var props = System.getProperties();
var keys =  props.keySet().toArray();
Arrays.sort(keys); 

// printing the content.
out.println("<HTML><BODY><PRE>");
for (var i=0;i<keys.length;i++)
{
   out.println(keys[i]+" --> "+ props.get(keys[i]));
}
out.println("</PRE></BODY></HTML>");

Copy and paste the example above and you will get an answer that
looks something like Figure 2:

height="618" alt=
"Output from the HookServlet: showing the System's Properties" />

Figure 2. Output from the HookServlet: Showing the System
Properties

Example: Listing the .jars to Start up Tomcat

The second example assumes you are running Apache Tomcat as your
web server. The code first gets the ClassLoader
instance responsible for loading the HttpResponse
class, and then (because it knows the classLoader instance is a
subclass of the URLClassLoader) gets and prints each
URL in it.

var loader = httpResponse.getClass().getClassLoader();
var urls = loader.getURLs();
out.println("<pre>");
for (var i=0;i<urls.length;i++)
{
   out.println ( urls[i] ); 
}
out.println("</pre>");

This produces an output like Figure 3:

alt=
"Output from the HookServlet: .jars used to start up Tomcat" />

Figure 3. Output from the HookServlet: .jars used to start up
Tomcat

Conclusions

In this article, we have shown a powerful technique that allows
the developer to interact dynamically with any Java
web application. The technique is based on the idea of sending
scripts from the client side to run on the server side. The
implementation we presented uses JavaScript to interpret the server-side scripts, although with a little effort it could be easily
adapted to use a different scripting language for Java.

The final result is a useful tool to help you working with any
Java web application at any stage of the development: the
HookServlet.

A final observation: the HookServlet is so
intrinsically powerful that almost any servlet could be replaced by
it ("almost" because in our current implementation the JavaScript
can only override the doPost method and none of the
other methods in the servlet). All of the work would therefore be
done in the HTML page that would embed both the client-side and
the server-side scripts. While this might not be the best solution
in many cases, there's much to be said about having client-side and
server-side code in one unique place and treating the server-side
as a collection of .jars and other resources ready to be
exploited.

Resources

width="1" height="1" border="0" alt=" " />
Lorenzo Puccetti is a software developer/architect living and working in London, England.
Related Topics >> Programming   |   Web Development Tools   |