Implementing the Domain Model


Before we begin our implementation of domain objects identified in Figure 6-1, we first examine a design pattern for simplifying the implementation of the entity bean interfaces. At this point, we suggest that you take a little detour to the section "Implementing the Business Interface Pattern" of Chapter 7. Business interface is an inappropriate stereotype for domain objects, as such, this same design pattern will be used with the stereotype <<Domain-Object Interface>>. We found that this business tier pattern used for session beans serves equally well for entity beans. A review of this pattern reveals several advantages:

  • The Domain-Object interface (Admin, PortalAlliance, NPO, and Campaign interfaces shown in Figure 6-1) shows only the interface methods relevant to the business tier. The container callbacks defined in the javax.ejb.EntityBean interface and the javax.ejb. EJBLocalHome interface (assuming that we are using local home interface) appear on the bean implementation, for example, the AdminBean. The client can use only the Domain-Object interface.

  • The analysis-level domain model contains only the Domain Object interfaces with their associated methods. We do not assume implementation aspects such as CMP at this time. This model directly maps to the interfaces described using Domain-Object interfaces. The analysis time artifacts can be used directly during development.

  • The accessors for container-managed fields are declared as abstract methods on the Bean class (for example, AdminBean). The corresponding implementation is provided by container provider's tools. When using the Domain-Object interface, we do not have to declare these methods as abstract methods on the Bean class. When new properties are added or old ones removed, only the Domain-Object interface will change.

The following discussion focuses on the CMP semantics defined in the EJB 2.0 specification. For the most part, using CMP implies that the developer provides the accessors for container-managed fields. If CMR is being used, the developer provides the accessors for the CMR fields; other than that, all of the implementation is generated by the vendor tool using the configuration options specified declaratively in the deployment descriptors.

EJB 2.0 specification introduced local interfaces for EJBs. Local interfaces are used when the domain objects are collocated in the same JVM as the business objects utilizing them. This improves the performance significantly by eliminating the overhead associated with remote interfaces, while taking away location transparency. The objects that implement the local home interface and local interface are local java objects, therefore the arguments and results of the methods of the local home interface and local interface are passed by reference. Because the local programming model is relatively less expensive in terms of making method calls, it can support fine-grained access to components. For our sample application, we have chosen to implement all entity beans using local interfaces. While designing applications using local interfaces, one must be aware of the pass-by-reference semantics inherent in the local programming model. The remote programming model uses pass-by-value semantics and therefore offers a level of isolation from inadvertent modification to the data.

Note

According to the EJB 2.0 specification, in order to be the target of a container-managed relationship, an entity bean with container-managed persistence must provide a local interface.

Defining the Admin Interface

In this section, we complete the Admin Domain-Object interface and define the CMP and CMR fields for the AdminBean. Figure 6-3 shows the CMP-and CMR-related accessors defined on the Admin interface that the container will implement. Once the primary key for an entity bean has been set, no attempt should be made to change it using the set accessor methods. Therefore, the set accessor method for the primary-key is not provided on the Domain-Object interface, instead it is specified only on the AdminBean as an abstract method.

click to expand
Figure 6-3: Defining the AdminBean and the Admin interface

The following snippet shows the accessors defined in the Admin interface. It has accessors for the CMP field adminID and the CMR fields alliance and npo. The adminID provided by the presentation tier is used to identify the association between the remote user and the associated NPO entity bean or the PortalAlliance entity bean. According to the Register NPO use case and the Register Portal-Alliance use case, only NPO and Portal-Alliance administrators can change their respective NPO and Portal-Alliance profiles. The implementation for the accessor methods is supplied by the container. All accessors must be public and must be structured according to the cmp-field and cmr-field element specification in the ejb-jar.xml file. This is discussed later in this section.

 package com.gc.persistence.admin; public interface Admin {     /* CMP field adminID */     public String getAdminID();     /* Because adminID is primary-key, setAdminID is     * defined only in the bean class */     /* CMR field alliance */     public PortalAllianceLocal getAlliance();     public void setAlliance(PortalAllianceLocal alliance);     /* CMR field npo */     public NPOLocal getNpo();     public void setNpo(NPOLocal npo); } 

In order to create the AdminBean entity bean that implements the Admin interface, the local home interface shown in the following snippet exposes two create methods: one method is for creating an AdminBean object with a local reference to the corresponding NPO entity bean, and the other create method is used for creating an AdminBean object with a local reference to the corresponding PortalAlliance entity bean. The corresponding implementations are shown in the AdminBean class. The local home interface also exposes a set of find methods. The findByPrimaryKey method is implemented by the container based on the prim-key-class element in the deployment descriptor. The rest of the find methods use EJB QL (Query Language) queries and therefore these methods are implemented by the container based on the query elements specified in the deployment descriptors. Please note that we have deferred discussing deployment descriptors for the later part of this section. For a complete discussion on EJB QL, please refer to Chapter 11 of the EJB 2.0 specification.

 package com.gc.persistence.admin; public interface AdminLocalHome extends javax.ejb.EJBLocalHome {     /* Create Methods */     public AdminLocal create(String adminId, NPOLocal npo)             throws CreateException;     public AdminLocal create(String adminId, PortalAllianceLocal alliance)             throws CreateException;     /* Finder Methods */     public AdminLocal findByPrimaryKey(String adminId)             throws FinderException, ObjectNotFoundException;     public AdminLocal findByEin(String ein)             throws FinderException, ObjectNotFoundException;     public AdminLocal findByPortalID(String portalID)             throws FinderException, ObjectNotFoundException; } 

Observe that the finder methods throw ObjectNotFoundException. The CMP implementation raises this exception when the corresponding entity bean is not found in the persistent store. The business tier (which is the client in this case) must catch this exception instead of trying to catch FinderException. Chapter 7 explains the difference between these two exceptions in the section "Handling Exceptions in Transactions."

The create methods of the AdminLocalHome are delegated to the ejbCreate methods of the EntityBean by the container. The ejbCreate methods shown in the following code will set the appropriate CMP field. Observe that the CMR fields must be set only in the ejbPostCreate methods. The parameter list for ejbCreate and ejbPostCreate is identical. As you will see later in the discussion on deployment descriptor, the container persists the objects and relationships based on the abstract persistence schemas of entity beans and their container-managed relationships.

 package com.gc.persistence.admin; public abstract class AdminBean implements EntityBean, Admin {     private EntityContext ctx;     public String ejbCreate(String adminID, NPOLocal npo)         throws CreateException{         this.setAdminID(adminID);         return null;     }     public String ejbCreate(String adminID, PortalAllianceLocal alliance)         throws CreateException{         this.setAdminID(adminID);         return null;     }     public void ejbPostCreate(String adminID, NPOLocal npo)         throws CreateException{         this.setNpo(npo);     }     public void ejbPostCreate(String adminID, PortalAllianceLocal alliance)         throws CreateException{         this.setAlliance(alliance);     }     /* The set method for adminID appears only in the bean class definition because     * it is the primary-key */     public abstract void setAdminID(String adminID); ... other container callback methods ... } 

Instead of using the setNpo method (or the setAlliance method) in the ejbPostCreate method, we could have easily done the set in the business tier session beans. However, this will break the encapsulation. We must let the logic for CMR be part of the AdminBean creation process.

The bean developer must define the entity bean class as an abstract class. The container-managed persistent fields and container-managed relationships are exposed to the client through get and set accessor methods. These fields are not present in the bean class since these are virtual fields. The bean implementation produced by the container is aware of these fields through cmp-field and cmr-field element declarations in the ejb-jar.xml deployment descriptor. One must therefore follow the JavaBean naming convention for specifying the names for CMP and CMR fields in the deployment descriptor, that is, the name must begin with a lowercase letter.

Specifying the Deployment Descriptors

In this section, we configure various deployment descriptors associated with setting up the SiteAdmin bean with container-managed persistence and container-managed relationships. This section discusses the specifics of configuring the ejb-jar.xml file, vendor-specific weblogic-ejb-jar.xml, and weblogic-cmp-rdbms-jar.xml files. We first discuss the ejb-jar.xml deployment descriptor file.

Note

The sample application GreaterCause was developed and tested on the WebLogic Server 7.0 (SP1); as such, all vendor-specific deployment descriptors discussed in this chapter will confirm to WebLogic Server 7.0.

 <enterprise-beans><entity>     <description>Admin Bean Description</description>     <!-- Logical name of the EJB within the ejb-jar file -->     <ejb-name>AdminEntityEJB</ejb-name>     <!-- Specify abstract schema type for use in EJB QL -->     <abstract-schema-name>Admin</abstract-schema-name>     <local-home>com.gc.persistence.admin.AdminLocalHome</local-home>     <local>com.gc.persistence.admin.AdminLocal</local>     <ejb-class>com.gc.persistence.admin.AdminBean</ejb-class>     <persistence-type>Container</persistence-type>     <prim-key-class>java.lang.String</prim-key-class>     <reentrant>False</reentrant>     <!-- Describe the container-managed fields -->     <cmp-field><field-name>adminID</field-name></cmp-field>     <!-- Name of the primary key field; this field is mapped to the     database schema in weblogic-cmp-rdbms-jar.xml file -->     <primkey-field>adminID</primkey-field>     <!-- Query for findByEin method in home interface; note the use     of abstract schema type 'Admin' defined previously using     abstract-schema-name element -->     <query>         <query-method>             <method-name>findByEin</method-name>                 <method-params>                     <method-param>java.lang.String</method-param>             </method-params>         </query-method>         <ejb-ql>              SELECT OBJECT(a)              FROM Admin AS a                      WHERE (a.npo.ein = ?1)         </ejb-ql>     </query>    <!-- Query for findByPortalID method in home interface -->     <query>         <query-method>             <method-name>findByPortalID</method-name>             <method-params>                 <method-param>java.lang.String</method-param>             </method-params>         </query-method>         <ejb-ql>              SELECT OBJECT(a)              FROM Admin AS a                      WHERE (a.alliance.portalID = ?1)         </ejb-ql>     </query> </entity></enterprise-beans> 

The ejb-name element specifies an EJB's logical name in the deployment descriptor. The name AdminEntityEJB is assigned to AdminBean. This name is used to reference the bean in several places within ejb-jar.xml, weblogic-ejb-jar.xml, and weblogic-cmp-rdbms-jar.xml.

The prim-key-class element contains the fully qualified name of an entity bean's primary key class. The definition of the primary key can be deferred to deployment time, in this case use prim-key-class as java.lang.Object. The findByPrimaryKey method of the local home interface uses this class name as method parameter type. Database-assisted key generation can also be supported by providing the object type of the key that is generated by the database; any primitives must be converted to the corresponding Java object types.

The primkey-field element specifies the cmp-field that contains the primary key. Once the primary key for an entity bean has been set, no attempt should be made to change it using the set accessor methods. Therefore set accessor methods are not provided on the Domain-Object interface. When the primary key is made of more than one CMP field, the composite key can be represented using a custom type. All fields in the primary key class must be declared public. The primkey-field element is not used when the primary key is a compound key, that is, it maps to multiple cmp-fields.

The container-managed persistent fields and container-managed relationship fields are specified in the deployment descriptor using the cmp-field and cmr-field elements, respectively. Java types assigned to cmp-field can be Java primitive types and Java serializable types. The names assigned to cmp-fields and cmr-fields must begin with a lowercase letter. The corresponding accessor methods defined in the bean class follow the JavaBean method naming convention, that is, the first letter of the name of the cmp-field or cmr-field is uppercased and prefixed by get or set. Note that all cmp-fields and cmr-fields are mapped to the database schema using the vendor-specific weblogic-cmp-rdbms-jar.xml file. We discuss this deployment descriptor later in this section.

The container-managed fields are virtual fields since they are not explicitly declared in the bean class. Instead, the bean developer declares an abstract set of get and set accessor methods for each container-managed field. These abstract methods are declared in the entity bean class. The corresponding implementation is generated by the container provider's tools at deployment time. For the purpose of our example, we have a slight deviation in that the abstract accessor methods are not made explicitly part of the entity bean class but rather these accessors are defined in a separate Domain-Object interface. In the case of the AdminBean class, the accessors are defined on the Admin interface, which is implemented by the AdminBean class and extended by the AdminLocal interface as shown previously in Figure 6-3. The advantages of doing this have been discussed in the section "Implementing the Domain Model."

The query element is used to specify queries for both the finder and select methods. The container will provide the implementation for methods declared in the query element. The container uses the query specified by the ejb-ql element as part of the method implementation. Queries are expressed using EJB QL (for a complete discussion on EJB QL, please refer to Chapter 11 of the EJB 2.0 specification). Input parameters to queries are designated by the question mark (?) prefix followed by an integer. This integer specifies the position of the parameter in the method declared in the deployment descriptor by the query-method element. For the findByEin method shown in the deployment descriptor in the preceding, there is only one method parameter of type java.lang.String.

As part of our discussion on ejb-jar.xml deployment descriptor file, we examine the relationships element declared in the descriptor file. The following snippet shows the descriptors required for configuring the relationship between Admin and NPO entity beans. Please note that the persistence mechanism is configured accordingly, and the mapping between the persistence layer and the EJBs is provided by a vendor-specific weblogic-cmp-rdbms-jar.xml deployment descriptor.

 <!-- Define container-managed relationships --> <relationships>     <ejb-relation>         <!-- Provide unique name for a relationship; this name is used in         weblogic-cmp-rdbms-jar.xml for mapping the relationship to the         database schema -->         <ejb-relation-name>Admin-NPO</ejb-relation-name>         <!-- Define the relationship in the context of role name 'admin' -->         <ejb-relationship-role>             <ejb-relationship-role-name>admin</ejb-relationship-role-name>             <multiplicity>One</multiplicity>             <relationship-role-source>                 <!-- Identify the EJB previously described                 using ejb-name element -->                 <ejb-name>AdminEntityEJB             </ejb-name> </relationship-role-source>             <cmr-field>                 <!-- get and set accessors are defined for this field;                 this also indicates the direction of the relationship -->                 <cmr-field-name>npo</cmr-field-name>             </cmr-field>         </ejb-relationship-role>         <!-- Define the relationship in the context of role name 'npo' -->         <ejb-relationship-role>             <ejb-relationship-role-name>npo</ejb-relationship-role-name>             <multiplicity>One</multiplicity>             <relationship-role-source>                 <ejb-name>NPOEntityEJB</ejb-name>             </relationship-role-source>         </ejb-relationship-role>     </ejb-relation> </relationships> 

Figure 6-1 shows the association Admin-NPO between Admin and NPO entity beans. We defined the accessors for this unidirectional relationship in the Admin interface using getNpo and setNpo CMR-related methods. Note that we have chosen the role-name as the cmr-field name. The following code fragment shows the accessors that form the Admin-NPO relationship.

 public interface Admin {     public NPOLocal getNpo();     public void setNpo(NPOLocal npo); } 

To explain the associated deployment descriptors, we take a bottom-up approach. The basic structure that establishes a relationship is a container-managed-relationship field that is declared using the cmr-field element. In the preceding snippet for the Admin entity bean, we have the Admin bean declaring a cmr-field element npo, the corresponding accessors are declared in the Admin interface, and the weblogic-cmp-rdbms-jar.xml defines a weblogic-rdbms-relation element that provides a concrete schema of how this relationship will be physically persisted. For the Admin-NPO relationship, the corresponding weblogic-rdbms-relation:column-map (subordinate to weblogic-rdbms-relations element) element indicates that the ADMIN table column name EIN is a foreign key associated with the primary key column EIN of the NPO table. We will see usage of the column-map element shortly.

The ejb-relationship-role element is defined in the context of the role name associated with the relationship-role-source element. For our sample descriptor, the source is identified by the logical name assigned to the AdminBean, which is AdminEntityEJB, and the corresponding role name identified by the ejb-relationship-role-name is admin. The relationship-role-source NPOEntityEJB does not have a cmr-field because the association between Admin and NPO is undirected when traversing from NPO to Admin.

The multiplicity element describes the multiplicity of the role identified by the ejb-relationship-role-name element—it can take the value One or Many. A little digression is in order to explain this. The multiplicity of 0..* specified in Figure 6-1 for the Campaign entity bean side of the PortalAlliance-Campaign relationship will be specified as <multiplicity> Many</multiplicity>. This creates a collection-valued relationship. The getCampaigns method on the PortalAlliance entity bean will return a Collection object containing objects of the type CampaignLocal (which extends the Campaign domain-object interface. We discuss this again in the section "Defining the PortalAlliance Interface." You can refer to section 10.3.6 of the EJB 2.0 specification for a detailed discussion of collection-valued relationships, but this knowledge is not required for understanding the rest of this chapter.

Recapping the preceding discussion, we have successfully defined the bean classes, corresponding interfaces, and the ejb-jar-xml deployment descriptor that implements a one-to-one unidirectional relationship from Admin to NPO entity bean. The following discussion explains the vendor-specific deployment descriptor necessary for vendors to generate the concrete classes for the abstract bean classes we defined earlier. We begin by discussing the declarations in the weblogic-ejb-jar.xml file—a snippet of this file appears here in the context of AdminBean class:

 <!-- Admin Entity Bean Definition --> <weblogic-enterprise-bean>     <ejb-name>AdminEntityEJB</ejb-name>     <entity-descriptor>         <entity-cache>             <max-beans-in-cache>1000</max-beans-in-cache>         </entity-cache>         <persistence>             <persistence-use>                 <type-identifier>WebLogic_CMP_RDBMS</type-identifier>                 <type-version>7.0</type-version>                 <type-storage>META-INF/weblogic-cmp-rdbms-jar.xml</type-storage>             </persistence-use>         </persistence>     </entity-descriptor>     <local-jndi-name>ejb/local/com.gc.persistence.admin.AdminLocalHome     </local-jndi-name> </weblogic-enterprise-bean> 

The ejb-name element provides the logical name by which the bean declarations are identified in the ejb-jar.xml deployment descriptor. The entity-descriptor:type-storage element defines the location of the deployment descriptor weblogic-cmp-rdms-jar.xml for the RDBMS-based persistence mechanism. The local-jndi-name element provides the JNDI name for the entity bean. The EJB specification recommends prefixing JNDI names with "ejb/."

Moving forward, we look at how the persistence mechanism ties into container-managed entity beans using the weblogic-cmp-rdbms-jar.xml deployment descriptor. We use the Admin entity bean example for this purpose.

 <weblogic-rdbms-bean>     <ejb-name>AdminEntityEJB</ejb-name>     <data-source-name>jdbc/gcOracleTxPool</data-source-name>     <table-map>         <table-name>ADMIN</table-name>         <field-map>                <cmp-field>adminID</cmp-field>                <dbms-column>ADMIN_ID</dbms-column>            </field-map>     </table-map> </weblogic-rdbms-bean> 

The value of the ejb-name element is a logical name that refers to the bean configuration defined in the ejb-jar.xml deployment descriptor. The value of the data-source-name element specifies the JNDI name given to the connection pool while configuring the server. We discuss this configuration in Chapter 9.

The table-map element defines the mapping between the entity bean and the database table. The table-name element identifies the table name, and the field-map entries identify the mapping between a cmp-field and the corresponding table column. This mapping must be provided for all the cmp-fields defined for the entity bean. The AdminBean has only one cmp-field.

 <weblogic-rdbms-relation>     <relation-name>Admin-NPO</relation-name>     <weblogic-relationship-role>         <!-- This role name was defined in the ejb-jar.xml file -->         <relationship-role-name>admin</relationship-role-name>         <relationship-role-map>             <column-map>                 <foreign-key-column>EIN</foreign-key-column>                 <key-column>EIN</key-column>             </column-map>         </relationship-role-map>     </weblogic-relationship-role> </weblogic-rdbms-relation> 

We can draw a parallel between the weblogic-rdbms-relation element of the weblogic-cmp-rdbms-jar.xml and the ejb-relation element of the ejb-jar.xml file. While the ejb-relation element specified the cmr-field names, the weblogic-rdbms-relation:column-map specifies the column name of the ADMIN table that will be used to persist the relationship. The foreign-key-column element provides the column name of the foreign key in the ADMIN table, while the key-column element provides the column name of the primary key for the NPO table that will map to the foreign key of the ADMIN table.

This concludes the implementation and configuration of the AdminBean class and its corresponding interfaces and deployment descriptors. In the following section, we discuss the semantics for implementing a one-to-many relationship that involves a collection-valued cmr-field.

Defining the PortalAlliance Interface

In this section, we define the methods pertinent to the PortalAlliance domain-object interface. This interface has the standard accessor methods for the cmr-field "campaigns" except that in this case we are dealing with a collection-valued cmr-field. Also, a couple of convenience methods have been declared to work in conjunction with EJB QL for returning Collection objects.

Figure 6-1 shows that a PortalAlliance object can be associated with zero or more Campaign objects. The direction of relationship is from PortalAlliance to Campaign. The accessors associated for this relationship are created for the PortalAlliance interface as getCampaigns and setCampaigns. Observe that the getCampaigns accessor method returns a Collection object, whereas the setCampaigns accessor method specifies a collection-valued parameter. We strongly recommend that you refer to section 10.3.6 of the EJB 2.0 specification for details on collection-valued relationships. However, this knowledge is not required for understanding the rest of this chapter. A convenience method addCampaign is specified on the PortalAlliance domain object interface for adding a single Campaign object to this collection-valued relationship. Another convenience method getRegionalCampaigns is added for extracting the qualifying campaigns based on a regionCode parameter. The PortalAlliance interface is shown here:

 public interface PortalAlliance {     /* CMP Field Accessors */     public String getPortalID();     public String getPortalName();     public void setPortalName(String portalName);     ...other cmp-field accessor methods ...     /* CMR Field Accessors */     public Collection getCampaigns();     public void setCampaigns(Collection campaigns);     /* Other Convenience Methods */     public Collection getRegionalCampaigns(String regionCode)             throws FinderException;     public void addCampaign(Campaign campaign); } 

As discussed before, the cmp-field's and cmr-field's accessor method implementation is provided by the container provider's tools; however, the bean class must provide implementation for the convenience methods getRegionalCampaigns and addCampaign that access the collection-valued cmr-field. The convenience methods are shown in the following code fragment:

 public abstract class PortalAllianceBean     implements EntityBean, PortalAlliance {     private EntityContext ctx;     /* Adding a new Campaign to a collection-valued relationship */     public void addCampaign(Campaign campaign){         Collection campaigns = getCampaigns();         campaigns.add(campaign);     }     public Collection getRegionalCampaigns(String regionCode)             throws FinderException{         return ejbSelectRegionalCampaigns(getPortalID(),regionCode);     }     public abstract Collection ejbSelectRegionalCampaigns(String portalID,         String regionCode) throws FinderException;     ... other bean methods ... } 

There are two ways we can add a Campaign object for a given PortalAlliance, using either the container-implemented setCampaigns method, or the bean class implemented addCampaign method. When the setCampaigns method is used, the collection-valued parameter completely replaces existing relationships. The setCampaigns method therefore has the semantics of the java.util.Collection's clear method, followed by java.util.Collection's addAll method. For adding a new Campaign to the existing relationship set, we use the addCampaign method. This method first retrieves a container-managed collection on which the java.util.Collection's add method is called. This has the effect of adding the new PortalAlliance-Campaign relationship in the CAMPAIGN table using the foreign-key PORTAL_ID specified for the CAMPAIGN table. Readers are recommended to review section 10.3.7 of the EJB 2.0 specification for further details on manipulating container-managed collections.

To obtain a filtered collection of objects based on a specific regionCode, we use the getRegionalCampaigns method on the local interface. This method delegates to ejbSelectRegionalCampaigns of the PortalAllianceBean class. This indirection is provided because the EJB specification does not permit exposing of the ejbSelect<method> method (where <method> is any given suffix that uniquely identifies the method name) to the client. The use of the ejbSelect<method> method is permitted only for an entity bean class. The ejbSelect<method> method was preferred in this case over the ejbFind<method> method because the ejbFind<method> method can only return an object (or collection) of type PortalAllianceLocal (the type corresponding to the interface itself), whereas the ejbSelect<method> method can return objects (or collection) of any cmp-field or cmr-field type; for the ejbSelectRegionalCampaigns, the desired collection is of type Campaigns. Observe that the ejbSelectRegionalCampaigns is declared as abstract as the actual implementation of the ejbSelect<method> method is provided by the container provider's tools. A corresponding EJB QL is provided in the ejb-jar.xml deployment descriptor using the query element, which is discussed in the following subsection.




Practical J2ee Application Architecture
Practical J2EE Application Architecture
ISBN: 0072227117
EAN: 2147483647
Year: 2003
Pages: 111
Authors: Nadir Gulzar

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