Servlets
CONTENTS
Basics
The canonical servlet
Filtering
Using other resources
Inclusion
Forwarding
Session management
URL Info
Security

Basics

Types of http requests:
Listeners
context initialization and destruction javax.servlet.ServletContextListener, javax.servlet.ServletContextEvent contextInitialized(), contextDestroyed()
attribute added, removed, replaced javax.servlet.ServletContextAttributeListener, javax.servlet.ServletContextAttributeEvent attributeAdded(), attributeRemoved(), attributeReplaced()
session creation, invalidation, timeout javax.servlet.http.HttpSessionListener, javax.servlet.http.HttpSessionEvent sessionCreated(), sessionDestroyed()
attribute added, removed, replaced javax.servlet.http.HttpSessionAttributeListener, javax.servlet.http.HttpSessionBindingEvent attributeAdded(), attributeRemoved(), attributeReplaced()
All are configured by adding listener elements to web.xml.
<listener> <listener-class>com.whazzup.servlet.SassyListener</listener-class> </listener>
Scope
For single threaded access, the servlet implements javax.servlet.SingleThreadModel.
Reading data from the request:
Writing data to the request:
Http request URL:
http://<host>:<port><request path>?<query string>
The request path is further broken down as:
<context><servlet><path info>
Both the context and servlet start with '/'
The path info may be null
Setting response content type:
response.setContentType("text/html");
to top

The canonical servlet

Here's a template to use:
package test; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class HelloWorldServlet extends HttpServlet { public void doGet (HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { res.setContentType("text/html"); ServletOutputStream out = res.getOutputStream(); out.println("<html>"); out.println("<head><title>Hello World</title></head>"); out.println("<body>"); out.println("<h1>Hello World</h1>"); out.println("</body></html>"); } public void doPost (HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { doGet(req, res); } public String getServletInfo() { return "Create a page that says <i>Hello World</i> and send it back"; } public void init(ServletConfig config) throws ServletException } public void destroy() { } }
Configure it in web.xml:
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" version="2.4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http:/java.sun.com/dtd/web-app_2_3.dtd"> <servlet> <servlet-name>hello</servlet-name> <servlet-class>test.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> </web-app>
to top

Filtering

Basic approach:
public final class AbcFilter implements Filter { private FilterConfig filterConfig = null; public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } public void destroy() { this.filterConfig = null; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (filterConfig == null) { return; } // get attribute ... = filterConfig.getServletContext().getAttribute(...); // get init parameter from web.xml ... = filterConfig.getInitParameter(...); // logging filterConfig.getServletContext().log(...); // passing on the request chain.doFilter(request, response); } }
To process or modify the response, pass along a temporary output stream.
This is done by creating a wrapper for the response (or the request).
The base classes are HttpServletRequestWrapper and HttpServletResponseWrapper.
public class XyzResponseWrapper extends HttpServletResponseWrapper { private CharArrayWriter output; public String toString() { return output.toString(); } public CharResponseWrapper(HttpServletResponse response) { super(response); output = new CharArrayWriter(); } public PrintWriter getWriter(){ return new PrintWriter(output); } } public final class XyzFilter implements Filter { private FilterConfig filterConfig = null; public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } public void destroy() { this.filterConfig = null; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (filterConfig == null) { return; } XyzResponseWrapper wrapper = new XyzResponseWrapper(response); chain.doFilter(request, wrapper); String result = wrapper.toString(); // modify or process the output PrintWriter out = response.getWriter(); out.write(result); out.close(); } }
Declaring the filter in web.xml: the filter element declares the filter, and the filter-mapping element associates it with servlets.
<filter> <filter-name>xyzFilter</filter-name> <filter-class>com.nugatory.servlets.XyzFilter</filter-class> <init-param> <param-name>protectInnocent</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>xyzFilter</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping> <!-- can also associate with a particular servlet --> <filter-mapping> <filter-name>xyzFilter</filter-name> <servlet>RstServlet</servlet> </filter-mapping>
to top

Using other resources

Inclusion

A servlet can use the dispatcher to include data in its response. A static resource (e.g. a text file) can be included directly. A programmatic resource (e.g. another servlet) can have its output included, but the other servlet may not set headers.
// in the original servlet String url = "/image"; RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(url); if (dispatcher != null) dispatcher.include(request, response); } // a skeleton programmatic resource public class ImageServlet extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { writeImageHtml(); } public void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { writeImageHtml(); } private void writeImageHtml() throws IOException { PrintWriter out = response.getWriter(); out.println("&lt;img src=\"" + request.getContextPath() + "/image.gif\">"); } }

Forwarding

Also uses the dispatcher. Note that forwarding is not possible if the current servlet has already written to the response (IllegalStateException).
// in the original servlet String url = "/anotherServlet"; RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(url); if (dispatcher != null) dispatcher.forward(request, response); }
to top

Session management

Access the session via request.getSession().
Store values on the session via session.setAttribute(name, value) and session.getAttribute(name).
To receive notification when an object is added to or removed from the session, the object should implement javax.http.HttpSessionBindingListener, with methods valueBound() and valueUnbound().
To receive notification when an object is activated or passivated, the object should implement javax.http.HttpSessionActivationListener, with methods sessionDidActivate() and sessionWillPassivate().
When directly writing a URL to a response, use the respons's encodeURL() so that tracking info can be added, if the browser doesn't support cookies.
out.println(response.encodeURL(request.getContextPath() + "/anotherServlet"));
to top

URL Info

Several methods on the request object permit you to determine parts of the URL that invoked the servlet. However, it's not so obvious exactly what they return.
With this URL:
http://localhost:8080/application/action.do?param=value
here is what the various methods return, assuming that the original URL calls a Struts action, which then forwards to a JSP page:
getContextPath() = /application getServletPath() = /jsp/page.jsp getRequestURI() = /jsp/page.jsp getRequestURL() = http://localhost:8080/application/action.do getPathInfo() = null getPathTranslated() = null getQueryString() = param=value getProtocol() = HTTP/1.1 getScheme() = http getServerName() = localhost getServerPort() = 8080
to top

Security

Security methods on HttpServletRequest:
getRemoteUser() Returns the username entered during authentication, or null if there was never an authentication.
isUserInRole() Checks if the user is in a particular security role; returns false if there was never an authentication. Expects a String identifying the role.
getUserPrincipal() Returns the java.security.Principal object for the user, or null if there was no authentication.
Security roles are defined in the deployment descriptor, like this:
<security-role> <description>The manager role</description> <role-name>manager</role-name> </security-role> <security-role> <description>The administrator role</description> <role-name>admin</role-name> </security-role>
It's possible to define "aliases" for the roles, as well, via the security-role-ref subelement of the servlet element (see later).
<servlet> ... <security-role-ref> <role-name>MGR</role-name> <role-link>manager</role-name> </security-role-ref> ... </servlet>
In the code, you can call isUserInRole("MGR"), and it will check if the user is in the manager role. The use of aliases makes the system a bit more flexible.
The role name can be mapped to a user group, or to an individual user.
Authentication mechanisms:
HTTP Basic Browser prompts for username and password via a dialog box. Dialog box also identifies the realm. Username and password are base64 encoded - not very secure! Subsequent requests are also authenticated - sent by browser with each request (how?). Improve security by using HTTPS, but it has to remain HTTPS for the entire session.
HTTP Digest Simlarly, browser pops up a username/password dialog. However a digest of the password is sent. In Tomcat, can indicate the digest algorithm to use: SHA, MD2 or MD5. Apparently not well supported by browsers, and not required in the servlet spec.
Form Based Web designed provides a login form, with username and password fields. Username field must be named "j_username", password field "j_password" and form action "j_action_check". Deployment descriptor identifies the login form and the error page. When a protected resource is accessed, the web server redirects to the designated login page. Get the originally requested page, or the error page.
HTTPS Client Browser presents a "certificate chain" as credentials. Server may also request certificates from the browser.
Here's how to do the forms based authentication:
<form method="POST" action="j_action_check"> <input type="text" name="j_username"> <input type="password" name="j_password"> </form>
The deployment descriptor specifies security constraints via the <security-constraint> element and its subelements:
The web-resource-collection element defines url-pattern and http-method subelements that specify a combination of resources and access methods.
The auth-constraint element specifies the applicable roles via role-name subelements. If the element is empty, then the resources are forbidden to all roles. If the element is absent, then the resources are available to everyone. The special value "*" indicates all roles defined in the deployment descriptor.
The user-data-constraint specify that the request be received over a particular transport layer. "INTEGRAL" requires content integrity guarantees, and "CONFIDENTIAL" requires confidentiality guarantees. "NONE" indicates that no guarantees are required. If none is specified, then all requests must be accepted.
A particular url pattern/http method combination may appear multiple times. If it is forbidden by any, it is forbidden. If it is allowed for all by any descriptor (e.g. via non-existent auth-constraint), it is allowed for all. If it is allowed for particular roles in each descriptor, it is allowed for the union of the roles in the individual descriptors.
Big example, lifted from the Servlet 2.4 spec:
<!-- specifies that the URLs are not accessible via DELETE or PUT --> <security-constraint> <web-resource-collection> <web-resource-name>restricted methods</web-resource-name> <url-pattern>/*</url-pattern> <url-pattern>/acme/wholesale/*</url-pattern> <url-pattern>/acme/retail/*</url-pattern> <http-method>DELETE</http-method> <http-method>PUT</http-method> </web-resource-collection> <auth-constraint/> </security-constraint> <!-- specifies that /acme/wholesale/* is accessible via GET by salesclerks; PUT is forbidden by the first descriptor above --> <security-constraint> <web-resource-collection> <web-resource-name>wholesale</web-resource-name> <url-pattern>/acme/wholesale/*</url-pattern> <http-method>GET</http-method> <http-method>PUT</http-method> </web-resource-collection> <auth-constraint> <role-name>SALESCLERK</role-name> </auth-constraint> </security-constraint> <!-- This one specifies a transport layer guarantee, but for POST only; CONTRACTORs can GET these resources without guarantee due to the second descriptor above. --> <security-constraint> <web-resource-collection> <web-resource-name>wholesale</web-resource-name> <url-pattern>/acme/wholesale/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>CONTRACTOR</role-name> </auth-constraint> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint> <!-- This one specifies multiple roles --> <security-constraint> <web-resource-collection> <web-resource-name>retail</web-resource-name> <url-pattern>/acme/retail/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>CONTRACTOR</role-name> <role-name>HOMEOWNER</role-name> </auth-constraint> </security-constraint>
The login-config element configures the login mechanism. The auth-method subelement indicates the method, and can be BASIC, DIGEST, FORM, CLIENT-CERT, or possibly a vendor-specify mechanism. The realm-name indicates the name of the realm, and is needed for BASIC authentication. The form-login-config indicates the login and error pages, and is needed for FORM authentication.
<login-config> <auth-method>FORM<auth-method> <form-login-config> <form-login-page>login.html</form-login-page> <form-error-page>login-error.html</form-error-page> </form-login-config> </login-config>
Finally, security roles are also specified in the deployement descriptor.
<security-role> <description>employees</role-name> <role-name>SALESCLERK</role-name> </security-role> <security-role> <description>privileged users</role-name> <role-name>CONTRACTOR</role-name> </security-role> <security-role> <description>ordinary users</role-name> <role-name>HOMEOWNER</role-name> </security-role>
to top