|< Free Open Study >
All servlets must implement the Servlet interface, although most will extend from a class that has already implemented Servlet.
The API provides the abstract class GenericServlet that implements the Servlet interface. It provides concrete implementations of all but the service() method defined in the Servlet interface, so when we extend GenericServlet we must at least implement this method. However, it also means that when we are developing a servlet, much of the standard work can be left to the methods inherited from GenericServlet. We only need to override the other methods if we specifically want to alter the default implementation. We will be looking more closely at GenericServlet in the next section.
The Servlet interface defines the following three lifecycle methods, called by the servlet container:
public void init(ServletConfig config) throws ServletException public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException public void destroy()
When we talk about the servlet's lifecycle we are talking about the period of time that a servlet instance is created, 'lives', and dies. The servlet container will create instances of the servlet according to its design and how often the servlet is called. The container has a lot of freedom in managing the servlets lifecycle, in that it can keep a single instance of a servlet around for a long time to process requests, pool a number of instances of the servlet to process requests, or instantiate a new servlet for each request. Obviously it makes sense for the container to manage servlet instances according to some optimal pattern of usage so as not to waste resources on the server.
The servlet lifecycle is clearly defined. A client makes a request to the web server, which redirects the request (as necessary) to the servlet container:
The loading and instantiation of the servlet is the responsibility of the servlet container. The container must locate the servlet classes, load the servlet using normal class loading procedures, and instantiate it so that it is ready for use.
The container initializes the servlet by calling the servlet's init() method. The container passes an object implementing the ServletConfig interface via the init() method. This object provides the servlet with access to the object that implements the ServletContext interface (which describes the servlet's runtime environment). The init() method is also responsible for performing any other initialization required by the servlet, which can include setting up resources that the servlet will require to process requests, such as database connections.
In the event that the servlet is unsuccessfully initialized, an UnavailableException or ServletException is thrown, the servlet is released, and attempts are made to instantiate and initialize a new servlet.
The servlet is now ready to handle client requests. The request and response information is wrapped in ServletRequest and ServletResponse objects respectively, which are then passed to the servlet's service() method. This method is then responsible for processing the request and returning the response.
Instances of both ServletException and UnavailableException can occur during request handling. If an exception is thrown the container is forced to clean up the request, possibly unloading the instance and calling the destroy() method of the servlet.
Once the servlet container decides to remove the servlet from service, the container must allow any service() method calls to terminate (or timeout). Then, it will call the servlet's destroy() method. Once the destroy() method has completed, the container will release the servlet instance for garbage collection. If it needs another instance of the servlet to process requests it must start the process again.
The Servlet interface defines another method that servlets must implement:
public ServletConfig getServletConfig()
The getServletConfig() method is designed to return a reference to the ServletConfig object, that contains initialization and startup parameters for the servlet. This object is passed to the servlet during initialization, and can be stored for future use by the servlet, although how the servlet will treat the ServletConfig object is not specified. Normally it is expected that a reference to it is stored in the servlet so that it can be accessed in the getServletConfig() method
It is important to understand that the container/server may receive many requests, and often these will occur simultaneously or virtually simultaneously so our container will be responsible for establishing separate threads to process each request. The service() method may be called simultaneously by the container in different threads to process many different requests.
While the container has the responsibility for handling the requests in separate threads, this can have implications for our servlets. We need to code our servlets to be thread-safe. For example, consider a class variable count. If this was accessed and updated from a service() method more than once, its value on the second and subsequent accesses could be altered by another thread servicing another request. Since the value can be altered in another thread, its value may become meaningless unless we manage or synchronize access to it. Servlets provide an alternative to this with the SingleThreadModel interface, which we will look at later in this chapter. Chapter 11 also looks in more detail at the synchronization issue.
When the servlet instance is being unloaded from memory by the servlet container, the servlet container will call the destroy() method on the servlet. This is only called once all calls to the service() method underway have completed or timed out. Servlet instances may be unloaded at any time by the servlet container according to the container's policies. Obviously, the container has to have sensible policies regarding the loading and unloading of servlet instances, as there is a performance cost with inefficient or excessive object creation and destruction. For example, it would not usually make sense to instantiate a new servlet for every request, so containers usually reuse the instance to service more than one request.
The purpose of the destroy() method is to make sure that any finalization of data, releasing of resources, and so on, is be carried out before the servlet instance is lost. This is one of the reasons why a server should always be shutdown 'gracefully', using the appropriate shutdown command, rather than just closing the server window. Shutting down a server gracefully allows the server to complete any requests that are under way and to call the destroy() methods on any remaining servlet instances, ensuring no data is lost and resources are released properly.
There is one more method from the Servlet interface that must be implemented by servlets:
public String getServletInfo() throws ServletException, IOException
The getServletInfo() method is designed to return a String object containing information about the servlet. This is expected to contain information such as the servlet's author, the version, and copyright information. This method is designed to allow web server administration tools to display information about the servlet. What it actually returns is up to the programmer. The default implementations return an empty string.
|< Free Open Study >