Chapter 6.
Creating a CMP Entity Bean
This chapter covers how to create a Container Managed Persistence (CMP) EJB component. We will create two CMP beans, Item and Supplier as shown below. The Item bean will be responsible for storing the details of items, such as their availabability and their prices, for MyStore. The Supplier Bean stores details of Suppliers to MyStore. Both beans interact with corresponding tables in the database. In CMP this interaction is controlled by the container, in this case the JBOSS CMP container.
All Items have been assigned a unique itemId for housekeeping purposes within MyStore, and all suppliers have been assigned a unique supplierID in addition to their username which is what they use in accessing the services of MyStore.
Note : It is normal practice to access the business methods of CMP beans via a Session bean, that encapsulates the business logic and acts as an interface to the lower-level EJB components. In this case Supplier and Items are accessed via StoreAccess.
Tasks :
-
Create a CMP bean named Items under package au.com.tusc.cmp.
-
Implement the ejbCreate method, with the values of all attributes being passed as arguments and then assigned to the attributes using mutator (setter) methods.
-
Add a finder method named findBySupplierID with the following query and signature:
query "SELECT OBJECT(b) FROM MyStoreItem as b where b.supplierID = ?1"
method "java.util.Collection findBySupplierID(java.lang.String supplierID)"
-
Add a finder method named findByOutOfStock with the following query and signature:
query "SELECT OBJECT(c) FROM MyStoreItem as c where c.quantity = 0"
method "java.util.Collection findByOutOfStock()"
-
Add a business method to get item details with the signature:
public ItemData getItemData()
-
Add another business method to register delivery of items with the signature:
public void fillStock(java.lang.Integer quantity)
-
Add callback methods, required for getting/setting bean context for bean with signatures:
public void setEntityContext(EntityContext ctx)
public void unsetEntityContext()
-
Deploy the Item Bean.
-
Add a field to the StoreAccess bean to store the Item reference (obtained from JNDI lookup):
private ItemLocalHome itemLocalHome
-
In the ejbCreate method of the StoreAccess bean store this reference in the itemLocalHome variable by invoking the getLocalHome static method in ItemUtil.
-
Add a business method to StoreAccess Bean with the signature:
public ItemData getItemData(String itemID)
-
Add another business method to StoreAccess Bean with the signature:
public java.util.ArrayList getOutOfStockItems()
-
Add another business method to StoreAccess Bean with the signature:
public java.util.ArrayList getItemBySupplier(String supplierID)
-
Create a test client named SessionCMPClient under package au.com.tusc.client.
-
Run your client and test the bean.
Create Items CMP Entity Bean :
Go To Package Explorer > Expand Mystore (project) node > select src, right click and a menu will pop up.
On the pop up menu > New > Lomboz EJB Creation Wizard.
Enter the package name au.com.tusc.cmp, the bean name Item and select the bean type as Container Manged Entity as shown below.
Go to Next and a new screen will pop up as shown below.
Enter MyStoreItem as the Schema Name.
Enter Item as the Table name.
Under Persistent Fields first enter itemID as the Field, with a Field Type of java.lang.String, ITEMID as its Database column, and VARCHAR for its SQL Type.
Press Add .. > It will add this field in Fields section, select this new field > Press Make Primary Key.
Similarly, add all the rest of the fields of the items table as shown below.
Add .. Field: supplierID, Field Type: java.lang.String, Database Column: SUPPLIERID, SQL Type: VARCHAR.
Add .. Field: description, Field Type: java.lang.String, Database Column: DESCRIPTION, SQL Type: VARCHAR.
Add .. Field: quantity, Field Type: java.lang.Integer, Database Column: QUANTITY, SQL Type: INTEGER.
Add .. Field: price, Field Type: java.lang.Float, Database Column: PRICE, SQL Type: DECIMAL.
After adding all these fields, press Finish.
This will create a package named au.com.tusc.cmp under src, and ItemBean will be be created within that package as shown below.
Note: In comparison with our earlier BMP Entity Beans (Customer & Manager), more tags have been generated at class level. Note also that CMP doesn't require a Data Access Object (DAO) interface, as communication between database and bean is controlled by the container.
Let's first the generate EJB classes and then we will examine these tags.
Go to node ItemBean under au.com.tusc.cmp > LombozJ2EE > Add EJB to Module > Select MyStoreMgr > Ok.
Go to MyStoreMgr node > LombozJ2EE > Generate EJB classes.
Note: All these steps are covered in previous chapters (chapters 3 and 1) in detail, so please refer to these if you have any queries.
Now, let's see what files have been generated by Xdoclet.
As shown below, the files generated are nearly the same as for BMP, except there are no Primary Key or DAO classes, and there is now ItemCMP which extends the ItemBean class. The remainder are the same as for BMP entity beans.
Now, let's examine these new tags, some of which have been covered in previous chapters.
-
@ejb.bean tag provides information about the EJB. It is the one compulsory tag for all EJBs.
-
@ejb.persistence tag is being used at two levels, at class level and method level. At class level it provides information about the persistence of a CMP entity bean, that is which database table this bean is going to interact with, which will provide that persistence. At method level it provides information about the mapping of the bean's persistent attributes to columns in that database table.
-
@ejb.finder tag defines a finder method for the home interface. This requires the EJB QL query to fetch the data and a signature for the method. This tag can be used for multiple finder methods.
-
@ejb.persistence-field method level tag is being deprecated in favour of @ejb.persistence tag, it provided information about persistent fields.
-
@ejb.pk-field tag defines the primary key.
The code snippet below shows how persistent attributes are declared in a CMP Entity Bean.
Note: All persistent attributes are declared with abstract accessor and mutator methods in ItemBean. Note also that in the case of a composite primary key you have to specify the @ejb.pk-field tag on all other attributes/properties which combine to form the overall key as well.
Implement ejbCreate Method :
The ejbCreate method is created by method is created by the Lomboz Bean wizard, but we still have to add some code to complete it. Modify the signature of ejbCreate, passing all the attributes as parameters and then setting all these attributes using their associated mutator methods, as shown below.
Errata note :- There is an error in the figure shown below. The Integer attribute 'qunatity' should be called 'quantity'.
Note : The other interesting aspect of ejbCreate here is its return type, as its return type has to be same as the primary key type (e.g. it has to of type String, Integer, Float, or whatever). In this case it is String - the type of itemID, and when implemented it should return null.(Refer to the EJB Spec 10.5.1).
Add Finder Method :
Note: An entity bean's home interface defines one or more finder methods, to find an entity object or collection of entity objects. The name of each finder method starts with the prefix 'find', such as findPrice or findQuantity in our case. Every finder method except findByPrimaryKey(key) must be associated with a query element in the deployment descriptor. The entity bean provider declares the EJB QL finder query and associates it with the finder method in the deployment descriptor. A finder method is normally characterized by an EJB QL query string specified via the query element. Refer to the EJB Spec 10.5.6. This is covered in the Exercise section of this chapter.
Now let's add a finder method to our bean class to find items supplied by a particular supplier.
In order to add this finder method we have to declare a class level tag as discussed above, that is @ejb.finder.
So, add this tag:
Note : In EJB QL, instead of the name of the table, the schema name is used (in this case it is MyStoreItem, rather then specifying Item as you would for an SQL query). Similarly the column names that would be used in SQL are replaced by their corresponding attributes as declared in the bean.
Generate your EJB classes to see what this tag and the previous finder tag have created in the Home Interface as shown below in this code snippet from the ItemLocalHome interface.
It has four methods, including the two finder methods generated by the finder tags declared at class level. The other two, create and findByPrimaryKey are created by Xdoclet(because of the <entitycmp/> tag in ejbGenerate.xml).
Add another finder method to find out-of-stock items in MyStore.
Add the following tag.
Code snippet from ItemLocalHome after generating EJB classes for these finder methods.
Now, generate your EJB classes and analyze the relevant classes. All the finder methods are complete. Let's add some business methods now.
Add Business Methods :
Now, add a business method with this signature:
public ItemData getItemData()
.. with Interface type as local.
Note : The steps to add a business method are covered in previous chapters (1 and 3), so please refer to them. Also we have chosen the Interface type as local because these methods will be invoked within the same Java Virtual Machine.
This will provide description of individual items in MyStore. Add some debug statements and return an instance of ItemData as shown below in this code snippet from the Item bean.
Add another business method with the following signature:
public void fillStock (Integer quantity)
.. with Interface type as local. This will increment the items available to MyStore after the delivery of further items. Add some debug statements and following lines to complete the method.
Code snippet of fillStock in ItemBean shown below.
Add Callback Method
Unlike BMP (where they were generated) we have to add callback methods which will be overridden in the ItemCMP class.
First import the following package: javax.ejb.EntityContext
Add a field to store the entity context.
protected EntityContext eContext;
Add a method setEntityContext with entityContext as its parameter and assign that to your entityContext variable.
Similarly add a method unsetEntityContext, which sets the entityContext variable to null.
Code snippet for both methods is shown below.
Now all business and finder methods are complete, so it's time to generate EJB classes.
Let's examine the generated ItemCMP class, which is of most interest .
Unlike our BMP bean all persistent attribute behavior is being overridden by abstract methods. This is because the EJB container is responsible for maintaining their persistence.
All the callback methods we have implemented are being overridden as shown below.
Note : There are no ejbFinder methods in this class as with BMP beans, as all this is controlled by the container. Also as pointed out before, there is no PrimaryKey class generated and there is no need for a DAO class, as this too is controlled by the container.
Now, before we deploy our bean, we will just have a look at ejb-jar.xml and jboss.xml to see what descriptors are generated.
Note : We don't have to write any descriptors for the data sources as we did with Session and BMP beans, as the EJB container is responsible for that.
As shown in the code snippet above from the ejb-jar.xml file, all abstract methods are generated as persistent fields under the tag <cmp-field> because of the tag @persistence-field declared at each accessor method. Also the primary key class descriptor is generated under the <primary-key-class> tag and the primary key field is generated under the <primarykey-field> because of the tag @field-pk declared for the relevant attribute(s).
Descriptors for finder methods are generated along with the query defined to fetch the data as shown below. These finder tags are generated by the @ejb.finder tag declared at class level.
And in the jboss.xml file, the following descriptors are generated due to the tag @ejb.bean declared at class level, as shown below.
Note : @ejb.bean tag has been covered in previous chapters.
Item Bean functionality is complete in and ready for deployment.
Deploy Item Bean :
Go to Lomboz J2EE View > expand node MyStore > expand MyStoreMgr > select Jboss 3.2.1 ALL.
Right click > select Debug Sever on the pop up menu.
Note : This is to start your server, if you are already running your server then skip these steps and go to the next one.
Go to MyStoreMgr node in LombozJ2EE view > right click > select Deploy on the pop up menu as shown below.
Note : All these steps have been detailed in previous chapters (1 and 3), so, please refer to them.
Messages in the console will indicate whether deployment of your bean has been successful or not.
Now, lets modify StoreAccessBean to invoke methods on ItemBean.
Add a field to store our Item reference (obtained from JNDI lookup).
In the ejbCreate method store this reference in the itemLocalHome variable by invoking the getLocalHome static method in the ItemUtil class as shown in this code snippet below from StoreAccess Bean.
Add a Business Method to StoreAccess :
Add another business method to the StoreAccess Bean which will invoke this business method on our Item Bean.
Now, add a business method with this signature:
public ItemData getItemData(String itemID)
.. with Interface type as Remote. As managers will log on to MyStore with username, once authenticated they will be identified by their userid. They can then retrieve their account details from MyStore using that userid. A Manager can invoke methods on the Item Bean to examine the inventory of MyStore.
Note : Steps to create business methods are covered in previous chapters.
Now invoke one of Item's finder methods via the reference variable we have created in the ejbCreate method.
Now invoke the business method of Customer on this reference.
Code snippet for this business method is shown below.
Add another business method to StoreAccess Bean.
Add a business method with this signature:
public java.util.ArrayList getOutOfStockItems()
.. with Interface type as Remote. This will return the items which are out of stock in MyStore.
Create two variables of type Collection and ArrayList respectively, as the finder method for Items returns a Collection and this method will return an ArrayList, after populating items which are out of stock from the returned Collection.
Now invoke one of the finder methods of Item on the reference variable we have created in the ejbCreate method.
Now iterate through the collection of out of stock items and add to the ArrayList.
Code snippet for this business method is shown below.
Add another business method to StoreAccess Bean.
Add a business method with this signature:
public java.util.ArrayList getItemBySupplier(String supplierID)
.. with Interface type as Remote. This will return the items which are provided to MyStore by a given supplier.
Create two variables of type Collection and ArrayList respectively as the finder method for Items returns a Collection and this method will return ArrayList, after populating items which are supplied by a particular supplier from the returned Collection.
Now invoke one of the finder methods of Item on the reference variable we have created in the ejbCreate method.
Now iterate through the collection of items for this supplier and add to the ArrayList.
Now all the methods in StoreAccess Bean for accessing Item's business methods have been added. The only remaining bit is the deployment descriptors required for linking/referencing of StoreAccess and Item Bean. So we will the add two tags shown below.
First add the tag shown below at class level in StoreAccess Bean.
This tag will generate deployment descriptors in 'ejb-jar.xml', as StoreAccessBean has to know which bean it is referring to, what is its view-type and ref-name. This will generate these descriptors as shown below.
Note : View type is local as both are in the same Java Virtual Machine, otherwise it would be Remote. Secondly ref-name is generated as ItemLocalHome, as we are using that rather than ItemHome (which was also generated, but is used in the Remote case).
Now add a second tag (shown below) at class level in StoreAccess Bean.
This tag will generate deployment descriptors in 'jboss.xml', as the application server has to know what jndi-name the Item bean has been registered with. This will generate these descriptors as shown below.
Note : Ref-name and jndi-name are used for bean as local (in same JVM).
Note : We can see in the code snippet above the deployment descriptors generated by tag @jboss. For the view type 'local' it generates incorrect deployment descriptors, as discussed in the previous chapter. So every time we use this tag we have to change the <ejb-ref> to <ejb-local-ref> before deployment. Caution here, that you do this change manually when you finally finish regenerating your EJB classes, because every time you regenerate your classes, 'jboss.xml' will be overwritten.
Now our Item Bean is complete after these changes, so deploy your bean again now, from the Lomboz J2EE View, as per the steps shown above and in previous chapters. Messages will appear in the console showing the status of deployment.
Once the bean is deployed successfully, create a test client which will invoke the loginUser method on StoreAccess Bean , getCustomerData on Customer Bean, getManagerData on Manager Bean and getOutOfStockItems on Item Beam.
Create your Test Client :
Go to Project MytStore node > select src node and expand it > select au.com.tusc.client package > right click.
Select New on the pop up menu > select Lomboz EJB Test Client Wizard.
Select package name au.com.tusc.client, name as SessionCMPClient and select Ejb Home as au.com.tusc.session.StoreAccessHome and Ejb Interface as au.com.tusc.session.StoreAccess.
This will generate required methods for you in your SessionCMPClient class and you only have to invoke loginUser, getCustomerData, getManagerData (Manager Bean was developed as part of an exercise in the previous chapter) and getOutOfStockItems as shown below.
Now, the last step is to write the code for your client.
In order to access out of stock items we need an Iterator and an ArrayList for items. So, declare two variables of these respective types, and import the packages for these types (which are java.util.ArrayList and java.util.Iterator).
And now add these lines of code to invoke the methods mentioned above.
Test your Client :
Now, in order to test your client, Select SessionCMPClient node > Go at top level menu and select the icon with the 'Running Man'.
On that select 'Run as' > select Java Application, as shown below.
Now on your console, if you get a reply saying two items out of stock which are 'CALCULATOR' and 'CLOCK', then your call is successful as shown below.
Exercise :
Now, here is an exercise for you. In order to proceed further, implement Supplier as a CMP Entity Bean. The tasks are given below:
-
Create a CMP Bean named Supplier under package au.com.tusc.cmp.
-
Implement the ejbCreate method, with all attributes passed as arguments and then assigned to attributes using mutator methods.
-
Add a find method named findUserID with query and signature:
query "SELECT OBJECT(b) FROM MyStoreSupplier as b where b.userID = ?1"
method au.com.tusc.cmp.SupplierLocal findUserID(java.lang.String userID)
Note : The method signature is find<cmp attribute> instead of findByPrimaryKey, because finder methods for non-key CMP attributes use this convention. Accordingly the return type for finder methods will be either Collection or <entity type>, as specified in the EJB specification, sections 10.5.6 and 10.5.2 respectively.
-
Add a business method to get supplier details with signature:
public SupplierData getSupplierData()
-
Add a business method to get request items from various suppliers with signature:
public void requestItem(String itemID, Integer quantity)
-
Add callback methods, required for setting/unsetting bean context with signatures:
public void setEntityContext(EntityContext ctx)
public void unsetEntityContext()
-
Deploy the Supplier Bean.
-
Add a field to StoreAcess Bean to store its reference:
private SupplierLocalHome supplierLocalHome
-
In the ejbCreate method of StoreAccess Bean store a reference in supplierLocalHome variable by invoking the getLocalHome static method in supplierUtil.
-
Add a business method to StoreAccess Bean with signature:
public ItemData getItemData(String itemID)
-
Add the following tags for deployment at class level for linking/referencing Supplier.
-
Test your Supplier Bean by running your Test Client created for Item named SessionCMPClient.
Note : All these steps are same as were done for Item. Implement this Bean which will be used in subsequent chapters.
In case you have difficulty, we have provided a SupplierBean class, modified StoreAccessBean class and SesiomCMPClient class. You can download these files under downloads below.
Downloads :