![]() | Monkeys at Keyboards: Java-Fu © Michael James Heron | ||||
| Topic: Java Programming Level: 3 Version: beta | |||||
13 - Java Servlets | |||||
| Previous | Table of Contents | Next |
| Forum |
| Chapter Objectives |
By the end of this chapter, the reader will be able to: |
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.
So, on with the show!
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.
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:
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:
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:
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:
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:
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:
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:
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:
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!
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:
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):
With this framework, we can easily add as many form elements as we want without needing to change the servlet:
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:
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:
Accessing this servlet gives something similar to the following output:
We won't look at why we may want to extract this header information in this section - this information is presented for interest only.
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:
Cookies are sent to the user's computer via the addCookie method of the response object:
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:
We can then call getValue on the cookie we find to get its value. Consider a full servlet making use of this:
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:
The interested student is directed towards the servlet.jar API documentation for further methods that are available.
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:
Whenever we need to send an object down our connection, we use writeObject. Simple! Let's have a look at our whole servlet:
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):
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.
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:
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:
Then, when we want to get the value of the attribute, we call getAttribute, passing the name of the attribute we want:
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.
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:
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.
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:
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.
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 ReadingThe following table details further reading on the topic in this chapter, and also any external resources that you may find useful.
|
| Previous | Table of Contents | Next |
© 2004-2006 Michael James Heron