Session Bean Implementation issues

Now that we've considered which type of session bean to use and some idioms frequently used with session beans, let's take a look at some important implementation issues concerning session beans. In this section we'll consider the EJB error handling model and how it affects bean developers; transaction propagation using EJB CMT and its implications for implementing session beans; and an EJB implementation pattern that can help us to avoid a common cause of deployment errors.

Error Handling in EJBs

As EJBs are managed objects, the EJB container steps in to handle some types of exceptions they throw. This section discusses the rules defined in the EJB specification, and how developers can use them to advantage. These considerations are important as we implement business logic in session beans and define session bean local and remote interfaces.

The EJB Container's Behavior on Exceptions

The EJB specification's approach to exceptions thrown by EJBs is simple and elegant. The specification distinguishes between application exceptions and all other exceptions, referred to as system exceptions.

An application exception is a checked exception defined in the throws clause of a method of an EJB's local or remote home or local or remote interface, other than java.remote.RemoteException. (Remember that Java RMI requires that all remote methods declare this exception in their throws clause.)

A system exception is an unchecked exception or throwable thrown by an EJB implementation class method at run time, or an uncaught java.remote.RemoteException resulting from a call on another EJB within the application. (It's up to the bean developer to decide whether or not a method in a bean class catches RemoteExceptions resulting from calling other EJBs. It will usually only make sense to do so if the error is recoverable, or to add context information to the nested exception.)

It is assumed that the EJB client understands application exceptions, and how they might be recovered from. It is up to the client to decide that a particular exception is unrecoverable. Consequently, the container does not step in when an application exception is thrown. It simply causes the EJB client to catch the same application exception. The container will not normally log application exceptions, and the status of the current transaction will be unaffected.

System exceptions are handled very differently. The container assumes that the client didn't expect such an exception, and that it's likely to prove fatal for the current use case. This is a reasonable assumption. In contrast to application exceptions, which are checked and which the client must know about because it is forced by the Java compiler to catch them, system exceptions may be meaningless to the client. Take, for example, a runtime exception from a JDO persistence manager. The client should not even know the EJB tier's persistence mechanism, and can't be expected to recover from a problem it doesn't understand.

Accordingly, the container takes drastic action. It marks the current transaction irreversibly for rollback. It discards the EJB instance so that it can service no further calls. The fact that the EJB instance will be discarded means that the developer need not make any effort to clean up any conversational or internal state maintained in the bean. This reduces developer workload and removes a potential source of bugs. The EJB container must log the offending system exception. If the client is remote, the EJB container throws a java.remote.RemoteException (or a subclass) to the client. If the client is local, the EJB container throws a javax.ejb.EJBException to the client.

A bean that has encountered an unrecoverable checked exception that it cannot rethrow should throw a javax.ejb.EJBException or subclass wrapping the checked exception. This will be treated as an unexpected exception by the container. This container behavior on unexpected exceptions should not be used as a substitute for explicitly rolling back transactions using the setRollbackOnly() method. It is a convenient cleanup facility offered by the container when a bean instance encounters an unrecoverable error. Since it results in the bean instance being discarded, it will reduce performance if this situation occurs frequently at run time.

Let's now consider some of the implications when designing and implementing session beans:

  • We must never allow an EJB method to throw a runtime exception in normal operation
    An uncaught throwable will cause not only the call stack to unwind as usual, but also the container to roll back the current transaction.

  • We should consider whether or not to catch RemoteExceptions thrown by EJBs whose remote interfaces we use in EJB methods
    We do have the simpler choice of declaring our EJB implementation to throw the RemoteException, but this will be treated as fatal error, and the bean instance will be discarded. If the exception is recoverable, it must be caught.

  • If a runtime exception is fatal, we needn't catch it, but may leave the EJB container to handle it
    A good example is a runtime exception from a JDO persistence manager. Unless we believe we can retry the operation, there's no point in catching such exceptions.

  • If an EJB method catches a checked exception that it cannot recover from, it should throw a javax.ejb.EJBException wrapping the original exception.

  • An application exception on an EJB local or remote interface must be a checked exception
    The container treats all runtime exceptions as fatal system errors. This means that we don't have the choice of using unchecked exceptions on the signatures of EJB methods or on any interfaces that are likely to be implemented by EJBs. This is perhaps the only unfortunate result of the EJB approach to error handling, as client code doesn't have the option of dealing with runtime exceptions only in unusual cases - an approach discussed in Chapter 4, which can prove very useful.

Important 

The guarantee that the EJB container will step in to handle an unchecked exception and ensure transaction rollback makes it particularly attractive to define fatal exceptions thrown by helper classes likely to be used by EJBs to be unchecked exceptions. For example, the JDBC abstraction framework discussed in Chapter 9 throws the unchecked DataAccessException, which EJBs that use it can simply ignore, on encountering an unrecoverable java.sql.SQLException. In the unlikely event that an EJB needs to intercept this exception, it still can implement a catch block.

Understanding EJB API Exceptions

The javax.ejb package defines eleven exceptions. Those of most interest to J2EE developers are:

  • javax.ejb.EJBException
    This is a convenient runtime wrapper exception intended for wrapping unrecoverable checked exceptions. We may use it to wrap fatal exceptions such as JDBC exceptions that must be caught. However, there's a strong case that any exception that we define ourselves that must be wrapped in an EJBException and rethrown should simply be an unchecked (runtime) exception. This will simplify EJB code. As the catch block that throws an EJBException doesn't need to perform any cleanup (the EJB container will roll back the transaction and discard the bean instance), catching and rethrowing the exception adds no value.

The following are standard EJB application exceptions. They are reported to clients:

  • javax.ejb.CreateException and subclasses
    This should be thrown from an ejbCreate() method if the failure to create the bean should be viewed as an application error. For example, a stateful session bean ejbCreate() method might throw this exception if it was passed invalid arguments by a client. If the failure to create a bean reflects a system exception, a runtime exception should be used (the EJBException wrapper exception if necessary). For example, there's no point in telling a remote client that a table is missing in a relational database. The remote client shouldn't even know that the EJB tier uses a relational database.

  • javax.ejb.RemoveException and subclasses
    Analogous to CreateException.

  • javax.ejb.FinderException and subclasses (entity beans only, but often encountered by session bean implementations)
    Finder exception must be included in the throws clause of every finder method on an entity bean's home interface. A finder should throw this exception on failure to locate a requested object, or encountering inconsistent results from the persistent store (for example, if a single object finder finds multiple matching records). Note that an empty Collection returned from a multi-object finder does not indicate an error. In the case of entity beans with CMP, finder methods will be generated by the EJB container.

Transaction Attributes for EJBs using CMT

EJBs are normally transactional components. As we saw in Chapter 6, one of the strongest arguments for using session EJBs to implement business logic is the option of using declarative Container Managed Transactions (CMT). We specify CMT behavior in EJB deployment descriptors, causing the container to create transactions if necessary for EJB method invocations, freeing the developer from the need to use JTA directly. This is an important productivity gain. However, it's important to understand exactly how CMT works.

There is an overhead associated with creating a transaction, so we don't want to create transactions unnecessarily. Yet, transactions are vital to the integrity of business logic that involves multiple updates of transactional resources.

Focusing on session beans with CMT, let's consider the transaction attributes allowed by the deployment descriptor and how they affect the behavior of our code. (As we saw in Chapter 6 we should always prefer CMT to BMT if we have a choice.) The EJB specification recognizes six transaction attributes, which are associated with EJB methods in the ejb-jar.xml deployment descriptor. The following table summarizes the behavior each confers:

Transaction attribute as
specified in deployment
descriptor

Meaning for the method so marked
(referred to as method m) and other
resources m invokes

Effect if a caller already
has a transaction

Notes

Recommended use


Required

The method is guaranteed to run in a JTA transaction. If the caller has a transaction, it will be used. If not, a new transaction will be created.

Method m will run in the caller's transaction.

If the caller's transaction is rolled back, the work of method m will be rolled back automatically.

This should be the default transaction attribute for session bean methods, which define the public interface of the EJB tier.

   

This is the most flexible option. It allows the caller to use method m as a piece of a larger, reversible, unit of work. Yet method m can still be used independently. For example, it is able to support calls from clients outside the EJB tier.

 

RequiresNew

The method is guaranteed to run in a new transaction, whether or not the caller has a transaction.

The caller's transaction is suspended during execution of the method.

This can be dangerous. If method m will not be reversed. This means that the caller does not have overall control of the business operation.
Note that the creation of a new transaction when the client already has a transaction context does not amount to a nested transaction. With nested transactions, rollback of the parent transaction will cause rollback of all child transactions. In the RequiresNew case we have two separate flat transactions, with no relationship and no effect on each other's rollback status.

Only useful in the unusual case that method m implements a distinct piece of work that must commit regardless of the success of any higher-level operation in progress. An example might be an auditing action, where a persistent record must be made of the fact that a user attempted a certain action, regardless of its success.

NotSupported

The method is assumed not to be interested in JTA transactions, and will execute without a transaction context. If the method invokes other EJBs or resource managers, no transaction context will be propagated.

The caller's transaction is suspended during execution of the method.

Rollback of the caller's transaction will have no effect on any changes made by method m.
Not available to entity beans.

Appropriate if the method's implementation is non-transactional.
Don't use if the method updates persistent data.
Can be useful for read-only methods. For example, Oracle SELECTs do not need to run in a transaction, and Oracle won't create a transaction if none exists. Therefore query methods will be most efficient with a NotSupported transaction attribute.

Supports

If the caller has a transaction, it will be propagated to the method, and execution will occur as in the Required case. If the caller does not have a transaction, the container will not create one, and the method will execute without one, as in the NotSupported case.

Method m will run in the caller's transaction, as in the Required case.

Potentially dangerous. The semantics of method m will vary depending on whether the client has a transaction context.
Not available to entity beans.

Don't use this transaction attribute for updates. In the case of read-only operations, it can be used safely way as NotSupported.

Mandatory

The method requires a JTA transaction, which must be provided by the client. If the client does not have a transaction context when calling the method, the container throws a
javax.transaction.TransactionRequiredException.

This is the only valid case.

Often an alternative to Requires. Limits the situations in which the method may be called. However, this may be a good thing.

Useful for entity bean methods when using a session façade. In this case, we want the client to work through the façade, rather than manipulate low-level components such as entity beans, possibly coming in under the radar of our business logic and breaking system integrity.
Of course, a client could still obtain the UserTransaction object and create a transaction before invoking method m.
Do not use for session bean methods, as this would prevent their use by clients without transactions (the ideal client scenario).

Never

The opposite of Mandatory. The method must not be called by a client with a transaction context. If it is, the container throws a java.rmi.RemoteException. Within the method, behaviour will be the same as in the NotSupported case.

Not permitted.

An alternative to NotSupported if it is considered essential to prevent a client with a transaction context calling the method. Not available to entity beans.

 

Important 

The safest EJB transaction attributes are Required (for session beans) and Mandatory (for entity beans). These attributes ensure the expected transaction behavior, regardless of the caller's transaction context A transaction attribute of NotSupported can be used to boost the performance of read-only methods, for which transactions are not required. This situation arises often, as web applications frequently use read-only data.

Note 

The EJB specification suggests that an "Application Assembler", distinct from the EJB developer, may set transaction attributes. This is potentially dangerous. As we've seen, changing the transaction attributes of an EJB method can change the method's semantics. Only if the bean developer defines the transaction attributes can they develop meaningful unit tests for EJBs. While the removal of the specification of EJB transaction attributes from Java code is desirable, their removal from the control of the bean developer is misguided.

Although the details of transaction management are best left to the EJB container, deciding whether a transaction should be rolled back is usually a matter of business logic (remember that if there's a runtime exception, the EJB container will automatically ensure rollback, whether a bean uses BMT or CMT). Accordingly, it's easy to ensure rollback in a bean using CMT.

All EJBs have access to an instance of the EJB context interface, supplied at run time by the container. There are three subinterfaces of javax.ejb.EJBContext, available at run time to the three kinds of EJB: javax.ejb.SessionContext, javax.ejb.EntityContext, and javax.ejb.MessageDrivenContext. The EJBContext superinterface offers a setRollbackOnly() method that allows CMT beans to mark the current transaction irreversibly for rollback.

The Business Methods Interface "Pattern"

As the EJB container, not the bean developer, provides the implementation of an EJB's component interface (the local or remote interface), how do we ensure that the component interface and bean implementation class remain in synch? Lack of such synchronization is a common cause of problems at deployment time, so this is an important issue, unless we are prepared to become dependent on tool support for EJB development.

We can choose to make the bean implementation class implement the component interface, but this has the following disadvantages:

  • As the component interface must extend either javax.ejb.EJBObject or javax.ejb.EJBLocalObject, the bean implementation will be forced to provide empty implementations of methods from these interfaces that will never be invoked by the container. This is confusing to readers.

  • It will be possible to return this to remote or local clients, in place of the container-generated implementation of the component interface. This is a serious programming error that probably won't be detected by the EJB container and may cause subtle problems at run time.

Fortunately, there is a simple and effective solution, which is widely recognized: to have both component interface and bean implementation class implement a common "business methods" interface. The business methods interface does not extend any EJB infrastructure interfaces, although it may extend other application business interfaces.

If we're dealing with an EJB with a remote interface, each method in the business methods interface must be declared to throw javax.remote.RemoteException. In the case of EJBs with remote interfaces, the business methods interface is normally an EJB-tier implementation detail: clients will work with the bean's remote interface, not the business methods interface.

In the case of EJBs with a local interface, the business method interface's methods may not throw RemoteException, which means that the business methods interface need not be EJB-specific. Often the business methods interface, rather than the EJB's local interface, is of most interest to clients of the EJB. In the sample application, for example, the com.wrox.expertj2ee.ticket.boxoffice.BoxOffice interface is an ordinary Java interface, which could be implemented without using EJB. Clients invoke methods on this interface, rather than the EJB's local interface, which extends it.

Use of a "Business Methods Interface" isn't really a design pattern – it's more a workaround for an inelegant feature of the EJB specification. But as it's often referred to as a pattern, I'm following established terminology.

Let's look at how we use this pattern for the sample application's BoxOffice EJB, and the naming conventions used. Using this pattern involves the following four classes for the EJB:

  • BoxOffice
    The interface that defines the business methods on the EJB, extended by the component interface and implemented by the bean implementation. In the sample application, this interface is defined apart from the EJB implementation and is used by clients. Even if clients will work only with the EJB's local or remote interface, such an interface can be introduced purely as an EJB tier implementation choice to synchronize the classes required to implement the bean.

  • BoxOfficeLocal
    The EJB component interface. The component interface should be empty. It will extend both the business methods interface and one or other of javax.ejb.EJBobject (in the case of a remote interface) or EJBLocalObject (in the case of a local interface), inheriting all its methods. In the sample application, we use a local interface. Note that I have suffixed Local to the end of the business interface name; a suffix of Remote should be used for remote interfaces.

  • BoxOfficeHome
    The bean's home interface. Unless a bean has both a local and remote interface (a relatively rare case), I don't consider it necessary to distinguish between BoxOfficeLocalHome and BoxOfficeRemoteHome.

  • BoxOfficeEJB
    The bean implementation class. This will implement both javax.ejb.SessionBean and the business methods interface. Some sources recommend names of the form BoxOfficeBean, but I avoid this as I tend to use the suffix "Bean" for implementations of a particular interface that are ordinary JavaBeans (as opposed to EJBs). If the bean implementation is tied to a particular technology, indicate that in the name: for example, JdbcBoxOfficeEJB. As with any implementation of an interface, the name should indicate what characterizes that implementation, not merely repeat the interface name as closely as possible.

The following UML class diagram illustrates the relationship between these classes and interfaces and those required by the EJB specification. The business methods interface is circled. Note that the bean implementation class, BoxOfficeEJB, extends a generic superclass, com.interface21.ejb.support.AbstractionStatelessSessionBean, that helps to meet the requirements of the EJB specification. We'll discuss this and other EJB support classes when we look at application infrastructure in the next chapter:

click to expand

The following is a complete listing of the BoxOffice EJB's local interface, which defines no new methods:

    public interface BoxOfficeLocal extends javax.ejb.EJBObject,          com.wrox.expertj2ee.ticket.boxoffice.BoxOffice {    } 

The following shows how the bean's implementation class implements the business interface as well as the javax.ejb.SessionBean interface required by the EJB specification:

    public class BoxOfficeEJB extends AbstractStatelessSessionBean          implements SessionBean, BoxOffice {      // Implementation omitted    } 

Note 

Although the EJB Business Methods Interface is widely advocated, not everyone agrees on naming conventions. Some sources advocating using a BusinessMethods suffix for the name of the business methods interface. Whatever naming convention you decide to adopt, use it consistently in all your EJB implementations to avoid confusion.

The Business Methods Interface pattern is a simple technique that can save a lot of time. However, it can't detect problems in synchronization between an EJB's home interface and implementation class. For example, home interface create() methods must correspond to ejbCreate() methods. Problems synchronizing between home interface and bean implementation class may produce mysterious "abstract method errors" in some servers. The generic superclasses discussed in the next chapter help to avoid such errors, where stateless session beans and message-driven beans are concerned.

Important 

Use the Business Methods Interface pattern to help avoid problems with keeping component interface and business methods in sync. Errors will be picked up at compile time, rather than when the beans are deployed to an EJB container. Although it may be possible to synchronize these classes with an IDE, the business methods interface uses the compiler, and is thus guaranteed to work whatever the development environment.



Expert One-on-One J2EE Design and Development
Microsoft Office PowerPoint 2007 On Demand
ISBN: B0085SG5O4
EAN: 2147483647
Year: 2005
Pages: 183

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