How Java Servlets Work II
The activity lifecycle of a servlet in a nutshell:
- User hits the url and port 8080
- The browser makes the Http request in the header
- The request object contains details on request object data such as url query parameters or post variables in the body
- Tomcat is listening on 8080
- Tomcat gets the request (along with the data provided in the request object)
- Tomcat checks web.xml/Annotations to see if there is any mapping from the URL to a servlet.
- If Tomcat finds a mapping, it instantiates a singleton of that servlet if it is the first time it’s being called. Then, Tomcat goes on to instantiate HttpServletRequest and HttpServletResponse objects. Next, Tomcat calls the service method of the singleton servlet and passes in it the request, and response objects instantiated earlier.
- If Tomcat does not find a servlet mapping in web.xml or Annotations, Tomcat goes to a static page
Http Protocol Methods Overview:
- GET and HEAD methods are safe, meaning without any side effects for which users are held responsible (read operation)
- HEAD request is a GET request that returns no body in the response, only the request header fields
- POST, PUT, DELETE methods are potentially unsafe (write operation)
- POST is non-idempotent (repeating it has consequences)
- GET, HEAD, PUT, DELETE, OPTIONS, TRACE methods are idempotent, meaning they can be safely repeated
- GET is typically used for querying and retrieving data by sending the parameters in the browser’s url
- POST is typically used for writing data with a form submission and sending the parameters in the browser’s HTML body
Each Servlet Container (Tomcat, Glassfish, Jetty, and others) has its own implementation of the HttpServlet specification. They do this by extending javax.servlet.http.GenericServlet and providing an abstract class to be subclassed by the developer to create an HTTP servlet suitable for a Web-based Application.
The developer then must override at least one of the methods - doGet(), doPost(), doPut(), doDelete(), init(), destroy()
, and maybe a few others depending on the container. For example, in Tomcat, if the developer hasn’t written a doPost()
method and the server is hit with a POST method, then Tomcat’s doPost()
method will respond with a http.method_post_not_supported
message.
There is almost no reason for the developer to override the service()
method since that method handles the standard HTTP requests by dispatching the requests to the handler methods for each HTTP request type doXXX()
methods listed above.
Who creates the Request and Response objects? Who creates the Servlet objects? When are these objects created and destroyed?
A web application is deployed in the Tomcat container so the servlets run inside this container. Tomcat takes care of initializing the servlets, creating the servlet objects as well as the request (HttpServletRequest)
and response (HttpServletResponse)
objects.
Request object has data about the request that was initiated by the browser Response object is a blank object. It’s a clean slate object generated by Tomcat. Both Request and Response objects are passed to the servlet by calling the servlet’s service method. The response object is sent as HTML by Tomcat back to the browser.
Question: If another user makes a GET request to the same resource that gets mapped to the same servlet that just got initialized by Tomcat, does Tomcat re-instantiate the servlet?
Answer: No, it doesn’t. The servlet gets initialized once and that same servlet object gets used by multiple clients hitting it until the app is halted.
On the other hand, each new request even if it was coming from the same user that just made the GET request, will cause a new request and response objects to be created. This is because HTTP is a stateless protocol, as we mentioned earlier.
Although servlet objects are reused with different requests, each request has its own servlet thread. So, each time a request is received, a new thread is created but a new servlet object is not created. Not having to create a new object for each request allows for better use of resources.
init()
- this method is called once when the servlet is instantiated. GenericServlet, which the HttpServlet extends from, has an init() and init(ServletConfig) methods. The latter one ends by calling the init() method within its body. Initialization code like opening the database for example can be done in the init() code. Note that you can use the initParams annotation to set initilization parameters.
public void init(ServletConfig config) throws ServletException {
// Called by the servlet container to indicate to a servlet
// that the servlet is being placed into service.
// This implementation stores the ServletConfig object
// it receives from the servlet container for later use.
// config - the ServletConfig object that contains
// configutation information for this servlet
}
service()
- this method is called everytime a request is sent. This method looks at the request and decides which method of the servlet needs to run, doGet()
or doPost()
for example, and calls it. GenericServlet, which the HttpServlet extends from, has an abstract service method that takes ServletRequest and ServletResponse parameters.
Then the HttpServlet class override’s the service()
method it inherited from GenericServlet class, casts the ServletRequest and ServletResponse to HttpServletRequest and HttpServletResponse respectively.
Then, it calls another service method that actually takes HttpServletRequest and HttpServletResponse paramaters, passing in the HttpServletRequest, and HttpServletResponse objects it cast earlier. The second service method then calls the doXXX()
method depending on the request that came in.
___
GenericServlet.service() method:
public abstract void service(ServletRequest req,
ServletResponse res)
throws ServletException,
java.io.IOException {
// Called by the servlet container to allow the servlet
// to respond to a request.
// This method is declared abstract so subclasses, such
// as HttpServlet, must override it.
// req - the ServletRequest object that contains the client's request
// res - the ServletResponse object that will contain the servlet's response
}
HttpServlet.service(ServletRequest, ServletResponse) method:
protected void service(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException,
java.io.IOException {
// Receives standard HTTP requests from the public
// service method and dispatches them to the doXXX
// methods defined in this class. This method is an
// HTTP-specific version of the Servlet.service
// (javax.servlet.ServletRequest, javax.servlet.ServletResponse)
// method. There's no need to override this method.
// req - the HttpServletRequest object that contains the request the client made of the servlet
// resp - the HttpServletResponse object that contains the response the servlet returns to the client
}
HttpServlet.service(HttpServletRequest, HttpServletResponse) method:
public void service(ServletRequest req,
ServletResponse res)
throws ServletException,
java.io.IOException {
// Dispatches client requests to the protected service
// method. There's no need to override this method.
// req - the HttpServletRequest object that contains the request the client made of the servlet
// res - the HttpServletResponse object that contains the response the servlet returns to the client
} ___
We don’t write the init and service methods in our MyServlet. So where do they come from?
They come via inheritance when we extend the HttpServlet class, which actually extends GenericServlet. The HttpServlet class reads the HTTP request, and determines if the request is an HTTP GET, POST, PUT, DELETE, HEAD,
etc. and calls one of the corresponding methods. The GenericServlet has the init(ServletConfig), init(), and service()
methods. The HttpServlet class overrides the service()
method it inherits from the GenericServlet class. MyServlet has the doGet()
and doPost()
methods.
So since HTTP is stateless, how can we remember say a logged in user’s credentials?
In order to save user data, we need to use something called a Session object. A Session object is something Tomcat provides for us (as an HttpSession java class) and we can use it to save data values at some point in the execution of a server and then retrieve the same data during the execution of the server.
How long does the Session object live?
The session object is alive as long as the user is using the application with the browser. This session is kind of a global object that is available across servlets. As long as the user’s browser is making calls to any servlet object in the application, they can pull up the session object and use it. Another user using the application with their browser will have a separate session object available for them.
Each user gets their own session object but what if we wanted to have an object available across the application for all the users?
That’s when the Context object comes into play. Note that regardless of how many users or servlets are in existence, there can only be one Context object per application. So if you need to access data across the application available to every user and servlet, you would use the Context object.