Designing the Use of JDO in a Web Application


Deciding when to close the persistence manager is the central design issue for using JDO in a Web application. There are at least three possible designs, which in this book are called the one-pm-per-request, one-pm-per-session, and one-pm-per-update designs. In the one-pm-per-request design, the controller opens a persistence manager for each request and closes it after the response is generated. This is the recommended design.

Before discussing the one-pm-per-session design, let's briefly examine the concept of sessions within Java servlets. Servlet containers provide HttpSession objects that are associated with the requests of clients who have recently used the Web application. The controller servlet and the JSPs that generate the response for the request can store objects of interest in the session object, where they can be retrieved on subsequent requests from the same client. In the one-pm-per-session design, the persistence manager is stored in the session object. It is reused whenever a request from the same client is received. In the one-pm-per-session design, the persistence manager is closed when the session invalidates or times out. The section "The One-PM-per-Session Design" later in this chapter examines this design in greater detail.

In the one-pm-per-update design, the controller uses a shared read-only persistence manager for read-only requests and obtains a new persistence manager for each write request. The read-only persistence manager is shared by all of the requests handled by the controller servlet instance. In this design, each update request opens and closes its own persistence manager, but determining when to close the shared read-only persistence manager is complicated. The section "The One-PM-per-Update Design" later in this chapter examines this design in greater detail.

Each design has different implications for the management of the application data objects. When the persistence manager is closed, the application data objects must be either dropped or made transient. As you may recall from Chapter 3, JDO does not define the behavior of managed data objects after their persistence manager is closed.

The Role of Session Serialization in the Servlet Container

The servlet container imposes constraints on the management of application data objects that are stored in the session. The session object and all of the objects stored within it can be serialized between requests. This may happen for a variety of reasons. When the servlet container is shut down, the container may serialize the sessions. When the servlet container comes back up, the previously active sessions are restored. If the servlet container is part of a cluster, then the various containers may pass, by serialization or a similar mechanism, sessions between them to achieve load balancing. Finally, if the servlet container is operating under load, it may serialize some of its sessions to conserve memory. As you know by now, acquiring an application data object through deserialization results in objects that are unmanaged by JDO.

At the same time, any reference to the persistence manager that is stored in the session must be released, since JDO does not require the persistence manager to be serializable. Since the servlet container serializes the session for a good reason, closing the persistence manager is generally a good idea when the session is serialized.

Design Goals

In evaluating the advantages and disadvantages of each of these three designs, there are five design goals that are desirable.

  • Good performance

  • Storage for conversational state

  • Transactional consistency

  • Simplicity of code and design

  • Scalability

Good performance is essential. The load on a Web application tends to grow steadily and spike sharply. Web applications must work with varying amounts of network latency. The user's tolerance tends to be low, usually not more than a few seconds for a page to render. As a result, performance is frequently an issue for Web applications.

When using JDO, caching of persistent state is critical for best performance. If the persistent state can be read from memory rather than from the datastore, time is saved by orders of magnitude. In the case of Web applications, the best performance gain is likely when caching occurs across requests and across clients.

A second factor of much less importance for performance is the amount of garbage generated. When objects in memory become unreferenced because they are no longer used by the application, they become garbage. As garbage, the objects continue to take up memory. The JVM's garbage collector finds the unused objects and returns the memory that they use to the heap, where it can be reused for the construction of new objects. In long-running server applications, the JVM collects virtually all of the garbage generated. If the application continually constructs lots of new objects, then the garbage collector stays busy using valuable CPU resources.

Although HTTP is a stateless protocol, the semantics of HTML often require the use of conversational state. When processing requests to update persistent information, the controller usually needs to know the identity strings of the objects to change. Likewise, it often needs to know the version of the objects when they were viewed. The need for conversational state arises in other ways as well. For example, when a form that contains check boxes is sent to the Web server, the request sends name-value pairs only for the check boxes that are turned on. If the browser received a turned-on check box that the user then turned off, the browser does not send the name-value pair for that check box when the user submits the form. As a result, the only way for the servlet controller to determine that the user turned off a check box is to know that his request did not return a name-value pair for the turned on check box that he received. To make this determination, the servlet controller uses conversational state.

The Java servlet responding to the request can store and find objects in the HttpSession object, using its getAttribute and setAttribute methods. By using the session object, the controller can store in server-side memory the conversational state of each active client. This state can be used when servicing subsequent requests from the same client. This is the way many Web applications store conversational state.

There are good alternatives to storing conversational state in the session object. The conversational state can also be kept in HTML. For example, the identity string of a persistent object can be encoded as a name, ID, or value within the HTML control that represents it. Likewise, the fact that a check box is turned on when sent to the user can be encoded within a hidden control in the form. As another alternative, the controller can also store the conversational state in persistent storage. With this approach, the request sends a key, which may be in a cookie or elsewhere, that the controller then uses to look up the client's conversational state.

A Web application's need for transactions can vary widely. Some applications may not need transactions because they do not perform updates. Some applications may need to store new persistent information but may not need to modify existing information. When the Web application does modify existing information, it may or may not need to verify that the version of the information that the client viewed is no older than the version of the information that the controller modifies. Finally, a Web application may use a transaction for each update request that it receives, or in a wizard-like fashion, it may handle as one transaction a series of update requests received from one client.

Web applications are often built quickly, modified frequently, and stressed considerably. All of these factors argue for simplicity. In addition, simple designs avoid the subtle flaws that can elude the inexperienced and trip up the expert.

Scalability is related to performance in that both address the issue of serving clients in a satisfactory manner. It differs from performance in that its primary concern is the potential degradation of performance due to an increase in the number of clients and the ability to remedy this degradation by adding more hardware to the system. A design that uses fewer resources, such as less memory, less network bandwidth, or less CPU time, is more scalable than a design that uses more of these resources. A design that supports the expansion of these resources, such as clustering of servlet containers, is also more scalable than one that does not.

Although the issues of performance, conversational state, transactions, simplicity, and scalability are discussed in the context of using JDO in a Web application, these issues did not originate with the use of JDO. They are present for any Web application regardless of the persistence mechanisms used. Although the issues are not new, they are relevant when determining how to best use JDO in a Web application.

Avoiding the Cost of Garbage Collection

It is a common practice to construct an object when needed, use it, and then drop it. After the object is dropped, the application code no longer refers to the object, and it becomes garbage. This is an appropriate practice for most coding needs. There are only two ways to avoid this create-and-drop cycle.

The first way recycles the objects. When recycling, the application gets the object from a factory that first looks into a pool of such objects and returns one of them if it has it. Otherwise, the factory creates the object. When the application is finished using the object, instead of dropping it, the application returns the object to the factory, which may put the object into its pool of such objects. In the early versions of the JVM, recycling objects was a good optimization when a class of objects was heavily used in a create-and-drop cycle. In the current versions of the JVM, the garbage collectors and object construction code have become quite efficient. Depending on a variety of factors including the size of the objects, recycling may now be less efficient than the create-and-drop cycle. For that reason, recycling of objects is no longer a design issue, but an optimization issue that should be driven by metrics.

The second way to avoid the create-and-drop cycle is to continue to use the same object without dropping it or recycling it. This practice avoids both the recycling code and the object creation and garbage collection code, but its usefulness is limited. In general, the code path from needing the object to finding it must be short. Otherwise, it may be cheaper to create the object and drop it after its immediate usefulness ends.

In short, the best advice is this: if it makes sense to continue to use the same object, then do it. But if the code must jump through hoops to make continued use possible, then regard continued use as a potential optimization rather than a design constraint. Avoid using object recycling unless you have metrics that can demonstrate its value.




Using and Understanding Java Data Objects
Using and Understanding Java Data Objects
ISBN: 1590590430
EAN: 2147483647
Year: 2005
Pages: 156
Authors: David Ezzio

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