How JSP Works: Servlets and JavaServer Pages
By Duane K. Fields and Mark A. Kolb
Web Development with JavaServer Pages
This article is the fourth excerpt in a series from the book, Web Development with JavaServer Pages.
First Excerpt: Writing Your First JSP
Second Excerpt: Tag Conventions in JSP
Third Excerpt: Running JSP
Authors: Duane K. Fields and Mark A. Kolb
Publisher: Manning Publications Company
US Price: $44.95
Canadian Price: C$ 65.95
Publication Date: May 2000
Copyright 2000 Manning Publications Company. Reprinted with permission.
Now that we've reviewed a few examples and looked at how JSP support can be added to a Web server, all the pieces are in place for considering the details of JSP operation.
Although the JSP specification does not mandate any one specific approach for implementing JavaServer Pages, it is currently the case that all major JSP implementations are based on servlets. As a first step in understanding how JSPs work, then, it is helpful to understand how servlets work.
As already mentioned, servlets are a Java-based analog to CGI programs, implemented by means of a servlet container associated with an HTTP server. A set of URLs and/or URL patterns is specified as being handled by the servlet container, so that whenever a request for a URL matching this set is received by the HTTP server, that request is forwarded to the servlet container for processing. For example, the URL http://server/account/login might be mapped to the servlet class com.taglib.wdjsp.fundamentals.LoginServlet. When the HTTP server receives a request for this URL, the server forwards this request to the servlet container, which in turn forwards it to an instance of the LoginServlet class.
The forwarding of requests is accomplished by packaging all of the request data-URL, origin of the request, parameters and parameter values, and so forth into a Java object. A similar Java object is constructed representing the response. This response object has methods for setting the status code of the response, and for accessing the output stream which will hold the results of processing the request. The servlet classes are responsible for defining service methods to handle the various types of HTTP requests, including a
doGet() method for handling HTTP GET requests and a
doPost() method for handling HTTP POST requests. The objects constructed by the servlet container to represent a single request and its corresponding response are passed as arguments to these methods, which are then called by the servlet container on a per-request basis.
Given a request object and a response object, the service method accesses the properties of the request and performs the appropriate computations on this data in order to construct its reply. The HTML that comprises that reply is written to the output stream associated with the response object. After the service method has finished running, the servlet container sends the contents of the response object back to the HTTP server, which in turn sends the response back to the Web browser which submitted the request in the first place. Multiple simultaneous requests for a servlet are handled by running each call to the servlet's service methods in a separate thread.
From this description, you can begin to imagine how this approach might be extended to support JavaServer Pages. After all, JSP execution starts with a request for a JSP page, processing is done on the JSP tags present on the page in order to generate content dynamically, and the output of that processing, combined with the page's static HTML, must be returned to the Web browser. By adding a few extra steps to the basic servlet process, however, performance can be improved considerably.
The primary component of a servlet-based implementation of JavaServer Pages is a special servlet often referred to as the page compiler. The container is configured to call this servlet for all requests with URLs that match the JSP file extension, and it is the presence of this servlet and its associated Java classes that turns a servlet container into a JSP container. As its name suggests, the task of this servlet is not just finding JSP pages in response to such requests, but actually compiling them: each JSP page is compiled into a page-specific servlet whose purpose is to generate the dynamic content specified by the original JSP document.
". . . each JSP page is compiled into a page-specific servlet whose purpose is to generate the dynamic content specified by the original JSP document."
Thus, whenever the HTTP server receives a request for a URL corresponding to a JSP, that request is sent to the JSP container, which invokes the page compiler servlet to handle the request. If this is the first time a request has been received for a particular JSP file, this servlet compiles the JSP file into a servlet.
To compile a page, the JSP page compiler parses through its contents, looking for JSP tags. As it parses the file, it translates its contents into the equivalent Java source code which, when executed, will generate the output indicated by the contents of the original file. Static HTML is translated into Java strings, which will be written unmodified and in their original sequence into an output stream. JSP tags are translated into Java code for generating dynamic content: Bean tags are translated into the corresponding object and property calls, while scripting elements are transferred as is. This code will be mixed in with the output of the original static HTML, so that the dynamic content is inserted into the output in the correct location. This source code is then used to write the service methods for a servlet, such that running it for a request has the effect of producing the content specified by the original JSP file. Once all the servlet code has been constructed, the page compiler servlet calls the Java compiler to compile this source code and add the resulting Java class file to the appropriate directory in the JSP container's class path.
Once the compiled JSP page servlet is in place, the page compiler servlet then invokes this new servlet to generate the response for the original request. Of course, this parsing, code generation, and compiling incurs quite a bit of overhead. Fortunately, these steps are required only the first time a request for a given JSP page is received. All subsequent requests can be passed directly to the already-compiled page servlet for immediate processing.
As long as the contents of the original JSP page remain unchanged, there is no need to generate a new servlet, since the Java code corresponding to those contents remains the same. For this reason, the very first step taken by the JSP page compiler when it receives a request for a JSP is to check the time stamp for the JSP file corresponding to the requested URL, to determine when that file was modified or created. The page compiler will also check the time stamp on the compiled servlet for this JSP page. If no compiled servlet is found, or if the time stamp on the JSP file is more recent than the one on the compiled page servlet, then a new servlet must be generated. This means that the (new or modified) JSP file must be parsed and translated into source code, and this new source code must be compiled. If the compiled servlet is newer than the JSP file, however, no new compilation is required and control can be transferred directly to the servlet to finish processing the request, saving considerable time. So while the first request for a new or recently modified JSP page will be slow, all later requests go straight to the compiled servlet for response generation.
This process is summarized in flowchart form in Figure 1, where Web browser requests are received by the HTTP server, and JavaServer Pages requests are routed to the page compiler servlet running in the JSP container. The JSP container then checks whether or not the servlet for the requested JSP page is up-to-date: Does a compiled servlet exist for this page, and, if so, is it newer than the current contents of the JSP page? If not, the JSP container must go through the process of parsing the page, generating the source code, and compiling it. The newly compiled servlet is then loaded into the servlet container. If the JSP page servlet is current, then the JSP container needs to make sure that the servlet is currently loaded, since it may have been unloaded after its original creation due to lack of use. In either case, control may then be transferred from the page compiler servlet to the JSP page servlet, which then handles the request. The response is generated by the JSP page servlet and routed back to the HTTP server, for return to the Web browser.
|Figure 1: Server process for creating and running JSP servlets
This unique page compilation feature lends additional performance benefits to JavaServer Pages, in comparison to other dynamic content systems. As discussed, most dynamic content systems rely on special tags, interpreted scripting languages, or a combination. For most of these systems, the file containing these tags and/or scripts must be parsed each time the document is requested. This parsing incurs overhead that is avoided with JavaServer Pages, since JSP files are parsed only the first time they are requested. JSP will be slower than other approaches for this first request, because of the compilation step, but will be faster than the other approaches for all subsequent requests.
". . . code associated with a JSP servlet class tends to remain resident in the system memory of the Web server."
In addition, because of the way the JVM that is running inside the JSP container operates, the code associated with a JSP servlet class tends to remain resident in the system memory of the Web server. As long as new requests for that JSP are being received on a regular basis, the servlet code remains loaded into the memory allocated to the JVM. Access to data and code stored in a computer's physical memory is much quicker than access to data and code stored on a computer's hard disk. Because JSP requests are handled by loading the corresponding servlets into memory and running them, rather than reading the JSP file from the local file system, JSP again enjoys a performance boost over content generation systems that rely on repeatedly reading files from disk.
Next week: Buffered output and scalability.
Duane is a Certified Java Programmer and an IBM Master Inventor. Mark is a former rocket scientist who now focuses on Web-based applications.
Writing Your First JSP
Tag Conventions in JSP
JSP: Buffered Output and Session Management