More Servlets
Recommended Reading
Chapters 1 and 2 - Java Servlets from "Servlets and Java Server Pages" by Falkner and Jones. You can ignore the sections on "Getting Java Support and Ant"
Handling the Client Requests using Forms
When you submit a form, the browser packs up the information you need to send and ships it off in the URL to the server. If you are using the GET methods, all name/value pairs are encoded in the URL sent to the server.
For example, if I used the Form below:
<form action="https://localhost:80/TestProgram" method=GET> <center> First name: <input type="TEXT" name="firstName" value="Bob"> <br /> Last Name: <input type="TEXT" name="lastName" value="Evans"> <br /> <input type="SUBMIT"> </center> </form>
It would generate the following URL that would be sent to the server
https://localhost:80/TestProgram?firstName=Bob&lastName=Evans
That is because the values are encoded as
param1=val1¶m2=val2¶m3=val3....
After the submit URL and a ? mark. Notice the "param" names are the "name" fields in the <input> tags, and the "value" fields in the URL are whatever appears in those fields when submit is pressed.
If you are using the POST command, the information is still packed in the URL, but it is humanly readable as is the GET parameters.
So, when the Servlet gets the URL, how does it get the information that the URL sent? Remember when we implemented doGet() how there were two arguments to the doGet() method? One of them was the HttpServletResponse, which we used to send output back to the browser, but the other was HttpServletRequest, which holds the information we are looking for.
If you look at the API for HttpServletRequest, and just as importantly ServletRequest, you'll lots of methods for extracting information from a request from a browser. The first ones we'll be using are from the ServletRequest class:
method |
function |
getParameter("<name>") | Returns the value as the user entered it. It will be a URL-decoded value of the FIRST occurrence of a name in the query string |
getParameterValues("<name>") | Returns an array of the URL-decoded values of all occurrences of the name in the query string. It returns a one-element array if the param in not repeated, or null if not found |
getParameterNames() | Returns an Enumeration of request params |
Forms with Servlets
Download and open the Eclipse project SimpleForm. In this project, we'll use a Form we created in the Web-Forms module, but we'll integrate it with a simple Servlet.
Before we look at the code, let's run it first.
Right click on the SimpleForm project and select Run on Server.
You'll see the following screen in your browser.
Type in your name (or leave mine in) and press the Submit button. You'll see the following appear
The building of the SimpleForm project.
At this point, we need to cover how we put the pieces together to make this project work. I've used several components of this project in examples, but this is the first time you've seen them all together.
To make this project yourself, you'll need to:
Step 1: Creating the Servlet.
The Servlet itself isn't too difficult. You can hand-craft your own servlet by making a class that extends HttpServlet and then overriding the doGet() and doPost() methods.
Since you don't know how the information will be sent to the Servlet, a common design is to have the same code process both the doGet() and doPost() internals. Why don't you know how it will be sent to the Servlet? Because the web page decides how it wants to send the request, and the servlet just has to deal with one of two possible forms.
One way is to create a method pattern as follows
public void doGet(HttpServletRequest request,
HttpServletResponse, response) {
// your code goes here...
}
public void doPost(HttpServletRequest request,
HttpServletResponse, response) {
doGet(request, response);
}
Then, you put whatever code you want to handle a submission event in the processRequest() method. Then you can get the parameter information from the HttpServletRequest, and output the HTML you want to the HttpServletResponse.
Okay, step 1: Make a SimpleForm project. Do it just like we did MyFirstServlet. See the screenshot below for the contents of the doGet() method.
Internals of the doGet() method |
Step 2: Create the web page and the Form inside of it
Next, create an HTML web page with the form data inside of it. We'll use the example we showed earlier, with the only modification being the URL that is the target of the submission.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>SimpleForm Servlet</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<h1>Simple Form Servlet Example:</h1><br />
Please Enter your name:<br /><br />
<form action="https://localhost:8080/SimpleForm/FormServlet" method=GET>
First name:
<input type="TEXT" name="firstName" value="Bob">
<br />
Last Name:
<input type="TEXT" name="lastName" value="Evans">
<br />
<br />
<input type="SUBMIT">
</form>
</body>
</html>
This is the same code we saw before, but with a different value for the "action" attribute. Note the action for the form is "MyFormServlet", which we mapped earlier.
Step 3: Provide the Servlet as the target of the web page submission
Already done above. NOTE: we use localhost to for the example running on your local computer, if we moved this to web7, it would need to be changed to "https://web7.jhuep.com/SimpleForm/FormServlet" instead.
Step 5: Run the project in Eclipse
Right click on the project name, and your web page should now appear!
HttpServletRequest
The HttpServletRequest contains data passed from the HTTP client to the HTTP servlet. The HTTP request contains the following parts
https://[host]:[port]/[request path]?[query string]
Samples of the above pattern would be
https://localhost/helloworld
https://localhost:8080/helloworld
https://localhost:8080/helloworld/index.jsp
https://web1.jhuep.com/helloworld/foo/index.jsp
There are different ways that query strings may be generated. One way is to explicitly send them by typing them in a URL. The other way is to be automatically generated when a form with a GET HTTP method is submitted.
Handling the Client Request: Http Request Headers
Below is a HTTP 1.1 Request as seen by a server
That is a lot of information to come across in the request. Java provides several methods to get information about headers, cookies, content type etc so you can make sense of a request.
If we replace the contents of our doGet() method with the following:
try {
out.println("<html>");
out.println("<head>");
out.println("<title>Servlet SimpleForm Headers</title>");
out.println("</head>");
out.println("<body>");
out.println("<b>Request Method:</b> " + request.getMethod() + "<br>");
out.println("<b>Request URI:</b> " + request.getRequestURI() + "<br>");
out.println("<b>Request Protocol:</b> " + request.getProtocol() + "<br>");
out.println("<table border=\"1\" align=\"CENTER\">");
out.println("<tr bgcolor=\"#FFAD00\">");
out.println("<th>Header Name</th><th>Header Value</th>");
out.println("</tr>");
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = (String)headerNames.nextElement();
out.println("<tr><td>" + headerName + "</td>");
out.println(" <td>" + request.getHeader(headerName) + "</td></tr>");
}
out.println("</table>");
out.println("</body>");
out.println("</html>");
We can look at the header information that is being sent to the server from the form.
Download and open Eclipse project SimpleFormHeaders. If you run this project, you will see the following output:
Some of the common HTTP 1.1 Request headers that are used as as follows:
Header |
Purpose |
Accept | Indicates MIME types the browser can handle. |
Accept-Encoding | Indicates encodings (e.g. gzip) the browser can handle |
Authorization | User identification for password-protected pages. Instead of HTTP authorization, use HTML forms to send a username and password, and store the data in a session object |
Connection | In HTTP 1.0, keep-alive means the browser can handle a persistent connection. In HTTP 1.1, persistent connection i the default. Persistent connections mean that the server can reuse the same socket over again for requests very close together from the same client. |
Cookie | Gives cookies previously sent to the client. Use getCookies(), not getHeader() to get this info. |
Host | Indicates host given in original URL. This is a required header in HTTP 1.1. |
If-Modified-Since | Indicates the client wants page only if it has been changed after a specified date. Best not to handle this situation directly, instead implement getLastModified instead. |
Refer er | URL of referring Web page. Useful for tracking traffic. |
User-Agent | String identifying the browser that made the request. |
We've mentioned encoding a few times now...what exactly does that mean? Often times your connection between the computers is the slowest part of the transaction. If you could send a web page in gzip format, there would be less content to send over the wire wouldn't there.
Since you can test to see if a browser accepts gzip'ed input using the Accept-Encoding, you can actually run your HTML through a gzip output stream to encoding it prior to transmission, or likewise un-encode an incoming zipped file.
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
String encodings = request.getHeader("Accept-Encoding");
String encodeFlag = request.getParameter("encoding");
PrintWriter out;
if ((encodings != null) &&
(encodings.indexOf("gzip") != -1) &&
!"none".equals(encodeFlag)) { title = "Page Encoded with GZip";
OutputStream out1 = response.getOutputStream(); out = new PrintWriter(new GZIPOutputStream(out1), false);
response.setHeader("Content-Encoding", "gzip); } else { title = "Unencoded Page"; out = response.getWriter(); } out.println(.... html content ...);
This used to be more important with slow dial-up modems, but is not used as often anymore.
Encoding text in URLs
One of the interesting problems in sending information in a URL is that not all characters that you might type can be placed in a URL. Something as simple as a " "(space) needs to be encoded so that it can be safely sent in the URL.
To manually convert to and from this URL encoding, Java provides the ...URLEncoder and URLDecoder classes! These will safely encode strings to and from the URLs. HttpServletResponse also has methods to encode URLs.
Generating the Server Response: HTTP Status Codes
Before we get into too much detail in responses, one of the first things we need to talk about is Response Status Codes.
Status codes are defined in the Section 10 of the HTTP spec. A more readable version can be found at that oh-so-reliable wikipedia. The wikipedia listing on status codes is just a re-written version of the spec, but far easier to comprehend. The key ones we often deal with are:
Code |
Meaning |
200 | Everything is OK! |
301 | Moved Permanently: This and all future requests should be directed to the given URI. |
400 | The request contains bad syntax or cannot be fulfilled. |
401 | Unauthorized: Similar to 403 Forbidden, but specifically for use when authentication is possible but has failed or not yet been provided |
403 |
|
Changing the status code lets you perform a number of tasks not otherwise possible. You can forward a client to another page, indicate that a resource is missing, or even instruct the browser to use a cached copy.
You set the status of a response before sending any of the document. You use the
public void setStatus(int statusCode);
method. Note that you should use the predefined static constants found in HttpServletRequest, and not provide the actual integer values. If you want to signal an error, you can use the method
public void sendError(int code, String message)
which will generate a small HTML document with the message provided as your error page.
Lastly, you can redirect the browser to a new URL
public void sendRedirect(String url);
Which will redirect the requesting browser to the new URL. This is used to chain HttpRequests between Servlets (and JSP pages).
Generating the Server Response: HTTP Response Headers
We've seen Response Status Codes, the next step is the Response Header. Response Headers have many purposes, some of the more common ones are:
The table below lists some common methods to set headers and their functions (the full API can be found in HttpServletResponse:
method |
function |
public void setHeader(String headerName, |
Sets an arbitrary header value |
public void setDateHeader(String name, |
Sets the date header and converts milliseconds since Jan 1, 1970 (the epoch) to GMT format |
public void setIntHeader(String name, |
Sets an integer header value. (Saves you from converting it to a String) |
addHeader(String headerName, |
Adds an arbitrary header value |
public void addDateHeader(String name, |
Adds a date header and converts milliseconds since Jan 1, 1970 (the epoch) to GMT format |
public void addIntHeader(String name, |
Adds an integer header value. (Saves you from converting it to a String) |
public void setContentType(String type) |
Sets the content type of the response sent to the client |
public void setContentLength(int length) |
Sets the length of the content body in the response In HTTP servlets, this method sets the HTTP Content-Length header |
public void addCookie(Cookie cookie) |
Adds the specified cookie to the response. This method can be called multiple times to set more than one cookie |
public void sendRedirect(String location) | Sends a temporary redirect response to the client using the specified redirect location URL. This method can accept relative URLs |
The setHeader/addHeader method allow you to set known, or new values, in the Response Header. Some of the common header components are:
Header Name | Function |
Cache-Control | a no-cache value prevents browsers from caching page. |
Content-Encoding | The way the document is encoded. The Browser reverses this encoding before handling the document. |
Content-Length | The number of bytes in the response. |
Content-Type | The MIME type of the document being returned. You should use the setContentType() to set this header value |
Expires | The time at which the document should be considered out-of-date and thus should no longer be cached. |
Last-Modified | The time the document was last changed. Again, you should provide a getLastModified() method in your code instead. |
Location | The Location the browser should reconnect. You should use sendRedirect() instead of setting this directly |
Refresh | The number of seconds until the browser should reload the page. You can also include a URL to connect to: |
Set-Cookie | The cookies that the browser should remember. Use the addCookie() method instead of setting this directly |
WWW-Authtenticate | The authorization type and realm needed in the Authorization header. |
Manipulating Header Attributes
Download and open the Eclipse project Updater. This is a very simple example but it shows setting of a header value, in particular the Refresh attribute.
Updating Servlet |
Note the response.setHeader() call prior to sending output to the output stream. We set the Header value "Refresh" to be "10" seconds, which will cause the browser to reload the Servlet every 10 seconds.
C is for Cookie, that's good enough for me....
Ah the joy of coming up with clever computer terms for simple ideas. The idea of a Cookie is that a Servlet would like to be able to send a simple name and a value to a client, but the client will remember the value the next time it connects. When it connects again, it returns the same name and value for the same site it visited.
Wikipedia tells us that
HTTP cookies are used for authenticating, tracking, and maintaining specific information about users, such as site preferences and the contents of their electronic shopping carts. The term "cookie" is derived from "magic cookie," a well-known concept in unix computing which inspired both the idea and the name of HTTP cookies.
Cookies are common tools that let servlets leave "traces" on a user's computer that can be monitored later on. Sometimes cookies can be used to track what you have visited, or they may just keep track of what you had in your shopping cart at Amazon.com last time you visited.
Cookies are not security threats in any way. They can't take over your machine, or any other evil actions. They keep track of things. Of course, sometimes, people don't want to be kept track of...which becomes a privacy issue instead of a security issue. This is because a Server can remember you previous actions.
Since some people are sensitive to cookies, they may disable their browser from accepting them. So if cookies are not critical to your task, avoid servlets that totally fail when cookies are disabled. Also don't put any sensitive info into cookies, as your users will be quite annoyed.
Setting a cookie is actually very simple.
Usually, cookies have fairly long lives. To set a cookie to live for a year (ugh...that's one stale cookie!).
cookie.setMaxAge(60*60*24*365);
If you don't set a cookie, it lasts for the session you are visiting the web page, and no longer.
Getting Cookies and using them
First, make sure Mom isn't around and get a step-stool... No wait, wrong cookies. (Forgive my bad jokes, my daughter hates them too...).
Using cookies is actually pretty simple.
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (int i=0; i < cookies.length; i++) {
Cookie c = cookies[i];
if (c.getName().equals("someName")) {
// you've found your cookie
doSomethingWith(cookie);
break;
}
}
}
So, the browser has cookies set for a site when the site visits the page, and you have to search through them to find the one you want.
Cookies Example
Download and open the Eclipse project cookies. In this project, we have an intro HTML page, that when you click on it will load the SetCookies servlet and show information about the cookies set for this page.
This Servlet is designed to first show whatever cookies it finds, but then tack on some cookies of its own. One of the cookies doesn't have a max age set, so it lives only for this session. The second cookie lives for an hour. When you click on the link at the bottom of the page, the page reloads and you will see both of the cookies set.
If you exit the browser and either revisit the Servlet or just re-run the project, you'll see that the long-lived cookie is now present when you first visit the page.
Code to set and show cookies |
A quick note. Look a the use of SimpleDateFormat to format the Date object used in the Cookie. Why? Because in Tomcat 8.5 and on, cookies no longer allow spaces, and the default Date format contains spaces.
So, when you first run the app, the following screen appears...
When you click on the link, you'll see...
Okay, I had to do some magic because Eclipse remembered the Cookie and didn't show the "no cookie" screen when I went to get a screenshot. In addition, this shows you can view your "web page" in the browser of your choice rather than the internal Eclipse browser. By switching to Firefox, I started without any cookies.
If you reload....
If you quit the browser and then re-visit the page, you'll see the following...
Notice that in our examples, we sent human-readable text as the value. There is nothing wrong with this, but usually when you see a Cookie value it is some obscure string or number that is used to index into a server's database. This also prevents the curious user from seeing just what is being tracked as well.
Session tracking
Session tracking enables you to track a user's "session" through multiple HTML pages or servlets. The idea is that a user visiting a web site is a single "session", and that it would be beneficial to be able to "carry along" information from one page to the next through that session. Since HTML pages are defined as being "stateless", some mechanism needs to be employed to do this. A session is defined as a series of related browser requests that come from the same browser during a certain time period. This implies that sessions have a life span that can "time out" if a web page is left unattended for too long.
Why do you need this? When a client is at an on-line store and they add an item to their shopping cart, how does the server know what's already in the cart? When they checkout, how does the server know what is in the cart?
Session tracking is accomplished in one of three ways, and each has its distinct advantages/disadvantages.
The Java Session Tracking API
Session objects are created an maintained as part of the web-container on the Server. They are automatically associated with a client using either cookies or URL-rewriting (hidden fields must be maintained manually).
Cookies are the easiest, and most secure, way of supporting sessions. The problem is, paranoid users may turn off the browsers's ability to accept cookies, so URL re-writing is a backup way of supporting sessions. URL rewriting involves encoding the session ID into the hyper-links on
the Web pages that your servlet sends back to the browser. When the
user subsequently clicks these links, the server then extracts the ID
from the URL address and finds the appropriate HttpSession
when your servlet calls the getSession()
method. Fortunately, Java manages the use of these two methods automatically. It attempts to use cookies, and if they aren't supported, it uses URL re-writing as a backup.
You get a Session object by calling
request.getSession(true);
to get either an existing or new session. The system then looks at cookie or URL extra information and sees if it matches the key to some previously stored session object. If it does, it returns that object. If not, it creates a new one, assigns a cookie or URL info as its key, and returns that new session object.
An HttpSession is a Hashtable-like mechanism that lets you store arbitrary objects inside of the session object. You can set and get objects using the following methods.
setAttribute(String key, Object value);
getAttribute(String key);
removeAttribute(String key);
getAttributeNames();
There are also method to set the maximum inactive interval for the Session.
getMaxInactiveInterval()
You can invalidate the session and unbind all objects associated with it by calling invalidate();
Download and open the Eclipse project session. In this project, we use a Session object to keep track of how many times we've visited this page in this session. The KEY value is assigned earlier in the code with a value of "accessCount".
HttpSession session = request.getSession(true);
Integer accessCount = (Integer)session.getAttribute(KEY);
try {
out.println("<html>");
out.println("<head>");
out.println("<title>Session Servlet</title>");
out.println("</head>");
out.println("<body>");
if (accessCount == null) {
out.println("<h1>Welcome to the Session Servlet</h1>");
accessCount = new Integer(1);
} else {
out.println("<h1>Welcome back to the Session Servlet</h1>");
accessCount = new Integer(accessCount.intValue() + 1);
}
.... more ....
In this example, we will use what we find in the Servlet Session to change what we write to the screen.
Run the servlet. The first time you visit the page, you'll see a welcome message like this:
If you click reload, you'll see a "Welcome back" message, and the number of times you've visited the page.
So, at this point, we know how to get things into and out of a Servlet. Two things still come to mind
Hopefully, in the next few weeks, you'll get the answers to these questions!
Scope Objects
Scope objects are used in Web Components, which are Servlet and JSP pages, to maintain application or servlet wide state information. Instead of passing values around as explicit arguments, you can use Scope objects to enable the sharing of information between web components using attributes maintained in these Scope objects. All of the Scope attributes are name/value pairs.
These attributes are accessed with getAttribute() and setAttribute() methods.
There are four Scope objects in Servlets (and JSPs).
Web context (ServletContext)
The ServletContext is used by servlets to
The ServletContext is shared by all objects in a Web application. The Web application is defined in the web.xml file and is usually all in a sub-directory of the URL namespace for the server. There is only one ServletContext per web application.
You get access to the ServletContext object by calling the getServletContext() method within the init method of the Servlet.
public class MyServlet extends HttpServlet {
private EmployeeDB employeeDB;
public void init() throws ServletException {
ServletContext sc = getServletContext();
employeeDB = (EmployeeDB)sc.getAttribute("employees");
if (employeeDB == null) {
throw new UnavailableException("Couldn't find Employee Database in ServletContext");
} } }
Forwarding Pages
One thing that is often done in Servlets is forwarding the request to another Servlet (or JSP). To partially process a request, and then forward the request to another Servlet/JSP you need to use a RequestDispatcher object. The function of this object is to merely forward a request to a new resource.
You use a RequestDispatcher by doing the following within the doGet()/doPost() routine:
String target = "new html/servlet/jsp url"; ServletContext servletContext = getServletContext(); RequestDispatcher dispatcher = servletContext.getRequestDispatcher(target); dispatcher.forward(request, response);
The request and response objects are from the arguments from the doGet() or doPost() methods that this code resides in.
Session (HttpSession)
ServletContext is useful for global attributes like databases, but is somewhat overkill for tracking things during a single user's interaction with a web site. The HttpSession object provides a means of maintaining client state information across a series of HTTP requests from the same user of a period of time. Web page shopping carts are a good example of information that is kept for a user session at a web site.
You get an HttpSession object by calling the getSession() method of the HttpRequest object. The HttpRequest object is an argument to the Servlet doGet/doPost methods.
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession();
Cart cart = (Cart)session.getAttribute("cart");
...
}
Request (HttpServletRequest)
We already referred to HttpServletRequests earlier in this lecture.
Page (PageContext)
Attributes are kept within the single page...not particularly useful.