Monkeys at Keyboards: Java-Fu
© Michael James Heron
Topic: Java Programming
Level: 3
Version: beta

I have a giant brain that is able to reduce any complex machine into a simple yes or no answer.
Space Ghost

13 - Java Servlets

PreviousTable of ContentsNext
Forum


Chapter Objectives

By the end of this chapter, the reader will be able to:



    13.1

    Introduction

    Before we can look at developing SOAP servers, we need to take a detour and look into another aspect of the Java platform - that of Servlets. Servlets can be thought of as applets that run on a server - they simplify the work of setting up web applications, and underpin may of the web's most successful applications.

    Servlets allow for Java developers to create applications that fulfil the role of traditional perl programs... they do however offer a number of advantages over perl, at least as far as Java programmers like ourselves are concerned.

    • They are substantially more efficient, and offer opportunities for optimisation that are just not possible using a CGI based architecture.
    • They are substantially more convenient, offering a large range of inbuilt programming structures and the opportunity for easily adding more.
    • They are substantially more powerful, allowing the developer to do things that simply cannot be done with perl.

    So, on with the show!

    13.2

    Installation

    As with many things in this module, we need to do some setup before we can start actually developing our code. The standard Java packages are comprehensive for most tasks, but we're looking at advanced technologies that are built on top of what is available by default - the pattern of supplementing the standard Java packages with custom sets of packages will be a familiar part of progressing as a Java developer.

    The archive we need to incorporate is called, fittingly, servlet.jar. This can be downloaded from the module website, and contains all of the classes and packages that we are going to be making use of through the course of this application.

    As far as development is concerned, that's all we need to do to write our servlets - but actual deployment is substantially more complicated. See the appendix to the book for details.

    I suggest the Tomcat standalone server system - a windows installer for this may be found on the module website. Setting this up properly can be difficult, since the interface is not entirely intuitive... however, it does neatly encapsulate all of the functionality you could hope for, at least as far as testing and development is concerned.

    13.3

    My First Servlet

    Okay, let's now turn to actually writing a servlet - the process for this is simply a variation on what we've been doing throughout the module - we import a set of new packages, and develop a set of new classes. Hopefully by now you are coming to see the power of object orientation in a way that was perhaps not apparent in second year - with previous programming paradigms, it was necessary to either develop libraries like this yourself, or spend lots of time trying to integrate the work of others into your own applications. A nightmare, and no mistake!

    For our first servlet, we need two new packages:

    import javax.servlet.*; 
    import javax.servlet.http.*;

    These may be found in the servlet.jar file mentioned above - if the compiler complains about not being able to find these, then the archive has not been incorporated correctly.

    Within these packages is a class called HttpServlet that serves as a base for Servlet programs in the same way JApplet serves as a base for applets... we extend from this to provide us with all our required functionality:


    public class MyFirstServlet extends HttpServlet {
    }

    Within our class, we define a method called doGet, which returns the HTML document that will be displayed when the servlet is accessed through a browser. This method takes two parameters, only one of which we will use in our first example:

    public void doGet (HttpServletRequest request, HttpServletResponse response) 
    {
    }

    Both of the objects passed to this method have lots of powerful functionality that go with them, but for now we'll look only at one of these objects (the HttpServletResponse object) and one method it provides: getWriter.

    The getWriter method returns an instance of PrintWriter that represents the HTML page that the browser will see - you can call the println method on this PrintWriter to write text to the user's browser. So, for our first servlet, the code looks like this:

    import java.io.*; 
    import javax.servlet.*;
    import javax.servlet.http.*;


    public class MyFirstServlet extends HttpServlet {
    public void doGet (HttpServletRequest request, HttpServletResponse response)
    {
    PrintWriter out = null;
    try {
    out = response.getWriter();
    }

    catch (IOException ex) {
    System.out.println ("An IO Exception has occured.");
    }

    out.println ("<h1>My god! Wooden eels!</h1>");
    }

    Compiling this and making it available on the delivery platform will allow a browser to access this servlet... it doesn't do anything particularly interesting - all it does is display some text. But nonetheless, it's pretty exciting that all of that complex HTTP stuff is handled for us, leaving us free to concentrate on the important things - like sending important information to web browsers, as we just saw:

    My God! Wooden Eels!
    Fig 13.1: My God! Wooden Eels!

    The text sent by the servlet will be parsed into the appropriate HTML layout as it reaches the browser - we can write full HTML documents to the browser in this way (but we'll see why we don't need to do that later in this chapter). For example:

    public void doGet (HttpServletRequest request, HttpServletResponse response) 
    {
    PrintWriter out = null;
    ...
    out.println ("<html>");
    out.println ("<head>");
    out.println ("<title>Surface! Surface!</title>");
    out.println ("</head>");
    out.println ("<body>");
    out.println ("<h1>My god! Wooden eels!</h1>");
    out.println ("</body>");
    out.println ("</html>");
    }

    }

    13.4

    Sending Information to a Servlet

    A servlet is of limited usefulness if it can only send information one way - in order for us to do something that's actually worthwhile, we need to be able to get information from the user as well as supply it. That's where the other parameter that gets passed to the method comes in handy.

    Before we can make use of this, we need to write a little HTML page that makes use of form elements... you should recall how to do this from the Javanomicon. We're not going to need anything particularly complicated to illustrate this concept... just a web site that provides a text box and a button will be ample:

    <html>
    <head>
    <title>Example HTML</title>
    </head>
    <body>
    <p>This is an example web-page, used to demonstrate the POWER OF SERVLETS!</p>

    <b>Your name</b>:

    <form method=GET action="MessagePassingServlet">

    <input type=text name=name size=60>
    <input type=submit value="Submit message">

    </form>
    </body>
    </html>

    Note that the action for the form element is set as MessagePassingServlet - this is the class name of the servlet that will handle this particular element.

    The HttpServletRequest object we are passed as a parameter to doGet contains a method called getParameter - we can use this to get the value ot those form elements that were on the form. In this case, we have a text input type with the name name, so we can get its value in the servlet with:

    request.getParameter ("name"); 

    As you can see, it's very simple - and we'll use both of the objects passed into the doGet method in the following example servlet so we can see them working together in perfect harmony!

    import java.io.*; 
    import javax.servlet.*;
    import javax.servlet.http.*;


    public class MessagePassingServlet extends HttpServlet {
    public void doGet (HttpServletRequest request, HttpServletResponse response)
    {
    PrintWriter out = null;
    try {
    out = response.getWriter();
    }

    catch (IOException ex) {
    System.out.println ("An IO Exception has occured.");
    }

    out.println (request.getParameter ("name") + "! Serve the first course!");
    }

    Note here that we've been using a method called doGet - this is related to the form method we choose for the HTML file - since it was a GET method above, it calls the method doGet. If we set it to POST, it would call a method called doPost - we can ensure our servlet works with both very easily:

    import java.io.*; 
    import javax.servlet.*;
    import javax.servlet.http.*;


    public class MessagePassingServlet extends HttpServlet {
    public void doGet (HttpServletRequest request, HttpServletResponse response)
    {
    PrintWriter out = null;
    try {
    out = response.getWriter();
    }

    catch (IOException ex) {
    System.out.println ("An IO Exception has occured.");
    }

    out.println (request.getParameter ("name") + "! Serve the first course!");
    }

    public void doPost (HttpServletRequest request, HttpServletResponse response)
    {
    doGet (request, response);
    }

    }

    Occasionally, we don't know in advance what parameters there actually are - consider for example if we were writing a generic framework for something like a guestbook - ideally people would be able to put whatever text boxes they wanted in their HTML page, and the servlet would write the contents of each to a file. The HttpServletRequest object passed has a method called getParamaterNames which will return an Enumeration of all the input names that were defined in the HTML file... we can easily step over all of these.

    An Enumeration works very much like an Iterator - it allows for a one way passage through a set list of values. The method hasMoreElements returns true when there's something left in the Enumeration, and the nextElement method returns the next element as an Object (so we must cast it):

    allParameters = request.getParameterNames(); 
    while (allParameters.hasMoreElements()) {
    myParameter = (String) allParameters.nextElement();
    out.println (myParameter + ": " + request.getParameter (myParameter));
    }

    With this framework, we can easily add as many form elements as we want without needing to change the servlet:

    <form method=POST action="MessagePassingServlet"><p><b>Your name</b>: 
    <input type=text name=name size=60></p><p><b>Your email</b>:
    <input type=text name=email size=60></p><p><b>Your message</b>:
    <input type=text name=message size=60></p><input type=submit value="Submit " +

    Our servlet will get a list of all the input names (name, email and message) and then display each of these in turn to the HTML document:

    	email: blah@blah.com
    name: Michael
    message: Hello world!

    13.5

    Big Brother is Watching

    When a browser sends a request for an HTML page, it often sends along a number of elements of information relating to the browser itself. These are bundled up as a header. The header may be comprised of a number of informational elements, or it may be comprised of none - within the servlet, these elements can be queried in the same way we queried parameters above - by stepping over an Enumeration. The method that gets all the header names is called getHeaderNames, and the method that gets the value of a particular method is called getValue:

    allParameters = request.getHeaderNames(); 
    while (allParameters.hasMoreElements()) {
    myParameter = (String) allParameters.nextElement();
    out.println (myParameter + ": " + request.getHeader (myParameter));
    }

    Accessing this servlet gives something similar to the following output:

    host: localhost
    user-agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7) Gecko/20040614 Firefox/0.8
    accept: text/xml,application/xml,application/xhtml+xml,text/html;
    q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
    accept-language: en-us,en;q=0.5
    accept-encoding: gzip,deflate
    accept-charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
    keep-alive: 300
    connection: keep-alive
    referer: http://localhost/drakkos/BigBrotherServlet.htm
    content-type: application/x-www-form-urlencoded
    content-length: 0

    We won't look at why we may want to extract this header information in this section - this information is presented for interest only.

    13.6

    Persistance and Cookies

    Servlets offer one particular benefit that traditional CGI programming cannot hope to offer, and that is persistence of the server software. Every time a user accesses a CGI script, it must be loaded into memory and executed - in cases where the CGI script does something trivial (for example, increases the value of a hit counter) the overhead that goes with executing the script can far outweigh the CPU requirements of the functionality itself. When the script has finished executing, it is discarded.

    Consider a CGI application that reads in a list of quotes from a text file, and displays a random one of these on the screen... because there is no persistence, this text file must be reloaded every time the script is accessed... very inefficient. There are of course techniques that can be used to provide greater efficiency - for example, using random access files can greatly reduce any IO bottlenecks - but that is addressing a symptom of the problem rather than the root cause.

    Once a servlet is loaded, it remains loaded - the server can therefore maintain an internal state that persists between accesses. The quote program indicated above, when implemented as a servlet, allows for the developer to read the quote file once and cache it.

    However, HTTP is a stateless protocol - it does not maintain a state for the user in-between accesses of the servlet. It is possible to do this within the servlet code, but this is extremely cumbersome and difficult to do... after all, how do you identify a particular user? IP addresses will change, referrer URLs will be different - there's no simple way to do it within the servlet.

    However, cookies have long been a part of internet development - a cookie is a small text file that is stored on the user's computer. The cookie contains information relevant to a particular servlet, allowing for the state of a user's transactions with a website to be maintained. When the user makes a request to the servlet, it includes the cookie information as part of the header.

    Cookies are created using the Cookie class (of course)... the constructor for this class takes two parameters. The first is the name that the cookie will have, and the second is its value. For example:

    Cookie myCookie = new Cookie ("username", "Michael"); 

    Cookies are sent to the user's computer via the addCookie method of the response object:

    myCookie = new Cookie ("favouriteColour", request.getParameter ("colour")); 
    response.addCookie (myCookie);

    We get the cookies back from the user by using the getCookies method on the request object - this returns an array of Cookie objects. To find a cookie with a particular name, we must loop over each element in the array and call getName on each, looking for the one that matches:

    yourCookies = request.getCookies(); 
    for (int i = 0; i < yourCookies.length; i++) {
    if (yourCookies[i].getName() .equals ("favouriteColour")) {
    myCookie = yourCookies[i];
    break;
    }

    }

    We can then call getValue on the cookie we find to get its value. Consider a full servlet making use of this:


    public void doGet (HttpServletRequest request, HttpServletResponseresponse) {
    PrintWriter out = null;
    Cookie myCookie = null;
    Cookie[] yourCookies;
    try {
    out = response.getWriter();
    }

    catch (IOException ex) {
    System.out.println ("An IO Exception has occured.");
    }

    yourCookies = request.getCookies();
    for (int i = 0; i < yourCookies.length; i++) {
    if (yourCookies[i].getName() .equals ("favouriteColour")) {
    myCookie = yourCookies[i];
    break;
    }

    }

    if (myCookie == null) {
    myCookie = new Cookie ("favouriteColour", request.getParameter ("colour"));
    response.addCookie (myCookie);
    out.println ("You just told me your favourite colour was " + request.getParameter
    ("colour"));
    }

    else {
    out.println ("You told me last time that your ““favourite colour was " + myCookie.getValue
    ());
    myCookie = new Cookie ("favouriteColour", request.getParameter ("colour"));
    response.addCookie (myCookie);
    }

    }

    There are a number of methods that go with the Cookie class that allow for them to be configured to meet your particular requirements. By default, cookies persist only until the browser shuts down - you can over-ride this by setting a maximum age with the setMaxAge method:

    myCookie.setMaxAge (60 * 60 * 24 * 7); 
    // seven days

    The interested student is directed towards the servlet.jar API documentation for further methods that are available.

    13.7

    Servlets and Serialization

    We've seen one particular area where serialization aids in development - that in transmitting objects over SOAP. We've still to see how to do that in practise, but to begin with we'll look at how we can do it with a servlet.

    Servlets are not limited purely to displaying HTML - they can be accessed easily from within Java code - indeed, that's one of the areas in which they are strongest. A combination of a Java applet and a Java servlet creates a powerful and flexible client/server architecture that can be implemented purely through HTTP.

    For example, let's consider an application that displays a quote generated from a quote servlet - obviously this can be dealt with purely as HTML, but we'll create a separate Quote Javabean that contains the quote and its source, allowing for the applet developer to deal with the presentation. This style of programming architecture should be familiar to you from second year.

    The process of sending an object is pretty much the same as sending some text... the main difference is that we don't use the getWriter method - this returns a PrintWriter, which isn't really what we want. We're going to create an ObjectOutputStream as we saw in an earlier chapter, so what we need for that is the actual output stream - luckily the response object provides just such a method for us: getOutputStream. We use the object returned from this method as the parameter to our ObjectOutputStream constructor:

    try { 
    out = new ObjectOutputStream (response.getOutputStream());
    }

    catch (IOException ex) {
    System.out.println ("An IO Exception has occured.");
    }

    Whenever we need to send an object down our connection, we use writeObject. Simple! Let's have a look at our whole servlet:

    import java.io.*; 
    import javax.servlet.*;
    import javax.servlet.http.*;
    import java.util.*;


    public class QuotesServlet extends HttpServlet {
    ArrayList allQuotes;

    public QuotesServlet() {
    Quote myQuote;
    allQuotes = new ArrayList();
    myQuote = new Quote ("Behold, for I now wear the human pants!", "Penny Arcade " +
    "Alien");
    allQuotes.add (myQuote);
    myQuote = new Quote ("I'm a knife. Knifin' around. cutcutcutcutcut", "Space " +
    "Ghost");
    allQuotes.add (myQuote);
    myQuote = new Quote ("Surface! Surface!", "Space Ghost");
    allQuotes.add (myQuote);
    }

    public void doGet (HttpServletRequest request, HttpServletResponse response)
    {
    Quote tmpQuote;
    ObjectOutputStream out = null;
    int num = (int) Math.random() * allQuotes.size();
    try {
    out = new ObjectOutputStream (response.getOutputStream());
    }

    catch (IOException ex) {
    System.out.println ("An IO Exception has occured.");
    }

    tmpQuote = (Quote) allQuotes.get (num);
    try {
    out.writeObject (tmpQuote);
    }

    catch (IOException ex) {
    System.out.println ("Something went wrong!");
    }

    }

    public void doPost (HttpServletRequest request, HttpServletResponse response)
    {
    doGet (request, response);
    }

    }

    Pretty simple, eh? Now for the application that makes the request and displays the quote. We're not interested in the layout really, so we'll just look at the method that actually does the connection and parsing of the quote from the servlet. You'll see that the code is not a million miles away from what we've already done with regards to serialization (in this text) and URL connections (from the Javanomicon):


    public void actionPerformed (ActionEvent e) {
    URL myURL = null;
    Quote myQuote = null;
    ObjectInputStream in = null;
    try {
    myURL = new URL ("http://localhost/drakkos/QuotesServlet");
    }

    catch (MalformedURLException ex) {
    System.out.println ("Badly formed URL");
    }

    try {
    InputStream is = myURL.openStream();
    in = new ObjectInputStream (is);
    myQuote = (Quote) in.readObject();
    }

    catch (IOException ex) {
    System.out.println ("IO Exception");
    }

    catch (ClassNotFoundException ex) {
    System.out.println ("huh");
    }

    quote.setText (myQuote.getQuote());
    author.setText (myQuote.getSource());
    }

    The first thing we do once we have a URL object is to call the openStream method on it - this provides us with a connection to the resource represented by the HTTP address. We use the InputStream provided by that method as the constructor for a new ObjectInputStream, and then we call readObject on the ObjectInputStream to read the actual object into a suitable container.

    13.8

    Sharing Information

    A web server is only rarely running a single servlet - in fact, many of them are likely to be running on the same machine. Each servlet has what is known as a context, which is usually shared by all the servlets within a particular directory. We can communicate between servlets, regardless of context, through the use of the structure we saw in the quotes application. However, servlets within a common context have another way of sharing information.

    The class that deals with the context of a servlet is called ServletContext - there is a method built into the standard HttpServlet class (the one we extend for all our servlets) called getServletConfig()... we call the getServletContext on this to get an instance of the ServletContext class:

    ServletContext context = getServletConfig() .getServletContext(); 

    Within this context we can set and get attributes that are available to all servlets - it's kind of like a Hashmap that is accessible to all of the servlets within a given context. We set the attributes by giving a string name, and an object that represents the context... the method setAttribute on the ServletContext object provides an interface to this:

    context.setAttribute ("the man", "Michael"); 

    Then, when we want to get the value of the attribute, we call getAttribute, passing the name of the attribute we want:

    String theMan = context.getAttribute ("the man"); 

    Within more complex contextual structures, it may be desireable to have centralised 'handler' objects that allow for common data sources to be setup and easily queried within related servlets.

    The ServerContext object also provides a getAttributeNames method that returns an Enumeration of all attributes set within the context, and also a removeAttribute name for when an attribute has served its purpose and should be deleted. Care must be taken with this if the attribute is to be used within other servlets, since unless you are sure that it is no longer required.

    13.9

    Session Tracking

    The final thing we're going to look at in this chapter is the idea of session tracking. Cookies are useful for storing small tickets of information, but for more complex storage requirements it is often necessary to make use of a session variable which will hold many elements of data in much the same way as the attributes above.

    We get hold of a session object (which gets stored in an object of the class HttpSession) by calling getSession on the HttpServletRequest object passed as the first parameter to our doGet/doPost methods:

    HttpSession mySession = request.getSession(); 

    Sessions are much more powerful than cookies, and can be configured to a much greater degree. We can place objects into a session using the setAttribute method (same parameters as above), and get them back using getAttribute. The methods getAttributeNames and removeAttribute are present as with the ServerContext object and work exactly the same way.

    mySession.setAttribute ("the man", "Michael"); 

    This in itself would be useful enough - cookies are limited to much smaller sets of information, and provide little way to easily relate contextually linked elements. However, sessions also provide a range of functionality for dealing with persistence and creation.

    The method isNew returns true if the client is unaware of the session - this relates usually to browser security sessions... if it returns new, then the session must be configured anew.

    Once we are finished with a session variable, we call invalidate on it to clear its state and unbind it from any other objects.

    There are a set of four methods that are useful for handling the persistence of a session:

    Persistance Methods
    Fig 13.2: Persistance Methods

    All of the functionality for handling these methods is dealt with internally in the HttpSession object - all you need to do is make use of them as an when you need them.

    13.10

    Conclusion

    Once again, we seem to have digressed from what can strictly be defined as 'component based programming'. There are reasons why servlets should be explored in this module, not the least of reasons being that it ties in to what we'll be doing when we start writing our own SOAP servers.

    However, there are other compelling arguments - servlets allow for self-contained functionality to be distributed over a range of computers in a very modular way... they are also accessed and manipulated according to a very strict set of rules governed by published protocols. Access to different servlets, since they work through a standard interface, can easily be implemented. In fact, it's perfectly possible to write a client that 'hot swaps' servlets mid session. They have all of the features that make component based programming such a valuable tool for application development.

    We've concentrated primarily on the structure of servlets as opposed to the structure of the HTTP protocol they are based on - there are many documents available for the interested student who wants to spend some time looking into this area.

    Further Reading

    The following table details further reading on the topic in this chapter, and also any external resources that you may find useful.

    ResourceDescription
    NothingThere's no links as yet, so go find your own you shiftless wasters.

    PreviousTable of ContentsNext

    © 2004-2006 Michael James Heron