Back

J2EE-Based Architecture and Design – The Best Practices

 

Ramanand Singh

 

Abstract

The Java 2, Enterprise Edition (J2EE) provides a uniform platform for building  robust, scalable, multi-user secure, distributed, server centric applications. J2EE is huge and it spawns a multitude of concepts. The widespread adoption of J2EE technologies has provided the development community with open standards on which to build service-based architecture for the enterprise. However, learning J2EE technologies is too often confused with learning to design with J2EE technologies. An effort has been made to distinguish the two. This article  discusses the items that  J2EE architects and designers  need to understand for a useful, scalable, and flexible architecture and design – bad practices, best practices, common recurring problems and their solutions. Good designs are discovered from experience.

 

1     Introduction

Software engineering is an evolving process. Like any other industries, working in software engineering industry helps master the tricks and tips of the trade. This document presents a discussion of practices of building distributed applications using J2EE platform. These practices are based on personal experiences from working on several applications using J2EE platform, as well as a collection of ideas and experiments by J2EE development community.

 


There are no silver bullets for the perils of architecting software systems. J2EE alone won’t address all the architecture and design needs. The principal goal of J2EE is to specify a platform – one that can be used to build distributed, object-oriented, platform independent, vendor neutral, enterprise applications based on an open standards.

 

This article starts with introducing technologies involved in and advantages of using J2EE. The article, then, goes on to list some of the bad practices J2EE architects and designers usually apply in their system. The idea here is to make software designers aware of such practices so that they can avoid them in their next J2EE-based software system architecture and design. The main focus of this article is the discussion of best practices for J2EE-based architecture and design. The hope is that a software architecture and design can significantly be improved following these practices. This can also increase the development productivity of the team. An example software architecture and design is presented for reference purposes. Finally, the article lists some related topics which should be tackled in future.

 

2     Advantages and Technologies of J2EE

The Java 2, Enterprise Edition is a complete platform for developing distributed, multi-tier, secure, transactional enterprise software applications. The goals of J2EE include better quality, maintainability, and portability of systems and increased productivity and economic return for businesses.

 

J2EE is based on the component usage model. It provides complete services for components and automatic handling of some application behaviors (such as declarative transactions). The promise of the J2EE standard is that third-party suppliers (internal teams as well as external vendors) will be able to market quality components that businesses can buy and use to build systems faster and more cost effectively than if they had to build their own infrastructure.

 

2.1   Advantages of J2EE Platform

The J2EE platform offers numerous advantages in the development of multi-tier distributed enterprise application. Some of these advantages are listed below:

 

2.2   J2EE Technologies

The technologies included in, as well as supporting, the  Java 2, Enterprise Edition is shown in Figure 1; they include the following:

 

Figure 1. Technologies in Java 2, Enterprise Edition

 


3      Bad Practices

Good practices are almost always the results of enhancements in not so good practices available in any system. It is, thus, very logical to point out some of the sub-standard practices of system design before embarking on the best practices. Any design practice or activity that creates a less than optimal solutions in the given conditions is referred to as a bad practice. A list of potential bad practices is presented in this section.

 

3.1    Exposition of Presentation Tier Data Structures

Sharing presentation tier data structures (e.g., HttpServletRequest, etc.) with any other tier or components (e.g., Domain Objects, etc.) increases coupling between these tiers and/or components, dramatically reducing the reusability of available services of a tier or component. If a method signature in a business service accepts a presentation tier specific parameter (such as HttpServletRequest), the complexity in business tier code will increase resulting in a tighter coupling between the tiers.

 

3.2    Security risks: Exposing Sensitive Resources to Client

If specific configuration files, property files, JSPs, and class files are not secured appropriately, then its client may inadvertently or maliciously retrieve such sensitive information. This may breach the security protocol established in the enterprise.

 

3.3    Allowing Duplicate Form Submissions

An enterprise application has very limited control over browser based clients. A user might submit a transactional form. If after receiving a confirmation page, the user clicks the Back button on the browser, then the same form can be resubmitted. If the application has no way of verifying that,  the same transaction may be repeated. This may result in a consumption of system resources without serving any useful purpose, in addition to creating a possible confusion to users.

 

3.4    Duplication of control code

Custom tag helpers may be included at the top of a JSP View to perform access control and other checks. If a large number of views include similar helper references, maintaining this code may become prohibitively expensive, since changes must be made in multiple places.

 

3.5    Fine –Grained Entity Beans

One  of the common practices in EJB design is to map the object model directly into entity beans, resulting in a large number of fine-grained entity beans. Another common practice in EJB design is to model each row of a relational database table as an entity bean. Such mapping also implements inter-table (i.e., primary/foreign key) relationships. More beans directly translate into more container and network overheads. Such mapping also transforms object relationships into inter-entity bean relationships, introducing a severe performance implications.

 

3.6    Making non-transaction services REQUIRED.

Developers sometimes put TRANSACTION_REQUIRED on everything, including obviously non-transactional, asynchronous things like sending email. Don't tie up the transaction pool with spurious transaction requirements.

 

3.7    Embedding the SQL code within the enterprise bean code

Enterprise beans using a relational data store with bean-managed persistence (BMP) often use SQL to manipulate bean instance state. Placing the SQL directly within the enterprise bean code can make the bean dependent on a particular vendor's SQL implementation. Instead of using SQL directly in enterprise bean methods, use Data Access Object class to abstract persistence operations (see Data Access Objects).

 

3.8    Unnecessary remote calls

Remote calls are expensive in network bandwidth, server load, and latency. Always be sure that every server round-trip is absolutely necessary. In particular, try to eliminate iterating server-side collections; instead, try to create a generalized business method that does whatever that iteration is trying to accomplish - even if it's just positioning a cursor.

 

For example, instead of doing this:

 

        for (i = 0, remoteObj.first(); i < firstItem && remoteObj.valid(); i++) {

            remoteObj.next();

        }

 

try this:

 

        remoteObj.seek(firstItem);

 

The iteration is still there, it is just within the seek() method on the server, instead of being on the client.

 


 

4      Best Practices

Best practices evolve from repeating the similar effort over and over, perfecting the way to design and develop a particular item in software development. Realizations of a number of  best practices in J2EE platform are based on using appropriate design patterns – both general design patterns and J2EE patterns. General design patterns have been there for a while and most of the designers and developers are familiar with such patterns. Those needing a comprehensive reference guide for general design patterns should consult the book by the same name [3]. A comprehensive resource for patterns related to J2EE-based application has been published by Sun Microsystems [1].

 

Patterns improve system design and productivity by leveraging a proven solution, providing a common vocabulary, and constraining solution space. They also provide a powerful mechanism for reuse, helping developers and architects to avoid reinventing the wheel.

 

Patterns help software designers to leverage and duplicate successful designs, as well as to convey a common vocabulary and format to other software designers and developers. Once the designers start using patterns as a communication tool, developers can build their vocabulary by learning and understanding patterns.

 

Patterns introduce design constraints or boundaries within a solution space to which design and implementation can be applied. This, however, does not stifle creativity. Instead, patterns describe a structure or shape at some level of abstraction. Designers and developers still have many options open to them for implementing the patterns within these boundaries.

 

This section presents a discussion of the best practices for J2EE platform-based application design and development. These practices are based on personal experiences on numerous distributed application projects. Although, patterns provide a number of advantages mentioned above, some of the best practices are based on non-pattern principles as well.

4.1    Abstraction and Modularity

A general strategy for achieving software scalability and flexibility is to build an abstraction layer in front of the component that you want to be scalable or replaceable, and to use the layer to hide the component from the rest of the application. This insulates the application from modifications in the component and provides modularity in the application design. Modularity helps achieve better separation of developer roles, such as web developers, business logic developer, etc.

 

The abstractions provided by J2EE are essential in dealing with the complex and fast-paced development tasks of e-commerce and other enterprise applications. Without the shielding that J2EE offers developers from the underlying technology, it would be a monumental task to develop, debug, and maintain such applications. The aim of shielding the developers from the underlying distribution and other system-level issues has been one of the goals of J2EE.

 

Designers must focus on achieving abstraction – both provided by the J2EE components (such as entity EJB) and application components for a given application.

 

The topics discussed here help achieve both abstraction and modularity.

 

4.1.1        Localize Disparate Logics

Business logic and presentation formatting must not be intermingled. Business logic can be extracted into one or more helper classes. This will help create a cleaner abstraction, increase cohesion, and reduce coupling, which improves modularity and reusability

 

Steps for this task:

 

The Service To Worker pattern will control flow of execution and access to business data, from which it creates presentation content.  The diagram in Figure 2 illustrates the flow of event for such separation. This design pattern is a combination of other design patterns – Front Controller, Dispatcher, View Helper. The localization of business logic is done into Helper component

Figure 2 Patterns for Logic Localization

 

and the controller component use Helper component to extract the data through its logic and use Dispatcher component to delegate further responsibilities.  

 

4.2    Decoupling

Decoupling increases reusability of components. It allows a business to quickly compose and deploy solutions based on reusable components from the lowest cost provider, no matter whether it is internal or external. Further, such solutions can change the target and even the nature of interactions based on changing business conditions. Thus a business can leverage flexible and dynamic business models, and through just-in-time integration. As a result, dynamic businesses can maximize their reach to customers, partners, suppliers, and marketplaces, and minimize their costs and time to market.

 

As an application evolves, its size increases. There will be more and more components added to the system with increasing functionalities. This results in a muddle of interfaces that applications have to deal with. That is, various parts of an application become highly coupled making maintenance highly difficult. However, keeping the interfaces coarse can limit the number of interfaces, and thus reduce the coupling.

 

A clean component decoupling is required for partitioning team for maximum parallel development. One group or team does not need to be in constant communications with others to perform its design and development work. Decoupling can be achieved through techniques presented in this subsection.

 

4.2.1  Minimize Duplication of Control Logic

Control logic may be scattered throughout the application, typically duplicated (e.g., in JSP views).  Such control logic must be extracted into one or more controller classes serving as the initial contact point for handling client requests. This will improve the modularity, reusability, and maintainability of the application.

 

Following techniques can be applied to perform this task:

 

 

4.2.2        Separate Tier-Specific Implementations

Implementation details specific to one tier should not be introduced in another tier. The service API exposed by the business tier to the presentation tier will likely be used by other clients as well. If the service API accepts parameters with some composite types, then every client to the service is forced to package its data in that composite data type. This drastically reduces the service component’s reusability.

 

Such separation can be accomplished with the following techniques:

4.2.3        Use Business Delegate Pattern for Decoupling Tiers and Hiding Implementation Details

Session beans are used as facades to entity beans. They provide coarse-grained interfaces to business services. Exposing the session bean to the application client increases the prevalence of session bean calls throughout the client code, creating a tight coupling between the application code and the session bean. The clients are also exposed to service level exceptions. Variation in client type increases this effect even greater, since the same session bean is being used by various different clients.

 

Business delegates are plain Java classes that encapsulate the business tier details and intercept service level exceptions on behalf of the client. The functioning and advantages of Business delegates, when used in the J2EE-based application  design (as illustrated in an example of Figure 3), are listed below:

 

Figure 3 Use of Business Delegate

 

4.3    Monitor Requests

Control of an incoming requests is often desired. It is desirable either in the case of duplicate requests or permission based requests from client. The duplicate requests can be monitored using Synchronizer Token pattern while permission based requests can be monitored by hiding resources from a client.

4.3.1  Duplicate Requests

Clients can make duplicate resource requests or access certain views out of order, causing the application to spend additional resources without any gain in its serviceability. One of the examples of such duplicate requests is when the user clicks the Back  or Stop button (in a browser based client) and resubmits a form.

 

Solution Techniques:

The synchronized token for this purpose works in the following manner:

/**

* Save a new transaction token in user’s current session,

* creating a new session if necessary.

* @param request The servlet request to be processed.

*/

Protected void saveToken(HttpServletRequest request)

{

   HttpSession session = request.getSession();

   String token = generateToken(request);

   If (token != null) {

      Session.setAttribute(TRANSACTION_TOKEN_KEY, token);

   }

}

 

4.3.2        Restricted Requests

Resources can be protected by hiding them from client direct access. Access of these resources should be controlled through the use of the controller in association of helper classes. This will simplify the controller class and it will provide a generic mechanism through which access control can be designed for a specific control without changing the overall design of the controller class.

 

When such client request arrives to the controller component, it delegates such requests to helper classes to perform access control check to see whether the resource should be served.

 

4.4    Persistence Independence

Make the application and/or infrastructure architecture sturdy enough to withstand minor persistence format changes as well as data store migration, and to avoid vendor lock-in. One way to achieve this would be to use abstraction, as described in Section 4.1    Abstraction and Modularity.  Use a data access layer between the application and the data store components. If the application wants to perform a persistence operation, it must go through the data access layer – it is not allowed to access the data store components directly. The effect is to limit the damage that data store changes can do to the application.

 

This data access layer is comprised of a set of classes called abstract data access objects, or abstract DAOs.  The abstraction can be achieved either through interfaces or abstract classes, depending upon whether the DAOs share base functionality. The dual nature of the data access layer – its ability to abstract away data store details while remaining adaptable to particular data stores – makes the design of the data access layer a bit complicated. If your API makes too many general assumptions about the data store, you may not be able to accommodate necessary data store changes. However, if the API is too general, then it becomes harder to be used by clients prepared to handle the special assumptions.

 

Figure 4 DAO Pattern

 

4.4.1  Working Mechanism of DAO

Relationships between different components of the DAO pattern are illustrated in Figure 4. Working principles of DAO pattern are listed below:

 

 

Figure 5 DAO Factory Design Pattern

 

4.4.2        Advantages of DAO

 

4.5       Use Session Bean as Façade to Entity Beans

The Session Bean implements business objects that hold client-specific business logic, business rules, and workflow. It provides business service interface from one or more entity beans to the clients. A session bean is a logical extension of the client program that runs on the server and contains information specific to the client.

 

Session beans should be consolidated together for improved performance and reduced number of objects in the business logic layer of the application architecture. Consolidated session beans behave as facades to entity beans, as shown in Figure 6.

 

Figure 6 Merging of Session Beans as Facades

 

With one-to-one mapping of a session bean to an entity bean, as shown in Figure 6A, the client has to interact with each session bean fronting the entity bean. Since the session bean, in this case, is essentially a proxy to the entity bean, it is similar to exposing the entity beans directly to the client. Merging of session beans and treating the merged session beans as facades, as shown in Figure 6B, provides the coarse-grained business service interface to the client.

 

Session Façade is a pattern and can be implemented as illustrated in Figure 7. The Session Façade abstracts the underlying business object interactions and provides a service layer that exposes only the required interfaces. It manages the interactions between the business data and the business service objects participating in the workflow. It also manages the life-cycle of these participants by creating, locating, altering, and deleting them as required by the work flow.

 

The Client represents the client of the Session Façade, which needs access to the business service. This client can either be a business delegate in the client tier or a session bean in the business tier. The Session Façade (SessionFacadeBean) in represented as a session bean, which manages the relationships between various Business Objects and provides a higher level abstraction to the client.

 

Figure 7 Session Facade Implementation

 

The BusinessObject is a role object that facilitates applying different strategeies, such as session beans, entity beans, and DAO.  If the BusinessObject represents a session bean, a decision has to be taken whether the façade session bean is a stateless or stateful. This decision is based on the business process that Session Façade is modeling.

 

Representing the BusinessObject by an entity bean is another choice for the Session Façade. The Session  Façade can wrap multiple entity beans and provide a coarse-grained method to perform the required business function. Yet another way of modeling is to use one or more DAOs to represent the business data.

 

A Session Façade provides the following advantages:

 

4.6       Decide on Component granularity

With component developers in the J2EE world, granularity is one of the first and the most important design questions. Granularity is how clients perceive components. In the case of components representing data (such as entity beans), granularity relates to whether clients see the data as large chunks or as simple attributes. In the case of components representing business logic (such as session beans), it relates to whether clients see complex business processes or simple operations. In both cases, if answer points to large data or complex logic, such a component is said to be coarse-grained. If the client only sees small data or simple logic, it is said to be fine-grained.

 

The answers of component granularity questions may affect the overall application complexity, maintainability, and performance. Nonetheless, it is important to consider the question of granularity during the design time. There are certain advantages of adhering to coarse-grained interfaces. These are decoupling and performance.

 

4.6.1        Decoupling

The first benefit, decoupling, is described in Section 4.2    Decoupling.

 

4.6.2        Performance

The second benefit is to do with performance. With a number of fine-grained interfaces, clients (external clients or components internal to the application) will have to deal with a number of remote interfaces. Depending on the deployment model, most of the method calls on these interfaces involve the following tasks:

Each of these tasks is performance intensive, and consumes both CPU and network resources. This can be reduced by getting the functionality executed in larger chunks - that is, maintaining coarse interfaces. The design trick is to remember that we are dealing with distributed components and not objects. That is, to consider the fact that a client method reaches the bean via a proxy on the server, reaches another proxy (or an adapter) object on the server side, and then finally the bean instance. In this journey, the data gets packed into formats suitable for network transfer (marshalling) and gets unpacked (unmarshalling) into Java types on the  server side. In addition, the container intervenes to check whether it has to start transactions or make the method call participate in existing method calls, or if the caller has the required role to invoke the method, and if so, takes appropriate actions.

 

Many J2EE application servers optimize remote calls when the beans/clients are co-located. For instance, when BeanA makes a call to BeanB, and when instances of both beans are within the same runtime processes (JVM), the container can make direct method calls on BeanB. However, even in such cases, all the above steps (except marshalling, unmarshalling, and the network access) happen. Although this considerably improves performance, there will still be an overhead, as the container will have to interpose its services for transactions and security.

 

Although an object view of distributed applications helps abstract the business domain concepts well, a rigorous application of object-oriented view would lead to several fine-grained abstractions, leading to fine-grained interfaces.

 

4.6.3        Component interfaces

An important consideration related to component granularity is the size or type of an interface. In any distributed component technology, the contract between a client and the component's implementations is specified through an interface. An interface is nothing but a collection of methods.

 

4.7       Performance-Tune Entity Beans

Improper use of entity beans can create performance issues.  Use the following tips to improve entity bean performance:

 

It is important to note that for this to work, one needs to have the freedom to define the data schema, a freedom that will not be available working with a legacy data schema. One also may find that (s)he is not allowed to define the data model, since another group of the organization handles that. This approach often proves to be a disaster, resulting in poor performance and significant rework later in the project.

 

By performance-tuning your entity beans in this way, it opens up the possibility to create fine-grained entity beans that model a single row in the database, as well as coarse-grained entity beans that model a complex set of data spanning multiple tables.

 

4.8       Transaction Management

Use short transactions for better scalability. Whether programmatic (bean-managed) or declarative (container-managed), J2EE transactions are short-lived in most scenarios.

 

Consider, for instance, Web-driven clients invoking methods on layers of EJBs. Irrespective of whether you use BMP or CMP transactions, the duration of a transaction cannot be more than that of the HTTP request-response process. This is to do with the general method of implementing transactions. J2EE-based application server containers manage transactions by associating the current service thread with the transaction context. A service thread is the thread started by the Web or EJB container on the receipt of a request from a client outside the J2EE platform (such as a HTTP request from a browser, or a request from a remote client application).

 

In the case of Web-based invocation of EJBs, the service thread starts when the container receives the HTTP request. This HTTP request ends with completely sending a response back to the client (browser) making the HTTP request. Since the service thread does not exist after this stage, the transaction context cannot be managed by the container and transactions cannot span across multiple HTTP requests. This implies that you cannot maintain transactions across business processes that involve multiple requests and responses from the client.

 

4.8.1        Reduce Database Contention

Short transactions reduce database contention. With short transactions, data is locked for very short intervals of time, and the database is not required to maintain the transaction logs for long intervals. Second, with Web-based interaction, long transactions are disastrous, as there is no guarantee that the client would ever make the next step in a given business process. Holding the database locks for long  could affect the performance drastically. With short transactions, you will have to design your applications such that the integrity of the business processes is maintained.

 

Consider a business process requiring the user to perform four steps in four separate HTTP requests. Since we're dealing with short transactions, you can start a transaction at the beginning of the first request and end it at the end of the last request. You can either have one transaction during the fourth request to commit all the data collected/processed during the first three requests, or four transactions with each transaction committing parts of data in the database. This decision raises two issues:

 

 

In order to avoid both of the above problems, consider implementing the business process such that the data committed to the database does not depend on the following HTTP requests during the course of any HTTP request-to-response. This guarantees the scalability and consistency.

 

4.8.2        Protect Against Stale States

In case your application is susceptible to stale state, you will have to devise custom solutions to make sure that you alert the user that the data has been updated in the database. During the time the data is first read and later updated, another application or component might have updated or deleted the same data. This is possible because there is no transaction protecting the data during this interval. This leaves the application and user with stale state that may not correspond to the data in the database. Once your application commits such stale state, the database integrity will be compromised because your application did not preserve the updates made by another application. Note that this problem is common to all applications that rely on information in memory, either via long-term or short-term caching. Although the database is consistent from the database point of view, from the point of view of the applications using the data, this is not the case.

 

Caching is implicit, in such situations,  as the client-tier maintains the state for a short while. One simple solution is to make a copy of the original data in memory, and just before the update, check to see if this in-memory copy matches the data in the database. Note that there are performance implications with any such approach - but at least the data will not be modified inadvertently and inconsistently.

 

4.8.3        Avoid Transaction Distribution

In J2EE, transactions are often distributed, and distributed transactions have their overheads. Distributed transactions occur in the following two scenarios:

 

 

The commit operation for such a distributed transaction would involve the two-phase commit protocol, supported by some J2EE platforms. In order to coordinate the actions performed by several database connections or databases, the two-phase commit protocol allows a transaction manager to query all database transactions involved if each of these actions can be committed permanently, and if so, to request for commit. In other words, this is a means of coordinating several otherwise independent transactions (performed across each single connection) into a single transaction. Note that the two-phase commit operation is expensive to do, as it would involve polling across different resource managers before committing the transaction.

 

Of the above two scenarios, the first scenario can be avoided completely by not accessing multiple database systems within a single service thread. However, the second scenario may not be avoidable. In general, any good container implementation would attempt to avoid this.

 

4.9       Use CMP Instead of BMP

Use container managed persistence (CMP) instead of bean-managed persistence (BMP) in EJB transactions. Here is why:

 

With CMP, the container can reduce the N+1 database calls problem into a single call, by performing one SELECT statement.  You'll typically set this up using container-specific flags (which do not affect bean portability). 

Database independence is very important for those who are providing beans to others.  Often times, those beans must be able to work with whatever target database the customer has. 

 

4.10   Use Appropriate EJB Security Model

EJB provides a declarative security support as well as a simple programmatic interface for explicit security checks within the bean code. In practice, EJB security settings need to be considered within the entire application's security model. Choose programmatic security when each user needs individual security checks.

 

The declarative security model is based on security roles that are declared in the deployment descriptor. Declarative security works best when the number of roles is fixed and doesn't depend on the number of clients. However, declarative security shouldn't be used when each user requires individual security constraints. Such applications require programmatic security checks within the EJB code. It is possible to combine both security models. For instance, an Account bean may use declarative security to ensure that only registered users access any methods. The bean code then includes additional constraints to ensure that each user gains access only to his or her account.

 

4.11   Using Message-Driven Beans (MDB)

Message-driven EJBs are the integration between EJBs and the JMS. Like other EJB types, message-driven EJBs live within an EJB container and benefit from EJB container services such as transactions, security, and concurrency control. However, a message-driven EJB doesn't interact directly with clients. Instead, message-driven EJBs are JMS Message Listeners. A client publishes messages to a JMS destination. The JMS provider and the EJB container then cooperate to deliver the message to the message-driven EJB.

 

4.11.1     Using Transactions with MDB

Message-driven beans, like other EJBs, make use of the EJB container's transaction service. Since these beans never interact directly with clients, they never participate in the client's transaction.

 

Like session beans, MDBs may specify either bean-managed or container-demarcated transactions in their ejb-jar.xml deployment descriptor. With container transactions a MDB may specify either Required or NotSupported. Since there is no client transaction, there is no reason to support the other transaction attributes. If the transaction attribute is NotSupported, the MDB will not participate in a transaction.

 

If the Required attribute is specified, the EJB container automatically starts a transaction. The message receipt from the JMS Queue or Topic is included in this transaction. The MDB’s onMessage() method is then called in the transaction context. When the onMessage() method returns, the EJB container commits the transaction. If the transaction aborts, the JMS message remains in the JMS destination and is delivered again to the MDB.

 

4.11.2     Error Handling in MDB Transactions

Message-driven beans with the Required transaction attribute need to be careful when aborting transactions. A transaction aborts either because it was explicitly marked for rollback or because a system exception was thrown.

 

A good way to handle error and exceptions in MDB is to separate application errors from system errors. An application error (such as an invalid stock symbol, insufficient balance in the account for requested withdrawal, etc.) should be handled by sending an error message to an error JMS destination. This allows the transaction to commit, and the message leaves the system. A system error might be that the back-end database has failed. In this case the transaction should roll back so that this message is still on the queue when the database recovers.

 

4.12   Consolidate Component Lookup

Service lookup and creation involves complex interfaces and network operations. All J2EE clients use the JNDI facility to lookup and create J2EE components, such as EJB and JMS components. It is easy to see that many types of clients repeatedly use the JNDI services, and the JNDI code appears multiple times across these clients. This results in an unnecessary duplication of code in the clients for lookup services, making the overall design more cumbersome and less maintainable. Also, creating a JNDI initial context object and performing a lookup on a component utilizes significant resources, negatively impacting the system performance.

 

A Service Locator object should be used to abstract all JNDI usage and to hide the complexities of initial context creation, and object lookup and creation. This pattern reduces the client complexities that results from the client’s dependency on and need to perform lookup and creation processes.  A simple implementation of this pattern is shown in Figure 9 as one of the components of the overall architecture. Various participants in this pattern include Client, Service Locator, InitialContext, and ServiceFactory.

 

Client is an object that requires the access to business objects. The Service Locator abstracts the JNDI services, vendor dependencies, lookup complexities, and business object creation, and provides a simple interface to clients. The InitialContext is the starting point in the lookup and creation process. The ServiceFactory object provides life cycle management for the BusinessService objects. The ServiceFactory object for EJB is an EJBHome object. The ServiceFactory for JMS components can be a JMS ConnectionFactory object, such as a TopicConnectionFactory (for publish/subscribe messaging model) or a QueueConnectionFactory (for a point-to-point messaging model).

 

4.13   Service Activation

Use a Service Activator (a J2EE design pattern) to receive and process client requests and messages asynchronously. The service activator locates and invokes the necessary business methods on the business service components (such as session EJB , entity EJB, etc.) to fulfill the requests. The Service Activator is a JMS listener and delegation service that requires implementing JMS message listener. It can be designed as a standalone component. A basic design for a service activator component is illustrated in Figure 8 below. Participants in this component include client, ServiceActivator (the main component), BusinessObject, and the message or request.

 

Figure 8  Service Activator Component

 

Any client needing to asynchronously invoke a business service, such as an enterprise bean, may create and send a message to the Service Activator. The Service Activator receives the message and parses it to interpret the client request. Consequently, the Service Activator identifies and locates the necessary business service component and invokes business methods to fulfill the client request asynchronously. It may use the service of Service Locator and also may send acknowledgement to the client.

 

5            Sample Software System Architecture and Design

A sample generic software system architecture and design is presented in Figure 9, based on ideas discussed throughout this document. The architecture presented here is by no means the most optimal one,  rather it demonstrates the use of some of the best practices discussed above. A brief description of various components follows:

 

 

Figure 9  Sample Architecture & Design

 

 

6            Conclusion and Future Works

Technologies involved in J2EE and its advantages are discussed in this article. A number of bad practices as well as best practices are presented here to make the software architects and designer prepared to take the fullest advantages of J2EE platform. This article is just one of the best practices important to software architects, designers, and developers. Similar principles must be practiced in other aspect of software engineering process. Some of these principles are presented in this section as future works necessary to be developed.

 

6.1      Best Practices for J2EE Project Management

There is more to a J2EE project than simply writing great code using leading edge technologies provided by J2EE platform. The success of most projects is rarely determined by technology alone, but instead on a myriad of people, process, and methodology-related issues.  The following items must be considered when planning for a J2EE-based project:

6.2      Bests Practices in Object Modeling

Object modeling is the crux of object-oriented software design and implementation. A good object modeling guideline is a must for consistent design and better productivity.

 

6.3      Bests Practices in Workflow Management

Workflow management is another important aspect of good software engineering process. Workflow management may include the following items: business modeling, requirements analysis, design, implementation, testing, deployment, configuration and change management, and environment management.

 

6.4      Bests Practices in Review Process

Various review processes (such as,  requirements, use cases, design, and code) should be established for a better success in software projects.

 

7           Glossary

 

JNDI

Java Naming and Directory Interface

JMS

Java Messaging Service

JTA

Java Transaction API

JavaMail

JavaMail

JDBC

Java Database Connectivity

RMI-IIOP

Remote Method Invocation – Internet Inter-ORB Protocol

Java IDL

Java Interface Definition Language

Connector

A technology to link J2EE applications to legacy systems

EJB

Enterprise JavaBean

JTA

Java Transaction Service

JSP

Java Server Page

XML

Extensible Markup Language

 

About Author

Ramanand Singh is an Enterprise Java Architect with Nalanda Technology, Inc. He has over 14 years of experience in software industry. His focus has been on architecture, design, and implementation of enterprise applications using Object-Oriented technologies, design patterns, Java technology, CORBA technology, and J2EE technology. Mr. Singh was an invited guest speaker recently at IEEE Computer Society meeting for a presentation entitled “Building n-tier Enterprise Application using J2EE Platform.” He can be reached at rsingh@nalandatech.com.

 

Bibliography

[1]           Deepak Alur, John Crupi, Dan Malks, “Core J2EE Patterns”, Sun Microsystems Press, 2001.

[2]           Ken Arnold, James Gosling, David Holmes, “The Java Programming Language, Third Edition”, Addison-Wesley, June 2000.

[3]           Eric Gamma, Richard Helm, Ralph Johnson, and John Vlissides, “Design Patterns: Elements of Reusable Object_oriented Software”, Addison-Wesley Publishing Company, September 1995.

[4]           Richard Monson-Haefel, “Enterprise JavaBeans, Second Edition”,  O’reilly & Associates, Inc., MARCH 2000.

[5]           Ed Roman, “Mastering Enterprise JavaBeans”, John Wiley & Sons, 1999.

[6]           Seth White, et. al., “JDBC API Tutorial and Reference, Second Edition”, Addison-Wesley, December 1999.

 

Online Resources

EJB2.0

“Enterprise JavaBeans Specification, Version 2.0”, Sun Microsystems, Inc., April 2001.

PORTLAND

The Portland Pattern Repository

PatternHome

Hill Side Pattern Home Page

Struts

Struts Framework


Back