Technical Article
Design Patterns for Optimizing the Performance of J2EE Applications
Reprinted from Java Developer's Journal by Vijay S. Ramachandran
December 2001
With the proliferation of J2EE as the platform of choice for server-side applications, the need for sharing developers' experiences and the availability of reusable designs has become crucial.
In this article, we get to know some of the reusable designs that can be used to improve the performance of a J2EE application. For the benefit of those who are not familiar with design patterns, a brief description is given at the beginning before delving into the details. For further details on design patterns, see the reference section at the end of this article.
What Are Design Patterns?
As software developers design and build different applications, we come across the same or similar problem domains. This leads us to find a solution for the same/similar problem everytime, and we end up "reinventing the wheel" again and again. It would be helpful to have a repository that discusses such common problem domains and proven solutions. It would be much better if such a repository discussed the problem domains, along with a solution best suited to solve the problem on hand. Such a solution could be the result of the hands-on experience of the development community.
In the simplest terms, such a common solution is a design pattern and the repository or place of reference that contains such patterns is a design-pattern catalog. A design pattern prescribes a proven solution from experienced hands for a recurring design problem. Apart from describing the problem and prescribing the solution, the pattern will also explain the implementation issues involved and consequences, if any, of using the pattern. These solutions are generic in nature. They are described in the well-defined "Pattern Templates"; the most common one in use is the template defined by the "Gang of Four." The pattern templates usually have a name that offers a good idea as to what that pattern is all about, followed by where the pattern is applicable, the motivation, implementation issues, and other descriptions.
Use of such patterns makes the design of an application transparent. These patterns have been used successfully by developers in their respective work and hence the pros and cons of their use as well as implementation issues are known beforehand. All design patterns are reusable and can be adapted to a particular context; this gives developers flexibility. The use of design patterns related to J2EE applications offer the added advantage of providing solutions for J2EE platform technologies.
Performance-Optimizing Design Patterns For J2EE Applications
This article does not explain the patterns with their formal template and a full-blown code sample. The "J2EE Blueprints" link in the reference section will be the place of reference for those interested in such details. Instead we look into some recurring problem domains that have a tremendous effect on the performance of a J2EE application. We also look into a working solution for each of the problem domains that we discuss, along with some important points to note and small code samples wherever applicable.
Multitiered J2EE applications consist of components communicating across tiers to access/change data. This often leads to remote calls between application clients/JSPs/servlets and EJBs or between EJBs. Such remote calls are costly and affect the performance of the application as a whole. An increase in the number of such remote calls increases network traffic too. Moreover, for all the advantages that EJBs offer, they come with a small price. The following three design patterns suggest good solutions to minimize some of the performance costs in typical J2EE applications.
Fast Lane Reader Problem Domain
Most Web applications need to display a list of data objects to the user. A financial service organization that offers its service over the Web will have to display its catalog of services to the customer who is visiting its Web site. An online banking application will have to display a list of recent transactions of a customer who is checking his or her account. If an organization has an internal Web application that allows its employees to enroll for benefits over its intranet, the application should display a list of benefits available to an enquiring employee.
Let's start with the financial organization's scenario. To display the catalog of services, typical designs might have an EJB that represents the whole catalog. There might be a CatalogEJB that implements all required business logic related to the catalog, such as adding a new service, removing an existing service or giving a list of service, details to the requesting components. In the case of other components requesting a list of service details, the requesting component will have to get a reference to the CatalogEJB, access the EJB's remote method that gets the list of services, and then displays.
This implementation method, while providing transactional access for the required list of objects, comes with the price of the overheads and remote calls associated with the use of the EJB. Given that the list of services is not going to change every second, the chances of the customer seeing stale data are low. So do we have to choose transactional access through the EJB and pay the associated price? Extending the same argument to the other examples, given that the bank customer's list of transactions does not change every second, do we have to have transactional access for listing the transactions? And given that the list of benefits given by the employer does not change every day, do we need transactional access to display the list of benefits to enquiring employees?
Suggested Pattern
The main issue to be addressed here is whether we should choose transactional access while reading a set of data that does not change rapidly. In such scenarios, the suggested pattern would be the Fast Lane Reader. Note that the problem domain that was described requires only read access to a set of data that does not change rapidly. We can opt to take the fast lane and read the data directly rather than opting for transactional access through EJBs. In the scenarios described in the previous section, the Fast Lane Reader will be a more efficient way to access data. Such a technique will also give a faster response to the customer while running a very small risk of displaying stale data. By bypassing the EJB the overheads associated with remote calls, transaction management, and other issues are avoided.
Again taking the financial services scenario and the sample code explained in Listing 1, one way to implement this pattern would be to have a separate object, the Data Access Object, which encapsulates all data read and update functionality of the CatalogEJB .
A servlet client that uses the Fast Lane Reader pattern will use the Data Access Object directly to read the list of services and display the list as shown in Listing 3.
Since we now have a Data Access Object that encapsulates all access to the catalog, the business methods of the CatalogEJB (like updateServices, addServices, etc.) can use the corresponding methods in Data Access Object to avoid duplication of code.
Comparing Listings 1 and 3, we can see how the servlet bypasses the EJB by accessing the Data Access Object directly to get the list of services from the Catalog. This method of implementation, while allowing the client to use the fast lane for direct access of data, will also allow the EJBs to provide transactional updates to the same data. Note that encapsulating all types of data access will also enable the EJB to use the same Data Access Object for updating the same table. This has the added advantage of avoiding code duplication.
Points to Note
- This pattern is good for faster and more efficient data retrieval only.
- There is a risk of the data being stale and hence this pattern should not be used when the data being accessed changes rapidly or when the tolerance for slightly stale data is very small.
- Because this pattern bypasses the EJB model and provides direct access for persistent data, the design of the application may become complex for large-scale applications.
- As the pattern name suggests, this pattern is ideal for data read. For updating the data, this pattern should not be used as transactional access is bypassed.
Page-by-Page Iterator Problem Domain
Now that we have seen an efficient way to access read-only data (that does not change rapidly), how much of such data should we read? To put it in a more general way, when we have to access a large remote list of objects, do we transfer the entire list or only part of it? The answer to this question will have a tremendous performance effect on the application because transfer of large lists means extra load on the network. Moreover, if EJBs were used to read such large lists, the performance cost is even more because of the costs introduced by the additional services provided by the EJB model. To take a concrete example, let us revisit the financial services example that we saw in the previous pattern. Let us assume that the list of services is large. We already saw how we can use the Fast Lane Reader pattern to avoid the overheads associated with EJBs. But, since the list is large, do we transfer the entire list to the requesting client every time? Do we have to stress the network every time a client requests the list of services? If we transfer the entire list every time, do all clients have the capacity to handle such large lists?
Suggested Pattern
In finding a common and efficient solution, we should note that the user may not be interested in looking at the entire list. The transfer of large lists means more time, resulting in bad user experience. Moreover, the requesting client may have a small amount of memory or a small display; hence, such a client may not be able to handle such large lists. Taking these into account, the suggested pattern would be the Page-by-Page Iterator pattern. The use of this pattern will enable the client to request the list and also specify its size. In response, the server will return a list of the requested size only.
Going back to the example we saw in the previous pattern, implementation of this example will require the getServiceNames() method of Data Access Object to take two arguments, namely the size of the list and the starting index. With these, it will create a collection of the required number of objects and return it as part of a "Page-by-Page Iterator." Listing 4 shows sample code for the Page-by-Page Iterator pattern. The information on the starting index and list size is passed to the iterator by the invoking client component.
The Data Access Object will have to be modified a little to make use of the Page-by-Page Iterator pattern while returning the list of services.
The code for other methods, except getServiceNames(), is the same as that in Listing 2. We see the steps that will be done in the getServiceNames() method instead of going into detailed code. These changes will allow the client who uses the Fast Lane Reader pattern and the Page-by-Page Iterator pattern to get the list of services of the required size. The arguments are used while deciding how many objects to return as part of the collection. And the servlet client that gets and displays the list would look like Listing 6.
Points to Note
- Clients can specify the list size so they control the amount of data they want to receive.
- While this pattern provides efficient access to large server-side lists, the number of server hits might increase a bit as not all data is transferred at once.
- The iterator does not keep its own copy of the list being traversed. As a consequence, insertions or removals will interfere with the traversal. But this may not be a problem if the client browses static data collections like list of services or search results.
- Network bandwidth is not wasted by transmitting unused data. This has to be weighed against the potential increase in number of server hits while deciding whether to use this pattern.
Value Object Problem Domain
So far we have seen two patterns that could improve the efficiency and performance of our J2EE application when large amounts of objects are being transferred between clients and server. All will be well if the actual objects that are being transferred have no attributes. But more often than not, the data being transferred are collections of objects that have their own identity. In the financial services sample application that we have been discussing, the list of services being returned from the server are actually collections of objects. Each of these services will have its own attributes, such as Service Name, Service Cost, Service Description, and Terms and Conditions. And, more often than not, a client requesting the list of services will also want to know about all the above mentioned information that belongs to a service. Given these circumstances, when do we transfer these finer details? It is definitely not efficient for the client to get a list of service names and then contact the server to get the finer details of each service name received. That would entail lots of remote calls and additional stress on the network.
Suggested Pattern
While coming up with a common solution to this scenario, we have to keep in mind that:
- The probability of the client asking for the finer details of a service is much more than the client asking for the complete list of services.
- All the attributes associated with the requested object will most probably be in the same database table and hence can be obtained in the same SQL call that gets the list of service names.
The suggested solution would be that we transfer coarse-grained data (in this example, the list of service names along with all its attributes) rather than transferring fine-grained data (e.g., requiring the client to make separate remote calls for each of the attributes, such as service name, cost, and terms ). We can do this by using the Value Object pattern. Use of this pattern in our sample case will result in the ServiceDataAccessObject aggregating each service and all its attributes into one instance of a Value Object, say ServiceInformation. Then a collection of these Value Objects is serialized and sent over the wire to the client where it will be deserialized and used.
To see some code examples, let us first define the ServiceInformation Value Object.
Now for the rest of the code: the iterator pattern will be the same as that in Listing 4, without any change. The data access object will also be the same as in Listing 5. The Data Access Object will set the iterator's collection as a set of value objects and the servlet will retrieve the attribute information from value objects and use it for display. The code of the servlet client that gets the value objects and displays the values would look like Listing 8.
Points to Note
- Use of this pattern reduces network traffic and improves response time for coarse-grained, read-only data.
- To further improve perfomance, the value objects can be cached.
- Use of this pattern also reduces the hits on server as the number of remote calls made by clients is reduced.
- The extra classes that represent various objects and attributes as Value Objects may add to complexity but this is a small price to pay when we consider the advantages of using this pattern.
- The Value Objects must be serializable and immutable. If clients have the capability to modify the Value Objects, then the state of the objects with the client and that with the server differ, leading to inaccurate states. But the consequence of making the Value Objects immutable is that, if the data changes rapidly, the values in the Value Object might be stale.
- Although, in the sample code, we saw the Value Objects being used for transferring information from server to client(s), this pattern can be used for data transfer in the reverse direction also.
Conclusion
In this article we saw three different patterns that we can use effectively to improve the efficiency, performance, and user experience of a J2EE application. The samples we saw to exemplify the use of the patterns seems to combine use of all three. But we must understand that there is no need to use all these three patterns together. For example:
- We can use the Value Object to send a single coarse-grained object (like contact information of a customer) from the server to the client(s).
- We can use the Page-by-Page Iterator to send lists of simple objects from EJBs to clients.
- We can use the Fast Lane Reader to read data from the server and display all of them in one shot.
The sample code we saw was also incomplete. The intention was just to give you an idea of what we are talking about. The Java Pet Store sample application and the patterns catalog of the J2EE Blueprints program provide formal definitions of the patterns we discussed along with full-blown, working sample code, UML diagrams, and other helpful information. Interested readers may find more detailed information on the Web site, link. The next version of Enterprise JavaBeans (EJB 2.0) removes some of the overheads associated with remote calls by introducing the concept of "local interfaces." In a future article, we can discuss the effect of such a feature on these patterns.
References
- Design Patterns section of the J2EE Blueprints Program.
- Buschmann, F., Meunier, R., Rohnert, H., Sommerlad, P., and Stal, M. (1996). Pattern-Oriented Software Architecture: A System of Patterns. John Wiley & Sons Ltd.
- Gamma, E., Helm, R., Johnson, R., and Vlissides, J. (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley.
About the Author
Vijay Ramachandran is a member of the J2EE Blueprints team for Sun Microsystems, contributed to the recent Java Pet Store sample currently focusing on the Web services features of forthcoming J2EE releases.