Document last updated January 21, 2001. We are very interested in feedback that would make these materials better. Feel free to write the author, Jonathan Revusky.
In this document, we dissect the various parts of this first minimal example. We assume that you managed to go through the concrete steps outlined in the accompanying instructions that explain how to build and deploy the example.
The minimal servlet consists of the following parts:
HelloServletInteraction.java
hello.html
web.xml
that provides the servlet's
initialization parameters and also defines a mapping of the servlet into the
servlet container's namespace.
Conventionally, to create a new servlet, you define your own concrete subclass of
the abstract class javax.servlet.http.HttpServlet
and implement the
methods doGet() and doPost(), as well as init() and destroy() if it is necessary
to initialize/cleanup any resources used while the servlet is running.
By contrast, the Niggle framework already provides a concrete
subclass of the abstract class HttpServlet that you can use directly:
com.niggle.servlet.NiggleServlet
. (clarifying note)
So really, the Niggle framework's comparable subclassing point is
com.niggle.servlet.ServletInteraction
. An instance of this class
represents the servicing of a single request to your servlet. A new instance of your
ServletInteraction subclass is instantiated each time the doPost() or doGet()
method in com.niggle.servlet.NiggleServlet
is invoked. Basically, a NiggleServlet
instance delegates the real work of processing the request to an instance of
ServletInteraction. And since com.niggle.servlet.ServletInteraction is an abstract class, you
must define a concrete subclass and also, you must tell Niggle about it via your servlet engine's
configuration mechanism.
Below is the full code of HelloServletInteraction.java:
import com.niggle.servlet.*; import javax.servlet.http.*; import java.io.IOException; public class HelloServletInteraction extends ServletInteraction { /** * The obligatory constructor. Your ServletInteraction subclass must have a * constructor with this signature. This is the case even though we don't * actually have to do anything in the constructor (besides invoking * the superclass's constructor, that is.) */ public HelloServletInteraction(HttpServletRequest request, HttpServletResponse response, NiggleConfig config) throws IOException { super(request, response, config); } /** * The "default" action handler. This will be invoked * if there is no "action" parameter defined in the request parameters. * Otherwise, if your request defines a parameter "action=foo" * the framework will take this as a signal to invoke the * ServletInteraction's execFoo() method. Our simple "Hello, World" * servlet has only a single "default" action so we only define execDefault(). */ public void execDefault() throws IOException { this.page = getPage("hello.html"); page.expose("title", "Hello, World"); page.expose("message", "Welcome to the brave new niggle world!"); } }
Take a look at the execDefault()
method above. In the first of the three lines,
the page
variable is set and in the next 2 lines, a couple of template
variables are set or exposed -- title
and message
.
So, what is going on here? Well, as you likely now at this point, the philosophy
of the Niggle framework is based on a rigorous separation of presentation from
application logic. Thus, the presentation details of any output to the client are
encapsulated in a page template, which is completely separate from the application
logic per se. So, the ServletInteraction class has a member variable called
page
and this must be set in the course of any execXXXX
method.
The above code is a case in point.
In short, the ServletInteraction instance has a member variable called page
that encapsulates this template construct. Note that the page variable
is an instance of the com.niggle.templates.Page
interface.
The last section deliberately (or necessarily) left certain questions unanswered. The code in the above example assumes the existence of a page template corresponding to "hello.html" and assumes a mechanism for exposing the template variables in the page was left deliberately vague.
Now it is time to get concrete about this. Below is the trivial "hello.html" template that uses the 2 template variables that the java code sets.
<html> <head> <title>${title}</title> </head> <body> <P>Message: ${message}</P> </body> </html>
Note that the above page template code looks just like an HTML page.
Well, it basically is an HTML page. The only thing special about it is
the presence of the two strings ${title}
and ${message}
that are treated specially. As you likely notice, they correspond to the 2
template variables defined in the java code above via the calls to
page.expose()
.
Essentially, the difference between a page template
and a plain old static HTML page is that some of the content is determined
dynamically. Note that, by default, the niggle framework leverages
the open-source Freemarker
template engine. A key feature of freemarker's syntax is that strings enclosed
in ${...}
are escaped, i.e. determined dynamically.
The third and last element of our minimal example is the configuration file that tells the servlet server about our servlet.
<?xml version="1.0"?> <web-app> <servlet> <servlet-name> helloniggle </servlet-name> <servlet-class> MinimalNiggleServlet </servlet-class> <init-param> <param-name>INTERACTION_CLASS</param-name> <param-value>HelloServletInteraction</param-value> </init-param> </servlet> </web-app>
This file defines the mapping of "helloniggle" to our actual servlet
class MinimalNiggleServlet
. In the later, more complex examples, we
will need to define more configuration parameters.
In this example, we actually create a trivial subclass of NiggleServlet even though it is
not strictly necessary. And, you probably noticed that our subclass contains no functionality at all!
In fact, as you might suspect, we could have specified com.niggle.servlet.NiggleServlet
as our servlet class in the web.xml file and the example would have worked equally well.
The reasoning behind creating a trivial subclass of NiggleServlet is really hypertechnical in nature. In the above example, we put all our classes, libraries, and supporting files under the WEB-INF directory. This means that we expect it all to be loaded via the servlet context classloader, not the "global" system classloader (i.e. system-level CLASSPATH). There is a looming gotcha here though. You see, the you will likely find that the most natural way of developing and deploying a niggle app will likely be to add niggle.jar and the other libraries it depends on to the system CLASSPATH. What this means is that classes in the com.niggle.* space will be loaded by the system classloader. However, it turns out to be more convenient if your servlet subclass is loaded by a servlet-specific classloader. That is why you might want to do as we do here, and define a completely trivial subclass of NiggleServlet and put it under WEB-INF/classes. This corresponds to the recommended deployment servlet deployment model, and, for one thing, allows the dynamic reloading of servlets available in most servlet runners to work. This is a feature that can help make your development and deployment cycles more efficient.
A related gotcha is that if your niggle-based servlet is loaded by the system classloader, your ServletInteraction class and the related page templates must also be located relative to the system CLASSPATH, since the attempt by the com.niggle.servlet.* code to load them will fail, as these resources do not exist in the context of the system classloader!
Back to main text