OpenEJB is the chosen EJB container implementation for Apache Geronimo. Although EJB 3.0 is currently available, it won't be supported in Geronimo until Version 2.0, when Geronimo will receive Java 1.5 certification.
In this three-part series, you'll learn about what Geronimo and OpenEJB can bring you and about EJB transaction concepts that you can implement now in EJB 2.1 -- and carry with you well into EJB 3.0.
The EJB framework provides the benefits of using transactions without the pain of transaction API programming. When implementing EJB transactions, you have two options:
- Tell the EJB container to do all the hard transaction work (container-managed transactions).
- Let your enterprise beans do some of the transaction work (bean-managed transactions).
In this installment, Part 1 of the series, you'll begin with an overview of transactions, and discuss EJB container-managed transactions as described in EJB 2.1. You'll conclude with some code snippets to show how you can implement container-managed transactions in the Geronimo application server.
In Part 2 you'll get an overview of bean-managed transactions in EJB 2.1 and see some sample code implementations.
In Part 3 you'll bring the two options together and learn about quirks and additional features related to both container-managed and bean-managed transactions.
So what is a transaction? Why are they so important? Consider this very simplistic case of a banking transaction: You transfer $100 from your checking account to your savings account. On further investigation, this might involve two smaller operations:
- Your bank subtracts $100 from your checking account.
- Your bank adds $100 to your savings account.
If the bank reduced your checking balance by $100 but failed to increase your savings balance by $100, you might be a little upset. Personally, I'd like to think of the two operations as one operation. So if $100 is never added to your savings account, $100 should never be subtracted from your checking account!
Similarly, there are business cases in applications that take on this all-or-nothing approach. Some large operations consist of one or more smaller steps. For the operation to complete, all steps within the operations must complete, or none at all. This behavior is known as atomicity.
Atomicity is one of four characteristics (or properties) that transactions must guarantee. The other three properties are:
- Consistency
- Isolation
- Durability
Together these four properties are called the ACID properties.
Transactions exhibit these well-known ACID properties:
- Transactions are atomic. All operations are considered a single unit of work. This is an all-or-nothing approach, as discussed previously.
- Transactions are consistent. After a transaction executes, it must leave the system in a consistent (or legal) state. What defines a legal state is up to the system. To follow the earlier example, after any withdrawals, your bank dictates you leave your checking account with a positive balance.
- Transactions are isolated. Each transaction behaves in isolation of other transactions executing on the same resource. This is achieved through lock synchronization of data.
- Transactions are durable. Updates to resources must survive system failures, such as hardware or network failures. In distributed systems, recovery processes are needed when networks fail or databases crash.
There are two popular transaction models: flat transactions and nested transactions. EJBs support the flat transaction model.
Flat transactions are a series of operations performed as a single unit of work. There can be only two results from this unit of work: either success or failure. If all steps in a transaction complete successfully, the transaction is committed and all persistent data changes performed by the operation become permanent. If any steps within the transaction fail, the transaction rolls back and reverses any data effects caused by the steps in the transaction.
Nested transactions allow for transactions (or units of work) to be nested in other transactions. Transactions nested in other transactions allow rollbacks to occur without affecting their parent transactions. Failed nested transactions can continue to retry. If failure continues, the parent transactions might be rolled back.
EJBs are a framework for component development. You develop EJBs that run within an EJB container. Among other things, the EJB container provides the benefit of transactions. The OpenEJB is the EJB container used by Geronimo to provide transaction management.
The EJB architecture supports distributed transactions. Some sample scenarios of when distributed transactions would be needed include:
- An application in a single transaction to update multiple databases.
- An application in a single transaction to send or receive messages from a Java Message Service (JMS) destination and update one or more databases.
- An application in a single transaction to update multiple databases via multiple EJB servers.
- A Java client to explicitly demarcate transaction boundaries before updating multiple databases on multiple EJB servers.
When implementing EJB transactions, you're demarcating transaction boundaries: who begins the transaction, who commits or aborts the transaction, and when you should use the transactions. It's up to the EJB container and server provider to provide transaction management and a low-level transaction communication protocol.
There are two demarcation options:
- The declarative option, in which you can delegate transaction implementation to the EJB container. (This option is the focus of the remainder of this article.)
- The programmatic option, in which the enterprise beans provide the commit or abort information themselves in their code. (This option will be covered in the second part of this series.)
With declarative transaction demarcation, the EJB container applies transaction boundaries on an enterprise bean's methods based on instructions declared by the application developer in the EJB deployment descriptor. This is called a container-managed transaction.
When implementing programmatic demarcation of transactions, the application developer is responsible for programming transaction logic and boundaries into enterprise bean code. This is called a bean-managed transaction.
Which transaction should I use?
Container-managed transactions are simpler and require no implementation of transaction logic in your code. Either your enterprise bean method must run within a transaction or not at all. Also, the Java client invoking the bean cannot abuse your enterprise bean, because the transaction will always start up and end.
If you want full control of transaction boundaries, use bean-managed transactions. This method allows you to directly control where commit or rollback logic will occur in your code.
Session beans and message-driven beans (MDBs) can use bean-managed transactions or container-managed transactions, while an entity bean must always use container-managed transactions. It's illegal for an entity bean to use bean-managed persistence.
Container-managed transactions
Transaction demarcation boundaries are provided through instructions or transactional attributes. These attributes describe how enterprise beans participate in transactions. You can specify different transaction attributes on each bean regardless of the number of beans. You can specify attributes for the entire bean or for individual methods of that bean. Attributes for methods take precedence over the bean.
Transaction attributes for session and entity beans
Possible attribute values for session and entity beans are:
Required-- Bean must always run within a transaction. If the client has started a transaction, the bean joins the transaction. If the client has not started one, the EJB container starts a new transaction. Use this attribute when you want your bean to always run within a transaction.RequiresNew-- Bean always starts a new transaction. If the client has started a transaction, this transaction is suspended until the new transaction commits or aborts. After the new transaction completes, the existing transaction resumes. Use this attribute when you want your bean to run as a single unit of work and exhibit all of the ACID properties.Supports-- If the client has started a transaction, the bean joins the transaction. However, the EJB container doesn't start a new transaction if one doesn't exist. Use this attribute for nonmission-critical operations on your enterprise bean.Mandatory-- The client must start a transaction when the bean is invoked. A new transaction can't be created. If there is no transaction already started when the bean is invoked, an exception is thrown. Use this attribute when a bean is part of a larger system. Usually a third party might be responsible for starting the transaction. This is a safe option to use because it guarantees the bean will be part of a transaction.NotSupported-- Bean cannot be involved in a transaction. If the client has started a transaction, the existing transaction is suspended until the bean's method has completed. After completion, the existing transaction is resumed. If the client hasn't started the transaction, a new transaction isn't created. Use this attribute when you don't require your bean to exhibit any of the ACID properties, such as for nonsystem-critical operations like reporting.Never-- If the client has started a transaction, the bean throws an exception. You might never want your bean to participate in a transaction. Use this attribute in those cases.
Transaction attributes for message-driven beans
There are only two transaction attributes used with message-driven bean message listener methods:
NotSupported-- Bean cannot be involved in a transaction. If the client has started a transaction, the existing transaction is suspended until the bean's method has completed. After completion, the existing transaction is resumed. If the client hasn't started a transaction, a new transaction isn't created.Required-- Bean must always run within a transaction. If the client has started a transaction, the bean joins the transaction. If the client hasn't started one, the EJB container starts a new transaction.
After you've decided on the correct transaction attributes for your enterprise bean methods, you can configure your EJB deployment descriptor.
Configure the EJB deployment descriptor
For each enterprise bean, you configure the following two pieces of a transaction in your deployment descriptor:
- Specify whether your bean should use container-managed or bean-managed transactions using the
<transaction-type>element in your EJB deployment descriptor. Possible values arecontainerorbean. Since entity beans must use container-managed transactions, this is required only for session beans and message-driven beans. - For container-managed transactions, you optionally specify the transaction attributes for your enterprise bean's methods. This is specified in the
<container-transaction>portion of the EJB deployment descriptor. The general form for each method is shown in Listing 1.
Listing 1. General form for each method
<method>
<ejb-name>EJBNAME</ejb-name>
<method-name>METHODNAME</method-name>
<trans-attribute>TRANSATTRIBUTE</trans-attribute>
</method>
|
Possible values for TRANSATTRIBUTE are:
NotSupportedRequiredSupportsRequiresNewMandatoryNever
It's also possible to specify transaction attributes for all methods for an enterprise bean. Use * for the <method-name> attribute.
Listing 2 shows a sample of how transaction attributes are specified for a container-managed enterprise bean. The ClaimRecord enterprise bean assigns all methods the Required attribute, with the exception of the updateClaimNumber method, which has been assigned the Mandatory attribute. The Coverage bean assigns all methods the RequiresNew attribute.
Listing 2. Transaction attributes in the ejb deployment descriptor file
<ejb-jar>
...
<assembly-descriptor>
...
<container-transaction>
<method>
<ejb-name>ClaimRecord</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>ClaimRecord</ejb-name>
<method-name>updateClaimNumber</methodname>
</method>
<trans-attribute>Mandatory</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>Coverage</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>RequiresNew</trans-attribute>
</container-transaction>
...
</assembly-descriptor>
...
</ejb-jar>
|
Now that you understand the general form for specifying transaction attributes in the EJB deployment descriptor, you can look at how to do this in Geronimo using OpenEJB. When developing EJBs in Geronimo, you can save time by using XDoclet to generate a large portion of the tedious EJB programming artifacts that are required. As part of these artifacts, XDoclet generates the EJB deployment descriptor.
As part of the normal development process, specify JavaDoc-style markup tags in your enterprise beans. By declaring markup tags in your enterprise beans, XDoclet generates ejbjar.xml. This includes any transaction attribute defined. You don't directly edit the deployment descriptor yourself (ejb-jar.xml).
Transaction attributes are specified in XDoclet using the @ejb.transaction markup. When you want to use it, you declare it above the method of the enterprise bean.
Sample XDoclet configuration and ejbjar.xml generation
The code snippets below show a simplistic sample session bean and entity bean and the resulting ejbjar.xml file generated by XDoclet. First, Listing 3 shows a stateless session bean called SampleSession. Pay attention only to the portions relevant to transactions (shown in bold).
Listing 3. Session bean
package org.my.package.ejb;
/**
* Sample session bean.
* Declare all my XDoclet tags here
* ...
* ...
* @ejb.bean name="SampleSession"
* type="Stateless"
* local-jndi-name="java:comp/env/ejb/SampleSessionLocal"
* jndi-name="org.my.package.ejb/SampleSessionLocal/Home"
* view-type="both"
*
* @ejb.permission unchecked="true"
*
* @ejb.interface generate="local,remote"
* remote-class="org.my.package.ejb.SampleSession"
* local-class=" org.my.package.ejb. SampleSession Local"
* @ejb.home generate="local, remote"
* remote-class="org.my.package.ejb.SampleSession Home"
* local-class="org.my.package.SampleSession LocalHome"
* @ejb.util generate="physical"
* ...
* ...
*/
public abstract class SampleSessionBean implements javax.ejb.SessionBean {
/**
* Perform a business operation. Add something
* @param someParam the value
* @ejb.interface-method view-type="both"
* @ejb.transaction type="Required"
*/
public void doSomething(java.lang.String someParam)) {
...
}
/*
* Perform another business operation. Add something
* @param someParam the value
* @ejb.interface-method view-type="both"
* @ejb.transaction type="RequiresNew"
*/
public void doSomethingElse(java.lang.String someParam)) {
...
}
/**
* @ejb.create-method
* @ejb.transaction type="Required"
*/
public void ejbCreate ()
throws javax.ejb.CreateException
{
}
public void ejbPostCreate ()
throws javax.ejb.CreateException
{
}
protected javax.ejb.SessionContext _ctx = null;
public void setSessionContext( javax.ejb.SessionContext ctx )
{
_ctx = ctx;
}
protected javax.ejb.SessionContext getSessionContext()
{
return _ctx;
}
}
|
The same markup @ejb.transaction is used to specify the transaction attributes for this entity bean. Listing 4 shows how transaction attributes are specified for an entity bean. Again, pay attention to the markup in bold only.
Listing 4. Entity bean
package org.my.package.ejb;
/**
*
* @ejb.bean
* type="CMP"
* cmp-version="2.x"
* name="ClaimEntry"
* local-jndi-name="org.my.package.ejb/ClaimLocalHome"
* view-type="local"
* primkey-field="name"
*
*
* @xx-ejb.data-object
* container="true"
* setdata="true"
* generate="true"
*
* @ejb.value-object
*
* @ejb.transaction type="Required"
* @ejb.permission unchecked="true"
* @struts.form include-all="true"
*
* @web.ejb-local-ref
* name="ejb/ClaimLocal"
* type="Entity"
* home="org.my.package.ejb.ClaimLocalHome"
* local="org.my.package.ejb.ClaimLocal"
* link="PhoneBookEntry"
*
* @ejb.persistence table-name="Claim"
*
*/
public abstract class ClaimBean
implements javax.ejb.EntityBean
{
* ... EJB entity bean implementation here
}
|
When XDoclet is executed during the build process, ejb-jar.xml is generated. Listing 5 shows the transaction-relevant parts of the file. Notice the <transaction-type> and <trans-attribute> elements shown in bold.
Listing 5. Generated ejb-jar.xml snippet
...
<ejb-jar >
<description><![CDATA[No Description.]]></description>
<display-name>Generated by XDoclet</display-name>
<enterprise-beans>
<!-- Session Beans -->
<session >
<description><![CDATA[Sample session
bean.]]></description>
<ejb-name>SampleSession</ejb-name>
<home>org.my.package.ejb.SampleSessionHome</home>
<remote>org.my.package.ejb.SampleSession</remote>
<local-home>org.my.package.ejb.SampleSessionLocalHome
</local-home>
<local>org.my.package.ejb.SampleSessionLocal</local>
<ejb-class>org.my.package.ejb.SampleSessionSessionSession
</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
</session>
...
<!-- Entity Beans -->
<entity >
<description><![CDATA[]]></description>
<ejb-name>Claim</ejb-name>
<local-home>
org.my.package.ejb.ClaimLocalHome</local-home>
<local>org.my.package.ejb.ClaimLocal</local>
<ejb-class>org.my.package.ejb.ClaimCMP</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.String</prim-key-class>
<reentrant>False</reentrant>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>Claim</abstract-schema-name>
...
</entity>
...
<container-transaction >
<method >
<ejb-name>Claim</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<container-transaction >
<method >
<ejb-name>SampleSession</ejb-name>
<method-intf>Local</method-intf>
<method-name>doSomething</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</method>
<trans-attribute>RequiresNew</trans-attribute>
</container-transaction>
<container-transaction >
<method >
<ejb-name>SampleSession</ejb-name>
<method-intf>Remote</method-intf>
<method-name>doSomething</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</method>
<trans-attribute>RequiresNew</trans-attribute>
</container-transaction>
<container-transaction >
<method >
<ejb-name>SampleSession</ejb-name>
<method-intf>Local</method-intf>
<method-name>doSomethingElse</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<container-transaction >
<method >
<ejb-name>SampleSession</ejb-name>
<method-intf>Remote</method-intf>
<method-name>doSomethingElse</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
...
</ejb-jar>
|
Container-managed transactions allow the EJB container to specify transaction boundaries, so the work is simplified for you. However, when transactions are aborted, you might need to perform some recovery of your bean state. For stateless session beans, a simple exception can be thrown. Stateful session beans represent conversational states (or business processes), which might span several bean method calls.
If you require your stateful session bean to be informed of transaction boundary status events, code your enterprise bean to implement the optional javax.ejb.SessionSynchronization interface. You'll have to implement the following methods defined on the interface:
afterBegin()-- The session bean is notified after a new transaction has begun but before the business method has been invoked. The bean instance can do any database reads it might require before the transaction is committed. This is useful when caching data needed by the transaction.beforeCompletion()-- The session bean is notified after the business method is completed but before the transaction is committed. If there's any cached data, it can be updated to the database. The bean can also perform a manual rollback of the transaction by invokingsetRollBackOnly()on its session context.afterCompletion(boolean committed)-- The session bean is notified after a transaction is committed. The Boolean committed indicates whether a commit or an abort occurred. If true, the transaction was successful. If false, the transaction was aborted. At this point, the conversational state/instance variable for the bean can be restored/reset.
Since the EJB container is responsible for controlling transaction boundaries, you should not invoke any method that might interfere with the container's demarcation of these boundaries. If you're implementing container-managed transactions, make sure your enterprise bean methods don't invoke the following:
- The
commit,setAutoCommit, androllbackmethods ofjava.sql.Connection - The
getUserTransactionmethod ofjavax.ejb.EJBContext - Any method of
javax.transaction.UserTransaction
In some cases you might want to explicitly abort a transaction. There are two ways to roll back a container-managed transaction:
- Let the container automatically roll back the transaction. This occurs if a runtime exception is thrown by any of your enterprise bean's methods.
- Invoke the
setRollBackOnly()method of theEJBContextinterface. This allows you to control when rollback occurs. Perhaps if some validation check fails or if you have a data integrity problem, you might want to roll back the entire transaction and throw an application exception. An application exception doesn't automatically cause the container to roll back an exception.
In this first part of this series, you were briefed on transactions and introduced to options for EJB transactions. You have seen how container-managed transactions let you focus on the business logic of your enterprise beans, leaving the complexity of transaction logic and management to the EJB container.
With container-managed transactions, you only need to be concerned with how enterprise beans participate in transactions, and you can accomplish this through simple configuration in the EJB deployment descriptor. The Geronimo application server, OpenEJB, and XDoclet help you simplify how to specify container-managed settings and free you from the tedious task of coding the EJB artifacts yourself.
Stay tuned for Part 2 of this series, where you'll learn about bean-managed transactions, and for Part 3, where you'll bring both options together.
Learn
-
Read "Dive into EJB Web applications on Apache Geronimo" (developerWorks, July 2005) to learn about using EJBs with Geronimo.
-
Learn about deploying JavaServer Pages (JSPs), servlets, and different EJBs on Apache Geronimo in "Deploy J2EE applications on Apache Geronimo" (developerWorks, January 2006).
-
Check out Dale Green's article, "Enterprise Beans," for more about deployment descriptors in Geronimo.
-
Read more about Sun's EJB specification.
-
Learn more about XDoclet.
-
Read Nicholas Chase's interview with David Blevins, the cofounder of OpenEJB, in "OpenEJB and Apache Geronimo's EJB implementation" (developerWorks, May 2006) to learn how OpenEJB came to be chosen as the EJB implementation in Geronimo.
-
Join the mailing list at the Apache Geronimo Web site.
-
Read "Building a better J2EE server, the open source way" (developerWorks, May 2005) for great information about the development process behind Geronimo, including its JSR-88 support.
- Check out the developerWorks Apache Geronimo project area for articles, tutorials, and other resources to help you get started developing with Geronimo today.
- Find helpful resources for beginners and experienced users at the Get started now with Apache Geronimo section of developerWorks.
- Check out the IBM Support for Apache Geronimo offering, which lets you develop Geronimo applications backed by world-class IBM support.
- Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products.
- Browse all the Apache articles and free Apache tutorials available in the developerWorks Open source zone.
- Browse for books on these and other technical topics at the Safari bookstore.
Get products and technologies
- Download Apache Geronimo, Version 1.0.
- Innovate your next open source development project with IBM trial software, available for download or on DVD.
- Download your free copy of IBM WebSphere® Application Server Community Edition V1.0 -- a lightweight J2EE application server built on Apache Geronimo open source technology that is designed to help you accelerate your development and deployment efforts.
Discuss
- Participate in the discussion forum.
- Stay up to date on Geronimo developments at the Apache Geronimo blog.
- Get involved in the developerWorks community by participating in developerWorks blogs.




