Sun Java Solaris Communities My SDN Account Join SDN
 
Article

Best Practices Part 1: Application Architecture and Development Best Practices

 
Contents
 
1. J2EE Architecture
1.2 Sun ONE Application Server Software
1.3 Sun ONE Application Server Request Flow
2. Best Practices for Application Development
Session Invalidation
 

Following best practices can ensure that your application does not suffer from common development and design mistakes that could affect performance. This paper outlines effective methods to use during the development and deployment phases of an application to improve the application's performance. Some of these guidelines are applicable to Sun ONE Application Server, and others are applicable to programming in the Java programming language and to using the Java2 Platform, Enterprise Edition (J2EE platform) in general. Depending on the application and context, these strategies can be followed in different ways.

 
1. J2EE Architecture

Here, we describe the three main facets of the J2EE architecture: its tiered structure, its software, and its request-handling mechanism.

 
1.1. n-tier System

A typical J2EE application is composed of an n-tier system in which a client obtains processed information from a Web server or an application server. The servers in turn access the information from enterprise systems such as RDBMS or ERP, process them by using contained business logic, and deliver the processed information to the client in an appropriate format. These layers can be designated as client layer (Web browser or rich Java client), middle layer (Web servers and application servers), and the back-end layer or data layer (enterprise systems such as databases). Figure 1 illustrates such an n-tier application:

 
Multitiered System Architecture
1.2 Sun ONE Application Server Software

The Sun ONE Application Server software resides in the middle layer and consists of the following components:

  1. Sun ONE Application Server
  2. Sun ONE Web Server
  3. Sun ONE Directory Server
Sun ONE Application Server

Sun ONE Application Server is part of a multithread environment and supports an application model predicated on J2EE technology-based standards. Sun ONE Application Server provides a comprehensive set of system and application services that support the deployment and execution of highly scalable, reliable business applications.

The following key features of the Sun ONE Application Server platform make it ideal for meeting business and technical requirements:

 
  • Rapid development of business applications: A comprehensive suite of application and system services has been proved by customers to dramatically reduce application development time. Sun ONE Application Server support for industry standards and its multilayered application programming model leverage existing skills of developers and promote code reuse for future applications.

  • Performance: The performance optimizations of Sun ONE Application Server include kernel-level multithreading, multiprocessing, a high-performance data access engine, connection caching and pooling, optimized communication with Web servers, and result-sets caching and streaming.

  • Scalability: With Sun ONE Application Server, application scaling is accomplished in two ways: by addition of more CPUs to a multi-CPU system or by addition of more servers to a group of Sun ONE Application Server installations. Applications can then be deployed to the new servers without modification of the application logic. Furthermore, application tasks can be assigned to the server best able to process the request efficiently, through either application partitioning or dynamic load balancing. Sun ONE Application Server offers the most sophisticated and fault-tolerant load-balancing architecture in the market.

  • High availability: Sun ONE Application Server addresses all potential points of failure for an Internet-based deployment. High-availability features include advanced cluster failover, failure detection, and autorecovery of failed servers and processes. Configurable failure-recovery options, distributed state, and session management ensure that critical state and session data are not lost in the event of a failure, and dynamic application enhancements can update applications without interruption to availability.

Sun ONE Web Server

Sun ONE Web Server receives and processes HTTP and HTTPS requests from a Web browser. In a system based on Sun ONE Application Server, Sun ONE Web Server receives requests from Web clients and routes them to Sun ONE Application Server for processing by means of the Web Connector, an NSAPI plug-in that resides on the Web server. The Web Connector exposes the Sun ONE Application Server functionality through the Web and helps balance and distribute user requests across different instances of Sun ONE Application Server.

Sun ONE Web Server also enforces and performs user authentication through the use of Access Control Lists (ACLs). ACLs are maintained on the Enterprise Server, specifying the URLs that are deemed protected and those that are public. On the basis of the ACLs, Sun ONE Application Server prompts for authentication information, either through basic authentication or certificate-based authentication, and then authenticates the user by a lookup in the user database, which is stored in Sun ONE Directory Server. This authentication is done without the need for any custom programming. Once the user has been authenticated, Sun ONE Application Server processes the requests.

 
Sun ONE Directory Server

Sun ONE Directory Server, the Sun ONE software implementation of the Lightweight Directory Access Protocol (LDAP), is the central repository for user and group information and provides unified management of users, groups, and ACLs across the enterprise. All Sun ONE server products are tightly integrated with Sun ONE Directory Server, which also stores application server configuration data. A single Sun ONE Directory Server can support multiple instances of Sun ONE Application Server, so all administration data for Sun ONE Application Server installations can be centralized in one place.

Since Sun ONE Directory Server is the core of the Sun ONE Server product suite, user information should be migrated into Sun ONE Directory Server, specifically the information related to authentication (user names, passwords, and public certificates). Sun ONE Directory Server exists in addition to the database. Although Sun ONE Directory Server is not a relational database and should not be used as such, it can store user profile and contact information. Developers should consider this capability in the application design and system architecture.

 
1.3 Sun ONE Application Server Request Flow

Requests to Sun ONE Application Server can come from two types or sources:

  1. The Web tier, through HTTP: A Web browser (or thin client) might send an HTTP request to a Web server that is configured with Sun ONE Application Server by means of the Web Connector plug-in. The plug-in redirects the request to an Sun ONE Application Server instance if a Java servlet, a page created with the JavaServer Pages technology (JSP page) or an HTML page it is seeking is contained in the context of an Sun ONE Application Server instance. The request is communicated through a TCP/IP-based protocol known as Kiva Communications Protocol (KCP).

  2. Rich clients through RMI over IIOP: The requests originate from stand-alone Java clients or CORBA clients through RMI or RMI over IIOP. The Sun ONE Application Server instance then executes the necessary code in a servlet, a JSP page, or an Enterprise JavaBeans component model and might fetch or write information to a database in the process.

The result: An HTML page is sent back to the Web-based client through the Web server or is sent to the rich client through RMI over IIOP.

Figure 2 illustrates the request flow inside Sun ONE Application Server:

 
Request Flow in Sun ONE Application Server

Sun ONE Application Server consists of several internal servers, called engines or processes, which take part in the Sun ONE Application Server request flow:

  • KXS: The Executive Server provides system-level services, for example:

    Protocol Management manages communication between Sun ONE Application Server and Sun ONE Web Server and between Sun ONE Application Server and Sun ONE Application Server.

    Request Management redirects requests to appropriate server processes (such as KJS or KCS).

    Load Balancing provides one KXS process for every Sun ONE Application Server instance.

  • KAS: The Administrative Server manages the rest of the system services and also manages failover. Every Sun ONE Application Server instance has one KAS process running at a given time.
  • KJS: The Java Server provides services to Java applications and hosts the J2EE Web Container and EJB Container.
  • KCS: The C++ Server provides services to C++ applications.
  • CXS: The CORBA Executive Server processes requests from rich clients and delegates them to KJS for further processing. More than one CXS process can be running at a given time.

These engines are multithreaded. More than one KJS or KCS process can be running at a given time, and developers can configure the number of required engines. Although the Sun ONE Application Server installation allows up to 32 Java technology-based servers and 32 C++ servers for each Sun ONE Application Server instance, it is advisable to have no more than two Java servers per CPU for performance reasons.

 
2. Best Practices for Application Development

Application design and development is the most important phase in the software engineering cycle. A good design can make an application run several times faster and make it far more scalable than a poorly designed application. Be careful to make your application flexible enough to adapt to future changes in design and to integrate with applications or components. A J2EE application consists of different types of components such as servlets, JSP pages, and components based on EJB architecture (beans), each of which has a different role. Servlets and JSP pages usually reside in the Web container handling the presentation and page logic, whereas beans interact with enterprise systems and process information in accordance with the business logic they contain.

When an application scales, the complexity of it also increases as new design strategies are required. Scaling requires clustering of servers, load balancing, and sharing of resources, which in turn, create new issues.

Here, we consider some of the application design best practices that can be adopted to make applications more scalable and flexible.

 
2.1. Design with Sun ONE Application Framework

Sun ONE Application Framework is a J2EE technology-based development framework meant for enterprise Web application development. It drives a page-centric approach to development and consists of features such as display fields, application events, and component hierarchies. It is based on widely accepted design patterns such as Model-View-Controller (MVC) and Service-to-Workers. The Sun ONE Application Framework is already in use in applications worldwide. Predicated on J2EE technology-based standards, the framework does not depend on application-server-specific features and so applications developed on it are portable. Figure 3 illustrates the framework.

 
Sun ONE Application Framework Layout
2.2. Reuse J2EE Technology-Based Patterns
P>Design patterns are a commonly used set of effective and time-tested solutions to general design problems encountered during the software engineering cycle. Here, we talk about patterns that fall into the object-oriented world and, in particular, the J2EE technology community. Since a J2EE application can be divided into different tiers according to presentation, business logic, and integration with back-end systems, the design patterns are discussed from the standpoint of a tier-based approach.

 
Presentation Tier Patterns
  • Front Controller: This pattern addresses the problem of request handling in the presentation tier. It provides a centralized mechanism to handle requests by means of a controller class, which can be a servlet or a JSP page. The controller can be made responsible for invoking security services, handling errors, or delegating the request for business processing.

  • View Helper: It is a common mistake to mix content generation code with business logic. Because the business logic and presentation logic can change over a period of time, you should separate these two aspects. The solution is to have a View component--which can be a JSP page or a servlet--handle the presentation logic and encapsulate the business logic in a helper class, which can be a bean or a custom tag.

  • Service-to-Worker: This pattern combines the Front Controller and View Helper patterns with a dispatcher component. It provides a centralized mechanism to control access, retrieve content, and manage different views for the same request. It consists of a controller that handles the request and performs various services such as authentication, content retrieval, and validation. It uses helper classes to generate content. A dispatcher manages Views and navigation and can be contained within a controller similar to a Front Controller pattern. The Views contain presentation logic and might use helper classes to generate display information.

  • Dispatcher View: This pattern's structure is similar to that of the Service-to-Worker pattern. The difference lies in the division of labor between its components. The dispatcher in the Service-to-Worker pattern plays a larger role than the one in the Dispatcher View pattern. Here, the content generation is deferred until the time of view processing.

Business Tier Patterns
  • Business Delegate: In a multitiered environment, a method invocation from a client to a server typically happens over the network. A client performing a large number of method invocations will experience poor application performance because of unavoidable bottlenecks in the network layer. With the Business Delegate pattern, several method invocations from the client can be contained within a single method inside a helper class within the server. This role is usually carried out by a session bean. Instead of calling a number of methods across a network, you can make a single call to the session bean from the client. As a result, all the methods are called within the server, thus avoiding the network bottlenecks.

  • Session Facade: This pattern is similar to that of a Business Delegate, but it hides the business logic inside the server and presents a coarse-grained approach to the client through a session bean. The Session Facade is typically applied in the workflow case in which different enterprises are involved in a complex process, and the client is abstracted from this complexity. This solution also removes the tight coupling between the client and the server and makes it easy to change the business logic on the server without significantly affecting the client.

Integration Tier Pattern
  • Data Access Object: The Enterprise JavaBeans components (beans) encapsulate the business logic and contain business-related data. Often this data depends on sources that themselves arise from different sources that might vary over time. For example, the data might be gathered from legacy systems like databases or LDAP servers, which vary in how connections are made and how data is gathered or written to them. The Data Access Object encapsulates data-source-specific information and provides the data client with a consistent and simplified interface across different data sources.

For more information on J2EE design patterns, refer to Core J2EE Patterns, Best Practices and Design Strategies by Deepak Alur, John Crupi, and Dan Malks. (See http://www.phptr.com/corej2eepatterns/.)

 
2.3. Use Servlets and JSP Pages

Servlets and JSP pages afford several areas in which to exercise best practices in J2EE application development. Here, we describe 11 such areas.

 
Model-View-Controller Approach
P>The MVC approach, also known as the Model 2 architecture, implements the classic MVC design pattern for the presentation and content layer of a J2EE application. It involves both JSP pages and servlets, as follows.

 
  1. Any request from the Web browser is delegated to a servlet (controller), which gathers data by using direct calls supporting the JDBC API (JDBC API calls) or through helper classes or beans.

  2. The servlet maps that data to bean objects (models).

  3. The models are stored in the request object or session and passed to a JSP page (View) that contains the presentation layout.

  4. If the JSP page contains an HTML form, the post is again sent to the same or a different servlet, which again delegates the request to other servlets or JSP pages.

The advantage of the MVC approach is that it leads to a clear separation between presentation and content. The disadvantage is its complexity: each request is handled by a JSP page and a servlet. For simple applications, you can program the JSP page to process the entire request and respond to the client without delegating the request to the controller servlet.

 
Base Servlet

For any application, you can provide a base servlet that performs operations such as user authorization, logging, or request redirection. Other servlets can inherit from the base servlet and override the base methods.

 
Object Scope

An object in a servlet or JSP page can exist in different scopes: request, application, session, and page scope. It is a good practice to ensure that the scope of the object be as small as possible without affecting the functionality. By following this practice, you ensure that the object is free for garbage collection once it serves its purpose.

Each type of scope and its usage is described in the following section. In the descriptions, a shopping-cart object is created in a servlet and placed in a certain scope. It is then accessed by a JSP page in which the access method varies according to the type of scope used.

 
  • Session: Place an object in a session scope when you expect the object to exist during the entire session of the client with the server.

      Note: Instead of storing objects in a session, you can store the object's ID. Doing so lowers the overhead of serializing the entire object compared to just its one field. Also, ensure that objects that are no longer needed are removed from the session so that they can be garbage collected.
  • 7Servlet:
      ShoppingCart cart = new ShoppingCart();
      HttpSession session = Httpsession.getSession(true);
      
      	session.setAttribute("cart", cart.getId());
      	

    8JSP:
      <jsp:useBean id="cart" scope="session" class="Cart" />
      	
  • Application: Place those objects that are used by different servlets and JSP pages and that are needed throughout the life of an application in an application scope. Be careful about creating and using these objects: too large a number of them will consume resources such as memory and thus affect the overall performance.
  • 9Servlet:
      Connection conn = new Connection();
      getServletContext().setAttribute("connection",conn);
      	

    10JSP:
      <jsp:useBean id="conn" scope="application" class="Connection" />
      	
  • Request: Place those objects that should only exist for a particular request in a request scope.
  • 11Servlet:
      Invoice cart = new Invoice();
      request.setAttribute("invoice", invoice);
      <redirect to jsp>
      	

    12JSP:
      Invoice cart = (Invoice) request.getAttribute("invoice");
      	
  • Page: Page scope applies to the scope of an object within a JSP page, so place local objects in this scope if they need to be accessed in a JSP tag.
  • 13JSP:
      pageContext.setAttribute("foo",Foo.class,PageContext.PAGE_SCOPE);
      
Session Invalidation

It is highly recommended that user sessions be invalidated explicitly by the application rather than the Web container ending the session after timeout.

 
Instance Variable Usage

Servlets are typically executed in a multithreaded environment, and different users could be executing the same servlet code. A client should not state information in servlet or instance variables of a JSP page. Since multiple users could request processing by the same servlet, the use of instance variables for storing conversational state could lead to information related to one user being overwritten by that of another user.

Store user conversational information in HttpSession, or use stateful session beans that are specifically targeted for this purpose. Temporary storage of user-related information in a servlet should be done in local variables that are scoped within the doGet or doPost methods (or the service method).

These recommendations do not imply that there should be no servlet instance variables. You can use servlet instance variables as long as they act as read-only variables during the request processing operations or do not hold request- or user-specific data.

 
Initialization Operations

To avoid performance overheads, perform expensive operations such as initialization of connections to a resource only once and thereafter let all components use the results. The init()method of the servlet serves as an ideal place to perform intensive operations. The servlet init() method is invoked when servlet instance is loaded and is guaranteed to be invoked once by the servlet container. The init()method is a thread-safe operation. You can place the output of the operations in servlet instance variables so that they can be used as described in "Instance Variable Usage." You should also use the init()method to cache static data that does not change. Otherwise, you will have to create the data in the service()method or doGet()or doPost()methods, thereby creating a redundancy.

 
Synchronization and Thread-Safe Coding

Servlet containers manage a pool of threads to respond to user requests. The same piece of servlet code could be executed in a different thread context by the servlet container while responding to concurrent requests from different users. It is essential that the servlet code be thread-safe so that requests can be correctly processed.

Avoid synchronized methods and blocks of code within a servlet because they drastically reduce the performance and scalability of the application they are part of. Synchronizing parts of code would lead to serializing the execution of user requests.

To work around such scenarios, use servlets that implement the single-thread model. Use these servlets sparingly because the container could create multiple servlet instances, thereby incurring additional object creation overhead. Remember that you have no control over the instance that would be used to handle a particular user request. The number of instances to be created for the single-thread model is specified in the deployment descriptor under Number of Singles; the default value is 10.

 
Code Optimization

The following simple coding techniques can significantly improve the performance of a servlet:

  • String concatenation: Use StringBuffer rather than the + operator to perform concatenations that involve runtime-dependent string variables.

  • Use of print(): Use print()instead of println()because it is called by println() anyway. You can achieve small improvement in performance by reducing a method call, and it is not necessary to introduce line breaks in HTML.

  • Synchronized block: Reduce the amount of code involved in the synchronized block because doing so allows only one thread to be active at a time. Instead of synchronizing an entire method, make the necessary lines thread-safe for a significant performance increase.

  • Content length: Increase content length; doing so results in reduced connections from the client to the server.

Servlet Reloading

Many application servers allow a servlet to be reloaded without restarting the server. This is a useful feature because you can view changes in the servlet code without restarting the server after every code change. You can also configure the reload time for the servlet. For example, setting the reload time for a servlet to 1 second results in the server reloading the servlet class every second. You can, and should, turn this feature off in a production environment to reduce overhead.

 
Usage of JSP Pages
P>JSP pages are excellent presentation-layer components. They should deal with layout and presentation of data that would be sent back to a user as response. JSP pages should not contain Java code that performs business logic operations; rather, any Java code within a JSP page should perform presentation-control operations. The bulk of changes in an application (after the first-cut code freeze) are done in the presentation layer. By having a clear separation between the request handling, control flow, and user response presentation operations, you can minimize and localize the impact of such changes. This practice, coupled with the capability of Sun ONE Application Server to dynamically detect and load changes to JSP pages, can help reduce the deployment and testing period.

Developers who use JSP technology should have a clear understanding of the default value of the different JSP tags. For instance, JSP pages create HttpSessions by default, to enable developers who use JSP technology to access and make use of the implicit session object. In pages and applications in which sessions are not used, this feature can lead to an overhead. Make an explicit declaration in the JSP page directive to reduce this performance overhead.

 
Custom Tag Libraries for JSP Pages

Custom tag libraries can help extend the functionality of JSP pages. They accomplish what would otherwise be achieved by use of Java code within JSP pages. Sun ONE Application Server provides custom tag libraries for performing RDBMS, LDAP, looping, and caching operations.

Tag libraries provide consistency across the presentation layer pages. By using tag libraries, you can change the entire set of presentation pages without modifying each page individually. Tag libraries also eliminate excessive Java code from JSP pages and obviate the widely used practice of copying pieces of code from one page to another.

You can customize and extend some Web page design tools to provide support for custom tag libraries, thereby easing the design of Web pages.

 
2.4. Exploit EJB Component Models

You can follow best practices for three components based on EJB architecture (beans): entity beans, stateful sessions beans, and stateless session beans.

 
Entity Bean Usage

Although entity beans transparently provide persistence and transactional services and are ideal for modeling business objects, they do come with a performance overhead. Entity beans are ideal to use in a transactional process. They are not ideal to use when performing queries to retrieve business objects that satisfy a particular criterion; instead, if no subsequent transactional operations are envisaged, then use a simple JDBC query followed by creation or population of simple beans. Another workaround is to use CachedRowSet objects.

 
Stateful Session Bean Usage

Use a stateful bean to store data that corresponds to the user conversational state. Sun ONE Application Server supports failover of stateful session beans. You can decide at the time of packaging or deploying whether the session bean needs to be marked for failover. Unlike HttpSession with its all-or-nothing choice for failover, session beans allow failover to be decided for each bean.

Since stateful session beans are private to a client, their demand on server resources increases as the number of users using an application rises. Stateful session beans remain in the container until they are either explicitly removed by the client or removed by the container when they time out. The container must also passivate inactive stateful session beans to secondary storage. If the client subsequently accesses the bean, the container is responsible for activating the bean. The passivation and activation processes also impose a performance overhead on the server.

Applications should explicitly remove the beans when they are no longer required, to reduce the overhead on the container (by eliminating the passivation process).

 
Stateless Session Bean Usage

Use a stateless session bean to access data or to perform transactional operations. Because stateless session beans have no affinity with the clients, they afford high scalability since a small number of such beans (typically managed by the bean container in a stateless bean pool) can serve a large number of clients. When a request for a service provided by a stateless session bean is received, the container can dispatch the request to any bean instance in the pool.

 
2.5. Communicate through Java Message Service

Java Message Service is a Java API for accessing message-oriented middleware (MOM) systems. It allows Java clients to communicate with one other through asynchronous messaging whereby the message is carried through a MOM provider such as Sun ONE Message Queue for Java technology (see http://www.iplanet.com).

For more information about Java Message Service, visit http://java.sun.com/jms.

Following are some basic guidelines for improving performance:

Optimize connections: The connection object is used for sessions that create message producers or consumers. Call the Connection.start()method after a producer is set up to enable the flow of messages to the Java Message Service (JMS) technology server. Set up the subscriber before calling this method to reduce the overhead of storing all the durable messages on the JMS technology server.

Process messages concurrently: Java Message Service provides a facility for consuming messages concurrently. You use this facility by creating a ConnectionConsumer that uses a server session pool. Each session in the pool can process a message separately, and each session can be specified to hold a maximum number of messages at any time. The ConnectionConsumer can be used as follows:

  • For queues (all one line):
    • QueueConnection.createConnectionConsumer(javax.jms.Queuejava.lang.String,
         javax.jms.ServerSessionPool, int);
      	

  • For topics (all one line):
    • TopicConnection.createConnectionConsumer(javax.jms.Topic,
          javax.jms.ServerSessionPool, int);
      	

Use sessions in Java Message Service: You can use sessions to create producers and consumers. Sessions can be a QueueSession for a queue or a TopicSession for a topic. You create them from a connection object as follows:

  • For queues (all one line):
    • queueConnection.createQueueSession(boolean transacted, int 
      	acknowledgement_mode);
      	

  • For topics (all one line):
    • topicConnection.createQueueSession(boolean transacted, int 
      	acknowledgement_mode);
      	

In the first argument, transacted denotes whether the session is in a transaction. It is advisable to use separate transactional and nontransactional sessions for sending messages that participate within a transaction and messages that do not. Using a single transactional session for both types of messages results in an overhead on the server and degrades the performance and throughput.

The second argument refers to the acknowledgment mode, which is discarded if the session is transacted. The different acknowledgment modes are as follows:

  • Session.CLIENT_ACKNOWLEDGE. Once a message is sent to the client, the server does not send subsequent messages until an acknowledgment is received from the client. The client acknowledges the message by calling the message's acknowledgment method. Since the client can build up a number of unacknowledged messages, use this feature with caution because it might result in the client exhausting its resources.

  • Session.AUTO_ACKNOWEDGE. The acknowledgment is made automatically after the client's onMessage()method has returned successfully. If an error occurs during delivery of the message or an exception is thrown before the message is acknowledged, the server may attempt redelivery of the message. Since a redelivered message will have its JMSRedelivered flag set to true, the application should always check this flag to avoid duplicating a process. Another approach is to check the message's JMSMessageID, which is unique for each message.

  • Session.DUPS_OK_ACKNOWLEDGE. In this mode, the server can send a message more than once to the same destination. This feature reduces the overhead related with ensuring "only and only once" delivery of messages and increases throughput. On the other hand, this mode might result in increased network traffic because of the additional number of messages sent over the network.

 
Message Delivery Modes

Messages delivered to the JMS technology server can be durable or persistent (DeliveryMode.PERSISTENT) or nondurable or nonpersistent (DeliveryMode.NON_PERSISTENT). When messages are durable, they are stored on the JMS technology server in either a database or a file system, depending on the server implementation. The lifetime of the message can be set when it is sent from the producer to the server. Since persistent messages involve the overhead of the storage and retrieval by the server, use durable messages only when necessary and use nondurable messages otherwise.

 
Message-Driven Beans

A message-driven bean (MDB) is a new type of EJB component model introduced in the EJB 2.0 specification. It models stateless JMS technology consumers. Like any other bean, an MDB has an instance pool at runtime; each instance is a MessageListener, and the bean's JMS-specific properties can be set along with other properties in the bean's deployment descriptor. The MDB does not have a remote or home interface because it is not accessed by a client as is an entity or session bean. MDBs behave like stateless session beans and consume messages asynchronously from a JMS technology server.

 

To write an MDB:

  1. Implement javax.ejb.MessageDrivenBean and javax.jms.MessageListener interfaces.

  2. Provide an implementation for ejbCreate()and ejbRemove().

    You can use the ejbCreate()method to allocate resources such as a database connection or connection factories that might be used by the MDB.

  3. Associate the MDB with its environment by using setMessageDrivenContext().

  4. Implement the business logic of the bean in its onMessage() method.

    The onMessage() method is executed whenever the bean consumes a message.

2.6. Follow General J2EE Technology Best Practices

You can follow best practices for other, more general, J2EE technology operations, as described in the following section.

 
Logging Operations

Applications should make use of common logging service calls. This standard way of performing logging can go a long way toward providing control and flexibility. Avoid using System.out.println or System.err.println calls. These calls are used for debugging and tracing and can be troublesome when you want to migrate code from the development environment to the testing or production environment. The logging calls also slow down the performance because I/O, which is a synchronized operation, needs to be performed.

A simple and effective way for performing logging is to have a boolean variable that can help turn on (or off) logging. All logging calls should be similar to the following:

if (LOG_ENABLED)
{
	log( Log this message );
}

With the boolean variable to control logging, you can instruct the entire component to turn logging on or off simply by modifying the value of one variable. This approach is better than the following approach:

public void log(String message)
{
	if (LOG_ENABLED)
	{
		System.out.println( Log this message );
	}
}

If the preceding mechanism were used to control logging, it would result in the creation of string objects that contain the messages. You can reduce the object creation overhead by placing the boolean control logic at the point of the logging method invocation instead of within the logging method itself.

The ideal way to use logging is to have multilevel logging support. In this case, each logging call also has an associated logging level. The logging call would specify whether the message being logged is a DEBUG, INFO, WARNING, or ERROR message. You can specify the minimum level for logging in the logging operation. You can change this level in the logging configuration file while migrating the application to the testing or production environment, so no code-level changes need to be carried out.

 
Thread Management

User-developed applications should neither create nor spawn threads. Developers usually spawn threads when a lengthy processing operation needs to be performed, and such an operation must not affect the response to user requests. J2EE containers manage their own pool of threads, thereby relieving you of the task of coding such system programming services. These thread pools optimally use server resources and thus make the application servers scalable as the number of user requests increase. Spawning threads within application code would imply that these threads would use and compete with the application server threads for critical server resources.

Use asynchronous processing in cases in which a certain user request would spend an inordinate amount of time processing the request and you want an immediate response or feedback to be sent to the user. You can use Java Message Service to provide asynchronous processing of messages and requests. Sun ONE Application Server supports Java Message Server and provides enhanced services for connection and user management.

 
Object Distribution

In a typical production environment, an application might be deployed in a cluster configuration involving multiple servers. Each server might have several KJS engines running at any given time. For failover and high availability, the session information is distributed and shared among all the application servers in a cluster. Because the serialization and synchronization of session data imposes some overhead, you should minimize the information in the session.

In a nondistributed application, it is normal practice to use static class variables. Static class variables are shared among all the instances of a particular class in a single Java virtual machine. In the case of a distributed environment in which more than one Java virtual machine is present in the same server or across a cluster, the value of the static field may not be the same in each Java virtual machine, so avoid using static fields. You can, however, use the static fields in a distributed application when they are declared final, because their value will not change.

 
Caching of Lookup Objects

Application components obtain objects such as RDBMS DataSource, JMS ConnectionFactory, JMS Queue, and the EJB Home object by performing a Java Naming and Directory Interface (JNDI) API lookup. You can reduce the overhead of looking up these objects by caching and placing the objects in a common accessible place. Lookup operations can check in the common repository before performing the JNDI API lookup. Performance is enhanced because unnecessary JNDI API lookups are avoided.

 
JDBC API Usage

Typically, you place JDBC technology calls in a try/catch block. Your application code should ensure that JDBC technology-related resources are released not only during the normal course of operation but also when exceptions are encountered. As a good programming practice, while performing query operations, close the ResultSet and Statement objects once the operation on the result-set data has been completed. Sun ONE Application Server closes all child objects of a connection when the close method on a connection is invoked for connections obtained from drivers managed by it.

 
Application Constants

Use a separate class or properties file to maintain application-wide, constant data. In the file, place properties such as default date format, default locale, application database lookup name, and the JNDI API lookup name for beans.

In the J2EE platform, applications can also have initialization parameters; place their values in the deployment descriptors. Doing so makes it easy to migrate code from one environment to another because such properties can be changed during the deployment process without the need to modify code.

 
Connection Management

Sun ONE Application Server provides connection pooling for data sources. Connection management service is provided for Sun ONE software-supplied drivers as well as for registered third-party drivers. In your applications, use registered data sources to obtain connections. Avoid using the conventional mechanism of invoking methods on the DriverManager object. Since Sun ONE Application Server provides connection management and a pooling mechanism, you need not build a custom pooling mechanism.

Obtain a connection for application components only when they need to perform a resource-related operation, and place the getConnection method as close to the operation as possible. Similarly, components should invoke close as soon as the desired operations against the resource has been completed. This practice ensures that connections are not unnecessarily held by application components and provides maximum concurrent usage of the limited number of connections to a resource.

 
Database Calls

To improve performance of database queries, use PreparedStatements with bind variables instead of building the complete statement and including the criteria in the WHERE clause.

If the complete SQL statement is generated on the fly, then the database server considers each such statement as a unique statement. The reason is that even though the SELECT and WHERE list are the same, the criteria in the WHERE clause are different. Each statement will have its own parse tree and execution plan. If you use a PreparedStatement with bind variables, the database server binds the values passed in just before execution. This means that statements with different criteria values will be parsed only once, and you can reuse the parsed form of the database statement across multiple requests and multiple clients.

 
SQL Queries

Almost all enterprises use information systems such as relational databases or enterprise legacy systems, and displaying or reporting data queried from such systems is a common activity. At times, the amount of data returned from queries can be huge. Such queries can also lead to intensive computing operations being performed in the database server. Another common problem faced by application developers is displaying query data in response pages. Since only a limited number of query results can be shown in a particular response, either applications need to store or cache the query results so that they can be used for subsequent requests or the queries need to be executed again.

Keep the size of results from online queries low to ensure that server resources can be better used for other purposes. Queries that typically return large amounts of data should be processed by offline reporting tools.

While performing JDBC technology queries, applications should use CachedRowSet objects. These objects can help hold query results without requiring a connection to remain open. Since the methods to loop and navigate a CachedRowSet and ResultSet are similar, the transition can be made smoothly.

 
SQL Updates and Deletes

It is good programming practice to check the number of rows affected when a database update or delete operation is performed. When updating or deleting based on the primary key, be sure to check the number of rows affected to ensure that only one row was affected. Update and delete may not return errors when no rows are affected. The program should handle this case appropriately.

 
2.7. Observe Java Technology Best Practices

You can also follow best practices for I/O, object serialization, and class-loading operations in the Java programming language.

 
I/O Operations

Any application in the Java programming language performs an I/O operation, whether it is for serializing objects, getting input from the user, or writing raw data to an output. I/O operations, if not performed correctly, can slow down an application's performance significantly. One of the main reasons for performance degradation can be failure to buffer a read or write operation. Storage media such as hard disks are inefficient when it comes to reading or writing small pieces of data. By default, most of the I/O streams in Java technology process one byte of data at a time. To increase the performance, always buffer the input or output stream with a filter stream class such as BufferedInputStream or BufferedOutputStream. Buffering significantly speeds up I/O operations.

 
Object Serialization

Objects are serialized to be stored in files or to be sent over a network. Distributed technologies such as RMI depend on object serialization. You make objects serializable by implementing the Serializable interface and writing the objects to an ObjectOutputStream whose destination can be a file or a network. You deserialize serialized objects by reading them from an ObjectInputStream. When an object instance is serialized, its instance fields are also serialized, whether they are primitive data types or Serializable objects. Serialization is a recursive process and can be costly if it involves a large number of objects containing deep references to other objects. Mark as Transient those fields that need not be serialized or that can be manually generated. Doing so reduces the overhead of serialization since not all fields have to be serialized in an object instance.

 
Class Loading

A large application comprises hundreds of classes. Having too many classes loaded into the memory can have a significant impact on the program's memory footprint. One of the problems frequently encountered is eager class loading, described in the next section.

Whenever a compiler encounters a method, it tries before compiling to load all the classes the method references. For a factory method, all the classes it references will be loaded into memory. For example:

public Car getCar(String carType)
{
	if(carType.equals("Sedan"))
		return	new Sedan;
	else if (carType.equals("Convertible"))
		return new Convertible;
	else
		return null;
}

Compiling the previous method results in loading both the Sedan- and Convertible-related classes. To avoid loading all the classes, you can take the following approach, which uses Java Reflection. This approach loads classes only when the method is called:

public Object getCar(String carType)
{	
	if(carType.equals("Sedan"))
		return Class.forName("Sedan").newInstance();
	else if (carType.equals("Convertible"))
		return Class.forName("Convertible").newInstance();
	else
		return null;
}

 
3. References
Books
World Wide Web