Choosing a server-side language
There are numerous computer languages, each competing for a share of the server-side application market. Each have different features and methods for performing similar tasks and some have been customised for the development of web applications.
Current popular languages include:
- Java (Covered in this course, numerous Java frameworks)
- Python
- JavaScript
- C++
- C# (.NET Platform)
- C
- Ruby
- Groovy
- Scala
...the list goes on an on.
In many respects, the programming language is not that important - most languages can be used to achieve the same objectives. If you are a single developer, then choose a language you like that has good community support.
If you are working in a large company, it is likely that the component languages will be chosen by the lead architect and you may have little say in the matter!
For the purpose of this course, we are going to use Java which most students have had some exposure to in the past.
Servlet/JSP Introduction
In 1997, JavaSoft (which has since been integrated into Sun Microsystems and in turn into Oracle ) finalised the concept of Java Servlets. This action consolidated previous scattered technologies into a single, standard, generic mechanism for developing modular server-side Java code. Since then, support for servlets has been implemented into nearly every web server and the use of servlets has grown massively in popularity.
So what exactly are Java servlets? Technically, a Java servlet is a dynamically loaded module, running entirely inside the Java Virtual Machine (JVM), that services requests from a Web server. In lay man's terms, this means that Java servlets are programmes written on the server-side, which are ready to process their respective tasks. Today, servlets are a popular choice for building interactive web applications and are largely replacing other server-side technologies such as CGI (Common Gateway Interface) scripts and PHP. Servlets are part of the Java 2 Platform, Enterprise Edition. specification.
Some of the primary advantages of servlets are as follows:
-
Servlets have portability - because servlets are written in Java and conform to specific global APIs, they are portable across operating systems and server implementations. In essence this means, that if you were to write servlets on a Windows/Intel platform using Microsoft IIS server, you could easily deploy these servlets on Apache webserver on a linux machine. This is particularly useful in scenarios where the server platform specifications are likely to change.
-
Highly Efficient - a servlet is only initialised the first time that the web server loads it. Once this servlet is loaded, then subsequent calls to the servlet only require the calling of a service() method. Once the servlet is loaded, it generally (memory permitting) remains in the server's memory as a single object instance. Additionally, multiple, concurrent requests are handled by seperate threads, so servlets are highly scalable.
-
Persistant - Servlets can maintain state between requests and stay resident in memory while serving incoming requests. This also allows servlets to hold on to external resources, such as database connections (connection pooling), that may otherwise take several seconds to establish.
-
Power - Servlets have access to the entire family of Java APIs, networking, database connectivity, imaging, data compression etc. Servlets can also access a library of HTTP-specific calls and receive all the benefits of the mature Java language.
Note : Throughout this chapter references are made to JSPs. This technology will be dealt with in the following chapter, so if you haven't studied JSPs previously, then feel free to ignore them for now at least!
Servlet Operation
Traditionally, what was required to support servlets was a web server with/patched with some form of servlet engine. Since the Java Servlet API 2.2 specification, the term Servlet Engine has been replaced by the term 'Servlet Container'. Servlets interact with Web clients via a request-response model that is implemented by the Servlet Container. The servlet container, combined with a web server or web application server services the network requests and responses as required.
The following are commonly used Web Servers that support servlets:
-
Apache Tomcat - Tomcat can act as a standalone server for deploying servlets, or alternatively can be combined with Apache Web Server, through a relatively simple process. Tomcat is a free, open source project and it is this servlet container that will be used throughout this course. Tomcat, the server is a result of the Jakarta open-source project and can be found at http://jakarta.apache.org.
-
JBoss - JBoss is an Open Source, standards-compliant, application server implemented in 100% Pure Java and distributed for free. With 150,000 downloads per month, JBoss is the most downloaded web-app server in the world based on the J2EE specification. Built into JBoss, Tomcat is used to service both Servlets and JSPs. JBoss can be downloaded for free from http://www.jboss.org
-
Other popular Application Servers which support servlets include JRun (now no longer supported) ATG Dynamo, BEA WebLogic, Borland Enterprise Server, and IBM WebSphere. For a comparison of various Application Servers, visit http://www.theserverside.com/reviews/matrix.jsp
The figure below shows the "standalone" implementation of a web server/application server, with a built in servlet/JSP container. In this scenario, users on the client-side make a request via their browsers (clients) to the web server. The web server serves out the web page, servlet or JSP output in the form of a "response". Each different type of "page" (loosely defined) is served by the web server and the web server would manage logging, basic security and other services provided by standard web servers. Most e-Commerce type companies would opt for a full blown application server, which adds a considerable amount of extra functionality to that of a standard web server, such as database connectivity, advanced security, monitoring, company support, performance and support for Enterprise JavaBeans (EJBs) (and not forgetting in almost every case, cost!).
Figure 4.2. Standlone Servlet Server Structure
It is not always desirable to completely replace your traditional web server with a new full-blown application server. Consider a scenario where a web server such as Apache had been used for 5 years previously: user security is in place, CGI, Perl, PHP server-side applications are actively running, virtual hosts have been set up, everything is configured satisfactorily - why change it!? In this scenario it is desirable to use an external servlet container to provide the facility to deploy servlets, while still maintaining your complete setup for your traditional web server. Indeed servlet containers are often designed with this in mind and popular containers such as Tomcat and JRun plug into virtually all popular web servers as well as providing the option to be used standalone as web-servers.
Figure 4.3. Servlet/JSP Container combined with Web Server
Most large scale server-side systems also run a database tier for managing persistant data. In the case of a servlet or JSP which accesses a database it is the servlet container which communicates with the database, rather than the webserver. Java applications, including servlets most commonly connect to databases through Java Database Connectivity (JDBC) which provides an API for SQL-based database access. This topic will be dealt with in more detail in the Database Connectivity chapter. In the case of application servers, which have built-in servlet containers, database connectivity and connection pooling are managed by the server itself, adding considerable functionality.
Figure 4.4. Introducing a Database Tier
Hello World Servlet
The following shows the code for the most simple servlet:
1. import java.io.*;
2. import javax.servlet.*;
3. import javax.servlet.http.*;
4.
5. public class HelloWorld extends HttpServlet {
6.
7. public void doGet(HttpServletRequest req, HttpServletResponse res)
8. throws ServletException, IOException {
9. res.setContentType("text/html");
10. PrintWriter out = res.getWriter();
11. out.println("<HTML><HEAD><TITLE>Hello World</TITLE></HEAD>");
12. out.println("<BODY><H1>Hello World!</H1></BODY></HTML>");
13. out.close();
14. }
15. }
Please note that the numbers on the left-hand side are simply there as an indication - they are *not* part of the code. Also, please do not attempt to compile and/or run this servlet at this point - servlets are remote applications and require some extra considerations. Essentially, there are two extra requirements:
We describe how this should be achieved below, but for first we provide a basic explanation for this servlet.
The import lines at the top of the file (lines 1-3) are simply required by the standard servlets to be compiled. They allow access to the servlet APIs to facilitate use of the HttpServletRequest, HttpServletResponse, ServletException classes as in the example above. We deal with the Servlet API in the sections following. Within the next line (line 5) the HelloWorld defines the name of the servlet, which is being called. For example, if this servlet was to be hosted on a webserver in a context called "student" it might have the following address:
http://localhost/student/servlet/HelloWorld
or
http://localhost:8080/student/servlet/HelloWorld (depending on your port)
Line 7 begins the definition of the doGet() method which handles a GET method type, as described in Chapter 2. Essentially, doGet is used to handle client requests where they are trying to "get" information from the server. When developing HttpServlets you will typically use one of two methods doGet() and doPost(), the latter used generally for form submission. Examples using the doPost() method will be provided later. This servlet extends the HttpServlet class and overloads the doGet() method inherited from it. Each time the web server receives a GET request for this servlet, the server invokes this doGet() method, passing it an HttpServletRequest object and an HttpServletResponse object.
The HttpServletRequest represents the client's request. This object gives a servlet access to information about the client, the parameters for this request, the HTTP headers passed with the request etc. The HttpServletResponse represents the servlet's response to be returned to the client. This data can be of any content type (not just html!) and hence line in line 9 we use the setContentType() method of the response object to set the content type of its response to "text/html", the standard MIME content type for HTML pages. Then, it uses the getWriter() method to retrieve aPrintWriter, which allows us to write a PrintStream to the client. During lines 11-13 we simply write some lines to be sent to the client and close the PrintWriter.
That's all there is to it (almost!). While the servlet may seem a little complicated, bear in mind that the complicated elements of the servlet are essentially "standard". While we enter into a considerable amount of detail in the following sections, it is important that students try the examples, modify them and attempt to write their own applications! Once you write a few servlets, the technical process involved in writing them will become easier.
Unfortunately, perhaps the most difficult element in getting started with writing servlets is not the servlets themselves, but rather the setting up of a Servlet Container (on which they run) and putting them within the correct Web Application Structure. The Servlet/JSP Container used for this module is Tomcat.
Setting up a Servlet/JSP Container - Tomcat
Tomcat is the servlet container that is used in the official reference implementation for the Java Servlet and JavaServer Pages technologies. Tomcat is developed in an open and participatory environment and released under the Apache Software License - it is the product of collaboration of the some of the best developers from around the world. Tomcat can be found at: http://jakarta.apache.org At that site, you can download either binary or source versions of Tomcat. For the purpose of this course, we are only concerned with the binary version as we will not be exploring lower level container code. In fact, we will use a preconfigured version downloadable locally for this course. This should minimise the amount of configuration involved with getting up and running quickly.
You will find instructions on downloading and installing Tomcat in the Downloads Section of this module.
Using Tomcat on Local Machines
A copy of Tomcat has been installed on the local network. However, for the purpose of this course a number of configuration and log files need to be directly modified. Additionally, it has been found in the past that running Tomcat across the network can have performance effects, particularly during high load usage encountered for exams. To start Tomcat, you should select START -> Programs -> Tomcat -> Tomcat Startup
This will perform either of the following:
-
First Execution: If Tomcat had not already been run, it will copy the entire installation of Tomcat from the network drive to C:\Temp\Tomcat. Upon completion it will set the JAVA_HOME and CATALINA_HOME environment variables and execute to startup.
-
Subsequent Executions: If Tomcat had already been run and you are restarting it after previously shutting down, a reboot or crash, it will simply run. This uses the version of Tomcat which was copied to C:\Temp\Tomcat upon the first execution.
When Tomcat starts up, it will be running on port 8080 (by default) on your local machine. You can immediately connect to this by opening a browser and trying: http://127.0.0.1:8080 (Note: This link will only work if you have run Tomcat)
Important: In the laboratory, due to port clashing issues, Tomcat will run on port 8080. In the notes, most of the time, the port is omitted as for self installations we use port 80 (the default for http). Please add in the port where applicable.
There are a number of important points we can highlight at this stage:
-
Tomcat is now running completely locally and should start up quickly and perform efficiently.
-
Tomcat has been set up locally with all required standard configuration files set up to begin developing. There are some additional steps shown below for third-party installations.
-
The web URL for the root installation of Tomcat is http://localhost:8080. Alternatively, you can try http://127.0.0.1:8080. Note: If you have problems using the 'localhost' URL, this is due to your proxy settings in your browser. What happens in this circumstance, is that your HTTP requests are being routed to the proxy server (namely proxy.eeng.dcu.ie) which by circumstance is hosting a password-protected application on port 8080. When the proxy server sees your request, it reads 'localhost' as being itself and your browser attempts to connect to port 8080 of the proxy server machine, rather than your own machine. IF YOU ARE BEING ASKED FOR A PASSWORD, THEN YOUR PROXY SETTINGS ARE INTERFERING. If this is occurring, you must also ensure that in your proxy settings you have selected to 'Bypass Proxy for Local Addresses' (IE) or similar in other browsers. Alternatively, just use http://127.0.0.1:8080 which is always a standard protected IP address for your local machine.
-
Feel free to experiment and edit any configuration files in C:\Temp\tomcat. If you feel that you have broken things irreversibly, simply delete the tomcat directory, shutdown Tomcat and start it up again (but only after backing up anything you think important). This will copy the entire application from the server and hence return you to the default installation.
-
On the left hand navigation side of the Tomcat main home page, you will find a link to some Examples. These examples should run correctly at this point and you may also view their source code. Note: These examples and source code will be available during the exam.
-
Back up/delete your work! - because you will be working in the C:\temp directory the files you create will be deleted each night and may be deleted by other users of that machine later. Likewise if you are undertaking assignments or private work you should delete your files from the C: drive when you are completed, after having saved your work on your private drive.
-
The log files for the output of Tomcat can be found in C:\temp\tomcat\logs. If something is not working as planned, check the logs files - you might find some useful information!
-
Please contact either myself or Liam Meany if you have any problems with the operation of Tomcat on the allocated machines.
Installing Tomcat Remotely
Remote students will be required to install Tomcat on the machine through which they will take the course. Work in Tomcat is not only recommended for study of the module, but as a requirement when completing assignments. Two seperate installations will be required:
-
The Java Standard Edition, otherwise known as JSE - this will be used both for the operation of Tomcat (which is written in Java) and the development of applications through the course. You should already be familiar with JDKs, as this module had recommended a basic knowledge of the Java Programming language. The version to install will include the Java Enterprise Edition in addition to the standard edition. For more details, visit the Download Information Page.
-
Tomcat should be installed on your course machine. Full instructions for downloading and installing Tomcat are also available from the Download Information Page.
Installation steps for both packages are fully provided in the instructions at this downloads page.
Getting Started with Tomcat
As with any new topic or application, perhaps the best place to start is with the examples. Tomcat thankfully comes preinstalled with a number of example Servlets (and JSPs) which you can use. Additionally, it comes with the source code for these examples, which can be viewed in your browser. The first place to start in Tomcat is the root context which is simply: http://localhost or http://localhost:8080 This will open up the default Tomcat home page, which provides links to documentation and administration tools.
At this point on the left hand side, you should select 'Servlet Examples'. This will lead you to a page with numerous sample files, which span topics in these Chapters. To start with you should both 'Execute' and 'Source' the Hello World example which is virtually identical to that covered within these notes. Feel free to work your way through these examples and learn from them, even though some of the material has not yet been covered - Remember to come back and visit them later.
Setting up our IDE for Development
Development of Java code for this module will take place in the Eclipse IDE. It has been installed on all of the machines locally. This will be available during the examination session also. Naturally, this will involve some extra minor installation for remote students. These steps are likewise detailed in the Download Information Pages.
With these two steps, remote students will be running a virtually identical installation to that experienced by students on campus.
Registering and Controlling Tomcat from Eclipse
The installation video for Eclipse shows how to set up Tomcat so that we can easily control it and deploy applications we develop.
This needs to be done before continuing with this section, as we will need to be able to deploy our servlets onto Tomcat. For more information, view the page '
Installing Eclipse'.
Writing our First Servlet - HelloWorld
As our first servlet, we will attempt to deploy our own HelloWorld servlet running in an application. All further servlets will follow these same basic steps:
-
Shut down Tomcat (if it is running) before running Eclipse - this will enable you to control it from within Eclipse itself
-
Upon running Eclipse, make sure that the Workspace setting says C:\temp\workspace (or whatever you have chosen at home). Under the 'Servers' tab, you should see something like 'Tomcat Server at localhost (Stopped)' (if you followed the steps for installing Eclipse).
Right-click anywhere in the 'Project Explorer' on the left and select 'New -> Dynamic Web Project'. Give the project a name such as 'student' and have the target runtime set as your Tomcat installation. Hit next twice and select the box 'Generate web.xml deployment descriptor' (Tomcat 7+ only). Select 'Finish' to create your project.
This generates a new project, with a number of directories. We will place HTML, JSPs and general web content (css, images, JavaScript etc.) in the WebContent folder and we will place our servlet source code into 'Java resources:src'.
We wish to create a new servlet, so we right-click the 'Java resources: src' folder and select 'New -> Servlet (find under 'Other->Web' if not immediately listed). Choose package 'org.dcu' and class name 'HelloWorld'. This will generate a template servlet we can use with the package 'org.dcu' defined at the top. Note: You can essentially choose anything, including blank for your package. The reason we use packages is to avoid clashes with other third party code we might introduce to our application - they give us the means of distinguishing 'org.dcu.User' from 'org.someotherpackage.User'. We are not currently concerned with methods, modifiers and superclass as to get started we will simply write our original example over the template code. So delete everything and type (copy/paste):
package org.dcu;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;
@WebServlet("/HelloWorld")
public class HelloWorld extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<html><head><title>Hello World</title></head>");
out.println("<body><h1>Hello World!</h1></body></html>");
out.close();
}
}
- Save to Compile - By default Eclipse will compile 'on-the-fly' compiling every time you save your code. Any errors in your code will appear in the bottom right panel under 'Problems'. So 'Save' and fix any compilation errors.
- Deploy to Tomcat - In the package explorer, right-click the HelloWorld.java file and select 'Run As -> Run on Server'. Tomcat should be already selected, so just hit 'Finish'.
-
Test your Servlet - This servlet has most likely been run inside a sample browser in Eclipse. You can equally use a normal browser to access it with http://localhost/student/HelloWorld (don't forget to add the 8080 port if you're running locally)
Finally, a video showing all of the above steps can be found below:
Web Application Structure
Prior to the Servlet API 2.2 Specification, there was little consistency between server platforms. However, servers that conform to the 2.2 specification are required to accept a Web Application Archive in a standard format. As defined by the Java Servlet API 2.2 specification, "a Web Application is a collection of servlets, JSPs, HTML pages, classes and other resources that can be bundled and run on multiple containers from multiple vendors". Single Web Applications are defined and represented within a WAR file (.war extension) or Web Archive. A WAR file contains all the elements that make up the Web application, such as images, HTML pages, JSP pages, servlets, and any other relevant documents. WAR files are actually JAR Archive (.jar) files created using the JAR utility and saved with an alternative extension. The different extensions of these two files are chosen to let people treat them differently.
A Web Application consists of the following items:
-
Servlets
-
JSPs
-
Utility Classes
-
Static Documents (html,css,gif,jpg etc.)
-
Client Side Applets, JavaBeans and classes
-
A deployment descriptor and metainformation about the application
A Web application contains a structure hierarchy of directories and files in a standard layout. Such a hierarchy can be accessed in its "unpacked" form, where each directory and file exists in the filesystem separately, or it its "packed" WAR file. The former format is more useful during development, while the latter is used when you distribute your application to be installed. Any Web Application that is to be deployed in Tomcat has to conform to this directory structure. The format of the structure can be seen in the figure below:
Figure 4.5. Web Application Directory Structure

-
Application Root Directory - This is the root directory of the Web Application. The name of this directory is named by the developer - for example the default context we created for this module was called "student". All JSP, HTML and image files are stored here. Typically, there would be an index.html or index.jsp file in this directory. The "other directories" could be, for example, "images" (containing gifs and jpgs for the html pages and servlets used in the Application), "style" (containing the .css or other style files used) or simply subdirectories of HTML or JSP pages.
-
/student/WEB-INF - This directory contains all resources related to the application that are not in the document root of the application. It should be noted that the WEB-INF directory is not part of the public document (ie. paths beginning in http://localhost:8080/student/WEB-INF cannot be viewed in a browser) and no files contained in this directory can be served directly to the client. The WEB-INF directory also contains the Web Application Deployment Descriptor.
-
/student/WEB-INF/classes - contains the application Java Servlets, class files, JavaBeans and utility classes.
-
/student/WEB-INF/lib - The lib directory contains the Java ARchive (JAR) files (or ZIP) that the web application depends upon. For example, this directory might contain a JDBC Database Connectivity driver in JAR or Zip format.
-
/student/WEB-INF/web.xml - The deployment descriptor is an XML file named web.xml and forms the heart of the Web Application. This file is discussed below.
At this point, you might be thinking? "Hold on - this looks nothing like my structure inside Eclipse?!" and you would be correct. The Eclipse setup follows a similar structure but with different naming. Everything inside "WebContent" maps to the application root. 'Java Resources: src' maps to WEB-INF/src. The web.xml and lib directories can be found in 'WebContent/WEB-INF'. However, when we package our whole application up into a WAR format we will see that it will be packaged correctly and in accordance with the specification. Try it now:
Packaging a WAR File
Select your Project -> Right Click -> Export -> War File. Choose a destination and select 'Export Source Files'
Note: you don't actually need to export source files, this but you will do it later for your assignment so we can correct your source code. This will generate a file called 'student.war' (if student is the name of your app) in the location you specify. If you unzip this (associate .war with your zip application or rename it to .zip) you will find the structure as shown in the diagram above.
Web Application Deployment Descriptor
A deployment descriptor is an XML text-based file with an .xml extension. For Web Applications, the deployment descriptor is an XML file named web.xml located in the WEB-INF directory. The file describes configuration information for the entire Web Application. Some of the properties defined in the web.xml file are as follows:
As we proceed through the servlet section, we will make reference to the web.xml file, and provide snippets of code which could be placed in this file. The example Web Application provided in the sections above, contains a very basic web.xml file, the minimum which could be used to allow deployment of servlets and JSPs. However, as a first snippet, here is a sample piece of configuration which might be used in a typical web.xml file:
<web-app>
<display-name>Sample Web Application</display-name>
<session-timeout>30</session-timeout>
<servlet>
<servlet-name>SampleServlet</servlet-name>
<servlet-class>com.dcu.SampleServlet</servlet-class>
<load-on-startup>1</load-on-startup>
<init-param>
<param-name>ServletParamName</param-name>
<param-value>ServletParamValue</param-value>
<description>An init param for this servlet</description>
</init-param>
</servlet>
</web-app>
The Servlet Life Cycle
The life cycle of a Java servlet is a very simple object-oriented design. A servlet is constructed and initialized. It then services zero or more requests until the service that it extends shuts down. At this point, the servlet is destroyed and garbage is collected. Hence the servlet life cycle addresses both the performance and resource problems of CGI and other such scripting languages - a servlet engine may execute all its servlets in a single JVM, sharing data and resources together, while still being prevented from accessing one another's private data. Servlets may also be allowed to persist between requests as object instances, taking up far less memory than fully-fledged processes. Different Application Servers/Servlet Containers have some leeway in how servlet life cycles are handled. However, there are three hard and fast rules that any servlet engine must conform to:
-
Create and initialize the servlet
-
Handle zero or more service calls from clients
-
Destroy the servlet and then garbage collect it
Figure 4.6. The Servlet Life Cycle
Initialization
The Servlet Container loads servlets either at server startup (pre-loading) or when they are accessed (dynamically). Most of the application servers or servlet/jsp containers allow for specifying which servlets are loaded at startup. A servlet's init(ServletConfig) method is called by the server immediately after the server constructs the servlet's instance. Whether called at startup or at first request, the init() method is guaranteed to be called before the first request is handled. The init() method is only ever called once. In the init() method, the servlet creates and initializes the resources that it will be using while handling requests. The init() method's signature is defined as follows:
public void init(ServletConfig config) throws ServletException
{
super.init(config);
}
The ServletConfig object supplies a servlet with information about its initialization parameters. These parameters are given to the servlet itself and are not associated with any single request. They can specify initial values, such as where a counter should begin counting, or the number of connections to be created in a connection pool. In some application servers, init parameters are set during a registration process - however typically they are defined in an XML-based configuration. The ServletConfig object also holds a reference to a ServletContext object that a servlet may use to investigate its environment. If the init() method is overridden in your servlet, you should call the super.init() to ensure that the ancestor's init(ServletConfig config) method is called.
Service
The service() method handles all requests sent by a client. It cannot start servicing the client requests until its init() method has been executed.Note: It is not always necessary to manually implement the init() method. The servlet container calls the service() method to handle requests and to write the formatted response to be sent to the client. The servlet container constructs a request object out of the HTTP request either asServletRequest or HttpServletRequest (in our examples, typically the later), and it constructs a response object to format the response as theServletResponse or HttpServletResponse object (again, typically the later). Eg.
public void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
The (Http)ServletRequest object contains information about the service request, encapsulating information providing by the client. The (Http)ServletResponse object contains the information returned to the client.
The service() method of the HTTPServlet class overrides the service() method of the GenericServlet class. The default implementation of the service() method has code to automatically redirect the HTTP request such as GET and POST to methods such as doGet() and doPost(). Although, you usually override the service() method in the case of a generic servlet, you should not do so while developing an HTTPServlet - doing so interferes with this delegation architecture.
The GET and POST method types were covered in more detail in Chapter 2 - in short GET methods are most commonly used for simply retrieving information from the server whereas the POST method is used for sending larger quantities of information to the server. The servlet container, upon receiving a GET request, calls the service() method of HTTPServlet, which in turn, redirects the request to the doGet() method. Likewise, upon receiving a POST request, calls the service() method of HTTPServlet, which in turn, redirects the request to the doPost() method.
Figure 4.7. Generic and HTTPServlets handling GET/POST requests
Destruction
The Servlet Container stops a servlet by invoking the destroy() method of the servlet inferface - this method signifies the end of a servlet's life. It is in the destroy() method that any resources that were created in the init() method should be cleaned up. Typically, you override the destroy() method to release any resources previously allocated in the init(). For example, any open database connections should be closed in the destroy() method. Any persistant information, which is needed to be stored should be stored during this method, ready for use the next time the servlet is loaded. The structure of the method is simple:
public void destroy()
Servlets By Example
Having toyed with the idea of creating a Context on an online instance of Tomcat, with all the examples for this Chapter - I have decided to try to motivate you, by simply providing the source code. This means, that in order to get these examples running you will need to compile the code and deploy the servlets yourself. Another advantage of this (over placing pre-deployed versions on a local server for you to modify) is that it equalises the playing field across the different course mechanisms, such as full-time, part-time or exclusively online. Getting to grips with creating, modifying and deploying servlets may seem cumbersome at first, but it will become easier and result in reduced time spent in completing assignments and exam questions.
In order to ease the process of development and deployment, the creators of Tomcat wrote a Manager Application, which you can optionally use while developing. We discuss this management tool below although we won't really use it in force as the server interface will handle deployment for us.
Tomcat Manager Application
Note: Before we began using the server interface, we were required to use the Tomcat Manager Application to Start, Stop and Reset our developed web applications. If you are using that environment, you will not technically *need* to use the Tomcat Manager. However, you should make yourself familiar with the environment regardless. In fact, for good practice, you should know how to create web applications manually using notepad, package them as WAR files and deploy them using configuration files and the Tomcat Manager.
(Remote) In the ZIP file provided for Tomcat, the following change has already been made. However, I mention it here in case anyone has built from a raw downloaded version of Tomcat. By default, there is no user set up with access to the manager application when it is first installed. This is the conservative approach to security, meaning that you actually have to actively turn on access to the management tool (as opposed to having to remember to turn it off/change passwords). To set up a user with manager privilege, you must edit the TOMCAT_HOME/conf/tomcat_users.xml and make it look like the following:
<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
<role rolename="manager"/>
<user username="admin" password="admin" roles="manager"/>
</tomcat-users>
Once Tomcat has been restarted, this line will take effect and when we use the Manager Application we will authenticate with Username: admin and Password: admin
(All Students) - to use the Manager Application you can select the link on the left-hand-side from http://localhost:8080 when Tomcat is up and running on your machine. Alternatively, you can access the main Manager page at http://localhost/manager/html/list. The application will ask you to authenticate, for which you should use the admin/admin username/password combination.
Figure 4.8 Tomcat Manager Screenshot
Using this Manager it is possible to start, stop, reload, install or remove a Web Application within Tomcat. Servlet contexts can be defined asreloadable="true" within the Tomcat config (meaning that when the class file of a servlet is changed, the web application is reloaded). If your servlets are automatically reloading without you manually forcing a reload of Tomcat, then this is the cause. However, if you find that your servlets are not reloading after compilation (give it a few seconds), then you can simply perform a manually reload instead using the server tab in Eclipse.
Servlets and Form Data
Chapter 2 introduced HTML Forms which we commonly use to gather input from the client. HTML is the language browsers use to render a view to the client, essentially the Graphical User Interface (GUI) for a servlet. While Java Applets use the java.awt package for its GUI, the servlet uses HTML pages instead. Please revisit the section on Forms in Chapter 2 if you have forgotten how they are used. When we encountered FORMs before, we had not implemented the server-side component - servlets will provide that component for us!
Figure 4.9 Form/Servlet Structure
Let us consider a very basic form, which simply asks for firstname, surname and age:
<html>
<head>
<title>Basic Form</title>
</head>
<body>
<h2>Please fill out this form:</h2>
<form method="GET" action="/student/FormServlet"
name="myform">
<br/>Firstname: <input name="firstname" />
<br/>Surname: <input name="surname" />
<br/>Age: <input name="age" />
<br/>
<input type="submit" value="Submit Form" /> <input type="reset" value="Reset" />
</form>
</body>
</html>
The above html code can be obtained from this HTML file. Now consider the following servlet:
package org.dcu;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
@WebServlet("/FormServlet")
public class FormServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<html><head><title>Form Example</title></head>");
out.println("<body><h1>Hello " + req.getParameter("firstname") + " "
+ req.getParameter("surname") + "</h1>");
out.println("You are " + req.getParameter("age") + " years old!");
out.println("</body></html>");
out.close();
}
}
Source file: FormServlet.java
As can be seen, it is a relatively simple process to obtain data from a web form and to use it in a servlet. In this example we are simply returning back that information to the client by personalising a 'Hello'. In more realistic applications, data in form/servlet combinations are frequently used to obtain data from databases, store information, application authentication and a myriad of other possibilities - essentially anywhere that user input is expected - if it can be done, Java can do it! A sample output from this file can be seen below:
Figure 4.10. Sample Output from FormServlet
At this point, the more experienced reader may be wondering at the use of the GET method for handling forms. Technically, they would be right to question this - the most common type of HTML Form would use the POST method. We will deal with a standard POST method form shortly, but this GET example shows some important points. You will notice that the GET method results in the form name/value pairs being visible in the URL. In the sample output above:
http://localhost/student/FormServlet?firstname=a&surname=b&age=22
When the user submits the above form, the form parameters are sent to the servlet, because this is the 'action' attribute that has been set to point at the servlet. The GET method causes any form data to be appended to the request URL as a query string, as above. Certain characters, such as 'spaces' are encoded by the browser because URLs cannot contain spaces. Spaces are encoded as '+' signs. So for example, if we had entered 'John P' in the firstname field of the form our URL would be:
http://localhost/student/servlet/FormServlet?firstname=John+P&surname=b&age=22
Again, it would be worth at this point checking back to Chapter 2 - Method Types to read the information regarding the GET and POST methods. Sometimes it is more suitable to use one or other of the methods. In short, where small quantities of insecure information is to be passed with the ability to bookmark / revisit the application output. In the example above, if we were to completely ignore the HTML form and simply type the full URL (including the query string) in the browser, we would obtain the same output from our servlet. Because we specify, in the HTML form, that we are using "GET" the browser knows to append the parameters to the request URL for the server. Likewise, the servlets doGet() method knows that it will be receiving parameters from the request URL.
The POST method is used to send large quantities of information (Why do you think GET is unsuitable for this?) in a more secure but non-bookmarkable way. Technically, POST requests can be bookmarked, but parameter information would not be included, so the server application would likely return an error (or throw you back to a previous step if it has been properly designed).
Thankfully, regardless of whether we use GET or POST, the methods used to retrieve data in servlets is the same in either case. By implementing the above example, using the POST method, we can see how little actually changes:
<html>
<head>
<title>Basic Form</title>
</head>
<body>
<h2>Please fill out this form:</h2>
<form method="POST" action="/student/FormServlet2"
name="myform">
<br/>Firstname: <input name="firstname" />
<br/>Surname: <input name="surname" />
<br/>Age: <input name="age" />
<br/>
<input type="submit" value="Submit Form" /> <input type="reset" value="Reset" />
</form>
</body>
</html>
The above html code can be obtained from this HTML file. The changes compared to the previous example 'form.html' have been highlighted in red. Now consider the servlet implementing the doPost method:
package org.dcu;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
@WebServlet("/FormServlet2")
public class FormServlet2 extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
out.println("<html><head><title>Form Example</title></head>");
out.println("<body><h1>Hello " + req.getParameter("firstname") + " "
+ req.getParameter("surname") + "</h1>");
out.println("You are " + req.getParameter("age") + " years old!");
out.println("</body></html>");
out.close();
}
}
Source: FormServlet2.java. Changes compared to 'FormServlet.java' have been highlighted in red also. As you can see, in both cases the code is largely the same regardless of the method which is used.
As can be seen from the sample output below, the servlet performs exactly the same function and in the same way. The only obvious difference is in the URL used - the POST method passes the parameters "invisible" to the end user.
Figure 4.11. Sample Output from FormServlet using POST
What happens if you try to bookmark this output page, shutdown the browser and try and view it later?
Regardless as to the method we use to submit data, the methods used to retrieve this data are the same in either case. The three methods used to retrieve request parameters are methods of ServletRequest:
public Enumeration ServletRequest.getParameterNames();
public String ServletRequest.getParameter(String name);
public String[] ServletRequest.getParameterValues(String name);
The second method getParameter(String name) will be sufficient for most applications. It returns a string containing the values of the specified parameter, or null if the parameter is not contained in the request. This method should be used only if you are sure the request contains only one value for the parameter. If the parameter has multiple values, you should use getParameterValues(), which returns the values of the parameter as an array of strings.
Self-Learning
Using what you have learned about Forms in Chapter 2 and in this section, write a Form which takes two integer numbers (let's call them a and b). Write a servlet which will output: a multiplied by b, a minus b and a plus b. Optionally, you could perform either JavaScript or server-side checking on the parameters to make sure that they are legal values (ie. integer numbers).
Note: For the first part of this you may find the following useful: public int Integer.intValue() and the constructor public Integer(String s)
Relative vs Absolute Paths
You may have noticed that in our forms we set the target to be action="/student/FormServlet". This is known as a "relative path". An absolute version of the path would be: action="http://localhost/student/FormServlet". Let's talk a little about each.
- Relative paths can have two different approaches.
- The first of these is to consider the path of the current resource and then link to the corresponding location of the destination relative to this. For example, imagine we have a file called 'page1.html' in the root of our application, mapped to the URL http://localhost/student/page1.html. If we wanted to link to an image called 'image.jpg' inside an 'images' folder in the same directory, we can simply use <img src="images/image.jpg" />. The path is chosen relative to the http://localhost/student/page1.html directory so essentially becomes http://localhost/student/images/image.jpg, which is what we want.
- The second approach is to use the "/" character to indicate that we wish to provide the path from the root of the server. We can use <img src="/student/images/image.jpg" />. This will also work and is a good approach, provided we are aware of the implications of changing our application name at a later date.
- Absolute Paths in general are a bad idea, unless you are addressing an external resource from another site (in which case you have no option but to include server and port details. Even in this case, our application then becomes prone to changes or downtime on a remote server affecting our web application. In particular though using absolute paths within a web application to point at other elements in the web application will cause problems. Taking the previous example, if you were to use <img src="http://localhost/student/images/image.jpg" /> this would most likely work on your own installation of Tomcat. However, if you were to package your application and send it to me and I were to deploy it on http://www.davids-secret-server.com:8080, then your image would not load for me, despite image.jpg existing in your bundle. Using a relative path instead would have worked, because <img src="/student/images/image.jpg"/> would look at "http://www.davids-secret-server.com/student/images/image.jpg" which would be found.
So in short, relative paths will work when server details and port details change - absolute pathing will not. Use relative!
Note: A good article on absolute vs relative pathing can be found at:
Server-Side Includes
A server-side include (SSI) enables you to embed a Java servlet within an HTML document. In many web/application servers, pages can be preprocessed by the server to include output from one or more servlets at certain points in a standard html page. Well almost standard! Special tags, which are extensions to the HTML language are placed in a file with a .shtml extension. The purpose of this change in name, is to inform the server that special preprocessing is required for this html page. It would be possible to use the extension .html if preprocessing was set up to cover *all* html files. However, this would cause additional load on the web server as it would preprocess all pages, even those without dynamic content before they are supplied to the client. Hence, the alternate name .shtml is used. The following are the typical stages involved:
-
The servlet is written and compiled
-
Special tags are placed in the required HTML file, where the servlet output is expected - this is saved as a .shtml file
-
When a client requests the .shtml file, the server recognises it as a server-side include file and passes it to a server application to parse the document and execute the servlet code specified. In the case of Tomcat, the server application is an internal servlet (org.apache.catalina.ssi.SSIServlet).
-
The results of the servlet applications are merged with the original HTML document and passed back to the client.
-
The client views the page as standard HTML
Server-side includes are particularly useful when a page is primarily static but contains a few distinct dynamic portions. They are also useful where you might have multiple HTML files where you want added functionality. Consider for example, if you wanted all the pages in a section of your website to show the current date. To make it slightly more challenging, we can provide an initial parameter, which allows us to specify the date format as being "long" or "short". Let us implement this example, firstly by writing the servlet:
package org.dcu;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.util.*; // For Date
import java.text.*; // For DateFormat
@WebServlet("/GetTime")
public class GetTime extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT,
DateFormat.SHORT);
Date currentDate = new Date();
// Check for null if initial parameter isn't set - will default short
if (req.getParameter("format")!= null) {
// if we set it to long, otherwise it will remain as short
if (req.getParameter("format").equals("long"))
df = DateFormat.getDateTimeInstance(DateFormat.LONG,
DateFormat.LONG);
}
out.println(df.format(currentDate));
out.close();
}
}
Source: GetTime.java
The servlet simply retrieves the current Date from the server, formats it in either a "LONG" or "SHORT" format, depending on the parameter provided and outputs this time/date to the available PrintWriter. It is possible to run this servlet in the standard way. Compile it, deploy it and type the URL in the browser such as: http://localhost/student/servlet/GetTime?format=long. It will simply output one line on a webpage such as: 16 January 2003 13:04:03 GMT. However, we wish to use this servlet within our .shtml files, which we do in the following way, using a standard Apache server-side include command:
<html>
<head>
<title>Date Example</title>
</head>
<body>
The current time (long version) is:
<!--#include virtual="GetTime?format=long" -->
<br/><br/>And the current time (short version) is:
<!--#include virtual="GetTime?format=short" -->
<br/><br/>And we can write standard html or even
include other servlets anywhere in this page!
</body>
</html>
You should create a file called date.shtml which you will place within the root of your application directory (in accordance with the web application structure). You may notice that if you double click to edit it after creation, it opens it in Explorer (this is due to Explorer being the default windows program to handle html/shtml files). You can edit it within Eclipse by right clicking it and selecting to Open it with a text editor.
In this example, the same servlet is actually included twice in the web page, providing two alternative time formats. Many application servers/servlet containers provide SSI functionality through the use of <SERVLET> tags, which work similarly to <APPLET> tags - however, despite popular belief, these tags were never an official standard - rather they became a defacto standard. Since Tomcat is the official reference implementation, it uses the standard Apache/NCSA tags, which all have the format:
<!--#element attribute=value attribute=value ... -->
If you view the page in a browser and select to view the source code, you will notice that these SSI tags are replaced by the plaintext output from the servlet. This is what we mean by "preprocessing" - the client browser never actually encounters these tags. Before the server provides the HTML page to the client, it parses for the special SSI tags and replaces them with the output from the servlet(s). Hence, it is important that in this case, if you want to try to create this example, you should either copy/paste the HTML source from above, or use the 'HTML file' link above.
Additional Remote Installation Setup
Note: This has been done already on the ZIP version of Tomcat you should have installed. If you have installed Tomcat from a download yourself, then you may need to follow this next step.
If you are remote and are having problems with this example, typically where it seems to be ignoring your special tags, then you need to ensure that you completed the extra steps that were discussed before in the Tomcat Installation Steps. Essentially, Tomcat needs a mapping set up to tell it to process .shtml files differently. The local machines in DCU here have been configured in this way. However, the standard installation of Tomcat is not set up straight away to handle the .shtml preprocessing. In order to enable it you need to make a few small changes to your Tomcat configuration file web.xml (NOT your context web.xml file). Again, if you downloaded the preconfigured version of Tomcat, this should already have been performed. These steps have been extensively detailed in the Downloads section.
In TOMCAT_HOME/conf/web.xml uncomment the following section of configuration
<servlet-mapping>
<servlet-name>ssi</servlet-name>
<url-pattern>*.shtml</url-pattern>
</servlet-mapping>
You will also need to uncomment the section that looks like the following:
<servlet>
<servlet-name>ssi</servlet-name>
<servlet-class>
org.apache.catalina.ssi.SSIServlet
</servlet-class>
........
<load-on-startup>4</load-on-startup>
</servlet>
Restart the server and you should be set up for pre-processing for the examples.
Self-Learning
Write a basic counter servlet, which increments every time it is used on a page. Include it on a basic page (which you also write!), deploy them both to a server and refresh the page a number of times to watch the counter increase. Consider the life-cycle of the servlet when trying this.
What is the main problem with making a counter such as this? (think of server reboots!) How would you go about overcoming this problem by coding the servlet differently?
I only recommend that you should write a servlet which isn't concerned about the possibility of server reboots/context reloads. However if you feel like it:
(Advanced): Modify your application so that your counter remains correct despite server/context reloads.
Session Tracking
What is session tracking? Session tracking is the capability of a server to maintain the current state of a single client's sequential requests. The HTTP Protocol is a stateless protocol and provides no way for a server to recognise that a sequence of requests are all from the same client. Stateless transactions are not a problem when viewing a website, unless you need to know the sequence of actions a client has performed while at your site.
This is probably best explained using two examples:
Example 1: We are designing a site for a club, which provides contact information, directions, photos news and other static pages. Do we particularly need to know the sequence of events a visitor takes? No
Example 2: We are designing a shopping cart system for a private company. This system should let you browse the shop inventory, choose items one at a time, login, enter credit card details, remove items from the cart etc. Are we concerned about the sequence of events? Yes, very much so. We need to know who the person is, whether they are logged in, whether their credit card is validated, when they add an item we wish to remember if they had previously selected items.
Why not simply use the the client's IP address as identification? The IP address is not sufficient to reliably identify an individual user. Consider for example DCU - most computers access the internet via a proxy web server and by the time they access a server, the server would see the IP address of the proxy as the client's IP. Hence if two users on the same intranet, using the same proxy connected were connected to a site, it would not be possible for the site to differentiate between them. Likewise, many clients access the Internet via multiple accounts on the same server (such as using xterms) - it would not be possible for the accessed web site to distinguish between these users, as again they would have the same IP.
So given that HTTP is stateless, we need to look at alternative ways to determine the actions that a particular client has taken. The obvious solution to this problem, is for a client to introduce itself as it makes each request. Each client needs to provide a unique identifier that lets the server identify it, or it needs to give some information that the server can use to properly handle the request. Thankfully, there are several different ways for HTTP clients to send this introductory information with each request:
-
User Authorization
-
Hidden Form Fields
-
URL Rewriting
-
Persistent Cookies
-
Session Tracking API
The first four of these methods are traditional-tracking techniques commonly used by developers in CGI or PHP. The final method discusses the built-in support for session tracking in the Servlet API. This method is by far the most reliable and powerful. However, we will first discuss the alternatives (which are still frequently used - even by servlet developers).
User Authorization
The vast majority of application/web servers provide some built-in system for user authorization. To access these notes on the web, you will have undergone an Authorization process on the Apache WebServer, which hosts the notes. Likewise, Tomcat provides functionality to require clients to 'login' before certain HTML pages or servlets may be accessed. While, we will not go into the detail behind how Tomcat handles this process - it is sufficient for this module to know that it can be done through relatively simple means. In fact, you can witness Tomcat authorization in action by attempting to use the 'Manager' applications.
Once the client user has authorized, their login information is passed with the HttpServletRequest and can be obtained by all servlets by including:
String name = req.getRemoteUser();
Pros/Cons of User Authorization
-
Advantage: Easy to Implement
-
Advantage: Works if the user accesses the site from an alternate machine or browser
-
Disadvantage: Users must all register for an account on the site
-
Disadvantage: Users must always log in every time they visit the site
-
Disadvantage: Users cannot simultaneously maintain more than one session at the same site
-
Disadvantage: completely unsuitable for anonymous session tracking
Hidden Form Fields
Using hidden form fields is perhaps one of the simplest methods for session tracking. As the name suggests, these are standard form fields which are not displayed to the client visibly in their browser. They are sent back to the server when the form that contains them is submitted. Having dealt with forms, and passing form data to servlets previously, it is enough at this stage to provide a sample form entry, which would be inserted within a form in a HTML page:
<INPUT TYPE=hidden NAME="user" value="molloyda">
These hidden fields are transmitted in the request - in fact, to the receiving servlet, there is no difference between a hidden field and a visible field. Let us consider a simple example, containing one html form and two servlets, the html form simply asks a user for a username. The first servlet will just welcome the user and ask them to type in their favourite colour (in the form generated by the first servlet), this will then be submitted to the second servlet, which simply displays both pieces of information.
First, we have the simple form:
<html>
<head>
<title>Example to Demonstrate Hidden Fields</title>
</head>
<body>
<H2>Please choose a Username:</H2>
<form method="POST"
action="/student/HiddenServlet1"
name="myform">
<br/>Username: <input name="username">
<br/><br/>
<input type="submit" value="Submit Form">
<input type="reset" value="Reset">
</form>
<br/><br/>No hidden variables are passed at this early stage
- we will actually pass them in the output of the HiddenServlet1 output.
</body>
</html>
HTML Code: hidden_example.html
Note: Do not try to submit this form from the link above. To get this example (and any other servlet) working we need to deploy these files into our installation of Tomcat.
HiddenServlet1.java
package org.dcu;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
@WebServlet("/HiddenServlet1")
public class HiddenServlet1 extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
String uname = req.getParameter("username");
out.println("<html><head><title>Hidden Forms Example</title></head>");
out.println("<body><h1>You have selected username= " + uname + "</h1>");
out.println("<form method=\"post\"" +
"action=\"/student/HiddenServlet2\" name=\"myform\">");
out.println("<br/>Now enter your favourite colour: ");
out.println("<input name=\"colour\">");
// However, we also want to keep track of which user is on the system
out.println("<input type=\"hidden\" name=\"username\"" +
" value=\"" + uname + "\">");
out.println("<br/><br/><input type=\"submit\" value=\"Submit Form\">");
out.println("<input type=\"reset\" value=\"Reset\">");
out.println("</body></html>");
out.close();
}
}
Source file: HiddenServlet1.java
Assuming that at this stage, we entered the value "molloyda" into the initial html form. We would receive the following output:
Figure 4.12. First Hidden Form Servlet Output
At this stage it is worth noting, that the "FORM" entry for the username remains hidden (yes, it is printed in large letters at the top, but that is just a text output, not a variable). The actual hidden form field is invisible.
HiddenServlet2.java
package org.dcu;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
@WebServlet("/HiddenServlet2")
public class HiddenServlet2 extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
String uname = req.getParameter("username");
String colour = req.getParameter("colour");
out.println("<html><head><title>Hidden Forms Example</title></head>");
out.println("<body><h1>You have selected username = " + uname + "</h1>");
out.println("<br/><br/><h1>You have selected colour = " + colour + "</h1>");
out.println("<br/><br/>Read the notes to see what this example shows!:)");
out.println("</body></html>");
out.close();
}
}
Source file: HiddenServlet2.java
Assuming that at this stage, we entered the value "Blue" into the second html form. We would receive the following output from the second servlet:
Figure 4.13. Second Hidden Form Servlet Output
So what is the significance? In short, we have managed to pass some information from a HTML form, to a servlet and then onto another servlet again! As long as we keep passing the username as a hidden form field in all our servlets, we effectively have session tracking. Because every servlet expects a hidden form field containing the username, the server will always know which requests belong to which users and act accordingly. Note: In reality for this example, a unique username would be chosen by the server (rather than the client itself) using large random numbers or codes, to prevent possible confusion between multiple users who all want to be called 'Joe'.
As another example, if we were coding a Shopping Cart application in this way, the item ids and quantities could be passed as hidden form fields as well as the unique username. As long as the user remains purchasing these hidden form fields will always be included in the HTML. When the user reached the 'CheckOut' servlet, the servlet could obtain all of this data from the form in the standard way.
Pros/Cons of Hidden Form Fields
-
Advantage: Easy to Implement
-
Advantage: Supported by all popular browsers
-
Advantage: Users need not necessarily log on (server can generate unique ids)
-
Disadvantage: As the quantity of information increases, the process becomes very burdensome (unless just a unique id is passed and corresponding information is stored on the server)
-
Disadvantage: Only functions for a sequence of dynamically generated forms (ie. if you had a static html page in the middle of two servlets the process would break down!)
-
Disadvantage: Browser shutdown will break the process
URL ReWriting
URL rewriting is another way to support anonymous session tracking. With URL rewriting, every local URL the user might click on is dynamically modified, or rewritten, to include extra information. The extra information can be in the form of extra path information, added parameters, or some custom, server-specific URL change. Typically, only a unique session ID is passed, mostly due to the limited space available in rewriting a URL (256 max characters). There are a number of methods for performing URL rewriting, but we will just consider two different formats - let us consider that we wish to maintain the session ID ABCD1234:
Original URL
http://server:port/context/servlet/ServletName
Session ID passed as extra path information
http://server:port/context/servlet/ServletName/ABCD1234
Session ID passed as added parameter
http://server:port/context/servlet/ServletName?sessionid=ABCD1234
Using extra path information works on all servers, and it works as a target for forms that use both the GET and POST methods. Using the added parameter works on all servers, but will not support as a target for forms that use the POST method, and it can could parameter naming collisions (but shouldn't, if the developer is in any way careful). As an example, let us just write one servlet (which will demonstrate the session ID passing, even though it is just repeatedly passing the session ID to itself on subsequent requests). Normally, in development all servlets would have similar code added to pass the session ID:
package org.dcu;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
@WebServlet("/URLRewriting")
public class URLRewriting extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
String sessionID;
out.println("<html><head><title>URL Rewriting Example</title></head>");
out.println("<body><h1>URL Rewriting Example</h1>");
if (req.getParameter("sessionid") == null) {
out.println("SessionID not set - generating unique sessionID");
sessionID = generateSessionID() + "";
}
else {
out.println("SessionID passed!");
sessionID = req.getParameter("sessionid");
}
out.println("<br/><br/>SessionID = " + sessionID);
out.println("<br/><br/>So now what is the difference between selecting to ");
out.println(" Refresh the page (before clicking the link) vs clicking ");
out.println("<a href=\"/student/URLRewriting?sessionid="
+ sessionID + "\">this link</a>, which is URL Rewritten?");
out.println("</body></html>");
out.close();
}
private long generateSessionID()
{
return System.currentTimeMillis();
}
}
Source file: URLRewriting.java
The above example when loaded first time will generate a unique session ID - in this example we just use the System.currentTimeMillis(), which calculates the number of milliseconds since Jan 1, 1970, which we can assume to be unique for our purposes. As long as the page is refreshed, it will continue to generate up unique session IDs. However, to demonstrate URL Rewriting we create a link (could alternatively been a target for a form etc.) which appends the session ID to the URL. When this link is selected, our session ID is passed on with our request. In this way, the session ID can be passed between any number of servlets - however, *all* links need to be URL Rewritten, or the chain could break down! Note - it would not have been possible to do this example in this way using hidden form fields, as we are using a simply link to a servlet as opposed to an actual form submission.
On a side note, the HttpServletRequest object has a method called encodeURL(), which can be used for URL Rewriting. However, we are more concerned in these notes at understanding the concepts behind URL Rewriting.
Pros/Cons of URL Rewriting
-
Advantage: Easy to Implement
-
Advantage: Supported by all popular browsers
-
Advantage: Users need not necessarily log on (server can generate unique ids)
-
Advantage: URL rewriting works for all dynamically created documents, rather than just forms!
-
Disadvantage: Typically only session IDs are passed, otherwise only small quantities of information could be URL rewritten into the URLs
-
Disadvantage: Unless custom techniques are used, URL rewriting does not work for static documents
-
Disadvantage: Browser shutdown will break the process
-
Disadvantage: Performing URL rewriting can be very tedious
Persistent/Non-Persistent Cookies
A cookie is a simple piece of information stored on the client, on behalf of the server. When a browser receives a cookie, it saves the cookies and thereafter sends the cookie back to the server each time it accesses a page on that server, subject to certain rules. Because a cookie's value can uniquely identify a client, cookies are often used for session tracking. Cookies originally caused some of the most unfounded security fears - users did not like servers being able to store data and possibly execute applications on their local machines. However, cookies do not provide functionality for servers to run applications on client machines - the data can only be accessed via the server and is of the order of a few thousand bytes of data at most.
Although cookies were not part of the official HTTP specification, they were originally introduced in Netscape Navigator and quickly became a de facto standard, supported by all popular browsers. They have since been drafted as an oficial standard.
There are two types of Cookies:
-
Non-Persistent Cookies - expire when the browser exits. No data is stored on the client machines harddrive after the browser has been shut down.
-
Persistent Cookies - do not expire when the browser exits. A maximum "age" for the cookie in seconds before it expires can be set using the Cookie.setMaxAge(int expiry) method. If this method is not called, the default value is -1, meaning that the cookie will be considered to be non-persisent.
cookie.setMaxAge(3600); // Sets expiry to be 1 hour
The Servlet API provides the javax.servlet.http.Cookie class for working with cookies. The constructor for a Cookie looks like the following:
public Cookie (String name, String value)
The following line initializes a Cookie object for example:
Cookie cookie = new Cookie("userid", "david_molloy832");
After creating a cookie, send it to the client with the response - the Servlet API provides an easy method for this process. A servlet can send a cookie to the client by passing a Cookie object to the addCookie() method of HttpServletResponse:
public void HttpServletResponse.addCookie(Cookie cookie)
The addCookie() method can be called multiple times to set many cookies, although the original specification allows only 20 cookies per server, so depending on the browser there will be different limits. So for example, continuing with our example, we can simply:
res.addCookie(cookie);
The cookie must be added after setting the content type, but before sending the output, as the cookie is sent back as part of the HTTP response header. A servlet retrieves cookies by calling the getCookies() method of HttpServletRequest, which returns an array of Cookie objects which contains all the cookies sent by the browser as part of the request, or null if no cookies were sent. The code to fetch cookies:
Cookie[] cookies = req.getCookies();
if (cookies != null) {
for (int i=0; i< cookies.length; i++) {
String name = cookies[i].getName();
String value = cookies[i].getValue();
}
}
There are a number of additional methods for setting cookies parameters, such as the version, domain, maximum age etc. So let us write an example persistent cookie servlet (ie. one with a 1 hour lifespan), which will both set and retrieve cookie parameters:
package org.dcu;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
@WebServlet("/CookieCount")
public class CookieCount extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
int countint = 0;
out.println("<html><head><title>Persistent Cookies</title></head>");
out.println("<body><h1>Persistent Cookies Example</h1>");
// First we grab all our cookies
Cookie[] cookies = req.getCookies();
if (cookies != null) {
for (int i=0; i< cookies.length; i++) {
String name = cookies[i].getName();
String value = cookies[i].getValue();
out.println("<br/><br/>Name: " + name);
out.println("<br/>Value: " + value);
// Let's make the servlet count the number of accesses!
if (name.equals("count")) {
countint = (new Integer(value)).intValue();
countint++;
Cookie cookie = new Cookie("count", countint + "");
cookie.setMaxAge(3600);
res.addCookie(cookie);
out.println("<br/><br/>You have accessed this page: "
+ countint + " times!");
}
}
}
else { // If cookies is null, there are no cookies, so make them now!
out.println("<br/>No cookie set - Adding Cookie now - set in future!");
Cookie idCookie = new Cookie("sessionid", generateSessionID() + "");
idCookie.setMaxAge(3600); // Sets the cookie lifespan to be one hour!
res.addCookie(idCookie);
Cookie countCookie = new Cookie("count", "0");
countCookie.setMaxAge(3600);
res.addCookie(countCookie); // quick cookie addition
}
out.println("</body></html>");
out.close();
}
private long generateSessionID()
{
return System.currentTimeMillis();
}
}
Source file: CookieCount.java
This example prints out all of the cookies on the client machine which "belong" to this server. The first time the servlet application is run it will not print any cookies (as none have yet been set) and the req.getCookies() will return null. Hence during this execution, we enter the 'else' section of code and simply generate a session id and set a cookie with the value of 0 (as a string). Subsequent calls to the servlet will display the session id which we have set and will increment the count variable. You will notice that we are required to do a little conversion of variable types, from string to int and back again. All cookies are stored as Strings so in order to increment the value we convert it to an int, and to store it again we convert it to a string. One easy way of converting ints to string is just to "add" an empty string to the variable (intval + ""). You should deploy this example and try it out. You will notice, that regardless of whether we restart the server, shutdown the client machine/browser the count variable will remain consistent. This is a major feature of "persistent" cookies - these cookies are saved locally on the client machine.
A sample output from the above example can be seen below - this represents the expected output when the page had been viewed more than once. (47 times in this case!) Note: If you are having problems with this example when you deploy it, ensure that cookies are enabled in your browser.
Figure 4.16. Persistent Cookies Example Output
Pros/Cons of Persistent Cookies
-
Advantage: Efficient and simple way to implement session tracking
-
Advantage: Persistent cookies persist even if browsers and/or servers are shutdown/restarted
-
Advantage: Users need not necessarily log on (server can generate unique ids)
-
Advantage: Works for all documents/pages - even if a static HTML page is viewed, the cookies can be accessed later regardless
-
Advantage: The ability to customize cookies makes them powerful and versatile
-
Disadvantage: The major problem with cookies is that clients sometimes configure their browsers to refuse cookies (for security concerns). If any clients might not accept cookies, you must fall back to one of the other solutions.
Session Tracking API
Fortunately for servlet developers, the Servlet API has its own built-in support for session tracking! This functionality is provided by theHttpSession object. Each client browser that accesses a Web server results in the generation of a session object. Sessions are useful if you want to share a user's information between various servlets. The Session Tracking section of the Servlet API should be supported in any web server that supports servlets. In order to create a new session, you use the HttpServletRequest object's getSession() method.
public HttpSession HttpServletRequest.getSession(boolean create);
So for example:
HttpSession session = req.getSession(true);
This method returns the current session associated with the user making the request. If the user has no current valid session, this method creates one if the single argument (create) is set as true, or returns null if create is false. To ensure that the session is properly maintained, this method must be called at least once before any output is written to the response.
There are four methods of the HttpSession object, we are concerned with:
-
public void setAttribute(String name, Object value) - this method binds a name/value pair to store in the current session. If the name already exists in the current session then it is replaced. While similar structure to it's persistent cookie cousin, you should notice that you can store an *object*, as opposed to simply a String value.
-
public Object getAttribute(String name) - this method is used to get an object that is stored in the session. The getValue() method takes a String representing the name that the desired object is bound to.
-
public Enumeration getAttributeNames() - returns an enumeration of the all current bound names stored in the session. It returns an enumeration that contains the names of all objects bound to this session or an empty (zero length) array if there are no bindings.
-
public void removeAttribute(String name) - allows the removal of an object, bound to a particular name, from the session. If there is no binding of this name, the method does nothing.
See the Java Enterprise Edition API documentation for further details. So let us again consider the previous persistent cookies example and rewrite it using the built-in session tracking with the Servlet API.
package org.dcu;
import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
@WebServlet("/SessionCount")
public class SessionCount extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
// Get the current session or create one
HttpSession session = req.getSession(true);
out.println("<html><head><title>Session Tracking Example</title></head>");
out.println("<body><h1>Session Tracking Example</h1>");
out.println("<br/>Session ID = " + session.getId());
// casting as an Integer count.value is a user selected name
Integer count = (Integer) session.getAttribute("count.value");
// If count is null this means that no value yet bound to this session
if (count == null) count = new Integer(1);
// Else we read in the current value, increment it and put it back
else count = new Integer(count.intValue() + 1);
session.setAttribute("count.value", count);
out.println("<br/><br/>You have visited this page " + count + " time(s)");
out.println("<br/><br/>Refresh the page to watch counter rise. ");
out.println("Try opening another browser and loading this page - result?");
out.println("</body></html>");
out.close();
}
}
Source file: SessionCount.java
This example first obtains or generates the session. It then attempts to get the 'Attribute' for 'count.value' from the session. If this object does not already exist, then we create a new Integer (with a value of 1) and place it into the session. Otherwise, if the object previous existed, we increment it and place the new value into the session - ready for the next servlet access. A sample output from the above servlet can be seen below.
Figure 4.17. Session Tracking API Example Output
Some tests worth trying - what happens if you simultaneously access the same servlet from two different browsers? Why? What happens if you restart the server? Why?
Rather than have you sit with your arms folded for 30 minutes, we won't suggest that you test this next factor. Sessions have a limited lifespan (purposefully!) - a session expires automatically after a set time of inactivity (this is configured in the Tomcat configuration files for example, defaults to 30 mins), or manually, when it is explicitly invalidated by a servlet. When a session expires (or is invalidated), the HttpSession object and the data values it contains are removed from the system. Any information stored in the users's session object is lost when the session is invalidated. Hence, typically any information which should be retained beyond that time should be stored in an external location, such as a database. As an example of when you might invalidate a session, if a user was selecting to 'Logout' of your system, you could invalidate their session and all information within. In order to manually invalidate a session, you use:
public void HttpSession.invalidate()
Pros/Cons of Built-in Session Tracking
-
Advantage: Extremely efficient and simple way to implement session tracking
-
Advantage: Session information often persists even if servers are shutdown/restarted (server specific)
-
Advantage: Users need not necessarily log on (server can generate unique ids)
-
Advantage: Works for all documents/pages - even if a static HTML page is viewed, Session exist for a set duration
-
Advantage: Part of the overall Servlet API, so standardized and included in J2EE
-
Advantage: Sessions can be set to 'time-out' after a set period of inactivity (consider 24 hour banking)
-
Advantage: Objects rather than simply Strings can be stored on the session
-
Disadvantage: Restricted to one browser (could be seen as an advantage however) and tracking lost if client browser shut down/crashes
- Disadvantage: Slightly more difficult if clients have all cookies (including session cookies) disabled
Building a Login System - Sample
In an effort to piece together some of the previous examples (principally the forms and session tracking examples), we will take a simple example of a custom built login system for any typically application. We are going to create four parts 1) A login html page 2) a JavaBean which will contain the properties of a 'User' 3) A LoginServlet which will perform the login 4) A "secure" page, 'AccountHome' which should only be accessed by an individual who has previously logged in.
1) Let us start with the login page: login.html
<html>
<head>
<title>Login</title>
</head>
<body>
<h2>Please enter your username and password:</h2>
<form method="POST" action="LoginServlet"
name="myform">
<br/>Username: <input type="text" name="usernamefield" />
<br/>Password: <input type="password" name="passwordfield" />
<br/><br/>
<input type="submit" value="Login" />
</form>
</body>
</html>
There are no big surprises here. We have a form which takes two control fields, one a text field and one a password field. A password type is used to prevent the characters from being seen while the client types in their details - in every other way a password type input acts the same as a standard text input. The method used by the form is "POST" - this is important since we do not want the username and password details appearing in the URL on the target servlet page.
2) So let us continue with the User JavaBean:
package org.dcu;
public class User {
private String firstname;
private String surname;
private String username;
private String password;
public User(String firstname, String surname, String username,
String password) {
super();
this.firstname = firstname;
this.surname = surname;
this.username = username;
this.password = password;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Source: User.java
This is something we haven't introduced into this module previously: What are JavaBeans? JavaBeans are used to contain an object oriented representation of a real-world entity we wish to represent. As a result, they contain the properties (in this case: firstname, surname, username and password) of the object that we wish to read and manipulate. The advantage they offer is that it means we can always operate in a object oriented sense, but where we want to get some data we can use any of the getter methods, such as user.getPassword(). Likewise, if we wish to manipulate an existing object, we can use any of the setter methods, such as user.setPassword(). More information on the JavaBean, the constructor and the getter/setter methods can be heard in the video file demonstrating this example.
Note:
Please pay particular attention to the video where it demonstrates how you can practically generate JavaBeans automatically. You should *NOT* be typing these files.
3) The core of our login system: LoginServlet.java. This is probably the most complex code you have seen yet in the course.
The complicated bit is the validateUser method we have created. This method firstly generates two dummy users against which we will authenticate. In reality, at this point we would be authenticating against user details in a database for example (again something we have not yet covered). So for now, we create two User objects and put them into an array (of type Vector). The validation then enumerates through the array and compares the username/password of each user against the values input by the client via the form. If a match is found, the corresponding User object is returned by the method (and the method finishes immediately). If all users are enumerated and no matches made, then a value of 'null' will be returned.
Moving back to the primary doPost() method we also see a new statement we have not seen before res.sendRedirect(). Rather than giving out a response through our 'out.println' statements, this instead redirects the response to a particular URL. So, for example, when the login fails we are redirecting to the login.html file to indicate that login had failed (ideally we should provide a message on the login page but we would normally do this with a JSP which we haven't covered yet).
Finally, by placing the User object in the session, we are then able to access this object from any other servlet in our application. This is important - as we will see on the next page.
package org.dcu;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.*;
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
User user = validateUser(req.getParameter("usernamefield"), req.getParameter("passwordfield"));
if (user!=null) { // login succeeded
out.println("Welcome " + user.getFirstname() + " " + user.getSurname() + ". You are now logged in!");
out.println("<br/><br/>Please click here to go to the <a href=\"AccountHome\">AccountHome page</a>");
HttpSession session = req.getSession();
session.setAttribute("theUser", user); // put the OBJECT on the session
}
else { // login failed
// no failure message for now but it will
// send the user back to try again
res.sendRedirect("login.html");
}
out.close();
}
/*
* This method will dummy the login process and return null if unsuccessful or a User object if successful
*/
private User validateUser(String username,String password) {
// Let us first make a Vector of dummy users that are allowable... these would normally be taken from a database
// or some other storage of all of our users
Vector<User> allUsers = new Vector<User>();
allUsers.add(new User("John", "Doe", "doej", "somepass"));
allUsers.add(new User("Mary", "Smith", "smithm", "mypass"));
// Now do the comparison
Enumeration<User> e = allUsers.elements();
while (e.hasMoreElements()) {
User user = (User) e.nextElement();
if ((user.getPassword().equals(password))&&(user.getUsername().equals(username)))
return user;
}
return null;
}
}
4) Finally, we have the AccountHome page, which is merely a secure page for our site. A few things should happen here:
- If a user has logged in and goes to this URL, they should see the contents of this page.
- If a user tries the URL without logging in, they should be kicked back to the login page
- If a user tries the URL after their session timing out, they should be kicked back to the login page
- If a user follows a bookmark to this page without going through the login first, they will be kicked back to the login page
To implement all of these security checks, we can simply test for a User object on the session. If it exists, then the user had logged in and the session is active. If it does not, then the user has not been validated and is kicked to the login page.
package org.dcu;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@WebServlet("/AccountHome")
public class AccountHome extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
HttpSession session = req.getSession(true);
User user = (User) session.getAttribute("theUser");
if (user==null) { // not logged in or timed out
res.sendRedirect("login.html");
} else { // logged in and active session
out.println("This is your secure account page!");
out.println("If you are seeing this, you logged in!");
}
out.close();
}
}
Deploy the example, play with it and get to understand it. The reason I have provided the example is that it should provide us with a feeling of piecing multiple components together towards the feeling that we are actually building a fully fledged web application.
One of the irritating things, as you have probably discovered, in using servlets is that they are very messy for outputting HTML.
To try to print out <img src="test.jpg" alt="A test image" /> requires the use of escapes when we are required to use out.println("<img src=\"test.jpg\" alt=\"A test image\" />"); In the next section we will introduce JSPs, which address this issue and others.
Login System - Video Tutorial