Using JSP for HTML layout for a Jython Servlet

So you know how to make Java Servlets with Jython and find it a useful alternative for programming in pure Java. I agree. It's just that there comes a point in (webapp) prototype's lifecycle where the source code has become so littered with HTML snippets that you begin to wonder whether there's some way to make things cleaner. (This, by the way, is roughly how all those templating engines for Python got started.)

My purpose is to give you a viable alternative for the layout part, namely using JSP ("Java Server Pages" or whatever-the-hell they are really called) and JSTL in combination with HttpServletRequest objects and Servlet forwarding. I do not claim that this is a better way to do HTML templating than, say, with some Python templating engine, but this is one idiomatic way how these things are done with Java. It's also pretty easy this way.

With this approach, HTML and logic is quite neatly separated, but the logic, in the Jython Servlet, is still dynamic. Change your servlet's code and the functionality is updated, on the fly.

Note that in addition to the stuff you need to do so that you can run Jython Servlets, you need to setup JSTL also.

Warning: you may find this tutorial pretty long-winded and boring. If you want to skip all the boring stuff, go straight to the final scripts and JSP files.

OK, enough with the introduction already

Our goal is to make a page that you can use to search for weblogs. (For a total of four different weblogs; not quite in the league of Google or Technorati, but we're getting there..) The design consists of one JSP page and one Jython Servlet. Let's begin with the JSP.

First, we need to "import" the JSTL tag library to the JSP page. Like this:

<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>

Then, we need the search form. At first iteration, it'll be just like any normal HTML form:

<form method="GET" action="search.py">
  <input type="text" name="q"> 
  <input type="submit">
</form>

So far, so good. Now we can go about implementing our servlet.

We make a script called, surprise surprise, search.py. It has the usual imports and declarations:

import java, javax, sys

class search(javax.servlet.http.HttpServlet):

    def doGet(self, request, response):
        pass

    def doPost(self, request, response):
        pass

To make it support both GETs and POSTs, we just delegate GETs to the POST method, like this:

    def doGet(self, request, response):
        self.doPost(request, response)

Request forwarding

We'll start with the request forwarding, which I briefly mentioned in the introduction. See, Java Servlets can forward the request to, say, a JSP page, which can then format the HTML. This way the servlet makes the number crunching and the JSP page makes the layout. The servlet "communicates" to the JSP page by putting stuff in the HTTP request (or in the HTTP session or in the servlet container context, but that's another story). And since this is possible in Java, it's possible in Jython, too.

First, you get a request dispatcher from the HttpServletRequest object given as the argument and then you make it forward the request to the page you instantiated the dispatcher to. Like this:

    def doPost(self, request, response):
        dispatcher = request.getRequestDispatcher("search.jsp")
        dispatcher.forward(request, response)

Now, if you go to the search.jsp page with your browser and submit the form, the location field in your browser should indicate that you're on the search.py page. Indeed you are, in a way, but the HTML comes from the JSP. (That's what we want.)

The real magic comes from "putting stuff in the request object". All you need to do in the servlet code is to call request.setAttribute(key, value) and the stuff is available to the JSP page.

Putting stuff in the request

The first functionality we implement with request forwarding has got nothing to do with searching, but is essential to any search page on the web, namely keeping the search term in the field after the submit. Simple stuff, but some people still seem to forget this.

We try to obtain the supplied search term in the servlet code (if there is no search term, we just put an empty string in it) and put its value to the request object:

    def doPost(self, request, response):
        searchterm = request.getParameter("q")
        if not searchterm:
            searchterm = ""

        request.setAttribute("current_q", searchterm)
                
        dispatcher = request.getRequestDispatcher("search.jsp")
        dispatcher.forward(request, response)

Now, in the JSP side, we can print this value with the JSTL tag out, like this:

<c:out value="${current_q}"/>

We can put this in the value attribute of the input field q:

<input type="text" name="q" value="<c:out value="${current_q}"/>">

This way we don't need to retype the search term each time. (This is a fundamental UI feature of search forms.)

So how about something a bit more complicated?

OK. So we have the search term already figured out in the servlet. Next, we implement the search functionality, in a method called get_results(self, searchterm). Our database is a list of tuples that consist of the name of the weblog and its URL. The code for the searching is mostly as simple as it gets, but has couple of curiosities: it uses Java's lists and hashmaps. We'll get to that later.

    sites = [
        ('Python owns us', 'http://weblog.hotales.org/portal/python'),
        ('Daily Python URL', 'http://www.pythonware.com/daily/'),
        ('online.effbot.org', 'http://online.effbot.org/'),
        ('Python News', 'http://www.python.org/')]
    
    def get_results(self, searchterm):
        search = searchterm.upper()
        results = java.util.ArrayList()

        for name, url in search.sites:
            if (name.upper().find(search) > -1 or
                url.upper().find(search) > -1):

                result = java.util.HashMap()
                result.put("name", name)
                result.put("url", url)
                results.add(result)

        return results

And we call this method from the doPost() method and put the results in the request object:

    def doPost(self, request, response):
        ...
        if searchterm:
            results = self.get_results(searchterm)
            request.setAttribute("results", results)
        ...

That's it. Back on the JSP side, we need to show the results.

Back to the JSP

First, we tell the user that these are the results for her search:

    Results for '<c:out value="${current_q}"/>':

Then, we format the results to a HTML table with the help of JSTL's forEach tag:

<table>
  <c:forEach items="${results}" var="result">
  <tr>
    <td>
      <a href="<c:out value="${result['url']}"/>">
        <c:out value="${result['name']}"/>
      </a>
    </td>
  </tr>
  </c:forEach>
</table>

What it says is that we loop through the Java collection that is in the attribute named results in the default context (the request context) and put the elements of the iteration to a variable called result. With each iteration, we make a table row and a cell in which we print a HTML link whose URL is in the result variable, in its 'url' attribute and name in 'name' attribute, respectively. The URL and name attributes correspond to the HashMap's entries. A HashMap entry with the key 'url' is accessible to a JSTL tag with the syntax like the above, ${variable['url']}.

(The more usual way in Java code is to use the so called Java Beans — objects with properties that can be accessed with getters and setters — technique. With them, you address the properties with JSTL syntax like this: ${result.name}. You can do that with Jython servlets too, but it is not that convenient anymore, since you need to compile the "beans" to Java classes yourself (well, as far as I know, anyway). It's easy to do the compiling, sure, but it's not so dynamic anymore.)

Now, if you type 'python' in the search box and submit the form, you should see results like this:

 Results for 'python':
Python owns us
Daily Python URL
Python News

That's it, then!

What we accomplished was a neat separation of the logic and the layout (not quite MVC, but enough for our purposes), but still a very dynamic way to program the logic. And most importantly, with Python!

Few technicalities

So what's with these Java's ArrayLists and HashMaps? JSTL accepts only Java's collections for its attributes, so we need to use them instead of Python's own lists and dictionaries. It's a bit awkward at times, but Java's collections have most of the functionality of Python's basic datatypes. And if we're not so concerned about the speed of the servlet, we can convert back and forward from Java's collections to Python's lists and dictionaries. Not very elegant, but works.

The complete files


28.1.2005, Jarno Virtanen. Python owns us, my weblog. Send feedback, corrections to jajvirta@gmail.com.