|[ Team LiB ]|
In this section, we look at some best practices you can adopt in your bean development to make it more robust.
Default Session Bean Class
While discussing Stateless Session Beans, we said that your bean implementation has to define the ejbActivate and ejbPassivate methods, in spite of the fact that Stateless Session Beans are never activated or passivated. This arises from the fact that the interface that Session Beans implement is common for both Stateless and Stateful Session Beans. Obviously, this isn't very clean.
Along the same lines, imagine a case of both Stateful and Stateless Session Beans in which the bean implementation has to define all those callback methods. If your bean implementation doesn't need several of those methods, you still have to define these methods to satisfy the requirements of the interface.
One way of overcoming these problems is to create a default Session Bean class that implements the javax.ejb.SessionBean interface and provides a default implementation for all these callback methods. Your bean implementation can then extend from this adapter class and override only those methods that you need in your bean implementation. In this way, the bean implementation is clean and doesn't hold unnecessary methods. The downside to this approach is that it affects the inheritance hierarchy of the bean class. Because Java doesn't allow multiple inheritance, you cannot subclass any other business class.
It's often tempting to implement the bean's remote or local object in your bean implementation. If you think about it, it seem to make sense because the bean implementation provides concrete implementation for the methods in the remote interface. The EJB specification allows you to do this, but we strongly discourage you from doing so. There are a couple of reasons why you should avoid implementing the remote interface.
The remote interface extends from the interface javax.ejb.EJBObject, whereas the local interface extends from the interface javax.ejb.EJBLocalObject. These interfaces provide several methods that are meant for the consumption of the client. If you attempt to implement the remote or local interfaces in your bean implementation, the compiler will force you to implement these methods also. These methods simply do not belong in your bean implementation. They belong in the client's stub object that is returned by the container so that the client can invoke those methods to get more information about the bean. If you implement these methods, you're cluttering the bean implementation. Of course, you can hide these methods by using the default implementation we discussed earlier, but simply hiding the methods does not make this a very good practice.
A second reason for discouraging this practice is that a business method of a bean many times accepts a component interface of another bean as a parameter. When the bean method is invoked from within the bean whose component interface is being passed as a parameter, the compiler takes care of not allowing the bean to send a reference to itself by using the Java keyword this; using the this keyword merely passes the bean class implementation instead of the object stub. The bean is forced to get the object interface from the context and pass that instead. Now imagine if the bean class implements the component interface. This time, using the this keyword will fool the compiler into thinking that the object being passed in is the remote stub. Clients should always interact with object interfaces of beans, and not the bean implementation directly. This is the only way in which the container has a say in what's going and can provide services such as transactions and security.
To overcome this, create a business interface that has all the business methods defined in it. The component interface can extend from this business interface and also from EJBObject, thus masking as a remote interface. On the other hand, the bean implementation can implement the business interface, therefore implementing only the business methods and not the other methods that it shouldn't implement. This can be seen in Figure 21.5.
Avoiding Stateful Session Bean Chaining
Chaining Stateful Session Beans is making one Stateful Bean dependent on the state of another Stateful Session Bean and so on. Although this is allowed, and can sometimes even be desirable, it can become an issue when a bean down the chain times out or is invalidated because of an uncaught exception. This renders invalid all the state in the beans up the hierarchy, and can result in loss of considerable amount of work. Therefore, try to avoid Stateful Session Bean chaining.
Stateful Session Beans can easily call other Stateless Session Beans because Stateless Session Beans do not hold any conversational state.
Invoking Stateful Session Beans from Stateless Session Beans doesn't make any sense because Stateless Session Beans aren't tied to any single client, whereas Stateful Session Beans are.
Understanding That Session Bean State Is Nontransactional
It's a common misunderstanding that Stateful Session Bean state is transactional. For instance, consider the total cost that's stored within the shopping cart bean. This is built up as and when a new booking is made using this bean instance. At the end, assume that the transaction is rolled back. It's critical to understand that the total cost will not come back to zero by itself. Your bean implementation should write code to ensure that it happens. Session Beans merely pass on their transactional context to the resources that they use. For instance, the data that was originally altered in the database is affected and gets rolled back.
You can simulate transactionality of Session Bean data by implementing the SessionSynchronization interface as described earlier in this chapter.
Choosing Transactions Carefully
Transaction granularity should be chosen carefully. Large transactions can lock up system resources, whereas several small ones tend to make the system slow. You must design your transaction granularity in such a way that your application draws a line between these two potential problems. Also, use JTA only when you require global transactions.
Preferring Container-Managed Transactions over Bean-Managed Transactions
You should typically prefer the use of container-managed transactions. The WebLogic Server container provides declarative transaction management, thus abstracting your code from transaction management nuances. You don't have to write explicit code to begin or end transactions. The container does it for you at the scope of each business method. Also, at deployment time you get to tell the container what type of transaction support you desire for each method. This should be the preferred way of getting transaction support from the container.
There can be few cases in which you might have to use bean-managed transactions. For instance, you might have multiple transactions within the same method. The WebLogic Server container demarcates transactions at a method level. A preferred mechanism of tackling this issue is to break this method into multiple methods and let the container handle transactions for each of these methods.
A second reason might be that you may want to span transactions across different method calls in your bean. That is, one method begins a transaction while another method commits or rolls it back. If possible, this practice should be avoided because you're losing control over how and when the other method is invoked. The business method should represent units of work, and only in very rare cases should you need to encompass transactions across units of work. Create a new method that invokes the two methods in sequence. Now use Container Managed Transaction to manage the transaction for this new method.
Third, you might want to control transaction's outcome depending on business logic. This can be avoided simply by letting the container start the transaction, and using setRollbackOnly() to force a rollback if the business logic elects to cancel the transaction.
Note that transactions cannot span method calls for Stateless Session Beans. This is because Stateless Session Beans are shared, and there's no way to ensure the order in which these methods will be invoked.
Avoiding Spanning Transactions Across User Think Time
It often seems useful to begin a transaction, and then pass control back to the presentation layer for more information. This is a very bad practice and should be avoided at all costs. The problem with this approach is that the user might take as much time as he wants to respond to the presentation layer. This means either that the resources are locked up until that time or your transaction times out in the meanwhile. Try to break down such operations into multiple transactions. Generally speaking, keep the scope of a transaction strictly within the server context.
Understanding the Implications of the Supports Transaction Attribute
The Supports transaction attribute brings your bean into the context of a transaction if the client has an active transaction. If the client does not, your bean does not come under a transaction context. Understand the implications of this behavior. Your bean no longer has control over whether it's being invoked from within a transactional context. If your bean can handle being used without a transaction as well as it can handle being invoked inside of a transaction, there's no problem in using the Supports attribute. However, to make it more explicit, consider using Required, RequiresNew, or Mandatory for cases in which you need a transactional context. Similarly, use Never or NotSupported for cases in which you do not need a transaction at all.
Preferring Stateless over Stateful Beans
Stateful Session Beans are meant to hold a conversational state for a client. Thus, they cannot be shared across clients. Every client creates an instance, uses it, and then eventually removes it. Therefore, the number of Stateful Session Bean instances in your container at any time can grow tremendously, making these beans resource intensive. In a clustered environment, Stateful Session Beans have no load balancing. The calls are always directed to the primary server hosting the bean instance.
On the other hand, Stateless Session Beans are shared and are reused by different clients. Calls to Stateless Session Beans are load balanced across cluster instances. All these factors make stateless Session Beans much more scalable. They use limited resources.
Understand that Stateless Beans can also hold state. The state is just shared across all clients that access them, and isn't specific to a client. So, unless you have to hold client-specific state in the server, give preference to Stateless Beans over Stateful beans.
Avoiding Stateful Session Passivation
The container provides passivation as a service to manage system resources more effectively. But if you think about it, the act of passivation and activation itself has overheads. It obviously takes time to serialize a bean's state to the file system. You must design your application to minimize passivation effects. Of course, the best-case scenario is to keep the client footprint on the container to the minimum. You can effectively use Stateless Session Beans along with Entity Beans to perform your business tasks. But if you must use a Stateful Session Bean, make sure that the user interface allows for a logout option, which will in turn remove the Stateful Session Bean instance. Memory is cheap nowadays, so it might even make sense to increase the RAM in your server. Keep the heap size and the cache size at optimal values at all times to minimize passivation effects.
Using Appropriate Design Patterns
Design patterns help you design robust and reusable application components. Several core patterns are available for EJB design, which you must consider while designing your EJB applications. Some of the design patterns you can consider are listed in the following sections.
When your presentation layer invokes EJBs directly, it's exposed to the details of the implementation of the underlying service. This isn't preferable because your presentation layer has to be modified accordingly when the underlying implementation changes. Another problem is that when your client layer invokes services directly, there might not be any caching discipline in your client, and different components of your client might invoke the same service over and over again. This gives rise to lots of network hits, which is undesirable.
To overcome these, you can use the Business Delegate pattern. This reduces the coupling between the presentation layer and the EJB layer and hides the implementation details of the service layer. This layer can also provide caching of data so as to minimize network overheads. You can learn more on this pattern by visiting http://java.sun.com/blueprints/corej2eepatterns/Patterns/BusinessDelegate.html.
In an enterprise system, there might be several server-side objects that provide fine-grained functionality. These objects are known as business objects. Such objects might include Session Beans, Entity Beans, and even other types of objects that offer business functionality for your client. Business methods typically involve accessing several of these business objects in a particular order to achieve a desired result.
The problem with letting the clients handle the business flow is the duplication of effort between different clients. Also, the clients become tightly coupled with the implementation aspects of the business objects. To avoid this, consider the use of a Session Façade. A Session Façade is typically a Session Bean that manages the flow between the different business objects and provides coarse-grained business functionality for your client systems. All client systems can now access the façade in a uniform manner, and the business logic is centralized. You can read more about this pattern by visiting http://java.sun.com/blueprints/corej2eepatterns/Patterns/SessionFacade.html.
A Service Locator pattern can be implemented to hide the nuances of creation of EJB objects. Client components reuse the singleton locator object and receive the EJB object without knowing how the object is created. This concept can be extended to use the locator class to create other types of services, such as JMS. You can read more about the Service Locator pattern by following the link http://java.sun.com/blueprints/corej2eepatterns/Patterns/ServiceLocator.html.
Avoiding Using Threads
The WebLogic Server container is configured to manage all threads within the JVM. If you create threads from within your Session Beans, the container loses control over the threads created explicitly by your bean implementation and cannot manage the runtime environment of your bean. The container also cannot effectively manage available resources, resulting in slow processing. Remember that when you code to create threads in your bean implementation, the same code will execute in potentially hundreds of bean instances, thus creating hundreds of threads!
Avoiding Using Nonfinal Static Variables
Static variables that are nonfinal are strongly discouraged in the context of EJBs. One reason for this is that the static (nonfinal) model will break in a clustered environment. Data stored in static variables in each server of your cluster might not be the same, which could cause strange behavior. If you have to use static variables, make sure that they're declared final.
|[ Team LiB ]|