JBoss.org Community Documentation

JBoss EJB3 Tutorials

A guide for using EJB3 on JBoss

Bill Burke

Jaikiran Pai

1.0


Target Audience
1. Installation
1.1. JBoss Application Server 5.x
1.2. Ant or Maven2
1.3. Set the EJB3_TUTORIAL_HOME
2. Introduction to EJB3 Stateless Beans
3. Introduction to EJB3 Stateful Beans
4. Introduction to Blob and Clob support in EJB3
5. Caching EJB3 Entities
6. Introduction to Callbacks and Callback Handlers in EJB3
7. Introduction to Composite Primary Keys and Primary Key Classes
8. Introduction to specifying dependencies in EJB3 beans
9. Introduction to using EJB2.1 client adaptors with EJB3
10. Introduction to embedding objects in EJB3 entities
11. Introduction to EJB injection in Servlets
12. Introduction to Entities in EJB3
13. Introduction to Extended Persistence Contexts
14. Introduction to dependency injection
15. Introduction to EJB3 Interceptors
16. Usage of JBoss specific deployment descriptors
17. Quartz scheduler integration
18. Binding your beans in JNDI
19. Introduction Join Inheritance in EJB3 Entities
20. Introduction to Message Driven Beans in EJB3
21. Configuring Message Driven Beans through deployment descriptors
22. Introduction to merging and querying of entities in EJB3
23. Referencing EJB3 beans in EJB2.1 and vice versa
24. Introduction to relationships between EJB3 entities
25. Introduction to binding the resources to ENC of EJB3 beans
26. Introduction to Secondary tables for EJB3 entities
27. Introduction to Security and Transactions in EJB3
28. Service POJOs (JBoss extension of EJB3)
29. Service POJOs (JBoss extension of EJB3) using deployment descriptors
30. Introduction Single Inheritance in EJB3 Entities
31. Configuring Stateful Session Beans with deployment descriptors in EJB3
32. Configuring Stateless Session Beans with deployment descriptors in EJB3
33. Table per Class inheritance in EJB3 Entities
34. Introduction to timer service in EJB3
35. Injecting Hibernate Session and Session Factory in JBoss EJB3
36. Asynchronous proxy for EJBs - JBoss specific extension to EJB3
37. Partial Deployment Descriptors
38. Message Driven POJOs (JBoss specific)
39. TODO
39.1. TODO - Provide better support for Maven

This tutorial is meant for EJB3 application developers on JBoss Application Server. The tutorial walks you through the EJB 3.0 features and how they deploy to JBoss. Please check the Chapter 1, Installation for system requirements.

It is very easy to create a Stateless Bean with EJB 3.0. All bean types are homeless in EJB 3.0 so all you have to do to create a Stateless bean is to create a bean class and have it implement at least one interface. Take a look at org.jboss.tutorial.stateless.bean.CalculatorBean

The first thing to notice is that the class is tagged as @Stateless. This marks the class as a stateless bean and the deployer will deploy that class as a stateless bean EJB container.

CalculatorBean also implements two interfaces. One is the remote interface of the EJB the other is the local interface.

Take a look at org.jboss.tutorial.stateless.bean.CalculatorRemote. To define this as the remote interface of Calculator bean you either annotate the bean class and specify what the remote interfaces are, or you annotate each remote interface the bean class implements with @javax.ejb.Remote. only need to annotate the bean class with @javax.ejb.Remote. Similar for org.jboss.tutorial.stateless.bean.CalculatorLocal as you need to annotate the bean class with @javax.ejb.Local for it to be the local interface of the CalculatorBean.

JNDI Bindings

The Calculator bean will have two JNDI bindings for the remote and Local interface. By default, JBoss will use ejbName/local and ejbName/remote for the local and remote interfaces, respectively.

Client

Open up org.jboss.tutorial.stateless.client.Client. You'll see that it looks up the stateless bean under "ejbName/remote". Also notice that there is no Home interface and you can begin executing on the stateless bean right away.

Building and Running

From the command prompt, move to the "stateless" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

$ ant
$ ant run

	

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
	

It is very easy to create a Stateful Bean with EJB 3.0. All bean types are homeless in EJB 3.0 so all you have to do to create a Stateful bean is to create a bean class and have it implement at least one interface. Take a look at org.jboss.tutorial.stateful.bean.ShoppingCartBean in the tutorial.

The first thing to notice is that the class is tagged as @Stateful. This marks the class as a stateful bean and the deployer will deploy that class as a stateful bean EJB container.

ShoppingCartBean also implements a remote interface. Take a look at org.jboss.tutorial.stateful.bean.ShoppingCart

@Remove

Take another look at org.jboss.tutorial.stateful.bean.ShoppingCartBean. Look for the method annotated as @Remove. Instead of explicitly calling EJBObject.remove() in your applications and thus polluting it further with J2EE specific code, any method tagged with @Remove will cause the stateful bean instance to be removed from the container at the end of the method call.

JNDI Bindings

The ShoppingCartBean will have its remote interface bound in JNDI, by default, under the ejbName/local and/or ejbName/remote for the local and remote interfaces, respectively.

Client

Open up org.jboss.tutorial.stateful.client.Client. You'll see that it looks up the stateful bean under "ejbName/remote". Also notice that there is no Home interface and you can begin executing on the stateful bean right away. When you access the bean in JNDI, an instance of the stateful bean will be created on the server. So, when you need a different instance of the stateful bean, you do an additional jndi.lookup() to get this new reference.

Building and Running

From the command prompt, move to the "stateful" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

$ ant
$ ant run

	

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
	

The EJB 3.0 specification has support for Blob and Clob types. The specification allows you to map the following types to an entity property:

  • java.sql.Blob

  • java.sql.Clob

  • Any Serializable Object

  • byte[], Byte[]

  • char[], String, Character[]

To use this feature just need to use the @javax.persistence.Lob annotation. The Lob annotation is an encapsulation of what type of lob you want. Below is an example of defining fields in an entity that are blobs or clobs.

@Entity
public class BlobEntity implements Serializable
{
   private long id;
   private Blob blobby;
   private Clob clobby;

   @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
   public long getId()
   {
      return id;
   }

   public void setId(long id)
   {
      this.id = id;
   }

   @Lob @Basic(fetch = FetchType.EAGER)
   public Blob getBlobby()
   {
      return blobby;
   }

   public void setBlobby(Blob blobby)
   {
      this.blobby = blobby;
   }

   @Lob @Basic(fetch = FetchType.EAGER)
   public Clob getClobby()
   {
      return clobby;
   }

   public void setClobby(Clob clobby)
   {
      this.clobby = clobby;
   }


}
		

Working with Blobs and Clobs:

Open up org.jboss.tutorial.blob.bean.LobTesterBean and look for the create() method. JBoss EJB3 is built on top of the Hibernate persistence engine. Hibernate has some helper methods for creating blobs and clobs that {{LobTesterBean}} uses.

Blob creation:

org.hibernate.Hibernate.createBlob(byte[] bytes) org.hibernate.Hibernate.createBlob(InputStream stream, int length) org.hibernate.Hibernate.createBlob(InputStream stream)

Clob creation:

org.hibernate.Hibernate.createClob(String string) org.hibernate.Hibernate.createClob(Reader reader, int length)

Blobs and clobs must only be accessed within a transaction. Blobs and clobs are also not serializable or detachable.

Mapping Strings/byte[] to Clob/Blob:

This is pretty easy, just look at BlobEntity2.java

@Entity
public class BlobEntity2 implements Serializable
{
   private long id;
   private byte[] blobby;
   private String clobby;

   @Id @GeneratedValue(strategy=GenerationType.AUTO)
   public long getId()
   {
      return id;
   }

   public void setId(long id)
   {
      this.id = id;
   }

   @Lob @Basic(fetch = FetchType.EAGER)
   public byte[] getBlobby()
   {
      return blobby;
   }

   public void setBlobby(byte[] blobby)
   {
      this.blobby = blobby;
   }

   @Lob @Basic(fetch = FetchType.EAGER)
   public String getClobby()
   {
      return clobby;
   }

   public void setClobby(String clobby)
   {
      this.clobby = clobby;
   }


}

		

Building and Running:

From the command prompt, move to the "blob" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

$ ant
$ ant run

	

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
	

View the tables and rows:

You can view the tables created by JBoss by going to the Hypersonic Service, scrolling down to the startDatabaseManager button and clicking it. A Hypersonic SQL window will be minimized, but you can open it up to look at the tables and do queries.

This tutorial shows you how to cache your entities using EJB 3.0 for JBoss

Caching primer :

To avoid roundtrips to the database, you can use a cache for your entities. JBoss EJB3 uses Hibernate as the JPA implementation which has support for a second-level cache. The Hibernate setup used for our JBoss EJB 3.0 implementation uses JBossCache as its underlying cache implementation. With caching enabled:

  • If you persist an entity (that has caching enabled) to the database via the entity manager the entity will also be added to the cache. Subsequent requests to fetch the entity, with this id, will be retrieved from cache and thus will save a database trip.
  • If you update an entity (that has caching enabled) and save the changes to the database via the entity manager the entity will also be updated in the cache.
  • If you remove an entity (that has caching enabled) from the database via the entity manager the entity will also be removed from the cache.

JBossCache allows you to specify timeouts to cached entities. Entities not accessed within a certain amount of time are dropped from the cache in order to save memory.

Furthermore, JBossCache supports clustering. If running within a cluster, and the cache is updated, changes to the entries in one node will be replicated to the corresponding entries in the other nodes in the cluster.

Enabling caching and choosing cache :

Take a look at META-INF/persistence.xml which sets up the caching for this deployment:

				
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.use_query_cache" value="true"/>
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.jbc2.JndiMultiplexedJBossCacheRegionFactory"/>
<property name="hibernate.cache.region.jbc2.cachefactory" value="java:CacheManager"/>
<property name="hibernate.cache.region.jbc2.cfg.entity" value="mvcc-entity"/>
<property name="hibernate.cache.region.jbc2.cfg.query" value="local-query"/>
<property name="hibernate.show_sql" value="true"/>
				
			

Note

These properties in the persistence.xml enabling caching and also configure JBossCache as the Cache manager. For more details about JBossCache and its configurations, have a look at the JBossCache project documentation.

Note

We have intentionally set the hibernate.show_sql property in the persistence.xml to true. When this property is set, Hibernate will print to STDOUT the sql queries that are fired to the database, when the entity is being operated upon. This will, later on, help us in verifying whether the entity is being picked up from cache or is being loaded from database through a SQL query.

Entities:

You define your entities org.jboss.tutorial.cachedentity.bean.Customer and org.jboss.tutorial.cachedentity.bean.Contact the normal way. The default behaviour is to not cache anything, even with the settings shown above, in the persistence.xml. A very simplified rule of thumb is that you will typically want to do caching for objects that rarely change, and which are frequently read. We also annotate the classes with the @Cache annotation to indicate that the entities should be cached.

				
@Entity
@Cache (usage=CacheConcurrencyStrategy.TRANSACTIONAL)
public class Customer implements java.io.Serializable
{
...
				
			

				
@Entity
@Cache (usage=CacheConcurrencyStrategy.TRANSACTIONAL)
public class Contact implements Serializable
{
...

				
			

This defines that the Customer and the Contact entities need to be cached. Any attempt to look up Customer or Contact by their primary key, will first attempt to read the entry from the cache. If it cannot be found it will try and load it up from the database.

You can also cache relationship collections. Any attempt to access the contacts collection of Customer will attempt to load the data from the cache before hitting the database:

				

@Entity
@Cache (usage=CacheConcurrencyStrategy.TRANSACTIONAL)
public class Customer implements java.io.Serializable
{
...

   @Cache (usage=CacheConcurrencyStrategy.TRANSACTIONAL)
   @OneToMany(mappedBy="customer", fetch=FetchType.EAGER, cascade=CascadeType.ALL)
   public Set<Contact> getContacts()
   {
      return contacts;
   }
...
}
				

			

Client :

Open org.jboss.tutorial.cachedentity.client.CachedEntityRun. It takes two arguments, they are the server:jndiport of the two nodes to use. If you look at the 'run' target of META-INF/build.xml you will see that they both default to localhost:1099, so in this case node1 and node2 will be the same, i.e. no clustering takes place.

Building and Running

From the command prompt, move to the "cachedentity" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure the "all" server configuration of JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] Saving customer to node1 = localhost:1099
     [java] Looking for customer on node2 = localhost:1099 (should be available in cache)
     [java] Found customer on node2 (cache). Customer details follow:
     [java] Customer: id=1; name=JBoss
     [java]     Contact: id=2; name=Kabir
     [java]     Contact: id=1; name=Bill

		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial

			

On the server you will notice these logs:

			
02:14:04,528 INFO  [STDOUT] Hibernate: insert into Customer (id, name) values (null, ?)
02:14:04,529 INFO  [STDOUT] Hibernate: call identity()
02:14:04,542 INFO  [STDOUT] Hibernate: insert into Contact (id, CUST_ID, name, tlf) values (null, ?, ?, ?)
02:14:04,543 INFO  [STDOUT] Hibernate: call identity()
02:14:04,544 INFO  [STDOUT] Hibernate: insert into Contact (id, CUST_ID, name, tlf) values (null, ?, ?, ?)
02:14:04,545 INFO  [STDOUT] Hibernate: call identity()
02:14:04,545 INFO  [EntityTestBean] Created customer named JBoss with 2 contacts
02:14:04,634 INFO  [EntityTestBean] Find customer with id = 1
02:14:04,645 INFO  [STDOUT] Hibernate: select customer0_.id as id0_1_, customer0_.name as name0_1_, contacts1_.CUST_ID as CUST4_3_, contacts1_.id as id3_, contacts1_.id as id1_0_, contacts1_.CUST_ID as CUST4_1_0_, contacts1_.name as name1_0_, contacts1_.tlf as tlf1_0_ from Customer customer0_ left outer join Contact contacts1_ on customer0_.id=contacts1_.CUST_ID where customer0_.id=?
02:14:04,682 INFO  [EntityTestBean] Customer with id = 1 found
02:14:04,756 INFO  [EntityTestBean] Find customer with id = 1
02:14:04,801 INFO  [EntityTestBean] Customer with id = 1 found

			

		

As you can see, the first time the customer with id = 1 was being requested through the entity manager, a database query was fired to fetch it, since it was not available in cache. The Customer object was then loaded from the database and stored in the cache. As can be seen in the next request to fetch the customer with the same id. This request to the entity manager, pulls up the entity from the cache instead of firing a database query.

The EJB 3.0 specification defines some callbacks, and allows you to handle these by implementing your own callback handlers. The callbacks defined for each bean are shown below:
  • Stateless session bean callbacks

    • PostConstruct - is invoked when the bean is first created, after any dependency injection is done.
    • PreDestroy - is invoked when the bean is removed from the pool or destroyed.

  • Message-driven bean callbacks

    • PostConstruct - is invoked when the bean is first created, after any dependency injection is done.
    • PreDestroy - is invoked when the bean is removed from the pool or destroyed.

  • Stateful session bean callbacks

    • PostConstruct - is invoked when the bean is first created, after any dependency injection is done.
    • PreDestroy - is invoked when the bean is removed from the pool or destroyed. It will happen before any @Remove annotated method is invoked.
    • PostActivate
    • PrePassivate

  • Entity bean callbacks

    • PrePersist - Is invoked right before the entity is created in the database. Will cascade to all entities to which this operation is cascaded.
    • PostPersist - Is invoked right after the entity is created in the database. Will cascade to all entities to which this operation is cascaded.
    • PreRemove - Is invoked right before the entity is deleted in the database. Will cascade to all entities to which this operation is cascaded.
    • PostRemove - Is invoked right after the entity is deleted in the database. Will cascade to all entities to which this operation is cascaded.
    • PreUpdate - Takes place right before the database is updated.
    • PostUpdate - Takes place immediately after the database has been updated.
    • PostLoad - Takes place right after data has been loaded from the database and associated with the entity

    The callbacks are not compulsory, and you can define the ones you want to handle.

Using callbacks on the bean class:

You use the callbacks listed above by annotating methods in the bean class. The annotations live in the javax.ejb package so for example PostConstruct would be javax.ejb.PostConstruct. You can call the methods what you like, but their method signature must return a void and take no arguments. Look at the org.jboss.tutorial.callback.bean.CustomerDAOBean stateless session bean and you will see that the preDestroyCallback() method has been annotated with @javax.ejb.PreDestroy. For session/message-driven beans, just like for interceptors, if the bean class extends another class any callback annotated methods on the super class will be invoked first.

Entity listeners for entity beans:

You can also separate out the callbacks for entity beans into a separate EntityListener class. Take a look at the org.jboss.tutorial.callback.bean.Customer entity bean, and you will see that the class has been annotated with @javax.persistence.EntityListener("org.jboss.tutorial.callback.bean.CustomerCallbackListener"). This specifies that the org.jboss.tutorial.callback.bean.CustomerCallbackListener class should be used as the callback listener class for the bean. Now open org.jboss.tutorial.callback.bean.CustomerCalbackListener and you will see that the class annotates the callback methods in the same way as when defining callbacks on the bean class itself. However, one __important difference__ is that callback methods defined in a listener class must take a single argument, which will be the bean we are working with. The parameter must be of type java.lang.Object, or the actual bean type.

Interceptors for session/message-driven beans:

Callbacks for session beans can also be put into a separate class, configured as an interceptor. This means that your interceptors can initialise themselves when constructed. The lifecycle methods in an interceptor must have the following signature:

Object <any method name>(InvocationContext)

org.jboss.tutorial.callback.bean.CustomerDAOBean specifies that it wants to use an external interceptor.

@Stateless
@Remote(CustomerDAO.class)
@Interceptors({LifecycleInterceptor.class})
public class CustomerDAOBean implements CustomerDAO
{
   ...
}
			

and org.jboss.tutorial.callback.bean.LifecycleInterceptor has a @PostConstruct annotated method. As shown, each interceptor lifecycle method must call proceed on the InvocationContext to invoke the next interceptor. Interceptors may contain both the @AroundInvoke methods for intercepting business methods, and lifecycle methods. If you want to configure lifecycle methods for your interceptors with xml, you will need to do this in the interceptor section of ejb-jar.xml, and the keywords are post-construct-method, post-activate-method, pre-passivate-method and pre-destry-method. For example:

<ejb-jar>
   <interceptors>
      <interceptor>
         <interceptor-class>org.acme.SomeInterceptor</interceptor-class>
         <around-invoke-method>
            <method-name>interceptorMethod</method-name>
         </around-invoke-method>
         <post-construct-method>
            <method-name>sendCancelMessage</method-name>
         </post-construct-method>
         <pre-destroy-method>
            <method-name>sendCancelMessage</method-name>
         </pre-destroy-method>
      </interceptor>
   </interceptors>
</ejb-jar>

			

Interceptor methods for handling lifecycle events follow exactly the same ordering and inheritance rules as business method interceptor methods, and an interceptor instance follows the lifecycle of the bean instance it is bound to.

Building and Running

From the command prompt, move to the "callbacks" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

$ ant
$ ant run

run:
     [java] Create Bill Burke and Monica Smith
     [java] Bill and Monica get married
     [java] Get all the Burkes
     [java] There are now 2 Burkes
     [java] Bill and Monica are moving abroad
	

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
	

In the jboss console window you should see:

23:52:11,162 INFO  [STDOUT] LifecycleInterceptor postConstruct
23:52:11,162 INFO  [STDOUT] PostConstruct - Have EntityManager: true
23:52:11,424 INFO  [STDOUT] -- CustomerDAOBean.create()
23:52:11,575 INFO  [STDOUT] doPrePersist: About to create Customer: Bill Burke
23:52:11,608 INFO  [STDOUT] doPostPersist: Created Customer: Bill Burke
23:52:11,644 INFO  [STDOUT] -- CustomerDAOBean.create()
23:52:11,644 INFO  [STDOUT] doPrePersist: About to create Customer: Monica Smith
23:52:11,645 INFO  [STDOUT] doPostPersist: Created Customer: Monica Smith
23:52:11,655 INFO  [STDOUT] -- CustomerDAOBean.find()
23:52:11,673 INFO  [STDOUT] doPostLoad: Loaded Customer: Monica Smith
23:52:11,700 INFO  [STDOUT] -- CustomerDAOBean.merge()
23:52:11,704 INFO  [STDOUT] doPostLoad: Loaded Customer: Monica Smith
23:52:11,705 INFO  [STDOUT] doPreUpdate: About to update Customer: Monica Burke
23:52:11,727 INFO  [STDOUT] doPostUpdate: Updated Customer: Monica Burke
23:52:11,735 INFO  [STDOUT] -- CustomerDAOBean.findByLastName(id)
23:52:12,101 INFO  [STDOUT] doPostLoad: Loaded Customer: Bill Burke
23:52:12,102 INFO  [STDOUT] doPostLoad: Loaded Customer: Monica Burke
23:52:12,113 INFO  [STDOUT] -- CustomerDAOBean.delete()
23:52:12,116 INFO  [STDOUT] doPostLoad: Loaded Customer: Bill Burke
23:52:12,118 INFO  [STDOUT] doPreRemove: About to delete Customer: Bill Burke
23:52:12,124 INFO  [STDOUT] doPostLoad: Loaded Customer: Monica Burke
23:52:12,125 INFO  [STDOUT] doPreRemove: About to delete Customer: Monica Burke
23:52:12,128 INFO  [STDOUT] doPostRemove: Deleted Customer: Bill Burke
23:52:12,128 INFO  [STDOUT] doPostRemove: Deleted Customer: Monica Burke

	

Now if you open up the persistence.xml, in this tutorial, and uncomment the following:

<property name="hibernate.show_sql" value="true"/>
	

and rebuild and run the tutorial, the output should be:

00:00:24,514 INFO  [STDOUT] LifecycleInterceptor postConstruct
00:00:24,514 INFO  [STDOUT] PostConstruct - Have EntityManager: true
00:00:24,759 INFO  [STDOUT] -- CustomerDAOBean.create()
00:00:24,760 INFO  [STDOUT] doPrePersist: About to create Customer: Bill Burke
00:00:24,760 INFO  [STDOUT] Hibernate: insert into CUSTOMER (id, CITY, FIRST, LAST, STATE, STREET, ZIP) values (null, ?, ?, ?, ?, ?, ?)
00:00:24,761 INFO  [STDOUT] Hibernate: call identity()
00:00:24,762 INFO  [STDOUT] doPostPersist: Created Customer: Bill Burke
00:00:24,773 INFO  [STDOUT] -- CustomerDAOBean.create()
00:00:24,773 INFO  [STDOUT] doPrePersist: About to create Customer: Monica Smith
00:00:24,774 INFO  [STDOUT] Hibernate: insert into CUSTOMER (id, CITY, FIRST, LAST, STATE, STREET, ZIP) values (null, ?, ?, ?, ?, ?, ?)
00:00:24,774 INFO  [STDOUT] Hibernate: call identity()
00:00:24,775 INFO  [STDOUT] doPostPersist: Created Customer: Monica Smith
00:00:24,784 INFO  [STDOUT] -- CustomerDAOBean.find()
00:00:24,785 INFO  [STDOUT] Hibernate: select customer0_.id as id8_0_, customer0_.CITY as CITY8_0_, customer0_.FIRST as FIRST8_0_, customer0_.LAST as LAST8_0_, customer0_.STATE as STATE8_0_, customer0_.STREET as STREET8_0_, customer0_.ZIP as ZIP8_0_ from CUSTOMER customer0_ where customer0_.id=?
00:00:24,786 INFO  [STDOUT] doPostLoad: Loaded Customer: Monica Smith
00:00:24,817 INFO  [STDOUT] -- CustomerDAOBean.merge()
00:00:24,818 INFO  [STDOUT] Hibernate: select customer0_.id as id8_0_, customer0_.CITY as CITY8_0_, customer0_.FIRST as FIRST8_0_, customer0_.LAST as LAST8_0_, customer0_.STATE as STATE8_0_, customer0_.STREET as STREET8_0_, customer0_.ZIP as ZIP8_0_ from CUSTOMER customer0_ where customer0_.id=?
00:00:24,818 INFO  [STDOUT] doPostLoad: Loaded Customer: Monica Smith
00:00:24,818 INFO  [STDOUT] doPreUpdate: About to update Customer: Monica Burke
00:00:24,820 INFO  [STDOUT] Hibernate: update CUSTOMER set CITY=?, FIRST=?, LAST=?, STATE=?, STREET=?, ZIP=? where id=?
00:00:24,820 INFO  [STDOUT] doPostUpdate: Updated Customer: Monica Burke
00:00:24,834 INFO  [STDOUT] -- CustomerDAOBean.findByLastName(id)
00:00:24,880 INFO  [STDOUT] Hibernate: select customer0_.id as id8_, customer0_.CITY as CITY8_, customer0_.FIRST as FIRST8_, customer0_.LAST as LAST8_, customer0_.STATE as STATE8_, customer0_.STREET as STREET8_, customer0_.ZIP as ZIP8_ from CUSTOMER customer0_ where customer0_.LAST=?
00:00:24,881 INFO  [STDOUT] doPostLoad: Loaded Customer: Bill Burke
00:00:24,881 INFO  [STDOUT] doPostLoad: Loaded Customer: Monica Burke
00:00:24,896 INFO  [STDOUT] -- CustomerDAOBean.delete()
00:00:24,897 INFO  [STDOUT] Hibernate: select customer0_.id as id8_0_, customer0_.CITY as CITY8_0_, customer0_.FIRST as FIRST8_0_, customer0_.LAST as LAST8_0_, customer0_.STATE as STATE8_0_, customer0_.STREET as STREET8_0_, customer0_.ZIP as ZIP8_0_ from CUSTOMER customer0_ where customer0_.id=?
00:00:24,897 INFO  [STDOUT] doPostLoad: Loaded Customer: Bill Burke
00:00:24,898 INFO  [STDOUT] doPreRemove: About to delete Customer: Bill Burke
00:00:24,898 INFO  [STDOUT] Hibernate: select customer0_.id as id8_0_, customer0_.CITY as CITY8_0_, customer0_.FIRST as FIRST8_0_, customer0_.LAST as LAST8_0_, customer0_.STATE as STATE8_0_, customer0_.STREET as STREET8_0_, customer0_.ZIP as ZIP8_0_ from CUSTOMER customer0_ where customer0_.id=?
00:00:24,899 INFO  [STDOUT] doPostLoad: Loaded Customer: Monica Burke
00:00:24,899 INFO  [STDOUT] doPreRemove: About to delete Customer: Monica Burke
00:00:24,900 INFO  [STDOUT] Hibernate: delete from CUSTOMER where id=?
00:00:24,900 INFO  [STDOUT] doPostRemove: Deleted Customer: Bill Burke
00:00:24,900 INFO  [STDOUT] Hibernate: delete from CUSTOMER where id=?
00:00:24,900 INFO  [STDOUT] doPostRemove: Deleted Customer: Monica Burke


	

Thus you can see how the callbacks on the entity bean wrap the interaction with the database.

The EJB 3.0 specification allows you to define a primary key class as a @Embeddable and use it as the primary key of your Entity bean. One or more properties can be used as members of the primary key for that particular table. This tutorial is an adaptation of the "relationships" tutorial. It adds a primary key class to Customer that holds both the name and id of the Customer.

	
@Embeddable
public class CustomerPK implements java.io.Serializable
{
   private long id;
   private String name;


   public CustomerPK()
   {
   }

   public CustomerPK(long id, String name)
   {
      this.id = id;
      this.name = name;
   }

   public long getId()
   {
      return id;
   }

   public void setId(long id)
   {
      this.id = id;
   }

   public String getName()
   {
      return name;
   }

   public void setName(String name)
   {
      this.name = name;
   }

   public int hashCode()
   {
      return (int) id + name.hashCode();
   }

   public boolean equals(Object obj)
   {
      if (obj == this) return true;
      if (!(obj instanceof CustomerPK)) return false;
      if (obj == null) return false;
      CustomerPK pk = (CustomerPK) obj;
      return pk.id == id && pk.name.equals(name);
   }
}

	
Mapping the primary key class:

Open up org.jboss.tutorial.composite.bean.Customer and look for the getPk() method. This defines the primary key class.

		
@EmbeddedId
public CustomerPK getPk()
{
   return pk;
}
		
		

The org.jboss.tutorial.composite.bean.CustomerPK class is mapped to org.jboss.tutorial.composite.bean.Customer just like any other embeddable object. The additional @EmbeddedId annotation specifies that it will be the primary key.

Note

If you provide a primary key class, JBoss cannot auto-generate the key for you. You must allocate a CustomerPK class and instantiate it with your id and name when you create the Customer.

Many To Many

There is a many-to-many relationship between org.jboss.tutorial.composite.bean.Customer and org.jboss.tutorial.composite.bean.Flight. In order to have a many-to-many relationship there needs to be a distinct join table that maps the many-to-many relationship. This is called an association table. You need to use the @JoinTable annotation to define this join table. The @JoinTable must be defined on both sides of the bi-directional relationship. Let's look at the Customer side of the relationship

	  		
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER, mappedBy="customers")
@JoinTable(name="flight_customer_table", joinColumns={@JoinColumn(name = "FLIGHT_ID")},
          	inverseJoinColumns={@JoinColumn(name = "CUSTOMER_ID"), @JoinColumn(name = "CUSTOMER_NAME")})
public Set<Flight> getFlights()
{
 		return flights;
}
	  		
  		

The mappedBy attribute specifies which side of the relationship is responsible for managing the relationship. If it is not set, then that side is responsible. So, for this example, the Flight Entity is responsible for managing the relation. In this example, we are specifying multiple inverseJoinColumns because Customer has a composite primary key. Let's look at the other side of the relationship in Flight:

  			
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER)
@JoinTable(name = "flight_customer_table", joinColumns = {@JoinColumn(name = "FLIGHT_ID")},
         inverseJoinColumns = {@JoinColumn(name = "CUSTOMER_ID"), @JoinColumn(name = "CUSTOMER_NAME")})
public Set<Customer> getCustomers()
{
	return customers;
}
  			
  		

The Flight Entity must also define the @ManyToMany and @JoinTable. The database associate table will look like this:

		
create table FLIGHT_CUSTOMER_TABLE (
  		CUSTOMER_ID integer,
  		CUSTOMER_NAME varchar,
  		FLIGHT_ID integer
);
		
		

Building and Running

From the command prompt, move to the "composite" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

	
$ ant
$ ant run

run:
     [java] Air France customers
     [java] Bill
     [java] Monica
     [java] USAir customers
     [java] Molly
     
	
Maven Users: Make sure the AS is not running.
$ mvn clean install -PRunSingleTutorial
	
View the tables and rows:

You can view the tables created by JBoss by going to the Hypersonic Service, scrolling down to the startDatabaseManager button and clicking it. A Hypersonic SQL window will be minimized, but you can open it up to look at the tables and do queries.

Dependencies of an EJB on a service or services, including other EJBs, may be specified through the <depends> tag of the jboss.xml deployment descriptor. The <depends> tag is analagous to the @org.jboss.ejb3.annotation.Depends annotation. The dependencies control the deployment of EJBs such that an EJB will not deploy until all of it's dependencies have successfully deployed.

jboss-service.xml :

Take a look at META-INF/jboss-service.xml. This service deployment descriptor starts a service based on org.jboss.tutorial.dependency.bean.DependedOn.

				
<server>
   <mbean code="org.jboss.tutorial.dependency.bean.DependedOn" name="jboss.test:service=DependedOn"/>
</server>
				

			

jboss.xml :

Take a look at META-INF/jboss.xml. This deployment descriptor indicates that the HasXmlMBeanDependencyBean is dependent on the jboss.test:service=DependedOn started by jboss-service.xml. The HasXmlMBeanDependencyBean will not deploy until the jboss.test:service=DependedOn service has successfully started.

Building and Running

From the command prompt, move to the "dependency" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] Lookup and bean access succeeded

		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

EJB 3.0 is backward compatible to EJB 2.x clients and supports the use of local and remote home interfaces as well as initialization methods (e.g. ejbCreate()). This capability is configured through annotations and/or through deployment descriptors.

Take a look at org.jboss.tutorial.ejb21_client_adaptors.bean.Session1Bean. Note that the class is annotated with @RemoteHome and the ejbCreate() method is annotated with @Init. The former annotation indicates that the bean provides a EJB 2.1 style home interface. The latter annotation indicates that when the create() method is invoked from the home interface, the bean is initialized via the ejbCreate method.

Note

The initialization method (annotated with @Init) name is not restricted to be ejbCreate(). You can specify any other name for that method.

org.jboss.tutorial.ejb21_client_adaptors.bean.Session2Bean illustrates the use of a local home interface.

Note

There's a very important difference between the remote and a business-remote interface. The EJB2.x remote interfaces, which extend from EJBObject, are referred through the <remote> tag in the ejb-jar.xml. On the other hand, the EJB3 style Plain Old Java Interface which is implemented by your EJB3 style POJO bean is known as the business-remote interface and is represented by the @Remote and it's corresponding <business-remote> tag in ejb-jar.xml. Similar is the case with <local> and the <business-local> tags in ejb-jar.xml.

In this tutorial, you will notice that we are using remote and local interfaces and not business-remote and business-local interfaces.

Note

Since we are not using any business-remote or business-local interfaces, in this tutorial, unlike the "jndibinding" tutorial, we cannot use the @RemoteBinding or @LocalBinding annotations to bind the EJBs. Instead, we configure the jndi-names for these beans through the META-INF/jboss.xml:

				
<session>
	<ejb-name>Session1</ejb-name>
	<jndi-name>Session1Remote</jndi-name>
</session>
<session>
	<ejb-name>Session2</ejb-name>
	<local-jndi-name>Session2Local</local-jndi-name>
</session>

				
			

Similarly, org.jboss.tutorial.ejb21_client_adaptors.bean.DeploymentDescriptorSession1Bean and org.jboss.tutorial.ejb21_client_adaptors.DeploymentDescriptorSession2Bean mimic the behavior of the first two beans, but use deployment descriptors to indicate the home interface(s) and initialization method(s). Take a look at the META-INF/ejb-jar.xml. Note the home and local-home tags that indicate the respective home interfaces. Also, note the init-method tag that indicates the initialization method(s) executed when beans are created via the home interface(s).

Building and Running

From the command prompt, move to the "ejb21_client_adaptors" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] Session1 init value is initialized
     [java] Session2 init value is initialized
     [java] DeploymentDescriptor Session1 init value is initialized
     [java] DeploymentDescriptor Session2 init value is initialized

		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

The EJB3 specification allows you to embed plain Java objects within your entities and map the properties of this embedded value object to columns within the entity's table.

The org.jboss.tutorial.embeddable.bean.Customer entity encapsulates the name of the customer in the org.jboss.tutorial.embeddable.bean.Name value object. The org.jboss.tutorial.embeddable.bean.Name value object must be tagged with the @Embeddable annotation.

	
@Embeddable
public class Name implements java.io.Serializable
	
	

The properties of Name must then be mapped to columns within Customer's table.

	
@Embedded
@AttributeOverrides({
   @AttributeOverride(name = "first", column = {@Column(name = "FIRST_NAME")}),
   @AttributeOverride(name = "last", column = {@Column(name = "LAST_NAME")})
})
public Name getName()
{
   return name;
}
	
	

Building and Running

From the command prompt, move to the "embeddable" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

	
$ ant
$ ant run

run:
     [java] Create Bill Burke and Monica Smith
     [java] Bill and Monica get married
     [java] Get all the Burkes
     [java] There are now 2 Burkes
     
	
Maven Users: Make sure the AS is not running.
$ mvn clean install -PRunSingleTutorial
	
View the tables and rows:

You can view the tables created by JBoss by going to the Hypersonic Service, scrolling down to the startDatabaseManager button and clicking it. A Hypersonic SQL window will be minimized, but you can open it up to look at the tables and do queries.

This tutorial aims at showing how EJBs can be injected in a Servlet. In this tutorial we build an EAR file which contains a EJB module (jar file) and a web module (war file).

Take a look at the META-INF/application.xml file:

			
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application PUBLIC
	"-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN"
	"http://java.sun.com/dtd/application_1_3.dtd">
<application>
  <display-name>jboss-ejb3-tutorial-ejb_injection</display-name>
  <description>Enterprise application showing injection of EJB in Servlet</description>
  <module>
    <web>
      <web-uri>jboss-ejb3-tutorial-enterprise_webapp.war</web-uri>
      <context-root>/jboss-ejb3-tutorial-enterprise_webapp</context-root>
    </web>
  </module>
  <module>
    <ejb>jboss-ejb3-tutorial-enterprise_ejb3app.jar</ejb>
  </module>
</application>

			
		

EJB module:

The EJB module in this tutorial consists of a EJB3 SLSB. Take a look at org.jboss.tutorial.enterprise_app_ejb_injection.bean.CalculatorBean:

				
@Stateless(name="calculator")
@Remote(CalculatorRemote.class)
@Local(CalculatorLocal.class)
public class CalculatorBean implements CalculatorRemote, CalculatorLocal
{
...
				
			

We will be using this bean in the servlet for performing the add and subtract operations.

Important

When a EJB packaged in a jar file is deployed as part of an EAR, the default jndi-name for the business-remote interface will be of the form EARName/BeanName/remote. Similarly the local business-remote will have the default jndi-name of the form EARName/BeanName/local.

Web module:

The web module consists of a index.html and a servlet org.jboss.tutorial.enterprise_app_ejb_injection.servlet.CalculatorActionServlet. Take a look at the WEB-INF/web.xml which configures the index.html as the welcome page and also maps the servlet.

				
 <servlet>
      <servlet-name>CalculatorActionServlet</servlet-name>
      <servlet-class>org.jboss.tutorial.enterprise_app_ejb_injection.servlet.CalculatorActionServlet</servlet-class>
 </servlet>

<!-- The servlet and jsp page mappings -->
<servlet-mapping>
   <servlet-name>CalculatorActionServlet</servlet-name>
   <url-pattern>/CalculatorAction</url-pattern>
</servlet-mapping>

<welcome-file-list>
	<welcome-file>index.html</welcome-file>
</welcome-file-list>

				
			

The org.jboss.tutorial.enterpise_app_ejb_injection.bean.CalculatorLocal will be injected in the org.jboss.tutorial.enterprise_app_ejb_injection.servlet.CalculatorActionServlet using the @EJB annotation:

				
private CalculatorLocal calculator;

/**
 * Injecting the EJB
 */
@EJB(name = "calculator")
public void setCalculator(CalculatorLocal calculator)
{
   this.calculator = calculator;
}
				
			

Important

For the injection to take place in a web module, your web.xml should use the 2.5 version of the web-app xsd:

						
<web-app version="2.5"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
						
					

Building and Running

From the command prompt, move to the "enterprise_app_ejb_injection" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

	
$ ant
     
	

This will deploy the ear into the JBossAS-5.x server ("default" configuration).

To access the servlet, open a web browser and enter http://localhost:8080/jboss-ejb3-tutorial-enterprise_webapp. This will bring up a page where you can enter two number and either click on Add or subtract.
Maven Users: Make sure the AS is not running.
$ mvn clean install -PRunSingleTutorial
	

This will create the EAR in the target folder of the tutorial. Copy this EAR to the deploy folder of JBossAS-5.x and start the server (if it's already not started). Then follow the steps mentioned above, to access the servlet from the web browser.

Entity Beans of EJB2.x have been given a complete overhaul in EJB 3.0. Entity beans (or Entities, as they are now called) are plain Java objects that can be allocated with new and attached/detached/reattached to persistence storage. Entities are not remotable and must be accessed through the new javax.persistence.EntityManager service. JBoss's EJB 3.0 entity implementation is built on top of Hibernate.

O/R Mapping :

First let's look at the example implementation of two related Entities. org.jboss.tutorial.entity.bean.Order and org.jboss.tutorial.entity.bean.LineItem. These two entities form a one-to-many relationship.

The persistence determines the persistent properties by looking for all getter/setter method pairs. By default, all getter/setter methods will be treated as persistence properties.

Defining an Entity is easy. First, annotate the class as an @Entity. In the minimum, you must at least define a primary key field using the @Id annotation.

		
@Id @GeneratedValue
public int getId()
{
   return id;
}
		
		

Note

Annotations must be defined on the getter method or on the field. However, you cannot have a mix of annotations on the field and on the methods. The following example where we are mapping the name through the field and mapping the age through the method is not allowed:

					
@Entity
public class Employee implements java.io.Serializable
{

	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	private long id;

	@Column (name="NAME")
	private String name;

	private int age;

	@Column (name="AGE")
	public int getAge()
	{
		return this.age
	}

	public String getName()
	{

	}
	// other getter/setters
}
					
				

The @Id annotation tells the container that id is the primary key property. The @GeneratedValue tells that it should be automatically generated by the container. The table name is specified using the @Table annotation

		
@Table(name = "PURCHASE_ORDER")
		
		

If the table name isn't specified it defaults to the simple name of the class. For instance, the LineItem entity would be mapped to the LINEITEM table.

One to Many Relationship :

The Order bean also defines a one to many relationship.

			
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="order")
public Collection<LineItem> getLineItems()
{
   return lineItems;
}
			
		

Generics must be used so that the container can determine what the entity Order is related to.

CascadeType.ALL specifies that when an Order is created, any LineItems held in the lineItems collection will be created as well (CascadeType.PERSIST). If the Order is deleted from persistence storage, all related LineItems will be deleted (CascadeType.REMOVE). If an Order instance is reattached to persistence storage, any changes to the LineItems collection will be merged with persistence storage (CascadeType.MERGE).

FetchType.EAGER specifies that when the Order is loaded whether or not to pre-fetch the relationship as well. If you want the LineItems to be loaded on demand, then specify FetchType.LAZY.

The mappedBy attribute specifies that this is a bi-directional relationship that is managed by the order property on the LineItem entity.

Many to One Relationship :

			
@ManyToOne
@JoinColumn(name = "order_id")
public Order getOrder()
{
   return order;
}
		
		

The @JoinColumn specifies the foreign key column within the LineItem table.

EntityManager :

The org.jboss.tutorial.entity.bean.ShoppingCartBean allocates and stores all instances of Orders and LineItems. If you look at the example you can see that ShoppingCart interacts with Order as a plain Java object. It allocates it with new. It can pass the Order back to the client. The checkout() method actually stores it with persistence storage by using the required EntityManager service. The EntityManager service is injected using field injection and the @PersistenceContext annotation.

			
@PersistenceContext
private EntityManager manager;

			
		

The EntityManager is central to EJB 3.0 as there are no homes. The EntityManager is used to do querying, creating, find by primary key, and removal of entities in EJB3.

Building and Running

From the command prompt, move to the "entity" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

	
$ ant
$ ant run

run:
     [java] Buying 2 memory sticks
     [java] Buying a laptop
     [java] Print cart:
     [java] Total: $3000.0
     [java] 2     Memory stick     1000.0
     [java] 1     Laptop     2000.0
     [java] Checkout

     
	
Maven Users: Make sure the AS is not running.
$ mvn clean install -PRunSingleTutorial
	
View the tables and rows:

You can view the tables created by JBoss by going to the Hypersonic Service, scrolling down to the startDatabaseManager button and clicking it. A Hypersonic SQL window will be minimized, but you can open it up to look at the tables and do queries.

Usually, an EntityManager in JBoss EJB 3.0 lives and dies within a JTA transaction. Once the transaction is finished, all persistent objects are detached from the EntityManager and are no longer managed. Any local caching the EntityManager instance had done is lost. JBoss EJB 3.0 allows you to define long-living EntityManagers that live beyond the scope of a JTA transaction. This is called an Extended Persistence Context. When you specify that an injected EntityManager is an extended persistence context, all object instances remain managed.

Extended persistence contexts can only be used within Stateful session beans. Take a look at org.jboss.tutorial.extended.bean.ShoppingCartBean.

			
@Stateful
@Remote(ShoppingCart.class)
public class ShoppingCartBean implements ShoppingCart
{
   @PersistenceContext(type=PersistenceContextType.EXTENDED)
   EntityManager em;

   @EJB StatelessLocal stateless;

   private Customer customer;

   public long createCustomer()
   {
      customer = new Customer();
      customer.setName("William");
      em.persist(customer);
      return customer.getId();
   }

   public void update()
   {
      customer.setName("Bill");
   }
...
}

			
		

To inject an extended persistence context, you use the type() attribute and set it to EXTENDED. If you look at the createCustomer() method you notice that it is persisting a Customer object and storing a reference to that created object within a member variable of the stateful bean. When the update() method is called, you see that the customer's state is modified. Since the entity manager used is EXTENDED, the customer member variable remains managed by the entitymanager and the modified state will be synchronized with the database.

Conversations :

An even more interesting use case is when you combine extended persistence contexts with non-transactional methods. If you interact with an extended persistence context outside of a transaction, the inserts, updates, and deletes will be queued until you access the persistence context within a transaction. This means that any persist(), merge(), or remove() method you call will not actually result in a JDBC execution and thus an update of the database until you manually call EntityManager.flush().

Why is this useful? Consider the usecase of a Setup Wizard on a website. The Wizard has seven steps, seven web pages to enter stuff in. It is extremely unwise to have a JTA transaction that spans multiple http requests, yet you do not want to commit anything to the database until all steps are complete. Your code can interact with the EntityManager as it wants and you do not have to worry about updates or writing a lot of gluecode in your application to do all the entity manager's work in the final step of the wizard. Efficient and highly performant. Because the managed persistent objects remain attached to the persistent context, all optmistic versioning checks can also be maintained within the application transaction. Here's an example on how to do this.

				
@Stateful
@Remote(ShoppingCart.class)
public class ShoppingCartBean implements ShoppingCart
{
   @PersistenceContext(type=PersistenceContextType.EXTENDED)
   EntityManager em;

   @EJB StatelessLocal stateless;

   private Customer customer;

   public long createCustomer()
   {
      customer = new Customer();
      customer.setName("William");
      em.persist(customer);
      return customer.getId();
   }

   @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
   public void never()
   {
      customer.setName("Bob");
   }



   @Remove
   public void checkout()
   {
   }
}

				
			

Calling the never() method will update the managed customer object reference, but will not result in a database update until checkout() is called. The spec requires that any time you invoke a transactional method of a stateful bean, that the EntityManager join the transaction. Therefore, our never() update will be committed at the end of the checkout() method (which by default has the REQUIRED trasaction attribute).

Building and Running

From the command prompt, move to the "extended_pc" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

	
$ ant
$ ant run

run:
     [java] Created customer: William
     [java] Customer's name should still be William as pc was not yet flushed:  Customer.getName() == William
     [java] Now that the pc has been flushed name should be 'Bob': Customer.getName() == Bob
     [java] Created customer: William
     [java] ShoppingCartBean.customer should stay managed because we're in an extended PC: Customer.getName() == Bill
     [java] Extended persistence contexts are propagated to nested EJB calls: Customer.getName() == Bill Jr.

     
	
Maven Users: Make sure the AS is not running.
$ mvn clean install -PRunSingleTutorial
	

To facilitate test driven development, the EJB 3.0 specification allows you to use annotations to inject dependencies through annotations on fields or setter methods. Instead of complicated XML ejb-refs or resource refs, you can use the @EJB and @Resource annotations to set the value of a field or to call a setter method within your session bean with anything registered within JNDI. You can use the @EJB annotation to inject EJB references and @Resource to access datasources.

Open up org.jboss.tutorial.injection.bean.ShoppingCartBean. ShoppingCartBean uses the Calculator stateless session EJB to do calculations. The example shows two ways to get access to the Calculator EJB. One is:

			
@EJB
private Calculator calculator;
			
		

When the ShoppingCartBean instance is created, the EJB container will set the calculator field using the jndiName of that particular referenced EJB.

You are not limited to injecting dependencies on fields. You can also use @EJB on a setter method. The below example from ShoppingCartBean uses the @EJB annotation to inject the reference to the Calculator session bean:

			
private Calculator set;

@EJB(beanName="org.jboss.tutorial.injection.bean.CalculatorBean")
public void setCalculator(Calculator c)
{
   set = c;
}

			
		

The @javax.annotation.Resource annotation allows you to inject resources.

		
@Resource(mappedName="DefaultDS")
private javax.sql.DataSource ds;
		
		

Note

In JBoss, whenever the mappedName() attribute is specified (with @Resource, @EJB), JBoss will use that as the GLOBAL jndi name to look it up.

The @Resource annotation is used to inject these singletons as well:

			
@Resource
javax.ejb.SessionContext ctx;

@Resource
javax.ejb.TimerService timer;

@Resource
javax.ejb.UserTransaction ut;

			
		

@EJB and @Resource also create an entry within the JNDI ENC of the bean. So, the above @EJB injection will create an entry for the reference calculator bean under "java:comp/env/ejb/calculator".

Building and Running

From the command prompt, move to the "injection" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

	
$ ant
$ ant run

run:
     [java] Buying 1 memory stick
     [java] Buying another memory stick
     [java] Buying a laptop
     [java] Print cart:
     [java] 2     Memory stick
     [java] 1     Laptop
     [java] Checkout

     
	
Maven Users: Make sure the AS is not running.
$ mvn clean install -PRunSingleTutorial
	

The EJB 3.0 spec defines the ability to apply custom made interceptors to the business methods of your session and message driven beans. EJB 3.0 interceptors take the form of methods annotated with the @javax.ejb.AroundInvoke annotation. These methods must have the following signature:

			
@javax.ejb.AroundInvoke
public Object <Arbitrary method name>(javax.ejb.InvocationContext ctx) throws java.lang.Exception

			
		

Note

You can apply interceptors to JBoss specific @Service and @Consumer beans

You can either define an interceptor method in the bean class itself, or in separate classes. There can only be one interceptor method per class.

Interceptor method in bean class :

Take a look at org.jboss.tutorial.interceptor.bean.EmailMDB . It contains this method:

			
@AroundInvoke
public Object mdbInterceptor(InvocationContext ctx) throws Exception
{
   System.out.println("*** Intercepting call to EmailMDB.mdbInterceptor()");
   return ctx.proceed();
}
			
		

This method will wrap the call to EmailMDB.onMessage(). The call to ctx.proceed() causes the next object in the chain of interceptors to get invoked. At the end of the chain of interceptors, the actual bean method gets called.

Similarly org.jboss.tutorial.interceptor.bean.EmailSystemBean has a method annotated with @AroundInvoke

				
@AroundInvoke
public Object myBeanInterceptor(InvocationContext ctx) throws Exception
{
   if (ctx.getMethod().getName().equals("emailLostPassword"))
   {
      System.out.println("*** EmailSystemBean.myBeanInterceptor - username: " + ctx.getParameters()[0]);
   }

   return ctx.proceed();
}

				
			

External interceptors :

The rest of this example will be looking at adding interceptors external to the bean class, more specifically to EmailSystemBean . Interceptors can be bound in three different ways:

  • Default

  • Class level

  • Method level

An external interceptor instance follows the lifecycle of the target bean instance.

Default interceptors :

We will see how class and method-level interceptors can be bound to a bean or a bean's method using annotations or xml. Default interceptors can only be bound via xml. A Default interceptor is an interceptor that is invoked whenever a business method is invoked on any bean within the deployment.

org.jboss.tutorial.interceptor.bean.DefaultInterceptor defines an @AroundInvoke method with the required method signature.

					
@AroundInvoke
public Object intercept(InvocationContext ctx) throws Exception
{
   System.out.println("*** DefaultInterceptor intercepting " + ctx.getMethod().getName());
   try
   {
      return ctx.proceed();
   }
   finally
   {
      System.out.println("*** DefaultInterceptor exiting");
   }
}

					
			

As mentioned, default interceptors can only be applied via xml. In META-INF/ejb-jar.xml we have the following:

					
<assembly-descriptor>
 <!-- Default interceptor that will apply to all methods for all beans in deployment -->
 <interceptor-binding>
      <ejb-name>*</ejb-name>
      <interceptor-class>org.jboss.tutorial.interceptor.bean.DefaultInterceptor</interceptor-class>
   </interceptor-binding>
   ...
</assembly-descriptor>

					
			

Using * for the ejb-name says that DefaultInterceptor is a default interceptor, i.e. it should intercept all calls to all beans within the deployment unit. We could have added more interceptor-class entries to bind more interceptors, as shown here:

				
<assembly-descriptor>
   <!-- Default interceptor that will apply to all methods for all beans in deployment -->
   <interceptor-binding>
      <ejb-name>*</ejb-name>
      <interceptor-class>org.jboss.tutorial.interceptor.bean.DefaultInterceptor</interceptor-class>
      <interceptor-class>org.jboss.tutorial.interceptor.bean.AnotherInterceptor</interceptor-class>
   </interceptor-binding>
   ...
</assembly-descriptor>

				
			

In this case DefaultInterceptor would be invoked before AnotherInterceptor.

Class-level interceptors :

Class-level interceptors can be bound using xml or annotations.

Class-level using annotations :

org.jbos.tutorial.interceptor.EmailSystemBean has been annotated:

					
@Stateless
@Interceptors ({TracingInterceptor.class})
public class EmailSystemBean
{
   ...
}

					
				

This says that the @AroundInvoke annotated method of org.jboss.tutorial.interceptor.bean.TracingInterceptor should wrap all calls to EmailSystemBean's business methods. The @Interceptors annotation can take an array of classes, so you can bind more than one class-level interceptor this way, e.g.

					
@Stateless
@Interceptors ({TracingInterceptor.class, SomeInterceptor.class})
public class EmailSystemBean
{
   ...
}

					
				

Class-level using xml :

In the META-INF/ejb-jar.xml we have the following:

					
<assembly-descriptor>
   ...
	  <!-- Class interceptor that will apply to all methods for EmailSystemBean -->
	   <interceptor-binding>
         <ejb-name>EmailSystemBean</ejb-name>
         <interceptor-class>org.jboss.tutorial.interceptor.bean.OtherInterceptor</interceptor-class>
      </interceptor-binding>
   ...
</assembly-descriptor>

					
				

Note that here we are specifying the ejb-name of the bean we want to apply the interceptor to. Hence, the @AroundInvoke annotated method of org.jboss.tutorial.interceptor.bean.OtherInterceptor will wrap all calls to EmailSystemBean's business methods.

Method-level interceptors :

Just as we can define default and class-level interceptors, we can also bind an interceptor to a particular business method of a bean

Method-level using annotations :

org.jboss.tutorial.interceptor.bean.EmailSystemBean's sendBookingConfirmationMessage() method has been annotated with the @Interceptors annotation specifying interceptors that will intercept when this perticular method is invoked.

				
@Interceptors({AccountsConfirmInterceptor.class})
public void sendBookingConfirmationMessage(long orderId)
{
   ...
}

				
			

Method-level interceptors are in addition to default and class-level interceptors, meaning that when we call EmailSystemBean.sendBookingConfirmationMessage() we get intercepted by

  • DefaultInterceptor (default)

  • TracingInterceptor (class-level)

  • OtherInterceptor (class-level)

  • AccountsConfirmInterceptor (method-level)

An interceptor method can choose to abort an invocation, by not calling InvocationContext.proceed() as is done in AccountConfirmInterceptor's interceptor method when a confirmation already exists.

Method-level using xml :

We can also bind interceptors to a single business method within a bean using xml. But first let's define an interceptor using xml. All the examples we have looked at so far have used the @AroundInvoke annotation to mark the interceptor methods. In META-INF/ejb-jar.xml we have the following:

				
<interceptors>
     <interceptor>
        <interceptor-class>org.jboss.tutorial.interceptor.bean.AccountsCancelInterceptor</interceptor-class>
        <around-invoke>
           <method-name>sendCancelMessage</method-name>
        </around-invoke>
		...

     </interceptor>
</interceptors>

				
			

This tells us that the sendCancelMessage() method of org.jboss.tutorial.interceptor.bean.AccountsCancelInterceptor is the interceptor method of this interceptor class. To bind interceptors to a particular method using xml, is much the same as how this was done for class-level interceptors apart from that we specify the method-name of the method we want to intercept. From our META-INF/ejb-jar.xml:

				
 <assembly-descriptor>
    ...
	   <!-- Method interceptor will apply to sendBookingCancellationMessage for EmailSystemBean -->
	   <interceptor-binding>
         <ejb-name>EmailSystemBean</ejb-name>
         <interceptor-class>org.jboss.tutorial.interceptor.bean.AccountsCancelInterceptor</interceptor-class>
         <method>
           <method-name>sendBookingCancellationMessage</method-name>
         </method>
      </interceptor-binding>

    ...
 </assembly-descriptor>

				
			

In the case of overloaded methods, we can further narrow down the method by specifying the method parameters, as in this example:

				
<assembly-descriptor>
   	...
	<!-- Method interceptor will apply to sendBookingCancellationMessage for EmailSystemBean -->
	   <interceptor-binding>
         <ejb-name>SomeBean</ejb-name>
         <interceptor-class>SomeInterceptor</interceptor-class>
         <method>
           <method-name>methodWithParam</method-name>
           <method-params>
           		<method-param>int</method-param>
           		<method-param>java.lang.String[]</method-param>
           </method-params>
         </method>
      </interceptor-binding>
</assembly-descriptor>

				
			

SomeInterceptor will only get applied to the method of SomeBean that matches the signature, i.e. methodWithParam(int, java.lang.String[])

Exclusion of default and class interceptors :

For some beans we may want to exclude the default interceptors configured for the deployment, and for some methods within a bean class we may want to exclude the class interceptors for the bean (and/or the default interceptors)

Annotations :

The @javax.ejb.ExcludeDefaultInterceptors annotation can be applied to a bean class or a method. If applied to a bean class, default interceptors will not get invoked for any of that bean class's methods. If applied to a bean business method, default interceptors will not get invoked when calling that method.

The @javax.ejb.ExcludeClassInterceptors annotation can be applied to a bean method. When this annotation is used class-level interceptors will not get invoked when calling that method.

org.jboss.tutorial.interceptor.bean.EmailMDB defines

						
@ExcludeDefaultInterceptors
public class EmailMDB implements MessageListener
{
   ...
}

						
					

so DefaultInterceptor is not invoked when invoking the EmailMDB.onMessage()

org.jboss.tutorial.interceptor.bean.EmailSystemBean's noop method is annotated with both @ExcludeClassInterceptors and @ExcludeDefaultInterceptors and so will not get intercepted by any of these.

						
@ExcludeClassInterceptors
@ExcludeDefaultInterceptors
public void noop()
{
   System.out.println("<In EmailSystemBean.noop business method");
   System.out.println("Exiting EmailSystemBean.noop business method>");
}

						
					

XML :

We can also exclude class and method-level interceptors within an interceptor-binding by specifying the exclude-class-interceptors and exclude-default-interceptors elements.

						
<assembly-descriptor>
      ...

	   <interceptor-binding>
         <ejb-name>EmailSystemBean</ejb-name>
         <exclude-default-interceptors>true</exclude-default-interceptors>
         <exclude-class-interceptors>true</exclude-class-interceptors>
         <method>
           <method-name>noop2</method-name>
         </method>
      </interceptor-binding>
	...
</assembly-descriptor>
						
					

Inheritance Ordering :

If an interceptor (or bean class) inherits from another class which has a method declared to be an interceptor method, the interceptor methods from the super class will be invoked first. However, if the interceptor method in the superclass has been overridden (regardless of if the overriding method is declared to be an interceptor method or not) the superclass method is not invoked.

Both org.jboss.tutorial.interceptor.bean.AccountsConfirmInterceptor and org.jboss.tutorial.interceptor.bean.AccountsCancelInterceptor inherit from org.jboss.tutorial.interceptor.bean.AccountsInterceptor.

AccountsInterceptor declares the following interceptor method:

				
@AroundInvoke
public Object intercept(InvocationContext ctx) throws Exception
{
   System.out.println("*** AccountsInterceptor intercepting " + ctx.getMethod().getName());
   try
   {
      return ctx.proceed();
   }
   finally
   {
      System.out.println("*** AccountsInterceptor exiting");
   }
}

				
			

This method is overridden by AccountsConfirmInterceptor, even though it is not AccountsConfirmInterceptor's interceptor method:

				
public class AccountsConfirmInterceptor extends AccountsInterceptor
{
   ...
   public Object intercept(InvocationContext ctx) throws Exception
   {
      //overrides AccountsInterceptor.intercept() so that will not be invoked
      return null;
   }


   @AroundInvoke
   public Object sendConfirmMessage(InvocationContext ctx) throws Exception
   {
      ...
   }
}
				
			

So when invoking AccountsConfirmInterceptor, we simply invoke its sendConfirmMessage() method. AccountsCancelInterceptor, on the other hand, does not override AccountsInterceptor.intercept(), so when invoking AccountsCancelInterceptor, we first get intercepted by AccountsInterceptor.intercept() followed by AccountsCancelInterceptor.sendCancelMessage().

Injection in interceptors:
Using Annotations :

Just like a bean class, an interceptor can be the target of dependency injection (see the "injection" tutorial for details about injection). The format for how this works is the same, and the injection works off the same ENC as the bean to which the interceptor is bound.

org.jboss.tutorial.interceptor.bean.AccountsConfirmInterceptor has dependency injection set up using annotations:

					
public class AccountsConfirmInterceptor extends AccountsInterceptor
{
   @Resource(mappedName="java:ConnectionFactory")
   QueueConnectionFactory cf;

   @Resource(mappedName="queue/tutorial/accounts")
   Queue queue;

   @PersistenceContext
   EntityManager em;

   ...

					
				

Remember that interceptors follow the same lifecycle as the bean they are bound to. The interceptors are created at the same time as the bean instance is created, and dependecy injection occurs before the first business method is called. In the example just shown, the container:
  • Looks up java:ConnectionFactory, binds it in the ENC and injects this into the field cf

  • Looks up queue/tutorial/accounts, binds it in the ENC and injects this into the field queue

  • Obtains the default EntityManager and injects this into the field em

Using XML :

Injection, into interceptors, can also be configured via xml, using the traditional ejb-ref, ejb-local-ref, resource-ref, resource-env-ref etc. which bind the global names. The only difference from beans is that we put this into the {{interceptors}} element defining the interceptor.

					
<interceptors>
     <interceptor>
        <interceptor-class>org.jboss.tutorial.interceptor.bean.AccountsCancelInterceptor</interceptor-class>
        <around-invoke>
           <method-name>sendCancelMessage</method-name>
        </around-invoke>
        <resource-ref>
        	<res-ref-name>jms/ConnFactory</res-ref-name>
           <res-type>javax.jms.QueueConnectionFactory</res-type>
           <res-auth>Container</res-auth>
           <mapped-name>java:/ConnectionFactory</mapped-name>
           <injection-target>
              <injection-target-class>org.jboss.tutorial.interceptor.bean.AccountsCancelInterceptor</injection-target-class>
              <injection-target-name>cf</injection-target-name>
           </injection-target>
        </resource-ref>
        <resource-env-ref>
        	<resource-env-ref-name>accountsQueue</resource-env-ref-name>
           <resource-env-ref-type>javax.jms.Queue</resource-env-ref-type>
           <mapped-name>queue/tutorial/accounts</mapped-name>
           <injection-target>
              <injection-target-class>org.jboss.tutorial.interceptor.bean.AccountsCancelInterceptor</injection-target-class>
              <injection-target-name>queue</injection-target-name>
           </injection-target>
        </resource-env-ref>
     </interceptor>
</interceptors>

					
				

  • The container looks up java:/ConnectionFactory in JNDI and creates the java:comp/env/jms/ConnFactory entry in the bean's ENC, and following bean creation it injects the connection factory into AccountsCancelInterceptor's cf field.

  • The container looks up queue/tutorial/accounts in JNDI and creates the java:comp/env/accountsQueue entry in the bean's ENC, and following bean creation it injects the queue into AccountsCancelInterceptor's queue field.

Interceptor Ordering :

By default the ordering of interceptors when invoking a method are

  • External interceptors

  • Default interceptors, if present

  • Class interceptors, if present

  • Method interceptors, if present

  • Interceptor method on the bean class (using @AroundInvoke)

Within each group (default, class, method) the order of the interceptors are from left to right as defined in the @Interceptors annotation, and then the xml interceptors.

Overriding interceptor ordering :

You can override the default sort order of the external interceptors by specifiying an interceptor-binding with an interceptor-order specifying the order of the interceptors

					
<assembly-descriptor>
      <interceptor-binding>
         <ejb-name>EmailSystemBean</ejb-name>
         <interceptor-order>
            <interceptor-class>org.jboss.tutorial.interceptor.bean.AccountsCancelInterceptor</interceptor-class>
            <interceptor-class>org.jboss.tutorial.interceptor.bean.DefaultInterceptor</interceptor-class>
            <interceptor-class>org.jboss.tutorial.interceptor.bean.OtherInterceptor</interceptor-class>
            <interceptor-class>org.jboss.tutorial.interceptor.bean.TracingInterceptor</interceptor-class>
         </interceptor-order>
         <method>
           <method-name>sendBookingCancellationMessage</method-name>
         </method>
      </interceptor-binding>
      ...
</assembly-descriptor>

					
				

So when invoking EmailSystemBean.sendBookingCancellationMessage we will get the following interception order, quite different from the default sort order
  • AccountsCancelInterceptor (method-level)

  • DefaultInterceptor (default interceptor)

  • OtherInterceptor (2nd class-level interceptor)

  • TracingInterceptor (1st class-level interceptor)

InvocationContext :

As you have seen the @AroundInvoke annotated interceptor methods all take a parameter of type javax.ejb.InvocationContext. The definition of InvocationContext is:

				

package javax.interceptor;

public interface InvocationContext {

	public java.lang.Object getTarget();

    public java.lang.reflect.Method getMethod();

	public java.lang.Object[] getParameters();

  	public void setParameters(java.lang.Object[] arg0);

	public java.util.Map getContextData();

	public java.lang.Object proceed() throws java.lang.Exception;
}
				

			

getBean() - returns the bean instance on which we are calling a method

getMethod() - returns the method we are calling on the bean instance

getParameters() - returns the parameters for the method call

setParameters() - allows you to modify the parameters for the method call

getContextData - The EJB interceptions are stateless, but the same InvocationContext instance is used for each interceptor method in a chain. If you want to pass values between interceptor methods in the business method invocation you can store them in the Map retured by getContextData().

proceed - As discussed, invokes the next interceptor, or if we are in the last interceptor invokes the target bean method.

In some case we might want access to the EJBContext, this can be injected into the interceptor instance using the @Resource annotation. The AroundInvoke methods share the JNDI name space of the bean for whose methods they are invoked and execute within the same transaction and security context as the business methods for which they are invoked.

Building and Running

Note

To build and run the example, make sure you have installed JBoss 5.x. See the Section 1.1, “JBoss Application Server 5.x” for details.

From the command prompt, move to the "interceptor" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] Starting
     [java]
     [java] Calling emailLostPassword
     [java] Waiting 2 seconds
     [java]
     [java] Calling sendBookingConfirmationMessage
     [java] Waiting 2 seconds
     [java]
     [java] Calling sendBookingConfirmationMessage
     [java] Waiting 2 seconds
     [java]
     [java] Calling sendBookingCancellationMessage
     [java] Waiting 2 seconds
     [java]
     [java] Calling noop
     [java] Waiting 2 seconds
     [java]
     [java] Calling noop2
     [java] Waiting 2 seconds
     [java] Done

		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

					
21:49:01,888 INFO  [STDOUT] *** DefaultInterceptor intercepting emailLostPassword
21:49:01,889 INFO  [STDOUT] *** TracingInterceptor intercepting emailLostPassword
21:49:01,889 INFO  [STDOUT] *** OtherInterceptor intercepting emailLostPassword
21:49:01,889 INFO  [STDOUT] *** EmailSystemBean.myBeanInterceptor - username: whatever
21:49:01,889 INFO  [STDOUT] <In EmailSystemBean.emailLostPassword business method
21:49:02,083 INFO  [STDOUT] Message sent successfully to remote queue.
21:49:02,130 INFO  [STDOUT] Exiting EmailSystemBean.emailLostPassword business method>
21:49:02,130 INFO  [STDOUT] *** OtherInterceptor exiting
21:49:02,130 INFO  [STDOUT] *** TracingInterceptor invocation of org.jboss.tutorial.interceptor.bean.EmailSystemBean.emailLostPassword() took 241ms
21:49:02,130 INFO  [STDOUT] *** DefaultInterceptor exiting
21:49:02,179 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
21:49:02,180 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
21:49:02,185 INFO  [STDOUT] *** EmailMDB.mdbInterceptor intercepting
21:49:02,185 INFO  [STDOUT]
----------------
EMailMDB - Got message, sending email
----------------
21:49:04,251 INFO  [STDOUT] *** DefaultInterceptor intercepting sendBookingConfirmationMessage
21:49:04,251 INFO  [STDOUT] *** TracingInterceptor intercepting sendBookingConfirmationMessage
21:49:04,251 INFO  [STDOUT] *** OtherInterceptor intercepting sendBookingConfirmationMessage
21:49:04,251 INFO  [STDOUT] *** AccountsConfirmInterceptor intercepting
21:49:04,395 INFO  [STDOUT] *** AccountsConfirmInterceptor - recording confirmation
21:49:04,431 INFO  [STDOUT] *** AccountsConfirmInterceptor - notifying accounts dept sendBookingConfirmationMessage
21:49:04,440 INFO  [STDOUT] <In EmailSystemBean.sendBookingConfirmationMessage business method
21:49:04,455 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
21:49:04,455 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
21:49:04,458 INFO  [STDOUT] *** DefaultInterceptor intercepting onMessage
21:49:04,459 INFO  [STDOUT]
----------------
AccountsMDB - Got message Confirming order 100
----------------
21:49:04,459 INFO  [STDOUT] *** DefaultInterceptor exiting
21:49:04,466 INFO  [STDOUT] Message sent successfully to remote queue.
21:49:04,467 INFO  [STDOUT] Exiting EmailSystemBean.sendBookingConfirmationMessage business method>
21:49:04,467 INFO  [STDOUT] *** AccountsConfirmInterceptor exiting
21:49:04,467 INFO  [STDOUT] *** OtherInterceptor exiting
21:49:04,467 INFO  [STDOUT] *** TracingInterceptor invocation of org.jboss.tutorial.interceptor.bean.EmailSystemBean.sendBookingConfirmationMessage() took 216ms
21:49:04,467 INFO  [STDOUT] *** DefaultInterceptor exiting
21:49:04,478 INFO  [STDOUT] *** EmailMDB.mdbInterceptor intercepting
21:49:04,478 INFO  [STDOUT]
----------------
EMailMDB - Got message, sending email
----------------
21:49:06,533 INFO  [STDOUT] *** DefaultInterceptor intercepting sendBookingConfirmationMessage
21:49:06,533 INFO  [STDOUT] *** TracingInterceptor intercepting sendBookingConfirmationMessage
21:49:06,533 INFO  [STDOUT] *** OtherInterceptor intercepting sendBookingConfirmationMessage
21:49:06,533 INFO  [STDOUT] *** AccountsConfirmInterceptor intercepting
21:49:06,547 INFO  [STDOUT] *** AccountsConfirmInterceptor - order has already been confirmed aborting
21:49:06,548 INFO  [STDOUT] *** AccountsConfirmInterceptor exiting
21:49:06,548 INFO  [STDOUT] *** OtherInterceptor exiting
21:49:06,548 INFO  [STDOUT] *** TracingInterceptor invocation of org.jboss.tutorial.interceptor.bean.EmailSystemBean.sendBookingConfirmationMessage() took 15ms
21:49:06,548 INFO  [STDOUT] *** DefaultInterceptor exiting
21:49:08,577 INFO  [STDOUT] *** AccountsInterceptor intercepting sendBookingCancellationMessage
21:49:08,577 INFO  [STDOUT] *** AccountsCancelInterceptor intercepting sendBookingCancellationMessage
21:49:08,577 INFO  [STDOUT] *** AccountsConfirmInterceptor - notifying accounts dept
21:49:08,593 INFO  [STDOUT] *** DefaultInterceptor intercepting sendBookingCancellationMessage
21:49:08,593 INFO  [STDOUT] *** OtherInterceptor intercepting sendBookingCancellationMessage
21:49:08,593 INFO  [STDOUT] *** TracingInterceptor intercepting sendBookingCancellationMessage
21:49:08,593 INFO  [STDOUT] <In EmailSystemBean.sendBookingCancellationMessage business method
21:49:08,605 INFO  [STDOUT] Message sent successfully to remote queue.
21:49:08,606 INFO  [STDOUT] Exiting EmailSystemBean.sendBookingCancellationMessage business method>
21:49:08,606 INFO  [STDOUT] *** TracingInterceptor invocation of org.jboss.tutorial.interceptor.bean.EmailSystemBean.sendBookingCancellationMessage() took 13ms
21:49:08,606 INFO  [STDOUT] *** OtherInterceptor exiting
21:49:08,606 INFO  [STDOUT] *** DefaultInterceptor exiting
21:49:08,606 INFO  [STDOUT] *** AccountsCancelInterceptor exiting
21:49:08,606 INFO  [STDOUT] *** AccountsInterceptor exiting
21:49:08,617 INFO  [STDOUT] *** EmailMDB.mdbInterceptor intercepting
21:49:08,617 INFO  [STDOUT]
----------------
EMailMDB - Got message, sending email
----------------
21:49:08,620 INFO  [STDOUT] *** DefaultInterceptor intercepting onMessage
21:49:08,620 INFO  [STDOUT]
----------------
AccountsMDB - Got message Cancelling order 100
----------------
21:49:08,620 INFO  [STDOUT] *** DefaultInterceptor exiting
21:49:10,628 INFO  [STDOUT] <In EmailSystemBean.noop business method
21:49:10,628 INFO  [STDOUT] Exiting EmailSystemBean.noop business method>
21:49:12,648 INFO  [STDOUT] <In EmailSystemBean.noop2 business method
21:49:12,648 INFO  [STDOUT] Exiting EmailSystemBean.noop2 business method>

						
					

Note

Look at the JBoss console window to see the output of the interceptions taking place, the EmailMDB and AccountsMDB might occur in slightly different places since they execute asynchronously.

You can ignore the [WARN] messages:

21:49:02,179 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container
21:49:02,180 WARN  [InterceptorsFactory] EJBTHREE-1246: Do not use InterceptorsFactory with a ManagedObjectAdvisor, InterceptorRegistry should be used via the bean container

					

The EJB 3.0 specification supports the usage of deployment descriptors to describe ejb metadata or override most metadata described via annotations. The jboss.xml deployment descriptor describes JBoss specific metadata, including remote and local JNDI names, cluster configuration, a security domain, additional references, security principal, and container configuration. Annotations which define the ejb (e.g. @Stateful, @Remote) cannot be overridden.

jboss.xml :

Take a look at META-INF/jboss.xml.

  • The jndi-name element defines the remote JNDI binding for the ShoppingCartBean stateful session bean. This JNDI binding overrides the default binding or a binding specified through the @RemoteBinding annotation. Similarly, the local-jndi-name element specifies the local JNDI binding.
  • The clustered element indicates that the ejb is clustered, with partition-name and load-balance-policy specifying the cluster name and load balance policy, respectively. These elements will override the parameters specified in the @Clustered annotation. In this example, you will see that the ShoppingCartBean ejb is clustered.

    Note

    If you build and run this example with the "default" server configuration, the deployment will fail, as the default server configuration does not support clustering. You will have to run this example on the "all" server configuration which supports clustering
  • The security-domain element specifies a security domain for the ejb, overriding any security domain set through the @SecurityDomain annotation. In this example, you will see that a security domain is set through the jboss.xml deployment descriptor and unless a Principal and Credential (i.e. user/password) is set in the client, requests to the ejb will fail with a javax.ejb.EJBAccessException.

    Note

    The jboss.xml shows using the security-domain at a bean level as well at the entire deployment level. Each bean can have its own security-domain and will override the one set at the deployment level.

Building and Running

From the command prompt, move to the "jboss_deployment_descriptor" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure the "all" server configuration of JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] Attempting to buy 1 memory stick with incorrect password
     [java] Caught javax.ejb.EJBAccessException as expected
     [java] Setting user/password
     [java] bill is a shopper, so is allowed to buy
     [java] Buying 1 memory stick
     [java] Buying another memory stick
     [java] Buying a laptop
     [java] Print cart:
     [java] 2     Memory stick
     [java] 1     Laptop
     [java] bill is not a clerk, so is not allowed to price check
     [java] Caught EJBAccessException as expected
     [java] Checkout
     [java] Should throw an object not found exception by invoking on cart after @Remove method
     [java] Successfully caught no such object exception.
     [java] Caught javax.ejb.EJBAccessException, for SLSB, as expected
     [java] Now passing the correct user/password to access the SLSB
     [java] Successfully accessed SLSB

		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

JBossAS comes bundled with Quartz integration packaged as a JCA Message-Inflow resource adapter quartz-ra.rar. This integration allows you to schedule stateless or stateful quartz jobs and have the job be posted to a Message Driven bean. The Quartz Resource Adapter creates a non-persistent scheduler. Jobs are created from the information in the MDB's activation config spec.

Note

Currently only cron jobs are allowed to be configured.

Take a look at the org.jboss.tutorial.jca_inflow_quartz.bean.AnnotatedQuartzMDBBean:

			
@MessageDriven(activationConfig =
{@ActivationConfigProperty(propertyName = "cronTrigger", propertyValue = "0/2 * * * * ?")})
@ResourceAdapter("quartz-ra.rar")
public class AnnotatedQuartzMDBBean implements Job
{
...
			
		

This is a simple MDB that implements the org.quartz.Job interface. The cronTrigger activation spec attribute is required. In this you specify a cron syntax as documented in the Quartz documentation. The @org.jboss.ejb3.annotation.ResourceAdapter annotation is used to tell the EJB container which resource adapter to use for the inflow implementation.

Using deployment descriptors :

Instead of using annotations, you can also use a deployment descriptor to configure the MDB for Quartz integration. Take a look at META-INF/ejb-jar.xml and META-INF/jboss.xml The ejb-jar.xml configures the cronTrigger through the <activation-config> element:

				
<message-driven>
   <ejb-name>ExampleMDB</ejb-name>
   <ejb-class>org.jboss.tutorial.quartz.bean.QuartzMDBBean</ejb-class>
   <transaction-type>Container</transaction-type>
   <activation-config>
      <activation-config-property>
         <activation-config-property-name>cronTrigger</activation-config-property-name>
         <activation-config-property-value>0/2 * * * * ?</activation-config-property-value>
      </activation-config-property>


   </activation-config>
</message-driven>
				
			

The jboss.xml configures the resource-adapter-name :

				
<message-driven>
   <ejb-name>ExampleMDB</ejb-name>
   <resource-adapter-name>quartz-ra.rar</resource-adapter-name>
</message-driven>
				
			

Building and Running

From the command prompt, move to the "jca_inflow_quartz" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure the "default" server configuration of JBossAS-5.x is running

			
$ ant
		     
			

This will deploy the jar into the JBossAS-5.x server ("default" configuration).

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

This will create the jar in the target folder of the tutorial. Copy this jar to the deploy folder of JBossAS-5.x

On the server side when the application is deployed, you will notice these logs:

			
18:46:57,053 INFO  [QuartzMDBBean] ************** JOB: job.4.1232457416127
18:46:58,023 INFO  [AnnotatedQuartzMDBBean] ************** here in annotated!!!!
18:46:58,026 INFO  [QuartzMDBBean] ************** JOB: job.2.1232457416077
18:47:00,019 INFO  [AnnotatedQuartzMDBBean] ************** here in annotated!!!!
18:47:00,021 INFO  [QuartzMDBBean] ************** JOB: job.2.1232457416077
18:47:00,023 INFO  [QuartzMDBBean] ************** JOB: job.4.1232457416127
18:47:02,020 INFO  [AnnotatedQuartzMDBBean] ************** here in annotated!!!!
18:47:02,023 INFO  [QuartzMDBBean] ************** JOB: job.2.1232457416077
18:47:03,019 INFO  [QuartzMDBBean] ************** JOB: job.4.1232457416127
18:47:04,022 INFO  [AnnotatedQuartzMDBBean] ************** here in annotated!!!!
18:47:04,024 INFO  [QuartzMDBBean] ************** JOB: job.2.1232457416077
18:47:06,019 INFO  [AnnotatedQuartzMDBBean] ************** here in annotated!!!!
18:47:06,021 INFO  [QuartzMDBBean] ************** JOB: job.2.1232457416077
18:47:06,024 INFO  [QuartzMDBBean] ************** JOB: job.4.1232457416127
18:47:08,019 INFO  [AnnotatedQuartzMDBBean] ************** here in annotated!!!!
18:47:08,021 INFO  [QuartzMDBBean] ************** JOB: job.2.1232457416077
18:47:09,020 INFO  [QuartzMDBBean] ************** JOB: job.4.1232457416127

			
		

By default, when the application is deployed in a jar, session beans will bind to JNDI in the form ejbName/remote for remote interfaces and ejbName/local in the case of local interfaces. When the EJBs are deployed in an .ear file, the default jndi binding will be prepended by the name of the .ear file. So if the ear file name is foo.ear the default jndi names would be foo/EJB-NAME/remote and foo/EJB-NAME/local. You can override this behavior by defining your own @org.jboss.ejb3.annotation.LocalBinding and/or @org.jboss.ejb3.annotation.RemoteBinding.

Local Interface JNDI Binding :

To change the JNDI name for your local interface use the @org.jboss.ejb3.annotation.LocalBinding annotation.

				
@Stateless
@LocalBinding(jndiBinding="custom/MySession")
public class MySessionBean implements MySession
{
}

				
			

Remote Interface JNDI Binding :

To change the JNDI name for your remote interface use the @org.jboss.ejb3.annotation.RemoteBinding annotation.

				
@Stateless
@RemoteBinding(jndiBinding="custom/remote/MySession")
public class MySessionBean implements MySession
{
}

				
			

Persistence unit JNDI Bindings :

Persistence units are not bound into JNDI by default. You can bind them by defining some jboss specific properties in persistence.xml.

				
<persistence>
   <persistence-unit name="manager1">
      <jta-data-source>java:/DefaultDS</jta-data-source>
      <jar-file>MyApp.jar</jar-file>
      <class>org.acme.Employee</class>
      <class>org.acme.Person</class>
      <class>org.acme.Address</class>
      <properties>
         <property name="jboss.entity.manager.jndi.name" value="java:/Manager1"/>
         <property name="jboss.entity.manager.factory.jndi.name" value="java:/Manager1Factory"/>
      </properties>
   </persistence-unit>
</persistence>

				
			

Client :

Open up org.jboss.tutorial.jndibinding.client.Client. You'll see that it looks up the stateless bean under "Calculator". Also notice that there is no Home interface and you can begin executing on the stateless bean right away.

Building and Running

From the command prompt, move to the "jndibinding" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] 1 + 1 = 2
     [java] 1 - 1 = 0

		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

The EJB3 specification allows you to define entities that inherit from one another. The inheritance relationships can be reflected in queries as well. So, if you queried based on the base class, the query is polymorphic.

The tutorial example uses the join table strategy to map an inheritance relationship of org.jboss.tutorial.joininheritance.bean.Pet, which is the base class for org.jboss.tutorial.joininheritance.bean.Cat and org.jboss.tutorial.joininheritance.bean.Dog.

With the join table strategy there is a table per class in the hierarchy, but the subclass tables only have the extra attribute they define in their subclass. A discriminator column is NOT required to differentiate between which class type is persisted in a particular row unlike the single table mapping. The persistence manager does not need a discrimiator column to figure out the type.

This is what the annotations look like for Pet:

			
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Pet implements java.io.Serializable
{
...
			

		

			
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Dog extends Pet
{
...

			
		

Polymorphic Queries :

org.jboss.tutorial.joininheritance.bean.PetDAOBean stateless EJB wraps some polymorphic queries.

				
public List findByWeight(double weight)
{
   return manager.createQuery("from Pet p where p.weight < :weight").setParameter("weight", weight).getResultList();
}


				
			

Even though the findByWeight method queries on Pet, either Dog or Cat instances can be returned.

Table Mapping :

The table mapping for this example looks like this:

				
create table PET (
  ID integer primary key,
  ANIMAL_TYPE varchar,
  NAME varchar,
  WEIGHT double
);

create table CAT (
  ID integer primary key,
  LIVES int
);

create table DOG (
  ID integer primary key,
  NUMBONES int
);

				

			

The join inheritance mapping is less efficient than the single table strategy as the SQL query is more complicated.

Building and Running

From the command prompt, move to the "joininheritance" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] Sox
     [java] Junior

		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

View the tables and rows:

You can view the tables created by JBoss by going to the Hypersonic Service, scrolling down to the startDatabaseManager button and clicking it. A Hypersonic SQL window will be minimized, but you can open it up to look at the tables and do queries.

This example shows you how to implement an MDB with EJB 3.0 using annotations.

Take a look at org.jboss.tutorial.mdb.bean.ExampleMDB. The @MessageDriven annotation defines the bean as an MDB. The activationConfig attribute contains much of the MDB configuration via @ActivationConfigProperty. Also notice that the MDB source contains properties for destinationType and destination

The following is the list of standard Activation Config Properties available from the JCA 1.5 specification. Also listed are the respective types and default values where defined.

Name Type Remarks Mandatory? Default value
destination java.lang.String The jndi name of the Queue or Topic Yes  
destinationType java.lang.String The type of destination valid values are javax.jms.Queue or javax.jms.Topic No  
messageSelector java.lang.String The message selector of the subscription No  
acknowledgeMode int The type of acknowledgement when not using transacted jms - valid values AUTO_ACKNOWLEDGE or DUPS_OK_ACKNOWLEDGE No AUTO_ACKNOWLEDGE
clientID java.lang.String The client id of the connection No  
subscriptionDurability String Whether topic subscriptions are durable. Valid values are Durable or NonDurable No NonDurable
subscriptionName String The subsription name of the topic subscription No  

The following is the list of Activation Config Properties available as JBoss extensions.

Name Type Remarks Mandatory? Default value
isTopic boolean Sets the destinationType No false
providerAdapterJNDI java.lang.String The jndi name of the jms provider No java:/DefaultJMSProvider
user java.lang.String The user id used to connect to the jms server No  
pass java.lang.String The password of the user No  
maxMessages int Read this number of messages before delivering messages to the mdb. Each message is delivered individually on the same thread in an attempt to avoid context excessive context switching No 1
minSession int The minimum number of jms sessions that are available to concurrently deliver messages to this mdb No 1
maxSession int The maximum number of jms sessions that are available to concurrently deliver messages to this mdb No 15
reconnectInterval long The length of time in seconds between attempts to (re-)connect to the jms provider No 10 seconds
keepAlive long The length of time in milliseconds that sessions over the minimum are kept alive No 60 seconds
sessionTransacted boolean Whether the sessions are transacted No true
useDLQ boolean Whether to use a DLQ handler No true
dLQJNDIName java.lang.String The JNDI name of the DLQ No queue/DLQ
dLQHandler java.lang.String The org.jboss.resource.adapter.jms.inflow.DLQHandler implementation class name No org.jboss.resource.adapter.jms.inflow.dlq.GenericDLQHandler
dLQUser java.lang.String The user id used to make the dlq connection to the jms server No  
dLQPassword java.lang.String The password of the dLQUser No  
dLQClientID java.lang.String The client id of the dlq connection No  
dLQMaxResent int The maximum number of times a message is redelivered before it is sent to the DLQ No 5
redeliverUnspecified boolean Whether to attempt to redeliver a message in an unspecified transaction context No true
transactionTimeout int Time in seconds for the transaction timeout No Default is the timeout set for the resource manager
DeliveryActive boolean Whether the MDB should make the subscription at initial deployment or wait for start() or stopDelivery() on the corresponding MBean. You can set this to false if you want to prevent messages from being delivered to the MDB (which is still starting) during server startup No true

The Destination :

The queue-example-service.xml file defines the queues for this tutorial.

Configuring Default MDB Properties :

You can configure MDBs to have default properties using the @org.jboss.ejb3.annotation.DefaultActivationSpecs annotations. Take a look at custom-ejb3-interceptors-aop.xml. Here we define a custom container configuration domain, "Custom Message Driven Bean", that adds a @DefaultActivationSpecs annotation and destinationType and destination properties to each MDB using this domain. Now take a look at org.jboss.tutorial.mdb.bean.DefaultedExampleMDB. The MDB is configured to use the "Custom Message Driven Bean" container configuration domain via the @AspectDomain annotation. Note there are no properties defined in the @MessageDriven annotation (they are all from the defaults).

Building and Running

From the command prompt, move to the "mdb" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] Message sent successfully to remote queue queue/tutorial/example
     [java] Message sent successfully to remote queue queue/tutorial/defaultedexample



		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

On the server console, you will notice the following logs:

				
15:37:57,148 INFO  [STDOUT] ----------------
15:37:57,148 INFO  [STDOUT] Received defaulted message
15:37:57,148 INFO  [STDOUT] ----------------
15:37:57,210 INFO  [STDOUT] ----------------
15:37:57,210 INFO  [STDOUT] Received message
15:37:57,210 INFO  [STDOUT] ----------------

				
			

You configure properties by using the <message-driven> element and sub elements which correspond to the @ActivationConfigProperty annotation.

ejb-jar.xml:

			
<message-driven>
	<ejb-name>ExampleMDB</ejb-name>
	<ejb-class>org.jboss.tutorial.mdb_deployment_descriptor.bean.ExampleMDB</ejb-class>
     <transaction-type>Bean</transaction-type>
     <message-destination-type>javax.jms.Queue</message-destination-type>
   <activation-config>
      <activation-config-property>
      	<activation-config-property-name>acknowledgeMode</activation-config-property-name>
      	<activation-config-property-value>AUTO_ACKNOWLEDGE</activation-config-property-value>
      </activation-config-property>
    </activation-config>

</message-driven>

			
		

jboss.xml

			
<message-driven>
   <ejb-name>ExampleMDB</ejb-name>
   <destination-jndi-name>queue/tutorial/example</destination-jndi-name>
</message-driven>
			
		

The queue/tutorial/example is configured through the queue-example-service.xml

Building and Running

From the command prompt, move to the "mdb_deployment_descriptor" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] Message sent successfully to remote queue.


		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

On the server console, you will notice the following logs:

				
23:37:38,175 INFO  [STDOUT] ----------------
23:37:38,175 INFO  [STDOUT] Received message
23:37:38,175 INFO  [STDOUT] ----------------

				
			

This example shows a bunch of things. First, it introduces the @Column annotation. It also shows how entities can be detached and re-attached to persistence storage using the EntityManager.merge(). It also shows some basic queries.

@Column :

EJB 3.0 has a complete Object/Relational mapping. You can use the @Column annotation to specify which column in the table your property should map to. Take a look at the org.jboss.tutorial.merge.bean.Customer entity.

				
@Column(name = "FIRST")
public String getFirst()
{
   return first;
}

				
			

Find by primary key :

The EntityManager service has a built in find by primary key method:

				
<T> find(Class<T>, Object pk)
				
			

The org.jboss.tutorial.merge.bean.CustomerDAOBean stateless EJB's find() method wraps remote calls to the EntityManager.

				
public Customer find(int id)
{
   return manager.find(Customer.class, id);
}

				
			

Queries :

EntityManager allows you to create query objects on the fly that can be reused over and over, or just one time. Queries also support named parameters. The org.jboss.tutorial.merge.bean.CustomerDAOBean reflects this usage in the findByLastName method.

				
public List findByLastName(String name)
{
   return manager.createQuery("from Customer c where c.last = :name").setParameter("name", name).getResultList();
}

				
			

Merging :

The Value Object pattern is built into EJB 3.0. You can detach an object from persistence storage and send it across the network to the client. The client can make updates locally to the object, send it back to the server and the changes can be merged/synchronized back to the database using the EntityManager.merge() method. This is exactly what the org.jboss.tutorial.merge.client.Client does.

Building and Running

From the command prompt, move to the "merge" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] Create Bill Burke and Monica Smith
     [java] Bill and Monica get married
     [java] Get all the Burkes
     [java] There are now 2 Burkes



		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

View the tables and rows:

You can view the tables created by JBoss by going to the Hypersonic Service, scrolling down to the startDatabaseManager button and clicking it. A Hypersonic SQL window will be minimized, but you can open it up to look at the tables and do queries.

EJB3 beans may reference EJB2.1 and vice versa. This tutorial demonstrates mechanisms for such references.

EJB2.1 referencing EJB3:

To start, take a look at the EJB3 SLSB org.jboss.tutorial.reference21_30.bean.Stateless3Bean, the EJB2.1 SLSB org.jboss.tutorial.reference21_30.bean.Stateless2Bean and the deployment descriptors for the EJB2.1 SLSB ejb21_app/src/main/resources/META-INF/ejb-jar.xml and ejb21_app/src/main/resources/META-INF/jboss.xml:

				
<session>
   	<ejb-name>ejb/Stateless2</ejb-name>
   	<home>org.jboss.tutorial.reference21_30.bean.Stateless2Home</home>
   	<remote>org.jboss.tutorial.reference21_30.bean.Stateless2</remote>
   	<ejb-class>org.jboss.tutorial.reference21_30.bean.Stateless2Bean</ejb-class>
   	<session-type>Stateless</session-type>
   	<transaction-type>Container</transaction-type>
    <ejb-ref>
			<ejb-ref-name>ejb/Stateless3</ejb-ref-name>
			<ejb-ref-type>Session</ejb-ref-type>
			<home>org.jboss.tutorial.reference21_30.bean.Stateless3Home</home>
			<remote>org.jboss.tutorial.reference21_30.bean.Stateless3</remote>
	</ejb-ref>
</session>
				
			

Important

Notice, the ejb-jar.xml use a 2.x dtd for this EJB2.x application:

						
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"
                         "http://java.sun.com/dtd/ejb-jar_2_0.dtd">
						
					

The ejb-ref element references the 3.0 EJB. This mapping will make available the Stateless3 bean in the ENC (java:comp/env) namespace of the Stateless2 EJB.

Important

Observe carefully, the use of <home> and <remote> in the ejb-ref. Remember that we can provide a EJB2.1 view for an EJB3 bean. See the "ejb21_client_adaptors" for more details. We have intentionally exposed the home and remote view for the EJB3 Stateless3Bean so that it can be referenced by a EJB2.1 bean, without which the EJB2.1 bean would not have been able to reference this bean through its local ENC (because the home/remote are mandatory for a ejb-ref for EJB2.1 app)

						
@Stateless
@RemoteHome(Stateless3Home.class)
public class Stateless3Bean
{
...
						
					

						
public interface Stateless3Home extends EJBHome
{

   public Stateless3Remote create() throws java.rmi.RemoteException, javax.ejb.CreateException;
...
						
					

EJB3 referencing EJB2.1 bean:

Two mechanisms for referencing EJB2.1 from a EJB3 are demonstrated. The first mechanism uses annotations exclusively. Note the @EJBs annotation on org.jboss.tutorial.reference21_30.bean.Stateless3Bean. The name parameter for the @EJB specifies the name with which the 2.1 EJB will be bound in the ENC (java:comp/env) namespace of the Stateless3Bean. The mapped-name parameter specifies the global JNDI binding of the 2.1 EJB.

				
@EJBs(
{@EJB(name = "injected/Stateless2", mappedName = "Stateless2")})
public class Stateless3Bean
{
...
				
			

Note

The testAccess method in the Stateless3Bean looks up this EJB2.1 using the ENC jndi-name:

						
Stateless2Home home = (Stateless2Home) jndiContext.lookup(Container.ENC_CTX_NAME + "/env/injected/Stateless2");
Stateless2 test2 = home.create();
						
					

Also note that since the bean being looked up is a EJB2.1 bean, the lookup will return the home interface of the bean.

The second mechanism of referencing a EJB2.1 bean in a EJB3 bean is through the deployment descriptors. Take a look at the ejb3_app/src/main/resources/META-INF/ejb-jar.xml and ejb3_app/src/main/resources/META-INF/jboss.xml

				
<session>
	<ejb-name>Stateless3Bean</ejb-name>
	<ejb-ref>
		<ejb-ref-name>ejb/Stateless2</ejb-ref-name>
		<ejb-ref-type>Session</ejb-ref-type>
	</ejb-ref>
</session>
				
			

Important

The ejb-jar.xml should use the ejb-jar 3.0 xsd:

						
<ejb-jar
        xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                            http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"
        version="3.0">
        				
					

This binds the Stateless2 bean to the ENC (java:comp/env) namespace of the Stateless3Bean. Also take a look at the ejb3_app/src/main/resources/META-INF/jboss.xml which maps the global jndi name of the Stateless2 bean with the ejb-ref-name.

				
<session>
	<ejb-name>Stateless3Bean</ejb-name>
	<ejb-ref>
		<ejb-ref-name>ejb/Stateless2</ejb-ref-name>
		<jndi-name>Stateless2</jndi-name>
	</ejb-ref>
</session>
				
			

This reference is then used to inject the Stateless2 bean in the Stateless3Bean using @EJB annotation:

				
@EJB (name="ejb/Stateless2")
private Stateless2Home stateless2Home;

				
			

Note

The org.jboss.tutorial.reference21_30.bean.Stateless3Bean also exposes a business-remote interface, so that the org.jboss.tutorial.reference21_30.servlet.EJBReferenceServlet can use the business-remote interface.
					
@Stateless
@Remote(Stateless3.class)
@RemoteBinding(jndiBinding = "Stateless3")
public class Stateless3Bean
{
...
					
				

Important

There's a very important difference between the remote and a business-remote interface. The EJB2.x remote interfaces, which extend from EJBObject, are referred through the <remote> tag in the ejb-jar.xml. On the other hand, the EJB3 style Plain Old Java Interface which is implemented by your EJB3 style POJO bean is known as the business-remote interface and is represented by the @Remote and it's corresponding <business-remote> tag in ejb-jar.xml. Similar is the case with <local> and the <business-local> tags in ejb-jar.xml.

Building and Running

From the command prompt, move to the "reference21_30" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

	
$ ant
     
	

This will deploy the ear into the JBossAS-5.x server ("default" configuration).

To access the servlet, open a web browser and enter http://localhost:8080/jboss-ejb3-tutorial-reference21_30_webapp. This will bring up a page where you can click on the Test button to check that the EJB2.1 and EJB3 beans are referencing each other.
Maven Users: Make sure the AS is not running.
$ mvn clean install -PRunSingleTutorial
	

This will create the EAR in the target folder of the tutorial. Copy this EAR to the deploy folder of JBossAS-5.x and start the server (if it's already not started). Then follow the steps mentioned above, to access the servlet from the web browser.

The "entity" tutorial only showed one-to-many and many-to-one relationships. This tutorial will show you one-to-one and many-to-many relationships.

One-to-One :

There is a unidirectional one-to-one relationship defined between the org.jboss.tutorial.relationships.bean.Customer and org.jboss.tutorial.relationships.bean.Address. Customer defines the uni-directional relationship.

			
@OneToOne(cascade = {CascadeType.ALL})
@JoinColumn(name = "ADDRESS_ID")
public Address getAddress()
{
   return address;
}

			
		

CascadeType.ALL specifies that when a Customer is created, if there is any Address association, then that Address will be created as well(CascadeType.PERSIST). If the Customer is deleted from persistence storage, the Address will be deleted(CascadeType.REMOVE). If a Customer instance is re-attached to persistence storage, any changes to the Address collection will be merged with persistence storage (CascadeType.MERGE).

Many-To-Many :

There is a many-to-many relationship between org.jboss.tutorial.relationships.bean.Customer and org.jboss.tutorial.relationships.bean.Flight. In order to have a many-to-many relationship there needs to be a distinct join table that maps the many-to-many relationship. This is called an association table. You can have JBoss automatically generate the association table for you, or you can use the @JoinTable annotation to define it yourself. If you use @JoinTable it must be defined on both sides of the bi-directional relationship. Let's look at the Customer side of the relationship:

			 	
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER, mappedBy="customers")
			 	
			 

The mappedBy attribute states that the Flight.customers property is responsible for mapping and managing the relationship. The spec allows for multiple join and inverse join columns. See the "Composite Primary Key" tutorial for more information.

Let's look at the other side of the relationship in Flight.

				
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER)
@JoinTable(table = @Table(name = "flight_customer_table"),
                  joinColumns = {@JoinColumn(name = "FLIGHT_ID")},
                  inverseJoinColumns = {@JoinColumn(name = "CUSTOMER_ID")})
public Set<Customer> getCustomers()
{
   return customers;
}

				
			

The database associate table will look like this:

				
create table FLIGHT_CUSTOMER_TABLE (
      CUSTOMER_ID integer,
      FLIGHT_ID integer
   );
				
			

Building and Running

From the command prompt, move to the "relationships" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] Air France customers
     [java] Bill
     [java] Monica
     [java] USAir customers
     [java] Molly



		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

View the tables and rows:

You can view the tables created by JBoss by going to the Hypersonic Service, scrolling down to the startDatabaseManager button and clicking it. A Hypersonic SQL window will be minimized, but you can open it up to look at the tables and do queries.

Resources (e.g. data sources, JavaMail sessions, JMS queues) may be added to the local jndi namespace (ENC) of individual EJBs. This is to separate the jndi names used in the bean code from the global jndi bindings set by the bean deployer. The mapping of the bean local jndi binding and the global binding may be handled via the ejb-jar.xml and jboss.xml deployment descriptors.

ejb-jar.xml :

Take a look at META-INF/ejb-jar.xml. For ENCBean, there are 3 <resource-ref> elements indicating resource reference names and types.

jboss.xml :

Take a look at META-INF/jboss.xml. For ENCBean, there are again 3 <resource-ref> elements indicating resource reference names and either the global jndi binding via the <jndi-name> element or the resource name. Resource managers are used to map resource names to global jndi bindings via the <resource-managers> element.

TestENCBean.java :

Take a look at org.jboss.tutorial.resource_ref.bean.TestENCBean. Each one of the resources are accessed from the bean local jndi namespace (i.e. java:comp/env) by the value set in the <res-ref-name> values in the deployment descriptors.

Building and Running

From the command prompt, move to the "resource_ref" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] Successfully accessed bean resource references

		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

On the server you will notice these logs:

			
13:44:09,500 INFO  [TestENCBean] Found data source resource ref
13:44:09,500 INFO  [TestENCBean] Found mail resource ref
13:44:09,500 INFO  [TestENCBean] Found jms queue resource ref
13:44:09,500 INFO  [TestENCBean] Found jms queue resource env ref

			
		

The EJB specification allows you to map an entity bean to multiple tables. You do this by using the @SecondaryTable annotation.

The org.jboss.tutorial.secondary.bean.Customer entity maps its address properties to a separate ADDRESS table. The first thing it does is define the secondary table.

			
@Entity
@Table(name = "CUSTOMER")
@SecondaryTable(name = "EMBEDDED_ADDRESS", join = {@JoinColumn(name = "ADDRESS_ID")})
public class Customer implements java.io.Serializable
{
}
			
		

The @JoinColumn of the secondary table must match the value of the Customer's primary key. To map individual properties to a secondary table you use the secondaryTable member value of @Column.

			
@Column(name = "STREET", secondaryTable = "EMBEDDED_ADDRESS")
public String getStreet()
{
   return street;
}

			
		

Building and Running

From the command prompt, move to the "secondary" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] Create Bill Burke and Monica Smith
     [java] Bill and Monica get married
     [java] Get all the Burkes
     [java] There are now 2 Burkes



		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

View the tables and rows:

You can view the tables created by JBoss by going to the Hypersonic Service, scrolling down to the startDatabaseManager button and clicking it. A Hypersonic SQL window will be minimized, but you can open it up to look at the tables and do queries.

The EJB 3.0 specification has made the XML deployment descriptors optional. This tutorial goes over how to use the transaction and security annotations of EJB 3.0.

Transactions :

Using transactions is easy, just use the <listing>javax.ejb.TransactionAttribute</listing> annotation. The javax.ejb.TransactionAttributeType enum has every transactional type. Here's an example for using REQUIRES_NEW transaction type:

				
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public int add(int x, int y)
{
   return x + y;
}

				
			

Security :

Take a look at org.jboss.tutorial.security.bean.CalculatorBean. The @javax.annotation.security.RolesAllowed and @javax.annotation.security.PermitAll are the EJB 3.0 security annotations. You can attach a method permission to any method and define which roles are allowed to invoke on that method. The javax.ejb.RunAs annotation can also be applied at the class level. There is also an additional JBoss specific annotation that you must supply at the class level @org.jboss.ejb3.annotation.SecurityDomain. The @SecurityDomain specifies the JAAS application-policy name which will be used by JBoss to authenticate and authorize. See the JBoss Application Server documentation for more details. In this particular example, the "other" domain is used. The "other" domain corresponds to a users.properties and roles.properties files that contain cleartext user, password, and user/role associations. If you open the tutorial jar file you will see these two files in there.

Client :

Open up org.jboss.tutorial.security.client.Client. You'll see that it looks up the stateless bean. Also notice that there is no Home interface and you can begin executing on the stateless bean right away. The client uses a JBoss's SecurityClient class to pass the user name and password:

				
import org.jboss.security.client.SecurityClient;
import org.jboss.security.client.SecurityClientFactory;

SecurityClient securityClient = SecurityClientFactory.getSecurityClient();
securityClient.setSimple("kabir", "invalidpassword");
securityClient.login();
				
			

Note

See the documentation of org.jboss.security.client.SecurityClient for more options

Building and Running

From the command prompt, move to the "security" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] Kabir is a student.
     [java] Kabir types in the wrong password
     [java] Authentication exception, principal=kabir
     [java] Kabir types in correct password.
     [java] Kabir does unchecked addition.
     [java] 1 + 1 = 2
     [java] Kabir is not a teacher so he cannot do division
     [java] Insufficient method permissions, principal=kabir, interface=org.jboss.ejb3.EJBContainerInvocation, requiredRoles=[teacher], principalRoles=[student]
     [java] Students are allowed to do subtraction
     [java] 1 - 1 = 0

		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

Note

If you want to change the roles for the user, through the roles.properties, you will have to restart the server, for the role changes to take effect. This is because by default JBoss caches the roles for a user and until the cache is flushed, either through this configuration or through server restart, the changes won't take effect.

Service POJOs allow you to define POJOs as JBoss services. The way you define them is very similar to how you define stateless or stateful session beans. One very important difference is that there will only ever be ONE instance of the service bean. i.e. it is not pooled - the bean instance is a singleton. The singleton bean contains shared state, so data set by one client is accessible by other clients.

Take a look at org.jboss.tutorial.service.bean.ServiceOne.java. It has been annotated with @org.jboss.ejb3.annotation.Service, this defines it as a singleton service in JBoss. It implements org.jboss.tutorial.service.bean.ServiceOneRemote and org.jboss.tutorial.service.bean.ServiceOneLocal just as you would do for a normal stateful/stateless bean.

ServiceOne also implements org.jboss.tutorial.service.bean.ServiceOneManagement. org.jboss.tutorial.service.bean.ServiceOne has been annotated with @org.jboss.ejb3.annotation.Management. JBoss will inspect this interface, and create and install an MBean implementing the attributes and operations defined in the @Management interface. The MBean will work on the same singleton bean instance as the remote and local interfaces.

Lifecycle :

Just as for "normal" MBeans in JBoss, the @Service supports lifecycle management. Lifecycle management consists of two things:

  • Lifecycle methods
  • Dependencies

Lifecycle methods :

org.jboss.tutorial.service.bean.ServiceOneManagement contains the four methods:

				
void create() throws Exception;

void start() throws Exception;

void stop();

void destroy();

				
			

You do not need to include all these methods, you can pick and choose. If present, the service container will call these methods as follows:

  • create : called by the server when the service is created and all the services it depends upon have been created too. At this point the service (and all the services it depends on) is installed in the JMX server, but is not yet fully functional.
  • start : called by the server when the service is started and all the services it depends upon have been started too. At this point the service (and all the services it depends on) is fully functional.
  • stop : called by the server when the service is stopped. At this point the service (and all the services that depend on it) is no longer fully operational.
  • destroy : called by the server when the service is destroyed and removed from the MBean server. At this point the service (and all the services that depend on it) are destroyed.

Depends and custom JMX object name :

Let's take a look at how to define the dependencies between services. Open org.jboss.tutorial.service.bean.ServiceTwo. Again it has been annotated with @Service, and it implements the @Management annotated interface org.jboss.tutorial.service.bean.ServiceTwoManagement.

				
@Service(objectName = ServiceTwo.OBJECT_NAME)
@Management(ServiceTwoManagement.class)
@Depends(ServiceOne.OBJECT_NAME)
public class ServiceTwo implements ServiceTwoManagement
{
...
				
			

The @org.jboss.ejb3.annotation.Depends annotation specifies that this service depends on the service created for ServiceOne. i.e. it cannot be started until the service created for ServiceOne has been started.

Note

You can specify an array of ObjectNames if you depended on more than one service.

You will also notice the use of objectName property of @Service. This is used to install the service under a custom ObjectName instead of the default ObjectName. So in this tutorial, our ServiceTwo will be registered at tutorial:service=ServiceTwo

Depends injection :

Take a look at org.jboss.tutorial.bean.ServiceThree. It has dependencies on other MBeans, but rather than annotating the class with @Depends, the dependencies are specified on fields and setter methods.

				
@Depends(ServiceOne.OBJECT_NAME)
public ObjectName serviceOneName;

private ServiceTwoManagement service2;

@Depends(ServiceTwo.OBJECT_NAME)
public void setServiceTwo(ServiceTwoManagement service2)
{
	this.service2 = service2;
}
				
			

With regard to the lifecycle dependencies, the effect of annotating fields and setters with @Depends is the same as if we annotated the class. So, ServiceThree cannot be started until ServiceOne (tutorial:service=ServiceOne) and ServiceTwo(tutorial:service=ServiceTwo) are started. Annotating the fields and setters with @Depends though, allows you to inject the dependencies. So in this tutorial, the ServiceThree.serviceOneName will be injected with the ObjectName which corresponds to ServiceOne. More interesting is the injection of ServiceTwo. setServiceTwo() takes a parameter, which is the ServiceTwoManagement management interface of ServiceTwo. The server creates a dynamic proxy for the ServiceTwoManagement interface, and injects that. This means that you can call the methods on the service2 field without caring that you are actually invoking methods on another service.

Interceptors :

You can define interceptors for your service beans in the same way as shown in the "interceptors" tutorial. This example defines one in the ServiceThree bean class itself:

				
@AroundInvoke
public Object intercept(InvocationContext ctx) throws Exception
{
   System.out.println("ServiceThree - Interceptor");
   return ctx.proceed();
}
				
			

Defining Management Interface via XML :

You can deploy a Service bean as an XMBean, where the management attributes and operations are defined via xml. Take a look at org.jboss.tutorial.service.bean.XMBeanService. Note the @Service annotation specifies an xmbean property. Also note there is no @Management annotation.

				
@Service(objectName = XMBeanService.OBJECT_NAME, xmbean = "resource:META-INF/service-xmbean.xml")
@Remote(XMBeanServiceRemote.class)
public class XMBeanService implements XMBeanServiceRemote
{
...
				
			

Now take a look at META-INF/service-xmbean.xml. This is the file referenced by the xmbean property and specifies the bean's management attributes and operations. Note the class, constructor, attribute and operation elements.

Building and Running

Note

To build and run the example, make sure you have installed JBoss 5.x. See the Section 1.1, “JBoss Application Server 5.x” for details.

From the command prompt, move to the "service" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure the "default" server configuration of JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] invoking remote business interface of ServiceOne...
     [java] Set the attribute value through ServiceOneRemote to 100
     [java] attribute value for (ServiceOne) singleton obtained via JMX is what we set via remote interface: 100
     [java] Invoking ServiceThree via JMX...
     [java] Hello from service One
     [java] ServiceThree - Calling ServiceTwo.sayHello() via MBean proxy
     [java] invoking XMBean (configured through deployment descriptor)...
     [java] Set the attribute value to 50
     [java] Invoking XMBean through JMX
     [java] attribute value for (XMBeanService deployment descriptor configured) singleton obtained via JMX is what we set via remote interface: 50
     [java] Hello from an XMBean

		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

On the server side when the application is deployed, you will notice these logs:

			
16:56:54,036 INFO  [STDOUT] ServiceOne - Created
...
16:56:54,082 INFO  [STDOUT] ServiceOne - Started
...
16:56:54,142 INFO  [STDOUT] ServiceTwo - Started
...
16:56:54,226 INFO  [STDOUT] ServiceThree - Started

			
		

Notice that the order is maintained because of the dependencies we have configured on the @Service.

This tutorial is similar to the "service" tutorial which shows how to use Service POJOs in JBoss. In this tutorial we will use deployment descriptors instead of annotations to configure the services.

Take a look at org.jboss.tutorial.service_deployment_descriptor.bean.ServiceOne and the corresponding deployment descriptor META-INF/jboss.xml. The <service> tag defines it as a singleton service in JBoss. It implements org.jboss.tutorial.service_deployment_descriptor.bean.ServiceOneRemote and org.jboss.tutorial.service_deployment_descriptor.bean.ServiceOneLocal using the <business-remote> and <business-local> tags.

ServiceOne also implements org.jboss.tutorial.service_deployment_descriptor.bean.ServiceOneManagement identified through the <management> tag. JBoss will inspect this interface, and create and install an MBean implementing the attributes and operations defined in the interface. The MBean will work on the same singleton bean instance as the remote and local interfaces.

The META-INF/jboss.xml also shows that the default ObjectName of the service can be overriden using the <object-name> tag:

			
<service>
   <ejb-name>ServiceOne</ejb-name>
   <ejb-class>org.jboss.tutorial.service_deployment_descriptor.bean.ServiceOne</ejb-class>
   <business-local>org.jboss.tutorial.service_deployment_descriptor.bean.ServiceOneLocal</business-local>
   <business-remote>org.jboss.tutorial.service_deployment_descriptor.bean.ServiceOneRemote</business-remote>
   <object-name>tutorial:service=serviceOne</object-name>
   <management>org.jboss.tutorial.service_deployment_descriptor.bean.ServiceOneManagement</management>
   <jndi-name>serviceOne/remote</jndi-name>
   <local-jndi-name>serviceOne/local</local-jndi-name>
</service>
			
		

Take a look at org.jboss.tutorial.service_deployment_descriptor.bean.ServiceThree and org.jboss.tutorial.service_deployment_descriptor.bean.ServiceTwo where we use the @Depends annotation to add dependencies between services.

Note

For a detailed explanation of @Depends in @Service, take a look at our "service" tutorial.

Building and Running

From the command prompt, move to the "service_deployment_descriptor" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure the "default" server configuration of JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] attribute value for singleton obtained via JMX is what we set via remote interface: 100
     [java] Hello from service One
     [java] Hello from service Two
		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

On the server side when the application is deployed, you will notice these logs:

			
17:37:17,869 INFO  [STDOUT] ServiceOne - Started
...
17:37:17,910 INFO  [STDOUT] ServiceTwo - Started
...
17:37:17,949 INFO  [STDOUT] ServiceThree - Started

			
		

Notice that the order is maintained because of the dependencies we have configured on the @Service.

The EJB specification allows you to define entities that inherit from one another. The inheritance relationships can be reflected in queries as well. So, if you queried based on the base class, the query is polymorphic.

The tutorial example uses the single table strategy to map an inheritance relationship of org.jboss.tutorial.singleinheritance.bean.Pet, which is the base class for org.jboss.tutorial.singleinheritance.bean.Cat and org.jboss.tutorial.singleinheritance.bean.Dog. With the single table strategy, the entire class hierarchy is persisted in one big single table. A discriminator column is required to differentiate between which class type is persisted in a particular row. This is what the annotations look like for Pet.

			
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "ANIMAL_TYPE", discriminatorType = DiscriminatorType.STRING)
public class Pet implements java.io.Serializable
{
}
			
		

The @DiscriminatorColumn specifies the column that will hold the type of the persisted entity. For subclasses, they must define the value of the discriminator column that will identify the class.

Here's the Dog entity which extends Pet:

				
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING)
@DiscriminatorValue("DOG")
public class Dog extends Pet
{
}
				
			

Polymorphic queries:

org.jboss.tutorial.singleinheritance.bean.PetDAOBean stateless EJB wraps some polymorphic queries.

				
public List findByWeight(double weight)
{
   return manager.createQuery("from Pet p where p.weight < :weight").setParameter("weight", weight).getResultList();
}

				
			

Even though the <listing>findByWeight</listing> method queries on Pet, either Dog or Cat instances can be returned.
Table Mapping :

The table mapping for this example looks like this:

				
create table PET (
  ID integer primary key,
  ANIMAL_TYPE varchar,
  NAME varchar,
  WEIGHT double,
  LIVES int,
  NUMBONES int
);

				
			

Building and Running

From the command prompt, move to the "singleinheritance" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] Sox
     [java] Junior

		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

View the tables and rows:

You can view the tables created by JBoss by going to the Hypersonic Service, scrolling down to the startDatabaseManager button and clicking it. A Hypersonic SQL window will be minimized, but you can open it up to look at the tables and do queries.

Take a look at the META-INF/ejb-jar.xml and org.jboss.tutorial.stateful_deployment_descriptor.bean.ShoppingCartBean. You specify a stateful bean with the "session" and "session-type" tags. Note that all bean types in EJB 3.0 are homeless, so there is no requirement for a "home" or "local-home" tag. The bean class is specified with the "ejb-class" tag.

ShoppingCartBean also implements a business-remote interface. Take a look at org.jboss.tutorial.stateful_deployment_descriptor.bean.ShoppingCart. To define this as the business-remote interface of ShoppingCartBean you need to use the "business-remote" tag in the ejb-jar.xml. Here's the META-INF/ejb-jar.xml:

			
<session>
         <ejb-name>ShoppingCart</ejb-name>
         <business-remote>org.jboss.tutorial.stateful_deployment_descriptor.bean.ShoppingCart</business-remote>
         <ejb-class>org.jboss.tutorial.stateful_deployment_descriptor.bean.ShoppingCartBean</ejb-class>
         <session-type>Stateful</session-type>
         <remove-method>
            <bean-method>
               <method-name>checkout</method-name>
            </bean-method>
            <retain-if-exception>false</retain-if-exception>
         </remove-method>
         <transaction-type>Container</transaction-type>
</session>
			
		

Note

There's a very important difference between the remote and a business-remote interface. The EJB2.x remote interfaces, which extend from EJBObject, are referred through the <remote> tag in the ejb-jar.xml. On the other hand, the EJB3 style Plain Old Java Interface which is implemented by your EJB3 style POJO bean is known as the business-remote interface and is represented by the @Remote and it's corresponding <business-remote> tag in ejb-jar.xml. Similar is the case with <local> and the <business-local> tags in ejb-jar.xml.

@Remove :

Take another look at META-INF/ejb-jar.xml. Look for the "remove-method" tag. Instead of explicitly calling EJBObject.remove() in your applications and thus polluting it further with J2EE specific code, any method specified in the "remove-method" tag will cause the stateful bean instance to be removed from the container at the end of the method call. This deployment descriptor behavior mimics the @Remove annotation.

JNDI Bindings through deployment descriptor :

The CalculatorBean will have its remote interface bound in JNDI. Take a look at META-INF/jboss.xml. Note the jndi-name tag. This specifies the jndi binding for the remote interface of the bean.

Client :

Open up org.jboss.tutorial.stateful_deployment_descriptor.client.Client. You'll see that it looks up the stateful bean under its jndi name. Also notice that there is no Home interface and you can begin executing on the stateful bean right away. When you access the bean in JNDI, an instance of the stateful bean will be created on the server. So, when you need a different instance of the stateful bean, you do an additional jndi lookup to get this new reference.

Building and Running

From the command prompt, move to the "stateful_deployment_descriptor" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] Buying 1 memory stick
     [java] Buying another memory stick
     [java] Buying a laptop
     [java] Print cart:
     [java] 2     Memory stick
     [java] 1     Laptop
     [java] Checkout
     [java] Should throw an object not found exception by invoking on cart after @Remove method
     [java] Successfully caught no such object exception.

		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

CalculatorBean is defined as a stateless session bean through the <session> and <session-type> elements. This marks the class as a stateless bean and the deployer will deploy that class as a stateless bean EJB container.

CalculatorBean also implements two interfaces. One is the business-remote interface of the EJB the other is the business-local interface. Take a look at org.jboss.tutorial.stateless_deployment_descriptor.bean.CalculatorRemote. To define this as the business-remote interface of Calculator bean you specify the interface with the <remote> tag. Similarly for org.jboss.tutorial.stateless_deployment_descriptor.bean.CalculatorLocal you need to specify the business-local interface with the <local> tag. Here's the META-INF/ejb-jar.xml:

			
<session>
         <ejb-name>Calculator</ejb-name>
         <business-local>org.jboss.tutorial.stateless_deployment_descriptor.bean.CalculatorLocal</business-local>
         <business-remote>org.jboss.tutorial.stateless_deployment_descriptor.bean.CalculatorRemote</business-remote>
         <ejb-class>org.jboss.tutorial.stateless_deployment_descriptor.bean.CalculatorBean</ejb-class>
         <session-type>Stateless</session-type>
         <transaction-type>Container</transaction-type>
</session>
			
		

Note

There's a very important difference between the remote and a business-remote interface. The EJB2.x remote interfaces, which extend from EJBObject, are referred through the <remote> tag in the ejb-jar.xml. On the other hand, the EJB3 style Plain Old Java Interface which is implemented by your EJB3 style POJO bean is known as the business-remote interface and is represented by the @Remote and it's corresponding <business-remote> tag in ejb-jar.xml. Similar is the case with <local> and the <business-local> tags in ejb-jar.xml.

JNDI Bindings through deployment descriptor :

The Calculator bean will have two JNDI bindings for the remote and Local interface. The META-INF/jboss.xml through the <jndi-name> and the <local-jndi-name> specifies the jndi-name for the remote and the local interfaces, respectively:

				
<session>
         <ejb-name>Calculator</ejb-name>
         <jndi-name>org.jboss.tutorial.stateless_deployment_descriptor.bean.CalculatorRemote</jndi-name>
         <local-jndi-name>org.jboss.tutorial.stateless_deployment_descriptor.bean.CalculatorLocal</local-jndi-name>
</session>
				
			

Client :

Open up org.jboss.tutorial.stateless_deployment_descriptor.client.Client. The client looks up the bean using the jndi-name specified in the jboss.xml. Also notice that there is no Home interface and you can begin executing on the stateless bean right away.

Building and Running

From the command prompt, move to the "stateless_deployment_descriptor" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] 1 + 1 = 2
     [java] 1 - 1 = 0

		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

The EJB3 specification allows you to define entities that inherit from one another. The inheritance relationships can be reflected in queries as well. So, if you queried based on the base class, the query is polymorphic.

This tutorial uses the table per class strategy to map an inheritance relationship of org.jboss.tutorial.tableperinheritance.bean.Pet, which is the base class for org.jboss.tutorial.tableperinheritance.bean.Cat and org.jboss.tutorial.tableperinheritance.bean.Dog.

With the table per class strategy there is a table per class in the hierarchy, and each table has every single property that particular class will persist.

This is what the annotations look like for Pet:

			
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Pet implements java.io.Serializable
{

...
			
		

The subclass annotations look like this:

			
@Entity
public class Dog extends Pet
{
...
			

		

Polymorphic Queries :

org.jboss.tutorial.tableperinheritance.bean.PetDAOBean stateless EJB wraps some polymorphic queries :

				
public List findByWeight(double weight)
{
   return manager.createQuery("from Pet p where p.weight < :weight").setParameter("weight", weight).getResultList();
}

				
			

Even though the findByWeight method queries on Pet, either Dog or Cat instances can be returned.

Table Mapping :

The table mapping for this example looks like this:

				
create table CAT (
  ID integer primary key,
  LIVES int
  NAME varchar,
  WEIGHT double
);

create table DOG (
  ID integer primary key,
  NUMBONES int
  NAME varchar,
  WEIGHT double
);

				
			

The table per class strategy is less efficient than the single table strategy as the SQL query is more complicated.
Building and Running

Important

Because of a bug in HSQL, this tutorial does not work against the HSQL DB (which JBoss ships by default). Till this is fixed, running the client is not possible. Alternately, you can configure a datasource to use some other database and then use that datasource in the persistence.xml

From the command prompt, move to the "tableperinheritance" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure the "default" server configuration of JBossAS-5.x is running

			
$ ant
		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

View the tables and rows:

You can view the tables created by JBoss by going to the Hypersonic Service, scrolling down to the startDatabaseManager button and clicking it. A Hypersonic SQL window will be minimized, but you can open it up to look at the tables and do queries.

This example shows you how to access javax.ejb.SessionContext as well as using the EJB Timer Service. It also explains how callbacks work in EJB 3.0.

SessionContext injection :

The javax.ejb.SessionContext is injected using the @javax.annotation.Resource annotation. When the stateless bean instance is created the field will be initialized with the correct SessionContext. Take a look at org.jboss.tutorial.timer.bean.ExampleTimerBean

				
private @Resource SessionContext ctx;

				

			

Timeout Callbacks :

The rest of the bean example registers a timer with the EJB Timer service. In the EJB 2.1 specification it was required to implement an interface to get ejbTimeout callbacks. In JBoss EJB3, it is implemented as an annotation. All you have to define is a method annotated with javax.ejb.Timeout.

Building and Running

From the command prompt, move to the "timer" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] Timer scheduled to trigger after 5 seconds

		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

On the server you will notice these logs:

			
INFO  [STDOUT] ---------------------
INFO  [STDOUT] Created a timer event to be triggered after 5000 milli seconds
INFO  [STDOUT] ---------------------
INFO  [STDOUT] ---------------------
INFO  [STDOUT] * Received Timer event: Hello World
INFO  [STDOUT] ---------------------

			
		

Persistent classes that are mapped using Hibernate *.hbm.xml files are supported in JBoss. The EJB3 Deployer will search the archive for any .hbm.xml files and add them to the definition of the underlying Hibernate SessionFactory. These .hbm.xml files can be virtually anywhere within the archive under any java package or directory. Take a look at the customer.hbm.xml for an example.

Class mappings defined in .hbm.xml files can be managed by EntityManagers just as annotated @Entity beans are. Also, you are allowed to have relationships between a .hbm.xml mapped class and an EJB3 entity. So, mixing/matching is allowed. Which means you can have some entities defined in .hbm.xml and some others through @Entity annotations.

Injecting Hibernate Session and SessionFactory :

You can inject a org.hibernate.Session and org.hibernate.SessionFactory directly into your EJBs just as you can do with EntityManagers and EntityManagerFactorys. The behavior of a Session is just the same as the behavior of an injected EntityManager. The application server controls the lifecycle of the Session so that you do not have to open, flush, or close the session. Extended persistence contexts also work with injected Hibernate Sessions.

         	
import org.hibernate.Query;
import org.hibernate.Session;

@Stateless
@Remote(CustomerRemote.class)
@RemoteBinding(jndiBinding = "CustBean")
public class CustomerBean implements CustomerRemote
{

   @PersistenceContext
   private Session session;

         	
         

Take a look at org.jboss.tutorial.hibernate.bean.CustomerBean for more details.

Accessing org.hibernate.Session and org.hibernate.Query from EntityManager:

You can get access to the current underlying Hibernate Session by calling the getDelegate method on the EntityManager :

      		
@Stateless
@Remote(CustomerRemote.class)
@RemoteBinding (jndiBinding="AnotherCustBean")
public class AnotherCustomerBean implements CustomerRemote
{

   @PersistenceContext
   private EntityManager em;

   public Customer getCustomer(long id)
   {
      org.hibernate.Session session = (Session) em.getDelegate();
      return (Customer) session.get(Customer.class, id);
   }
...
			
      	

Take a look at org.jboss.tutorial.hibernate.bean.AnotherCustomerBean for more details.

You can get access to the current underlying Hibernate Query by typecasting your reference to a org.hibernate.ejb.QueryImpl.

      		
public List<Customer> getCustomers(String fname)
{
   org.hibernate.ejb.QueryImpl queryImpl = (QueryImpl) em.createQuery("from Customer where fname ='" + fname + "'");
   org.hibernate.Query query = queryImpl.getHibernateQuery();
   return query.list();
}

			
      

Building and Running

From the command prompt, move to the "hibernate" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

	
$ ant
$ ant run

run:
     [java] Jai Pai created with id = 1
     [java] Jaikiran Pai created with id = 2
     [java] Jai NoLastName created with id = 3
     [java] Searching for customer with id = 2
     [java] Found customer Jaikiran Pai with id = 2
     [java] Searching for customer with id = 3
     [java] Found customer Jai NoLastName with id = 3
     [java] Searching for customers with first name Jai
     [java] Found 2 customers with first name Jai
     [java] Searching for customers with first name Jaikiran
     [java] Found 1 customers with first name Jaikiran

     
	
Maven Users: Make sure the AS is not running.
$ mvn clean install -PRunSingleTutorial
	

A JBoss extension to EJB 3.0 is that from the remote or local interface of a stateful session bean, stateless session bean or service bean you can obtain an asynchronous proxy. Methods called on the asynchronous proxy will be executed asynchronously, and the results can be obtained later on.

Important

This JBoss specific feature of obtaining a asynchronous proxy to invoke any method on the bean asynchronously is NOT the same as the Asynchronous Methods feature in EJB 3.1. In EJB 3.1, you can mark your bean business methods to be asynchronous by using the @Asynchronous annotation.

Example :

Take a look at org.jboss.tutorial.asynch.bean.Echo and org.jboss.tutorial.asynch.bean.EchoBean. They define a normal stateless session bean with a remote interface, nothing special. Now take a look at org.jboss.tutorial.asynch.client.Client. It shows an example of asynchronous calls on a remote interface. We will walk through what it does here. The following lines just obtain the remote interface of the bean and call a method following the standard synchronous usage pattern:

      			
InitialContext ctx = new InitialContext();
Echo echo = (Echo) ctx.lookup("EchoBean/remote");
System.out.println("-------- Synchronous call");
String ret = echo.echo("normal call");
System.out.println(ret);

      			

      		

Next we obtain the asynchronous proxy and make a call via that. The method will be invoked asynchronously :

      			
Echo asynchEcho = org.jboss.ejb3.common.proxy.plugins.async.AsyncUtils.mixinAsync(echo);
System.out.println("-------- Asynchronous call");
ret = asynchEcho.echo("asynchronous call");
System.out.println("Direct return of async invocation is: " + ret);

      			
      		

We use the org.jboss.ejb3.common.proxy.plugins.async.AsyncUtils's mixinAsync method to create the asynchronous proxy. All methods invoked on this proxy are invoked asynchronously, and the returned value will always be null (or 0 in the case of numeric primitive types). In this example, the echo() method called has low overhead, but imagine if this was a method that took a long time. In this case it would be good to be able to go ahead with some other tasks.

In Client.java, we make another call on the normal remote interface while "waiting" for the asynchronous call:

      			
System.out.println("-------- Synchronous call");
ret = echo.echo("normal call 2");
System.out.println(ret);
      			
      		

Now that we have finished everything we want to do, while waiting for the asynchronus call to complete, we invoke the getFutureResult(asynchProxy) API on the org.jboss.ejb3.common.proxy.plugins.async.AsyncUtils. This will return us an instance of java.util.concurrent.Future. To obtain the final result of the asynchronous call, we invoke the get() API on the returned java.util.concurrent.Future object.

      			
System.out.println("-------- Result of Asynchronous call");
Future<String> future = (Future<String>) AsyncUtils.getFutureResult(asynchEcho);

// blocking call
ret = (String) future.get();
System.out.println(ret);

      			
      		

Note

The java.util.concurrent.Future.get() API is blocking and if the asynchronous operation is not yet done, it will wait for the operation to complete.

Building and Running

From the command prompt, move to the "asynch" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

	
$ ant
$ ant run

run:
     [java] -------- Synchronous call
     [java] normal call
     [java] -------- Asynchronous call
     [java] Direct return of async invocation is: null
     [java] -------- Synchronous call
     [java] normal call 2
     [java] -------- Result of Asynchronous call
     [java] asynchronous call

     
	
Maven Users: Make sure the AS is not running.
$ mvn clean install -PRunSingleTutorial
	

EJB 3.0 allows for partial deployment descriptors to augment or override the behavior of source code annotations. This tutorial describes the use of partial deployment descriptors.

Overview :

Beans in EJB 3.0 can be specified via source code annotations and/or a deployment descriptor. The deployment descriptor is used to augment or override the source code annotations. There are some limitations on which annotations may be overridden, however. The annotations that specify the bean itself (e.g. @Stateless, @Stateful, @MessageDriven, @Service, @Consumer) cannot be overridden. The EJB 3.0 ejb-jar.xml deployment descriptor xsd specifies the majority of tags as optional in order to support annotation augmentation and overrides. The deployment descriptor does not need to specify all of the required information, just that additional information to override or augment the source code annotations.

Example :

This section contains examples of complete and partial deployment descriptors for completely specifying or overriding specific behaviors of EJBs.

Complete deployment descriptor :

Take a look at the META-INF/ejb-jar.xml. The ejb-jar.xml in this tutorial configures the CompleteXMLDD bean only through the deployment descriptor. The PartialXMLDD bean is configured through the deployment descriptor as well as through annotations.

					
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar
        xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                            http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"
        version="3.0">
	<description>Partial deployment descriptors for EJB in JBoss</description>
   	<display-name>Partial deployment descriptors</display-name>
   	<enterprise-beans>
      <session>
         <ejb-name>CompleteXMLDD</ejb-name>
         <business-remote>org.jboss.tutorial.partial_deployment_descriptor.bean.CompleteXMLDD</business-remote>
         <ejb-class>org.jboss.tutorial.partial_deployment_descriptor.bean.CompleteXMLDDBean</ejb-class>
         <session-type>Stateless</session-type>
         <transaction-type>Container</transaction-type>
         <ejb-ref>
            <ejb-ref-name>ejb/PartialXMLDD</ejb-ref-name>
            <ejb-ref-type>Session</ejb-ref-type>
            <mapped-name>PartialXMLDD/remote</mapped-name>
            <injection-target>
             	<injection-target-class>org.jboss.tutorial.partial_deployment_descriptor.bean.CompleteXMLDDBean</injection-target-class>
             	<injection-target-name>partialXMLDDBean</injection-target-name>
             </injection-target>
         </ejb-ref>
         <resource-ref>
             <res-ref-name>TimerService</res-ref-name>
             <res-type>javax.ejb.TimerService</res-type>
             <res-auth>Container</res-auth>
             <res-sharing-scope>Shareable</res-sharing-scope>
             <injection-target>
             	<injection-target-class>org.jboss.tutorial.partial_deployment_descriptor.bean.CompleteXMLDDBean</injection-target-class>
             	<injection-target-name>timerService</injection-target-name>
             </injection-target>
         </resource-ref>
         <post-construct>
         	<lifecycle-callback-class>org.jboss.tutorial.partial_deployment_descriptor.bean.ExternalCallbackListener</lifecycle-callback-class>
         	<lifecycle-callback-method>postConstruct</lifecycle-callback-method>
         </post-construct>

         <security-identity>
            <run-as>
               <role-name>admin</role-name>
            </run-as>
         </security-identity>
      </session>
      <session>
         <ejb-name>PartialXMLDD</ejb-name>
         <business-remote>org.jboss.tutorial.partial_deployment_descriptor.bean.PartialXMLDD</business-remote>
         <ejb-class>org.jboss.tutorial.partial_deployment_descriptor.bean.PartialXMLDDBean</ejb-class>
         <session-type>Stateful</session-type>
         <init-method>
         	<create-method>
         		<method-name>create</method-name>
         	</create-method>
         	<bean-method>
         		<method-name>init</method-name>
         	</bean-method>
         </init-method>
         <remove-method>
         	<bean-method>
         		<method-name>remove</method-name>
         	</bean-method>
         </remove-method>
         <transaction-type>Container</transaction-type>

         <env-entry>
            <env-entry-name>id</env-entry-name>
            <env-entry-type>java.lang.String</env-entry-type>
            <env-entry-value>5678</env-entry-value>
         </env-entry>
         <resource-ref>
            <res-ref-name>DefaultDS</res-ref-name>
            <res-type>javax.sql.DataSource</res-type>
            <res-auth>Container</res-auth>
            <res-sharing-scope>Shareable</res-sharing-scope>
            <injection-target>
            	<injection-target-class>org.jboss.tutorial.partial_deployment_descriptor.bean.PartialXMLDDBean</injection-target-class>
            	<injection-target-name>ds</injection-target-name>
            </injection-target>
         </resource-ref>
      </session>
   </enterprise-beans>
   <interceptors>
   	<interceptor>
   		<interceptor-class>org.jboss.tutorial.partial_deployment_descriptor.bean.FirstInterceptor</interceptor-class>
   		<around-invoke>
   			<method-name>interceptorMethod</method-name>
   		</around-invoke>
   	</interceptor>
   	<interceptor>
   		<interceptor-class>org.jboss.tutorial.partial_deployment_descriptor.bean.SecondInterceptor</interceptor-class>
   	</interceptor>
   </interceptors>
   <assembly-descriptor>
   	  <security-role>
         <role-name>admin</role-name>
      </security-role>
      <security-role>
         <role-name>normal</role-name>
      </security-role>
      <method-permission>
         <role-name>normal</role-name>
         <method>
            <ejb-name>CompleteXMLDD</ejb-name>
            <method-name>sayHello</method-name>
         </method>
      </method-permission>
      <method-permission>
         <unchecked/>
         <method>
            <ejb-name>CompleteXMLDD</ejb-name>
            <method-name>sayBye</method-name>
         </method>
      </method-permission>
      <method-permission>
         <role-name>admin</role-name>
         <method>
            <ejb-name>PartialXMLDD</ejb-name>
            <method-name>echoMessage</method-name>
         </method>
         <method>
            <ejb-name>PartialXMLDD</ejb-name>
            <method-name>changeMessage</method-name>
         </method>
      </method-permission>
      <container-transaction>
         <method>
            <ejb-name>CompleteXMLDD</ejb-name>
            <method-name>greetWithNotSupportedTransaction</method-name>
         </method>
         <trans-attribute>NotSupported</trans-attribute>
      </container-transaction>
      <container-transaction>
         <method>
            <ejb-name>CompleteXMLDD</ejb-name>
            <method-name>greetWithRequiredTransaction</method-name>
            <method-params>
               <method-param>java.lang.String</method-param>
            </method-params>
         </method>
         <trans-attribute>Required</trans-attribute>
      </container-transaction>
      <interceptor-binding>
      	<ejb-name>CompleteXMLDD</ejb-name>
      	<interceptor-class>org.jboss.tutorial.partial_deployment_descriptor.bean.FirstInterceptor</interceptor-class>
      	<interceptor-class>org.jboss.tutorial.partial_deployment_descriptor.bean.SecondInterceptor</interceptor-class>
   	  </interceptor-binding>
      <exclude-list>
         <method>
            <ejb-name>CompleteXMLDD</ejb-name>
            <method-name>uncallableMethod</method-name>
         </method>
      </exclude-list>

   </assembly-descriptor>

</ejb-jar>

			
		

Transactions :

The following ejb-jar.xml file overrides any @TransactionAttribute annotations for the greetWithNotSupportedTransaction method of the CompleteXMLDD bean and adds a @TransactionAttribute annotation for NOT SUPPORTED.

            	
@TransactionAttribute (TransactionAttributeType.REQUIRES_NEW)
public String greetWithNotSupportedTransaction(String name)
{

...
<container-transaction>
   <method>
      <ejb-name>CompleteXMLDD</ejb-name>
      <method-name>greetWithNotSupportedTransaction</method-name>
   </method>
   <trans-attribute>NotSupported</trans-attribute>
</container-transaction>
...
            

References :

The following ejb-jar.xml file creates a EJB reference and injects the EJB to the injection target of the partialXMLDDBean member variable.

<ejb-name>CompleteXMLDD</ejb-name>
...
<ejb-ref>
   <ejb-ref-name>ejb/PartialXMLDD</ejb-ref-name>
   <ejb-ref-type>Session</ejb-ref-type>
   <mapped-name>PartialXMLDD/remote</mapped-name>
   <injection-target>
    	<injection-target-class>org.jboss.tutorial.partial_deployment_descriptor.bean.CompleteXMLDDBean</injection-target-class>
    	<injection-target-name>partialXMLDDBean</injection-target-name>
    </injection-target>
</ejb-ref>
...
            

Callbacks :

The following ejb-jar.xml file adds a @PostConstruct annotation to the postConstruct method of the CompleteXMLDD bean.

            	
<ejb-name>CompleteXMLDD</ejb-name>
...
<post-construct>
	<lifecycle-callback-class>org.jboss.tutorial.partial_deployment_descriptor.bean.ExternalCallbackListener</lifecycle-callback-class>
	<lifecycle-callback-method>postConstruct</lifecycle-callback-method>
</post-construct>
...
            	
            

Building and Running

Note

To build and run the example, make sure you have installed JBoss 5.x. See the Section 1.1, “JBoss Application Server 5.x” for details.

From the command prompt, move to the "partial_deployment_descriptor" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”

Ant Users:

Make sure your JBossAS-5.x is running

			
$ ant
$ ant run

run:
     [java] jai is a normal user
     [java] Hello, jai. I am the CompleteXMLDDBean. I have the following resources with me:
     [java] Timer Service : org.jboss.ejb3.timerservice.jboss.TimerServiceFacade@184a6b9
     [java] PartialXMLDD Bean : Proxy to jboss.j2ee:jar=jboss-ejb3-tutorial-partial_deployment_descriptor.jar,name=PartialXMLDD,service=EJB3 implementing [interface org.jboss.ejb3.proxy.intf.EjbProxy, interface org.jboss.tutorial.partial_deployment_descriptor.bean.PartialXMLDD, interface org.jboss.ejb3.proxy.intf.StatefulSessionProxy, interface org.jboss.ejb3.proxy.intf.SessionProxy]
     [java]
     [java] Welcome jai, you are in a method with no transaction supported
     [java] Welcome jai, you are in a method with a REQUIRED transaction
     [java] Bye, jai. Hope to see you again
     [java] We'll try calling an uncallable method
     [java] Caught expected exception : Caller unauthorized
     [java] bill is an admin
     [java] Sending Hello World message to bean. We expect the bean to change it
     [java] This message has been changed
     [java] Now calling echo message
     [java] Hello World
     [java] We are done with the bean, let's remove it
     [java] Bean removed


		     
			

Maven Users: Make sure the AS is not running.

$ mvn clean install -PRunSingleTutorial
			

The idea of Message Driven POJOs is to give a message consumer (an MDB), a typed interface that message producers can send messages through. Both the publisher and subscriber would be typed interfaces. This further facilitates the removal of all the lookups and bootstrap code you have to do to obtain and send a message and receive and dispatch a JMS message. With regular JMS you have to :

  • Get a connectionfactory
  • Get a connection
  • Get a destination
  • ... and so on

For the Message Driven POJOs, you just do:

  • Get a producer
  • Invoke on producer

Model :

Message Driven POJOs will have the same model as Stateless/Stateful beans. There is a bean class tagged as @org.jboss.ejb3.annotation.Consumer that must implement one or more @org.jboss.ejb3.annotation.Producer interfaces. Just as a stateless bean is tagged as @Stateless and implements one or more @Remote or @Local interfaces. Take a look at org.jboss.tutorial.consumer.bean.ExampleConsumerBean

				
@Consumer(activationConfig =
{@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
      @ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/tutorial/example")})
@Depends ("jboss.messaging.destination:service=Queue,name=tutorial")
public class ExampleConsumerBean implements ExampleProducerRemote, ExampleProducerLocal, ExampleProducerXA
{
...
   public void method1(String msg, int val)
   {
      System.out.println("method1(" + msg + ", " + val + ")");
   }

   public void method2(String msg, Map<String, String> map)
   {
      System.out.println("method2: " + msg);
      for (String key : map.keySet())
      {
         System.out.println("method2 key/val: " + key + ":" + map.get(key));
      }
   }
				
			

Here's one of the @Producer interfaces :

				
@Producer
public interface ExampleProducerRemote extends ExampleProducer
{
...
				
			

You can see in this example that the ExampleConsumerBean implements the @Producer interfaces and defines the methods which can receive JMS messages. These interfaces will be used by clients(JMS Publishers) to send messages to the consumer via JMS.

Note

For each @Producer interface the @Consumer implements, there will be a proxy that implements that @Producer registered in JNDI under the fully qualified name of that @Producer interface.

Let's now look at the client org.jboss.tutorial.consumer.client.Client

				
public static void main(String[] args) throws Exception
   {
      InitialContext ctx = new InitialContext();
      ExampleProducerRemote remote = (ExampleProducerRemote) ctx.lookup(ExampleProducerRemote.class.getName());

      // you can typecast the returned proxy to obtain a ProducerManager interface that allows you to manage
      // interaction with JMS.
      ProducerManager manager = ((ProducerObject) remote).getProducerManager();


      // connect - internally creates a JMS connection
      manager.connect();

      try
      {
         // Call method1
         remote.method1("Remote method1 called", 1);
         System.out.println("Remote method1 called");

         // Call method2
         Map<String, String> map = new HashMap<String, String>();
         map.put("hello", "world");
         map.put("great", "ejb3");

         remote.method2("Remote method2 called", map);
         System.out.println("Remote method2 called");
      }
      finally
      {
         // instead of typecasting, you can use a helper class that does everything for you.
         ProducerConfig.close(remote);
      }
				
			

When the @Consumer is deployed by the EJB3 container, it looks for all of its @Producer interfaces and registers each one of them in JNDI under their fully qualified class name. The client looks up the ExampleProducerRemote from the JNDI and uses the returned proxy to send the message. The returned proxy can be cast to org.jboss.ejb3.mdb.ProducerObject. It then gets a org.jboss.ejb3.mdb.ProducerManager, that manages the JMS connection for this proxy. To start being able to send messages to the Queue, the client calls connect on the ProducerManager. When the client calls method1() on the proxy, this method call is converted into a JMS message and published to the Queue of the Consumer. The consumer will receive the message and invoke its method1 method.

Producer default values :

The proxy registered in JNDI will know how to contact the JMS Queue/Topic to publish messages. You can specify explicitly through the connectionFactory attribute of the @Producerannotation what the JMS ConnectionFactory JNDI name is, or you can rely on defaults.

Note

The default value for the ConnectionFactory JNDI name is "ConnectionFactory". If you additionally tag the producer as @ProducerLocal instead of @Producer, then "java:/ConnectionFactory" will be used.

@ProducerLocal :

If you tag a producer as @ProducerLocal, the proxy will lookup the connection factory via the default InitialContext when connect() is called. Otherwise, the ConnectFactory reference will be embedded directly within the proxy.

@MessageProperties :

The methods defined in a Producer are turned into JMS messages. The default message properties are a Time To Live of 0, a Priority of 4, and a delivery mode of PERSISTENT. You can override these default values in a couple of ways.

  • You can use the @MessageProperties anntotation and tag the Producer class directly to override the values:
    						
    @Producer
    @MessageProperties(delivery=DeliveryMode.NON_PERSISTENT, timeToLive=1000, priority=1)
    public interface ExampleProducer
    {
    ...
    						
    					
    In this configuration, all method calls on ExampleProducer will use the JMS message properties defined with the @MessageProperties annotation on the interface.
  • You can specify @MessageProperties on a per method basis :
    						
    public interface ExampleProducer
    {
       void method1(String msg, int val);
    
       @MessageProperties(delivery = DeliveryMode.NON_PERSISTENT)
       void method2(String msg, Map<String, String> map);
    }
    						
    					
    So, in the above example, method1() uses the default message properties, and method2() overrides the defaults via the @MessageProperties annotation attached to it.