In this chapter, we will discuss how to develop and deploy Java servlets. Some of the topics covered are:
An explanation of what servlets are
Developing, configuring, packaging and deploying our first servlet
HTML form processing
Forwarding HTTP requests
Redirecting HTTP responses
Persisting data across HTTP requests
What is a Servlet? A servlet is a Java class that is used to extend the capabilities of servers that host applications. Servlets can respond to requests and generate responses. The base class for all servlets is javax.servlet.GenericServlet
, this class defines a generic, protocol-independent servlet.
By far, the most common type of servlet is an HTTP servlet; this type of servlet is used in handling HTTP requests and generating HTTP responses. An HTTP servlet is a class that extends the javax.servlet.http.HttpServlet
class, which is a subclass of javax.servlet.GenericServlet
.
A servlet must implement one or more methods to respond to specific HTTP requests. These methods are overridden from the parent HttpServlet
class. As can be seen in the following table, these methods are named so that knowing which one to use is intuitive.
HTTP Request |
HttpServlet Method |
---|---|
GET |
|
POST |
|
PUT |
|
DELETE |
|
Each of these methods take the same two parameters, namely an instance of a class implementing the javax.servlet.http.HttpServletRequest
interface and an instance of a class implementing the javax.servlet.http.HttpServletResponse
. These interfaces will be covered in detail, later in this chapter.
Application developers never call the above methods directly; they are called automatically by the application server whenever it receives the corresponding HTTP request.
Of the four methods listed above, doGet()
and doPost()
are, by far, the most commonly used.
An HTTP GET request is generated whenever a user types the servlet's URL in the browser, when a user clicks on a link pointing to the servlet's URL, or when a user submits an HTML form using the GET method that has an action pointing to the servlet's URL. In any of these cases, the code inside the servlet's doGet()
method gets executed.
An HTTP POST request is typically generated when a user submits an HTML form using the POST method and an action pointing to the servlet's URL. In this case, the servlet's code inside the doPost()
method gets executed.
In chapter 1, we deployed a simple application that printed a message on the browser window. That application basically consisted of a single servlet. In this section, we will see how that servlet was developed, configured, and packaged.
The code for the servlet is as follows:
package net.ensode.glassfishbook.simpleapp; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class SimpleServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) { try { response.setContentType("text/html"); PrintWriter printWriter = response.getWriter(); printWriter.println("<h2>"); printWriter .println("If you are reading this, your application server is good to go!"); printWriter.println("</h2>"); } catch (IOException ioException) { ioException.printStackTrace(); } } }
As this servlet is meant to execute when a user enters its URL in the browser window, we need to override the doGet()
method from the parent HttpServlet
class. As we explained, this method takes two parameters: an instance of a class implementing the javax.servlet.http.HttpServletRequest
interface, and an instance of a class implementing the javax.servlet.http.HttpServletResponse
interface.
Even though HttpServletRequest
and HttpServletResponse
are interfaces, application developers don't typically write classes implementing them. When control goes to a servlet from an HTTP request, the application server (GlassFish, in our case) provides objects implementing these interfaces.
The first thing our doGet()
method does is to set the content type for the HttpServletResponse
object to "text/html". If we forget to do this, the default content type used is "text/plain", which means that the HTML tags used a couple of lines down will be displayed on the browser, as opposed to them being interpreted as HTML tags.
Then we obtain an instance of java.io.PrintWriter
by calling the HttpServletResponse.getWriter()
method. We can then send text output to the browser by calling the PrintWriter.print()
and PrintWriter.println()
methods (the previous example uses println()
exclusively). As we set the content type to "text/html", any HTML tags are interpreted properly by the browser.
To compile the servlet, the Java library included with GlassFish must be in the CLASSPATH. This library is called javaee.jar
, it can be found under [glassfish installation directory]/glassfish/lib
.
To compile from the command line using the javac
compiler, a command like the following must be issued (all in one line):
javac -cp /opt/glassfish/lib/javaee.jar:. net/ensode/glassfishbook/simpleapp/SimpleServlet.java
Of course, these days very few developers compile code with the "raw" javac
compiler; instead, either a graphical IDE or a command line build tool like Apache ANT or Apache Maven is used. Consult your IDE or build tool documentation for information on how to add the javaee.jar
library to its CLASSPATH.
Maven
Apache Maven is a build tool similar to ANT. However, Maven offers a number of advantages over ANT including automatic download of dependencies and standard commands for compilation and packaging of applications. Maven was the build tool used to compile and package most of the examples in this book, therefore it is recommended to have Maven installed in order to easily build the examples.
When using Maven, the code can be compiled and packaged by issuing the following command at the project's root directory (simpleapp
in this case): mvn package
.
Maven can be downloaded from http://maven.apache.org/.
Before we can deploy our servlet, we need to configure it. All Java EE web applications are configured via an XML deployment descriptor named web.xml
. The web.xml
deployment descriptor for our servlet is as follows:
<?xml version="1.0" encoding="UTF-8"?> "<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5" xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://java.sun.com/xml/ns/ javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">" <servlet> <servlet-name>SimpleServlet</servlet-name> <servlet-class> net.ensode.glassfishbook.simpleapp.SimpleServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>SimpleServlet</servlet-name> <url-pattern>/simpleservlet</url-pattern> </servlet-mapping> </web-app>
The first few lines are boilerplate XML stating the XML version and encoding, plus the schema used for the XML file and other information. It is safe to just copy and paste these lines and reuse them across applications. The<servlet>
and<servlet-mapping>
XML tags above are used to actually configure our servlet.
The<servlet>
tag contains two nested tags.<servlet-name>
defines a logical name for the servlet, and<servlet-class>
indicates the Java class defining the servlet.
The<servlet-mapping>
tag also contains two nested tags:<servlet-name>
, which must match the value set inside the<servlet>
tag and<url-pattern>
, which sets the URL pattern for which the servlet will execute.
<url-pattern>
can be specified in one of two ways, by using a path prefix (which is what the example above does), or by specifying an extension suffix.
Path prefix values for<url-pattern>
indicate that any URL paths starting with the given path will be serviced by the corresponding servlet. Path prefix values must start with a forward slash.
Java EE web applications run from within a context root. The context root is the first string in the URL that is not the server name or IP address, nor the port. For example, in the URL http://localhost:8080/simpleapp/simpleservlet
, the string simpleapp
is the context root. The value for<url-pattern>
is relative to the application's context root.
Extension suffix values for<url-pattern>
indicate that any URLs ending in the given suffix will be serviced by the corresponding servlet. In the above example, we chose to use a path prefix. Had we chosen to use an extension suffix, the<servlet-mapping>
tag would have looked something like this:
<servlet-mapping> <servlet>SimpleServlet</servlet> <url-pattern>*.foo</url-pattern> </servlet-mapping>
This would direct any URLs ending with the string .foo
to our servlet.
The reason the<servlet-name>
tag is specified twice (once inside the<servlet>
tag and again inside the<servlet-mapping>
tag) is because a Java EE 5 web application can have more than one servlet, each of which must have a<servlet>
tag in the application's web.xml
. The<servlet>
tag for each must have a corresponding<servlet-mapping>
tag, and the<servlet-name>
nested tag is used to indicate which<servlet>
tag corresponds to which<servlet-mapping>
tag.
A Java EE 5 web.xml
file can contain many additional XML tags. However, these additional tags are not needed for this simple example. Additional tags will be discussed in future examples when they are needed.
Before we can execute our servlet, we need to package it as part of a web application in a WAR (Web ARchive) file.
All Java EE 5 web applications must be packaged in a WAR (Web ARchive) file before they can be deployed. A WAR file is nothing but a compressed file containing our code and configuration. WAR files can be created by any utility that can create files in ZIP format (for example, WinZip, 7-Zip, etc.). Also, many Java IDEs and build tools such as ANT and Maven automate WAR file creation.
A WAR file must contain the following directories (in addition to its root directory)
WEB-INF
WEB-INF/classes
WEB-INF/lib
The root directory contains JSPs (covered in the next chapter), HTML files, JavaScript files, and CSS files.
WEB-INF
contains deployment descriptors such as web.xml
.
WEB-INF/classes
contains the compiled code (.class
files) and may optionally contain property files. Just as with any Java classes, the directory structure must match the package structure, therefore this directory typically contains several subdirectories corresponding to the classes contained in it.
WEB-INF/lib
contains JAR files containing any library dependencies our code might have.
The root directory, WEB-INF
, and WEB-INF/classes
directories can have sub directories. Any resources on a subdirectory of the root directory (other than WEB-INF)
can be accessed by prepending the subdirectory name to its file name. For example, if there was a subdirectory called css
containing a CSS file called style.css
, this CSS file could be accessed in JSPs and HTML files in the root directory by the following line:
<link rel="stylesheet" type="text/css" media="screen" href="css/style.css" />
Notice the css
prefix to the file name, corresponding to the directory where the CSS file resides.
To create our WAR file "from scratch", create the above directory structure in any directory in your system, then follow the following steps:
Copy the web.xml
file to WEB-INF
.
Create the following directory structure under WEB-INF/classes: net/ensode/glassfishbook/simpleapp
.
Copy SimpleServlet.class
to the simpleapp
directory from step 2.
From the command line, issue the following command from the directory right above WEB-INF: jar cvf simpleapp.war
.
You should now have a WAR file ready for deployment.
When using Maven to build the code, the WAR file is automatically generated when issuing the mvn package
command. The WAR file can be found under the target
directory, it is named simpleapp.war
.
Before we can execute our application, it needs to be deployed.
As we discussed in Chapter 1, there are several ways of deploying an application. The easiest and most straightforward way to deploy any Java EE application is to copy the deployment file (WAR file in this case) to the [glassfish installation directory]/glassfish/domains/domain1/autodeploy
directory.
After copying the WAR file to the autodeploy
directory, the system log should show a message similar to the following:
[#|2007-02-17T11:36:22.906-0500|INFO|sun-appserver9.1|javax.enterprise.system.tools.deployment|_ThreadID=12;_ThreadName=Timer-4;|deployed with moduleid = simpleapp|#]
[#|2007-02-17T11:36:23.424-0500|INFO|sun-appserver9.1|javax.enterprise.system.tools.deployment|_ThreadID=12;_ThreadName=Timer-4;|[AutoDeploy] Successfully autodeployed : /opt/glassfish/domains/domain1/autodeploy/simpleapp.war.|#]
The system log can be found under [glassfish installation directory]/glassfish/domains/domain1/logs/server.log
.
The last line should contain the string "Successfully autodeployed" indicating that our WAR file was deployed successfully.
To verify that the servlet has been properly deployed, we need to point our browser to http://localhost:8080/simpleapp/simpleservlet
; after doing so, we should see a page like the one shown in the following screenshot.
Unsurprisingly, this is the same message as we saw when deploying the application in Chapter 1, as this is the same application as we deployed then.
Earlier in this chapter, we mentioned that URL paths for a Java EE 5 application are relative to their context root. The default context root for a WAR file is the name of the WAR file itself (minus the .war
extension). As can be seen in the screenshot above, the context root for our application is simpleapp
, which happens to match the name of the WAR file. This default can be changed by adding an additional configuration file to the WEB-INF
directory of the WAR file. The name of this file must be sun-web.xml
. An example sun-web.xml
that will change the context root of our application from the default simpleapp
to simple
would look like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 8.1 Servlet 2.4//EN" "http://www.sun.com/software/appserver/dtds/sun-web-app_2_4-1.dtd">
<sun-web-app>
<context-root>/simple</context-root>
</sun-web-app>
As can be seen in the above example, the context root for the application must be in the<context-root>
tag of the sun-web.xml
configuration file. After redeploying the simpleapp.war
, directing the browser to http://localhost:8080/simple/simpleservlet
will execute our servlet.
Servlets are rarely accessed by typing their URL directly in the browser. The most common use for servlets is to process data entered by users in an HTML form. In this section, we illustrate this process.
Before digging into the servlet code and HTML markup, let's take a look at the web.xml
file for this new application.
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>FormHandlerServlet</servlet-name>
<servlet-class>
net.ensode.glassfishbook.formhandling.FormHandlerServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FormHandlerServlet</servlet-name>
<url-pattern>/formhandlerservlet</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>dataentry.html</welcome-file>
</welcome-file-list></web-app>
The above web.xml
file is very similar to the one we saw in the previous section; however, it contains an XML tag we haven't seen before, namely the<welcome-file>
tag. The<welcome-file>
tag determines what file to direct to when a user types a URL ending in the application's context root (for this example, the URL would be http://localhost:8080/formhandling
, as we are naming our WAR file formhandling.war
and not specifying a custom context root). We will name the HTML file containing the form dataentry.html
; this will cause GlassFish to render it in the browser when the user types our application's URL and does not specify a file name.
If no<welcome-file>
is specified in the application's web.xml
file, GlassFish will look for a file named index.html
and use it as the welcome
file. If it can't find it, it will look for a file named index.jsp
and use it as a welcome
file. If it can't find it either one, it will display a directory listing.
The HTML file containing the form for our application looks like this:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Data Entry Page</title> </head> <body> <form method="post" action="formhandlerservlet"><table cellpadding="0" cellspacing="0" border="0"> <tr> <td>Please enter some text:</td> <td> <input type="text" name="enteredValue" /></td> </tr> <tr> <td></td> <td><input type="submit" value="Submit"></td> </tr> </table> </form> </body> </html>
Notice how the value for the form's action
attribute matches the value of the servlet's<url-pattern>
in the application's web.xml
(minus the initial slash). As the value of the form's method
attribute is "post", our servlet's doPost()
method will be executed when the form is submitted.
Now let's take a look at our servlet's code:
package net.ensode.glassfishbook.formhandling; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class FormHandlerServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) { String enteredValue; enteredValue = request.getParameter("enteredValue"); response.setContentType("text/html"); PrintWriter printWriter; try { printWriter = response.getWriter(); printWriter.println("<p>"); printWriter.print("You entered: "); printWriter.print(enteredValue); printWriter.print("</p>"); } catch (IOException e) { e.printStackTrace(); } } }
As can be seen in the above example, we obtain a reference to the value the user typed by calling the request.getParameter()
method. This method takes a single String
object as its sole parameter; the value of this string must match the name of the input field in the HTML file. In this case, the HTML file has a text field named "enteredValue":
<input type="text" name="enteredValue" />
Therefore the servlet has a corresponding line:
enteredValue = request.getParameter("enteredValue");
to obtain the text entered by the user and store it in the string variable named enteredValue
(the name of the variable does not need to match the input field name, but naming it that way is good practice to make it easy to remember what value the variable is holding).
After packaging the preceding three files in a WAR file called formhandling.war
, then deploying the WAR file, we can see the rendered dataentry.html
file by entering the following URL in the browser: http://localhost:8080/formhandling
.
After we enter "some text" in the text field and submit the form (either by hitting "enter" or clicking on the Submit button), we should see the output of the servlet.
The HttpServletRequest.getParameter()
method can be used to obtain the value of any HTML input field that can only return one value (text boxes, text areas, single selects, radio buttons, hidden fields, etc.). The procedure to obtain any of these fields values is identical, in other words, the servlet doesn't care if the user typed in the value in a text field, selected it from a set of radio buttons, etc. As long as the input field's name matches the value passed to the getParameter()
method, this code will work.
When dealing with radio buttons, all related radio buttons must have the same name. Calling the HttpServletRequest.getParameter()
method and passing the name of the radio buttons will return the value of the selected radio button.
Some HTML input fields like checkboxes and multiple select boxes allow the user to select more than one value. For these fields, instead of using the HttpServletRequest.getParameter()
method, the HttpServletRequest.getParameterValues()
method is used. This method also takes a string containing the input field's name as its only parameter, and returns an array of strings containing all the values that were selected by the user.
Let's add a second HTML file and a second servlet to our application to illustrate this case. The relevant sections of this HTML are shown below.
<form method="post" action="multiplevaluefieldhandlerservlet"><p>Please enter one or more options.</p> <table cellpadding="0" cellspacing="0" border="0"> <tr> <td><input name="options" type="checkbox" value="option1" /> Option 1</td> </tr> <tr> <td><input name="options" type="checkbox" value="option2" /> Option 2</td> </tr> <tr> <td><input name="options" type="checkbox" value="option3" /> Option 3</td> </tr> <tr> <td><input type="submit" value="Submit" /></td> <td></td> </tr> </table> </form>
The new HTML file contains a simple form having three checkboxes and a submit button. Notice how every checkbox has the same value for its name
attribute. As we mentioned before, any checkboxes that are clicked by the user will be sent to the servlet.
Let's now take a look at the servlet that will handle the above HTML form.
package net.ensode.glassfishbook.formhandling; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MultipleValueFieldHandlerServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) { String[] selectedOptions = request.getParameterValues("options"); response.setContentType("text/html"); try { PrintWriter printWriter = response.getWriter(); printWriter.println("<p>"); printWriter.print("The following options were selected:"); printWriter.println("<br/>"); if (selectedOptions != null) { for (String option : selectedOptions) { printWriter.print(option); printWriter.println("<br/>"); } } else { printWriter.println("None"); } printWriter.println("</p>"); } catch (IOException e) { e.printStackTrace(); } } }
The above code calls the request.getParameterValues()
method and assigns its return value to the selectedOptions
variable. Farther down the doPost()
method, the code traverses the selectedOptions
array and prints the selected values in the browser.
This code uses the enhanced for
loop introduced in JDK 1.5. Refer to http://java.sun.com/j2se/1.5.0/docs/guide/language/foreach.html for more information.
If no checkboxes are clicked, the request.getParameterValues()
method will return null; therefore it is a good idea to check for null before attempting to traverse through this method's return values.
Before this new servlet can be deployed, the following lines need to be added to the application's web.xml
file:
<servlet> <servlet-name>MultipleValueFieldHandlerServlet</servlet-name> <servlet-class> net.ensode.glassfishbook.formhandling.MultipleValueFieldHandlerServlet </servlet-class> </servlet>
and:
<servlet-mapping> <servlet-name>MultipleValueFieldHandlerServlet</servlet-name> <url-pattern>/multiplevaluefieldhandlerservlet</url-pattern> </servlet-mapping>
to assign a logical name and URL to the new servlet.
After re-creating the formhandling.war
file by adding the compiled servlet and the HTML file and redeploying it, we can see the changes in action by typing the following URL in the browser window: http://localhost:8080/formhandling/multiplevaluedataentry.html
.
After submitting the form, control goes to our servlet, and the browser window should look something like this:
Of course, the actual message seen in the browser window will depend on what checkboxes the user clicked on.
In many cases, one servlet processes form data, then transfers control to another servlet or JSP to do some more processing or display a confirmation message on the screen. There are two ways of doing this: either the request can be forwarded or the response can be redirected to another servlet or page.
Notice how the text displayed in the previous sections' example matches the value of the value attribute of the checkboxes that were clicked, and not the labels displayed on the previous page. This might confuse the users. Let's modify the servlet to change these values so that they match the labels, then forward the request to another servlet that will display the confirmation message on the browser.
The new version of MultipleValueFieldHandlerServlet
is shown next.
package net.ensode.glassfishbook.formhandling; import java.io.IOException; import java.util.ArrayList; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MultipleValueFieldHandlerServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) { String[] selectedOptions = request.getParameterValues("options"); ArrayList<String> selectedOptionLabels = null; if (selectedOptions != null) { selectedOptionLabels = new ArrayList<String>(selectedOptions.length); for (String selectedOption : selectedOptions) { if (selectedOption.equals("option1")) { selectedOptionLabels.add("Option 1"); } else if (selectedOption.equals("option2")) { selectedOptionLabels.add("Option 2"); } else if (selectedOption.equals("option3")) { selectedOptionLabels.add("Option 3"); } } } request.setAttribute("checkedLabels", selectedOptionLabels); try { request.getRequestDispatcher("confirmationservlet"). forward(request, response); } catch (ServletException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
This version of the servlet iterates through the selected options and adds the corresponding label to an ArrayList
of Strings. This ArrayList
is then attached to the request object by calling the request.setAttribute()
method. This method is used to attach any object to the request so that any other code we forward the request to can have access to it later.
This code uses generics, a feature introduced to the Java language in JDK 1.5, see http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html for details.
After attaching the ArrayList
to the request, we then forward the request to the new servlet in the following line of code:
request.getRequestDispatcher("confirmationservlet").forward(
request, response);
The String
argument to this method must match the value of the<url-pattern>
tag of the servlet in the application's web.xml
file.
At this point, control goes to our new servlet. The code for this new servlet is shown below:
package net.ensode.glassfishbook.requestforward;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ConfirmationServlet extends HttpServlet
{
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
{
try
{
PrintWriter printWriter;
List<String> checkedLabels = (List<String>) request
.getAttribute("checkedLabels");
response.setContentType("text/html");
printWriter = response.getWriter();
printWriter.println("<p>");
printWriter.print("The following options were selected:");
printWriter.println("<br/>");
if (checkedLabels != null)
{
for (String optionLabel : checkedLabels)
{
printWriter.print(optionLabel);
printWriter.println("<br/>");
}
}
else
{
printWriter.println("None");
}
printWriter.println("</p>");
}
catch (IOException ioException)
{
ioException.printStackTrace();
}
}
}
This code obtains the ArrayList
that was attached to the request by the previous servlet. This is accomplished by calling the request.getAttribute()
method; the parameter for this method must match the value used to attach the object to the request.
Once this servlet obtains the list of option labels, it traverses through it and displays them on the browser.
Forwarding a request as described above only works for other resources (servlets and JSP pages) in the same context as the code doing the forwarding. In simple terms, the servlet or JSP that we want to forward to must be packaged in the same WAR file as the code that is invoking the request.getRequestDispatcher().forward()
method. If we need to direct the user to a page in another context (or even another server), we can do it by redirecting the response object.
One disadvantage of forwarding a request as described in the previous section is that requests can only be forwarded to other servlets or JSPs in the same context. If we need to direct the user to a page on a different context (deployed in another WAR file in the same server or deployed in a different server) we need to use the HttpServletResponse.sendRedirect()
method.
To illustrate response redirection, let's develop a simple web application that asks the user to select their favorite search engine, then directs the user to his/her search engine of choice. The HTML page for this application would look like this:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Response Redirection Demo</title> </head> <body> <form method="post" action="responseredirectionservlet"> Please indicate your favorite search engine. <table> <tr> <td><input type="radio" name="searchEngine" value="http://www.google.com">Google</td> </tr> <tr> <td><input type="radio" name="searchEngine" value="http://www.msn.com">MSN</td> </tr> <tr> <td><input type="radio" name="searchEngine" value="http://www.yahoo.com">Yahoo!</td> </tr><tr> <td colspan="2"><input type="submit" value="Submit" /></td> </tr> </table> </form> </body> </html>
The HTML form in this markup code contains three radio buttons; the value for each of them is the URL for the search engine corresponding to the user's selection. Notice how the value for the name attribute of each radio button is the same, namely "searchEngine". The servlet will obtain the value of the selected radio button by calling the request.getParameter()
method and passing the string "searchEngine" as a parameter, as demonstrated in the code below:
package net.ensode.glassfishbook.responseredirection; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ResponseRedirectionServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { String url = request.getParameter("searchEngine"); if (url != null) { response.sendRedirect(url); } else { PrintWriter printWriter = response.getWriter(); printWriter.println("No search engine was selected."); } } }
By calling request.getParameter("searchEngine")
, this code assigns the URL of the selected search engine to the variable url
. Then, (after checking for null
, in case the user clicked on the submit button without selecting a search engine), it directs the user to the selected search engine by calling response.sendRedirect()
and passing the url
variable as a parameter.
The web.xml
file for this application should be fairly straightforward and is not shown (it is part of this book's code download).
After packaging the code and deploying it, we can see it in action by typing the following URL in the browser: http://localhost:8080/responseredirection/
.
After clicking the submit button, the user is directed to their favorite search engine.
It should be noted that redirecting the response as just illustrated creates a new HTTP request to the page we are redirecting to; therefore any request parameters and attributes are lost.
In the previous section, we saw how it is possible to store an object in the request by invoking the HttpRequest.setAttribute()
method and how later this object can be retrieved by invoking the HttpRequest.getAttribute()
method. This approach only works if the request was forwarded to the servlet invoking the getAttribute()
method. If this is not the case, the getAttribute()
method will return null.
It is possible to persist an object across requests. In addition to attaching an object to the request object, an object can also be attached to the session object or to the servlet context. The difference between these two is that objects attached to the session will not be visible to different users, whereas objects attached to the servlet context are.
Attaching objects to the session and servlet context is very similar to attaching objects to the request. To attach an object to the session, the HttpServletRequest.getSession()
method must be invoked. This method returns an instance of javax.servlet.http.HttpSession
, we then call the HttpSession.setAttribute()
method to attach the object to the session. The following code fragment illustrates the process:
protected void doPost(HttpServletRequest request, HttpServletResponse response)
{
.
.
.
Foo foo = new Foo(); //theoretical object
HttpSession session = request.getSession();
session.setAttribute("foo", foo);
.
.
.
}
We can then retrieve the object from the session by calling the HttpSession.getAttribute()
method.
protected void doPost(HttpServletRequest request, HttpServletResponse response)
{
HttpSession session = request.getSession();
Foo foo = (Foo)session.getAttribute("foo");
}
Notice how the return value of session.getAttribute()
needs to be cast to the appropriate type. This is necessary because the return value of this method is java.lang.Object
.
The procedure to attach objects to and retrieve objects from the servlet context is very similar. The servlet needs to call the getServletContext()
method (defined in the class called GenericServlet
, which is the parent class of HttpServlet
, which in turn is the parent class of our servlets). This method returns an instance of javax.servlet.ServletContext
, which defines a setAttribute()
and a getAttribute()
method. These methods work in the same way as their HttpServletRequest
and HttpSessionResponse
counterparts.
The procedure to attach an object to the servlet context is illustrated in the following code snippet:
protected void doPost(HttpServletRequest request, HttpServletResponse response)
{
//The getServletContext() method is defined higher in
//the inheritance hierarchy.
ServletContext servletContext = getServletContext();
Foo foo = new Foo();
servletContext.setAttribute("foo", foo);
.
.
.
}
The above code attaches the foo
object to the servlet context; this object will be available to any servlet in our application, and will be the same across sessions. It can be retrieved by calling the ServletContext.getAttribute()
method, as is illustrated next.
protected void doPost(HttpServletRequest request, HttpServletResponse response)
{
ServletContext servletContext = getServletContext();
Foo foo = (Foo)servletContext.getAttribute("foo");
.
.
.
}
This code obtains the foo
object from the request context; again a cast is needed because the ServletContext.getAttribute()
method, like its counterparts, returns an instance of java.lang.Object
.
This chapter covered how to develop, configure, package, and deploy servlets. We also covered how to process HTML form information by accessing the HTTP request object. Additionally, we covered forwarding HTTP requests from one servlet to another, as well as redirecting the HTTP response to a different server. Lastly, we discussed how to persist objects in memory across requests by attaching them to the servlet context and the HTTP session.
3.144.151.106