Web Application Basics


The Model-View-Controller Design Pattern

Before we plunge into the various elements of a web application, let's first discuss a key concept in application design: the Model-View-Controller, or MVC , design pattern. (The J2EE world refers to this pattern as Model 2. ) Regardless of what you call it, many web applications use MVC as a basis for their architecture and operation. Therefore, we want to describe briefly how this design pattern works. We'll be using MVC terminology throughout this chapter to describe the roles of various web application components , so you need to be familiar with this subject before we move ahead.

Architects and developers like MVC because it allows them to divide the application into pieces that can be assigned to different developers with different skill sets. These pieces map cleanly to easily understood subcomponents of the application. Traditionally, the design pattern breaks down as follows :

  • Model: The model encapsulates business logic and database schemas. Naturally, developers skilled in database technology and business logic develop the model portion of the application. In J2EE applications, model code is often implemented using JavaBeans or Enterprise Java Beans (EJBs). The model remains independent of the application using it; therefore, multiple applications sometimes share the same underlying model.

  • View: The view focuses on the look and feel of the application. Your developers with skills in human interface design and implementation usually work on the view. These components focus on presenting dynamic information to the user , but they do not gather this dynamic information themselves . In the J2EE model, JavaServer Pages, or JSPs, handle the responsibilities of the view.

  • Controller: The controller handles the flow of the application. Application developers usually handle the components comprising the controller functions of your application. These components guide the flow of the view and instantiate model objects in order to fulfill the application's requirements. In J2EE, the servlet and its associated classes play the role of the controller in the web application.

Because of the multi-tiered nature of web sites, MVC is an excellent choice for web application design. Using MVC yields applications that are flexible, easily maintainable , and extendable. MVC also makes it easy to divide the application among developers with different skill sets. In fact, web sites thrive under the MVC model. Many web sites change their look and feel constantly. By splitting out their presentation (view) components from their application (controller) code, they isolate the presentation of the web site from the logic flow. This allows the design team to make cosmetic changes to the web site without calling the programmers (who aren't generally known for their artistic abilities anyway). Changing a page's look not only doesn't require a programmer if you use MVC, but the update cannot impact the program's code. Likewise, if another drapplication needs a column added to a database table, the resulting updates to the model do not impact either the controller or the view of other applications that use the same table.

Figure 2.1 shows how MVC works within a Java application server. (If some of the components are unfamiliar to you, we explain them later in this chapter and in the next .) The client, using a browser, makes a request for a URL that invokes a servlet. The network sends the request to the correct machine, and the HTTP server on that machine receives the request. The HTTP server passes it on to the plug-in, which determines that it is a request for the application server to handle. The application server takes the request and maps it to a servlet. The application server's web container instantiates the servlet, if required, and begins executing it to satisfy the request. During execution, the servlet calls model objects as needed to read or update data and forwards the resulting data to a JSP. The JSP formats the data into a web page and returns the generated page back to the client.

Figure 2.1. Model-View-Controller within a Java web application. From "WebSphere Programming Model: Best Practices for Http Servlets and Java Server Pages," paper presented by Ken Hygh at Solutions 2001, San Francisco, CA. IBM Corp. 2001. Reprinted by permission of IBM Corp.

graphics/02fig01.gif

Many leaders in the field of Java web application design recommend the MVC approach for web site architecture. [1] While the J2EE model does not enforce this design pattern, we expect (and hope) that your web developers used this approach to assemble your web application. Keep in mind that MVC is a design strategy. By itself, it does not guarantee terrific performance for your web application. However, knowing your web application's overall design structure (even if you're not a developer) often proves helpful for several reasons:

[1] MVC is a cornerstone of both the J2EE Design Patterns, <http://java.sun.com/blueprints/patterns/j2ee_patterns/model_view_controller/>, and the Apache Struts Project, <http://jakarta.apache.org/struts/>. Also, check the bibliography.

  • Understanding the design of the application allows you to follow its flow more easily. This is frequently useful in tracking down bottlenecks.

  • Knowing the breakdown of responsibilities for the various components of the application allows your team to involve the right skills for problem solving.

  • Proper application structure often proves an effective indicator of overall application quality.

Servlets

A servlet is a small Java program. [2] These little programs provide the intelligence behind Java web site applications. Each incoming HTTP request for a dynamic page goes to a servlet. The servlet decides how to satisfy the dynamic request by marshalling data from any number of sources and passing it to the presentation logic to generate the dynamic page. For any given request, the servlet might choose between multiple presentations to return to the user. For example, if the site supports both French and English pages, the servlet looks at the parameters on the incoming HTTP request and picks the correct format for the output. Or a servlet might choose an error page rather than the normally displayed page if the servlet couldn't communicate with the database or a validation error occurred.

[2] From Javadoc of the javax.servlet.Servlet class. Retrieved February 22, 2002 from the World Wide Web: <http://java.sun.com/j2ee/j2sdkee/techdocs/api/javax/servlet/Servlet.html>.

Servlet Scope

Large web sites contain lots of functions for their users. Each function provided maps to a different request sent to the web application providing the intelligence behind the web site. How the web site maps these requests to functionality varies. Some web sites use a new URL for each request, while others generate one or more URLs with a series of parameters attached to differentiate between functions. Regardless of the form your requests take, your web site needs a strategy for dealing with a variety of requests . Specifically , you need a strategy for controlling how you map an incoming request to the function you must provide and to the JSP you eventually use to return the dynamic page to the requestor .

One popular strategy maps the incoming requests into a small set of servlets. Each servlet handles a small set of requests and uses one or two specific JSP pages to return dynamic pages to the requestor. The servlet contains only the logic required to satisfy the small set of requests it handles. This approach also allows you to update and add new function to the web site easily, as the logic for the function resides in discrete servlets and JSPs. Updating or adding functions does not impact other servlets. Another strategy uses a "front controller" to manage incoming requests. [3] The controller potentially handles all the requests for a functional area of your web site (say, the banking or brokerage areas of a finance web site). The controller uses "helper" objects, such as JavaBeans, to pull together data for the JSPs. (See Figure 2.2 for an example of the two common request management strategies.)

[3] See Deepak Alur, John Crupi, Dan Malks, Core J2EE Patterns .

Figure 2.2. Small servlets versus front controller strategy. From IBM Software Group Services Performance Workshop presented by Stacy Joines, 2001, Hursley, U.K. IBM Corp. 2001. Reprinted by permission of IBM Corp.

graphics/02fig02.gif

Regardless of the request management strategy you use, keep the following performance considerations in mind.

  • Keep code paths short: Design your control logic to quickly recognize each request and begin meaningful processing to build a response. Some web applications traverse enormous "decision trees" (usually an enormous "if" construct) to determine how to answer the user's request. The decision trees often prove inefficient, especially if the site handles a lot of different URL/ parameter combinations.

  • Maintain the right granularity as your site grows: As your web site grows, you add web application logic to support new features. For example, if you operate a web banking site, you might grow your web site to include brokerage functions. To support the brokerage functions, you need new web application logic.

How you grow your site really depends on the request management strategy you use (front controllers with helper objects versus a larger group of self-contained servlets). In any case, we recommend building new helper objects or self-contained servlets as you add new function. (Also consider adding a new controller for major new functional areas of your web site.)

Again, the idea is to keep the code paths in your web site short. Often, when developers shove significant new function into existing servlets or helper objects, the code paths within these objects become unnecessarily long. This results in not only suboptimal performance but also in maintenance difficulties for the development team. For example, in our banking web site, we might add new servlets to support our new brokerage functions. (For web sites using the front controller strategy, we might add another front controller and set of helper objects specifically for the brokerage functions.) Likewise you potentially need new JSPs, JavaBeans, and other objects to support this new feature.

As with any design practice, you want to avoid extremes of object creation as well. We sometimes encounter developers who create a servlet for every possible request permutation the web site might receive. Obviously, thousands of little servlets prove just as difficult to maintain and extend as one gigantic servlet. When your site grows, consider refactoring the web application classes if you're generating lots of classes containing similar logic. (Refactoring simply means reorganizing your application. It often involves centralizing common logic currently distributed throughout your objects.) [4]

[4] See Martin Fowler, Refactoring: Improving the Design of Existing Code .

Servlet Operation

Now that we better understand what servlets do, exactly how do they work? The application server loads the servlet either as the web container starts or when the servlet receives its first request, depending on administrative settings. (Note: JSPs and servlets share many runtime characteristics, although their roles at the web site differ . We discuss the specifics of loading and running JSPs in more detail later in this chapter.) When the servlet loads into memory, the application server calls the servlet's init() method. This gives the servlet an opportunity to obtain static resources and perform initialization steps. (As we'll discuss later in this chapter, good management of static resources often dramatically improves servlet performance.)

The init() method only runs once, at servlet load time. The servlets we most commonly see inherit from javax.servlet.http.HttpServlet. They return HTML or other appropriate formats in response to an HTTP request. As the HTTP requests arrive for the servlet, they trigger execution of the servlet's doGet() or doPost() methods, depending on the request. The servlet programmer provides the logic inside these methods .

Servlets typically run as multi-threaded entities. That is, the application server creates only one instance of the servlet, and multiple threads may execute code in this instance simultaneously . For example, if your web site receives four simultaneous requests for the same servlet, those requests execute inside the doGet() or doPost() method on separate threads simultaneously. Because of this multi-threading , you cannot use instance variables inside your servlet to maintain state information.

The servlet specification does provide a single-threaded alternative. By implementing the javax.servlet.http.SingleThreadModel interface, your servlet becomes single-threaded. This means the application server creates an instance of your servlet for each simultaneous request. If you've never written a multi-threaded application before, you might feel tempted to use the single-threaded model. Don't do it! The single-threaded model does not perform well, particularly in high-volume web sites nor does it scale well. In this model, each request operates within its own fully instantiated servlets. This requires more memory and related overhead than the multi-threaded model. While the single-threaded model might seem like a shortcut, it is actually a performance dead end. Take the time and learn how to write good multi-threaded servlets instead.

Obviously, threading is an important issue for servlet developers. We discuss threading in more detail in Chapter 4. Other excellent books provide detailed tutorials on managing threading issues inside your servlets. [5] Take the time to learn and master these issues before writing your web application. You will reap returns in improved application performance, fewer application errors, and less rewriting.

[5] See Hunter and Crawford, Java Servlet Programming .

Servlets, Threads, and Queuing

If the request specifies a page generated by a servlet or JSP, the HTTP server plug-in (see Chapter 3 for a more detailed discussion of HTTP servers) passes the request into the web container. The web container runs inside a J2EE application server running in a Java Virtual Machine (JVM) and has a limited number of threads available to handle servlet requests. Typically, the number of HTTP "listeners" (see Chapter 3) greatly exceeds the number of available servlet threads in the application server's web container. (For example, a typical installation may have 150 HTTP server listeners, but only 20 “50 servlet threads.) Figure 2.3 shows the different numbers of listeners and threads, including servlet threads, that make a call to a database through a connection pool (we'll discuss connection pools later in this chapter). The application server assigns the incoming request to a thread, and the requested servlet runs on this thread. After the servlet satisfies the request and returns the data to the user, the thread returns to the available servlet thread pool inside the application server's web container. If all of the threads in the web container are busy when a request arrives, the servlet request queues. Both queued requests and requests executing inside the servlet engine tie up the HTTP server listener thread originally assigned to them.

Figure 2.3. Threading/queuing "funnel." From IBM Software Group Services Performance Workshop presented by Stacy Joines, 2001, Hursley, U.K. IBM Corp. 2001. Reprinted by permission of IBM Corp.

graphics/02fig03.gif

At runtime, you want a threading "funnel effect," as demonstrated in Figure 2.3. [6] You do not want equal numbers of worker threads at every layer in a multi-tiered application. At first, this seems counterintuitive, but keep in mind that the work occurring at one layer does not always involve the next layer. For example, if your HTTP server handles requests for both static and dynamic data, some of the HTTP listeners handle the static requests without involving the application server. Therefore, not all of your HTTP listeners engage the application server simultaneously. Likewise, not all the servlet threads make database calls simultaneously, so you need fewer database connections in the connection pool than configured servlet threads.

[6] See Gennaro Cuomo, "A Methodology for Production Performance Tuning," an IBM WebSphere Standard/Advanced White Paper (March 2000): <www.ibm.com/support/manager.wss?rs=180&rt=0&org=SW&doc=7000616>.

Application servers allow you to set the maximum threads allocated for executing servlets and JSPs. You may think the default setting for your application server thread pool is quite low (some vendors set the default as low as 15 “20 threads), especially if you plan to handle a lot of user requests per second. Actually, the default settings, despite the small number of threads, probably represent the optimum number of threads for the average web application. This seems counterintuitive: If you want to handle more simultaneous requests through your web site, you should allocate more threads, right? Wrong. Actually, more threads often prove detrimental to web performance. As you allocate more threads, you create more work for the JVM, which manages the threads. Adding more threads generates more overhead as the JVM tries to give each thread its fair share of CPU and other resources. This is known as context switching overhead , and it increases as the number of threads increases , reducing the amount of useful work that the greater number of threads in the JVM can actually accomplish. So, start your performance testing by leaving the servlet/JSP thread pool sizes at their defaults. You might try adjusting these thread pool sizes up (and down) using small increments . If you see improvement, consider moving up a little bit more. However, keep in mind the key to servlet/JSP thread pools: Less is often more.

The Frozen Web Site Danger

Before we talk about specific servlet performance issues, let's talk about how poor programming practices sometimes literally stall web sites. We call this phenomenon the "frozen web site." The web site becomes nonresponsive to requests, even requests for static elements like gifs, jpegs, and static HTML pages.

How can failures in servlet programming impact the HTTP server? The answer lies in the queuing between the HTTP server plug-in and the application server engine. We discussed in the previous section how the HTTP server plug-in queues requests for the web application server until a servlet thread becomes available, as shown in Figure 2.3. However, poorly written servlets sometimes stall as a servlet waits indefinitely for a nonresponsive resource ”perhaps a remote database or a remote system overloaded with traffic. In any case, the servlet programmer didn't provide an exit strategy for the servlet in this circumstance.

If the servlet stalls, the thread running the servlet never returns to the application server's thread pool. This reduces the resources available to perform useful work. The nonresponsive remote system may stall every servlet thread in the web site. The stall happens quickly if the servlets call the remote system on almost every servlet request. If the web site uses the remote system infrequently, it may lose threads over the course of hours or days before the stall finally occurs. As new requests arrive they queue to wait for an available servlet thread, occupying the HTTP server threads as they wait. Eventually, the HTTP server runs out of threads to respond to simple static requests. At this point, the web site is "frozen," as shown in Figure 2.4.

Figure 2.4. A frozen web site. From IBM Software Group Services Performance Workshop presented by Stacy Joines, 2001, Hursley, U.K. IBM Corp. 2001. Reprinted by permission of IBM Corp.

graphics/02fig04.gif

Frozen web sites exhibit some variety in symptoms. As we mentioned above, all of the available servlet/JSP threads might stall immediately if almost every request accesses the nonresponsive back-end system. In these cases, the web site appears to "hang" fairly quickly. That is, the site at first stops responding to dynamic requests, and shortly thereafter stops responding to static requests as well. However, other functions, which monitor the application server without using the HTTP interface, continue to function normally. (For example, depending on the application server vendor, the administrative interface may continue to work, although the application server no longer can serve pages.) If your web site only accesses the nonresponsive system infrequently, it may take hours or days to become nonresponsive. In these cases, the web site often experiences increasingly poor throughput and response time before it completely stops responding to requests.

The "acid test" for a frozen web site is a thread trace. A thread trace gives you details on the activity of the threads in a JVM. In the case of frozen web site, the thread traces shows the statement on which each thread is waiting. (See Chapter 12 for more details on thread traces.)

Avoid the frozen web site by planning for the eventuality of a back-end outage . Set time limits on any requests to a remote resource, and exit if the remote resource fails to respond during the time limit. If the remote resource doesn't respond, cut your losses and return an error page if you cannot obtain the data you need in a reasonable time. Do not stall your site waiting for resources. Some web containers automatically grow their thread pools in these situations. However, the thread pool cannot grow infinitely. Either performance becomes unacceptable, or the JVM collapses because of the number of running threads. Even with self-adjusting thread pools, then, the web application must prepare for and handle nonresponsive remote systems.

Web sites need short timeout periods. Unlike thick clients , which serve one user, web sites may have dozens or even hundreds of users making requests every second. The servlet often cannot afford to wait 30 seconds for a database or other back-end resource to respond. Keep the timeout values short (a few seconds at most). Place them in external resource files, such as a resource bundle, and load them at servlet initialization (when the servlet's init () method executes). This allows the administrator to tune the timeout values later, if required, without modifying the code. A good web site keeps moving no matter what happens to remote resources. Plan for outages, and build an error path that allows your servlets to handle outages consistently. (Usually, the web site team sets up an error JSP to return to the user in these cases.) Again, it is far better to return an error page to a single user than to stall the web site and affect every user.

Servlet Performance Tips

As with most specifications, the servlet specification gives the programmer little guidance on the best way to write a servlet. We encounter many teams with strong backgrounds in thick client development struggling over the transition to server-based applications. Some "best practices" from the thick client world make for disaster when applied to a servlet application. A naive code port from a client-server application to a web application typically performs poorly.

Web sites amplify small programming errors and inefficiencies . For example, in the previous section we discussed thread management and thread stalls. In a thick client environment, an overly generous timeout for a back-end resource might annoy the end user but wouldn't create problems for the system as a whole. On a web site, an overly generous timeout might bring the entire site to its knees. In the client-server paradigm, one client's operation doesn't interfere with another's, but the same is definitely not true in server-side programming for Java web sites.

In this section we discuss some common application performance problems. Some of these issues might not make a tremendous difference in a thick client application. However, because of the volume of requests a large web site executes each day, these minor points become critical to the performance of a web application. Let's look at some of these best performance practices for your servlets. [7]

[7] See Harvey W. Gunther, "Application Server Development Best Practices for Performance and Scalability, Version 1.1.0," for many detailed servlet, JSP, and EJB best practices. Retrieved from the World Wide Web: <http://www-3.ibm.com/software/Webservers/appserv/ws_bestpractices.pdf> on March 23, 2002.

Use Servlets to Control Application Flow

Servlets control the flow of your application. Do not use the servlet to generate HTML or other output formats; use JSPs instead. This works even if you're not returning HTML, as JSPs can build XML or other output datastreams. This also keeps your view logic separate from the control logic of the web site, which makes it easier to change the look and feel of your web site over time.

From a performance perspective, using the servlet strictly as the controller often helps the web application avoid garbage collection issues. The outbound presentation layer usually consists of lots of strings. The JSPs generated by your application server build these strings efficiently , and efficient string handling generally reduces your garbage collection cycles. (See Chapter 4 for more details on string management and garbage collection.)

Acquire Static Resources at Initialization

Earlier we discussed the multi-threaded nature of servlets and noted that you cannot store state information in instance variables shared by multiple threads. However, some information fits very well into instance variables. For example, programmers often repeat the Java Name and Directory Interface (JNDI) lookup for a common resource such as a data source unnecessarily. The JNDI context is threadsafe, so if the programmer places it in a class variable, all the servlet threads may share it safely. We recommend performing any common lookups inside the servlet's init() method, and then storing the results in class variables. With this technique, the servlet only looks up the context for a given resource at initialization time. Given the performance overhead associated with JNDI lookups, this technique also significantly speeds up servlet processing.

In Listing 2.1, we use JNDI caching in a servlet example using the IBM WebSphere Application Server. This code gets the data source information once in the init() method and stores it in a class variable called myDataSource . Subsequently, every request that calls the doGet() method obtains a database connection from the data source defined in myDataSource . This spares each request from doing its own JNDI lookup of the data source. The IBM WebSphere Application Server (Version 3.5.2 and above) provides a JNDI lookup cache to speed up applications. However, doing the caching inside the servlet is even faster. (Check with your application server provider for more details about any similar JNDI caching strategies.)

Listing 2.1 Example of caching JNDI lookups inside a servlet
 public class MyServlet extends HttpServlet {   private static final String dsName = "myDataSource";   private static Context context = null;   private static DataSource myDataSource = null;   public void init(ServletConfig config) {     Hashtable parms = new Hashtable(2);     parms.put(Context.INITIAL_CONTEXT_FACTORY,               "com.ibm.websphere.naming.WsnInitialContextFactory");     parms.put(Context.PROVIDER_URL, "iiop:///"); // local machine       context = new InitialContext(parms);       myDataSource = (DataSource)context.lookup("jdbc/" + dsName);     }   public void doGet(HttpServletRequest request,                     HttpServletResponse response) {     acctNumber = request.getParameter("account");     if (acctNumber == null  "".equals(acctNumber)){       // do error page       }     AccountInfo bean = new AccountInfo(acctNumber,myDataSource);     // the AccountInfo bean will use the DataSource in order to get     // its data from the database, by getting a connection, etc.     .     .     .     request.setAttribute("acctInfo",bean);     getServletContext().getRequestDispatcher("foo.jsp").forward(        request,response);     } // doGet } // class MyServlet 
Manage Servlet Logging

The servlet standard lacks a detailed logging strategy at this point (see the discussion below on using the servlet.log() method). Until a standard exists, you might consider several of the free logging frameworks currently available, or you might implement your own custom framework. Regardless of your approach, your application needs a logging strategy. From a performance perspective, logs usually end up at the hard disk at some point. Of course, anything interacting frequently with the hard disk presents a potentially performance expensive operation. Before you choose a logging framework, or write one yourself, consider the following suggestions regarding logging.

  • Support Logging Levels and Use Them Sometimes you require more information than at others. Allow the application programmers to give a log message a weighted "importance" value. For example, severe application errors always rate a "Level 1" importance, while trace information always rates a "Level 4." Use code reviews to keep the team in synch as to the importance assigned to various classes of messages.

  • Reduce Production Logging When the application runs in production, keep logging to a minimum. At this stage, you only want to capture application errors, not trace information (unless you're trying to find a problem in production, which should be rare!). This greatly reduces the time the system spends interacting with the hard disk. (Remember, hard disk interaction is generally very performance expensive.) It also reduces the size of log files on the system, and avoids potential disk space issues.

  • Make Logging Levels Runtime Configurable If your system encounters an error, you probably want to record more detailed log information for awhile to capture the trace information around the error. However, turning the logging on for the course of the day might be too expensive in terms of performance. Instead, consider building a special "administration page" as part of your web application to toggle logging levels on the fly. (Actually, custom administration pages often prove useful for other things, such as setting various application parameters. Web site administrators use them to set everything from timeouts to the location of custom log files.)

  • Consider Buffered Writing Some logging mechanisms prevent disk overflows by setting an absolute maximum file size for the log file. After reaching this size, the log begins writing again at the top of the file. The danger, of course, lies in losing information in the history you might need. However, it does defeat any danger of stalling the system by running out of logging disk space.

  • Take Care with Memory Buffers Some logging systems write the log to a memory location, and dump to hard disk only on request, or only after the memory buffer fills up. While this is certainly faster than a pure disk I/O system, avoid overcoming your JVM heap with log data. Set a hard maximum for the buffer in memory, and make sure your applications still have enough room to run.

  • Avoid servlet.log() The standard provides a logging interface via the servlet.log() method. However, the implementation of this method varies greatly among application servers. Some servers just write the information to stdout while others write the information to their administrative database logs. The latter makes for a very, very expensive logging technique, and greatly impacts performance. We recommend avoiding the servlet.log() method, particularly if you plan to port your application to multiple application server vendors' platforms.

  • Avoid system.out.println() Many programmers place these statements in their application code for debugging or logging, and leave them when the code goes into production. This is a serialized resource, and requires file I/O on every call, which impacts performance. If you can't easily get these out of the code, consider setting your application server's standard out to /dev/null (depending on operating system) so these statements do not trigger file I/O.

  • Use an Available Logging Framework Rather Than Writing Your Own Rather than reinventing a logging framework, consider using one of the many available (for example, Apache's Log4j or JTrack's jLog). We do not recommend writing your own logger, as this tends to introduce errors and performance problems into many projects. Rely on existing technology if at all possible.

File-Serving Servlets

Many application servers include a servlet that acts as a miniature HTTP server. This servlet is a convenience feature, allowing the application server to serve static content. This permits the web site programmer to test web applications without defining the static content of the application to an external HTTP server. However, the file-serving servlet lacks performance efficiency. Normally, a request for static content stops at the HTTP server, which handles the request. However, if the file-serving servlet handles your static content, the request must travel through the HTTP server, the application server plug-in, and the application server itself before reaching the file-serving servlet. The increase in path length increases the overhead required to return the servlet.

Whenever possible, we strongly recommend using an HTTP server or similar server (such as a caching proxy server) to deliver static content to your users. However, if you plan on using your application server to provide security for static as well as dynamic content, you may require the application server's web container and the file-serving servlet to properly secure your static content. Of course, the J2EE 1.2 standard complicates this advice through the use of Enterprise Archive (EAR) and Web Archive (WAR) files to consolidate all the components of the application (including static elements). With this approach, the administrator deploys the web application into the application server, which serves both the static and dynamic content. While this approach might work for small web sites, or sites wanting to secure their static content, we still recommend using an HTTP server to serve your static content. Making this work with your application and packaging tools may require some effort, but is well worth the resulting performance gains.

JavaServer Pages (JSPs)

The previous sections discussed JavaServer Pages (JSPs) a great deal without really defining them or what they do. Let's briefly discuss the operation of a JSP, and its role in a Java web application. Early in the development of the servlet standard, the need for a mechanism to separate the logic layers of the web site from the presentation layers became apparent. The JSP developed out of this, and provides the presentation layer with sufficient abstraction to handle dynamic data output while keeping the feel of an HTML page. (Using MVC terminology, JSPs function as the view. )

A JSP exists to return dynamic data in a predefined format (often HTML). The JSP receives the data to display from a servlet and places the data into the format using special JSP tags defined by the specification. You may extend the function of the JSP by writing your own tags and creating your own "tag library," which is strongly recommended. This allows you to isolate Java code out from the body of your JSPs.

The JSP standard tries to keep the JSP page familiar to a less technical audience: the graphic designers and layout specialists for your site. These folks come with tremendous artistic talent but not a lot of Java programming skills. Good JSPs isolate the design folks from the programming team working on the servlets. As we mentioned earlier in our discussion of servlets, avoid putting presentation logic inside servlet code. Your layout team, the folks high in artistic talent but low in programming skill, cannot modify presentation logic managed inside a servlet. They do not know how to write or modify the servlet's Java code. Don't put the look and feel of your web site in the not-so-artistic hands of your servlet developers; use JSPs to build your presentation layer.

Basic JSP Runtime Operation

So how does the JSP work? Operationally, a JSP is just a special form of a servlet. The first time a request requires a JSP, the web application server runs the JSP page through a parser to turn it into a servlet. The web application server then compiles this intermediate "servlet state" of the JSP and launches it on a thread just like any other servlet. At this point, the web application server makes no differentiation between the JSP and any other runnable servlet. (Figure 2.5 illustrates the differences between servlet loading versus JSP loading.)

Figure 2.5. Servlet loading versus JSP loading

graphics/02fig05.gif

Usually the application server checks the file system periodically for a more recent copy of the JSP. If it finds one, it parses, compiles, and starts the newer version. The interval at which the application server checks for new JSPs is usually configurable via an administration setting.

JSP Performance Tips

Web applications servers execute JSPs much as they do regular servlets; thus, our previous discussions on good servlet programming techniques also apply to JSPs. Again we want to emphasize that you should not place Java code inside your JSPs. If you need to embed Java logic to control the layout of the page, use a tag library or JavaBean (see below) to hide the code from your presentation team. While JSPs behave at runtime like any other servlet, they do involve a few unique performance considerations. Let's review the most important performance tips for your JSPs.

Reload Intervals

Application servers check for updates to the JSP and reparse and recompile the JSP if a more recent version exists. Of course, these checks require file I/O to look at time-stamps on the JSP files. Again, anything interacting frequently with the hard disk is performance expensive. If your application server permits, avoid auto-reloading JSPs in production (this is also a good practice for site security as well). If you cannot turn the auto-reload function off completely, set the reload interval as high as possible.

HTTP Session Created by Default

The J2EE specification supports creating "implicit" objects inside JSPs without requiring a formal declaration by the programmer. To support this feature, some application servers automatically create an HTTP session object for each JSP page if it does not already exist. As you will see, a session object remains in memory until it times out and is removed by the container. The default timeout is often as high as 30 minutes. Obviously, these session objects might take up a lot of heap space in the JVM without the web team even being aware of their existence!

Find out how your web application server manages this portion of the J2EE specification. If you do not use implicit objects and do not want an HTTP session object created in your JSP page, use a tag to prevent the web container from creating one automatically. The tag for turning off the HTTP session default creation is

 <%@ page session="false"%> 
Precompile or Preclick

The first time the web site receives a request for a JSP, the web application server parses, compiles, and starts the JSP as a running servlet. The parsing and compiling steps often require several seconds to perform and use significant amounts of CPU. If at all possible, get these parsing and compiling steps out of the way before the web site comes under load.

Some application servers provide JSP precompilers to eliminate the compile overhead at runtime. Make use of this feature if your web application server supports it. (The IBM WebSphere Application Server provides a batch compiler for JSP files.)

If your application server doesn't provide a JSP precompiler, consider developing a process to request as many JSPs as possible when your web application comes up. You might use an inexpensive load test tool for this, or it might just be a manual process performed quickly by the administrator after restarting the machine. Obviously, for large web sites, touching every JSP on restart isn't always feasible . However, the sooner the most frequently used JSPs become compiled, the sooner your web site gets back to normal response time and CPU utilization. (Also, precompiling your new or updated JSPs alerts you to any parsing or compilation errors before your user community encounters them.)

Using JavaBeans and Custom Tags

"Regular" JavaBeans make a nice complement to servlet/JSP programming by implementing the Model portion of MVC. Beans frequently play the role of a "contract" between a servlet and a JSP. The Bean defines the data the JSP might expect from the servlet, as well as methods available to access this data. Regular Beans also act as high-level wrappers for Enterprise JavaBeans (EJBs) by defining the data interface without revealing the EJB interactions underneath used to access the data or function.

JSPs use custom tags to camouflage their interactions with JavaBeans. The custom tag looks like just another HTML tag, only with special attributes defined by your development team. Behind the tag, your developers provide logic to interact with a JavaBean to retrieve dynamic data to populate the tag. Custom tags hide the Java implementation details from your design team and eliminate visible Java code in your JSPs.

Servlets pass JavaBeans to JSPs via the HTTP session or the HTTPServletRequest object. Again, the Bean contains the dynamic data the JSP will turn into an output page for the requesting user. Your design and programming teams may coordinate JavaBeans and custom tags manually, or they may use some of the tools available to perform "drag-and-drop" creation of JSPs with custom tags and JavaBeans.

JavaBean and Custom-Tag Performance Tips

Just as with any other feature, misusing JavaBeans and custom tags negatively impacts your web application's performance. Keep the following best practices in mind when using JavaBeans throughout your application and custom tags in your JSPs:

Avoid bean.instantiate()

JavaBeans come with their own simplified "persistence" mechanism. Beans may place their contents in a file for "reconstitution" at a later time. For this reason, if your code creates a new JavaBean using the bean.instantiate() method, the servlet or JSP code checks the hard disk for any persistent Bean contents to load. Checking the hard disk takes a relatively long time, and greatly impacts the performance of your web application, so avoid using this method when creating Beans. Instead, use the MyBean aBean = new MyBean() technique to create a new JavaBean instance in your web application code.

Use a Database If You Need to Persist Bean Data

If you must persist the contents of your JavaBean, use a database, not the file system. Databases do a much better job of handling simultaneous updates than does a native file system. Again, think of the impact if thousands of servlets every hour tried to dump Bean contents directly to the hard drive. The native file system does not perform well enough to support this kind of burden , and web site performance would tumble. Instead, use a database or EJB (which interacts with the database) to store Bean contents persistently.

Use Custom Tags in Your JSPs

We discussed this earlier in the section, but it bears another brief mention here. Early JSPs depended heavily on embedded Java code to retrieve dynamic data from JavaBeans and to place the dynamic data into the page returned to the user. This usually involved liberal use of the scriptlet tag inside the JSP. It also required involvement by your development team to build this embedded Java and to maintain it as the look and feel of the web site changed over time.

The custom tag feature of the J2EE standard allows your developers to build a library of custom HTML tags to hide the Java required to retrieve dynamic data. These tags allow your designers to work with a familiar interface to place dynamic data in the JSPs' layout.

While custom tags provide no direct benefit to performance (we can't say a custom tag runs faster than a lump of code in a scriptlet tag), they do simplify the web application and make it more maintainable by the folks with the correct skill sets. Simplification and organization often go a long way in making your code run faster. Likewise, using custom tags might prevent your designers from making ad hoc updates to the Java code used to retrieve dynamic elements. Scriptlet tags make it much easier for folks outside the development team to add underperforming code to your web site.

JSPs and XML/XSL

Previous sections discussed JSPs returning HTML pages. Recently, however, many web sites are using XML (Extensible Markup Language) and XSL (Extensible Style Language) to generate dynamic pages for the user requests. For example, the JSPs might generate XML instead of full HTML pages. The XML contains only the dynamic data, while the XSL stylesheet defines how to translate this data into a format usable by the requesting client.

The XML/XSL concepts often proves beneficial for web sites supporting several user devices. With XSL, your web site might use one stylesheet to generate WML (Wireless Markup Language) for wireless devices such as mobile phones or personal digital assistants (PDAs), while another stylesheet provides the format for traditional HTML pages. While XML/XSL allows you to readily support multiple device types, this flexibility comes at a cost. The XML/XSL approach requires repeated parsing of the XML to pull together the final output. Repeated string parsing in almost any language quickly becomes a performance drain. Java often incurs an even higher overhead because of the extra garbage collections sometimes required to clean up after string processing. (See Chapter 4 for more details on string management in Java.)

Some client devices and browsers support XSLT (XSL transformation). If all your clients support XSLT, consider offloading the page assembly to the client's machine. This pushes the parsing of the XML to the client machine, distributes the parsing penalty, and reduces the processing burden at your web site. However, many devices and browsers do not support XSLT. In these cases, you must parse the XML and build the output at the server.

Assorted Application Server Tuning Tips

Before we move ahead with our performance discussions, let's cover a few remaining performance topics. These tips mostly concern some performance settings available to your web application objects. Consult your documentation on how to manipulate these parameters for your application server.

Load at Startup

You choose when a servlet starts. If you so specify, the web container starts the servlet when the web container itself starts. Otherwise, the web container only starts the servlet if it receives a request for the servlet. Starting the servlet at web container startup often proves beneficial if the servlet's init() method performs significant initialization. This also proves beneficial if the servlet's init() method performs initialization that affects other areas of the web application. However, it comes at the expense of slowing the overall application server start-up time. Check your application server documentation on how to set the "load at startup" parameter for your servlets.

Web Container Thread Pool Size

The number of threads available in the web container to execute servlets, as we have noted, affects the throughput and performance of your web site. As we discussed earlier, less is often more when allocating threads because of the context switching overhead. Adjust the thread pool size for your web container in small increments to find the optimum size for your particular application and operating environment. Finding the optimal setting is an iterative process best addressed during your performance testing.

Servlet Reload

The J2EE specification requires the web container to detect updated servlets, and to start executing the new version automatically. Often, development teams use this feature during the development phase to quickly produce and test servlet updates. However, the automatic reload function is rarely desirable in production. Some points to consider include the following:

  • Performance: Enabling this function causes the web container to check the timestamp on the Java .class file that contains the servlet, and compare it against the last known timestamp. This check either happens at regular intervals (see below), or every time the servlet receives a request. Either way, a file check takes processing time away from your application.

  • Seamless Operation: Most application servers implement servlet reloading through the use of Java's custom class loaders. Some application servers will destroy all HTTP session objects when reloading any servlet to prevent mismatches in class definitions. Obviously, dropping all of the HTTP session objects on a production web site leads to high customer dissatisfaction.

  • Security: Automatic reload allows the replacement of running code without stopping the application server. Whether innocently or maliciously, this gives people in your organization the opportunity to easily move unauthorized code into the production web environment.

    Turn off the automatic reload in production, if at all possible. However, if you choose to use automatic reload, consider increasing the reload interval. Some application servers set the interval quite low (as low as a few seconds in some cases). Increase the reload interval to several minutes, if possible, to minimize the application server's interaction with the file system, and improve performance.

    Note: Your application server may differentiate between servlet and JSP reload intervals and provide individual settings for servlet and JSP reload settings.



Performance Analysis for Java Web Sites
Performance Analysis for Javaв„ў Websites
ISBN: 0201844540
EAN: 2147483647
Year: 2001
Pages: 126

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net