Book Home Enterprise JavaBeans Search this book

10.5. Describing Beans

The beans contained in a JAR file are described within the deployment descriptor's enterprise-beans element. So far, we've only talked about deployment descriptors for a single bean, but there's no reason that you can't package several beans in a JAR file and describe them all within a single deployment descriptor. We could, for example, have deployed the TravelAgent, ProcessPayment, Cruise, Customer, and Reservation beans in the same JAR file. The deployment descriptor would look something like this:

<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise
JavaBeans 1.1//EN" "http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd">

<ejb-jar>
 <description>
    This Deployment includes all the beans needed to make a reservation:
    TravelAgent, ProcessPayment, Reservation, Customer, Cruise, and Cabin.
 </description>
 <enterprise-beans>
   <session>
      <ejb-name>TravelAgentBean</ejb-name>
      <remote>com.titan.travelagent.TravelAgent</remote>
      ...
   </session>
   <entity>
      <ejb-name>CustomerBean</ejb-name>
      <remote>com.titan.customer.Customer</remote>      
      ...
   </entity>
   <session>
      <ejb-name>ProcessPaymentBean</ejb-name>
      <remote>com.titan.processpayment.ProcessPayment</remote>
      ...
   </session>
   ...
 </enterprise-beans>
 <assembly-descriptor>
 ...
 </assembly-descriptor>
 ...
</ejb-jar>

In this descriptor, the enterprise-beans element contains two session elements and one entity element describing the three beans. Other elements within the entity and session elements provide detailed information about the beans; as you can see, the ejb-name element defines the bean's name. We'll discuss all of the things that can go into a bean's description later.

Multiple bean deployments have the advantage that they can share assembly information, which is defined in the assembly-descriptor element that follows the enterprise-beans element. In other words, beans can share security and transactional declarations, making it simpler to deploy them consistently. For example, deployment is easier if the same logical security roles control access to all the beans, and it's easiest to guarantee that the roles are defined consistently if they are defined in one place. It's also easier to ensure that the transactional attributes are applied consistently to all beans because you can declare them all at the same time.

10.5.1. Session and Entity Beans

The session and entity elements, which are used to describe session and entity beans, usually contain many elements nested within them, but the lists of allowable subelements are similar. Therefore, we'll discuss the session and entity elements together.

Like the ebj-jar element itself, a session or an entity element can optionally have description, display-name, small-icon, and large-icon elements. These are fairly self-explanatory and, in any case, mean the same as they did for the ejb-jar element. The description lets you provide a comment that describes the bean; the display-name is used by deployment tools to represent the bean; and the two icons are used to represent the bean in visual environments. The icons must point to JPEG or GIF images within the JAR file.

The other elements are more interesting:

<ejb-name> (one required)

This is the name of the bean component. It is used in the methodx element to scope method declarations to the correct bean. Throughout this book, we use ejb-names of the form "Name Bean" as the ejb-name for bean. Other common conventions use the ejb-names of the form "Name EJB" or "TheName."

<home> (one required)

This is the fully qualified class name of the bean's home interface.

<remote> (one required)

This is the fully qualified class name of the bean's remote interface.

<ejb-class> (one required)

This is the fully qualified class name of the bean class.

<primkey-field> (optional; entity beans only)

This element is used to specify the primary key field for entity beans that use container-managed persistence. Its value is the name of the field that is used as the primary key. It is not used if the bean has a custom primary key or if the entity bean manages its own persistence. This element is discussed in more detail in Section 10.5.2, "Specifying Primary Keys", later in this chapter.

<prim-key-class> (one required; entity beans only)

This element specifies the class of the primary key for entity beans. Its value is the fully qualified name of the primary key class; it makes no difference whether you're using a custom compound primary key like the CabinPK, or a simple primkey-field like an Integer, String, Date, etc. If you defer definition of the primary key class to the deployer, specify the type as java.lang.Object in this element.

<persistence-type> (one required; entity beans only)

The persistence-type element declares that the entity bean uses either container-managed persistence or bean-managed persistence. This element can have one of two values: Container or Bean.

<reentrant> (one required; entity beans only)

The reentrant element declares that the bean either allows loopbacks (reentrant invocations) or not. This element can have one of two values: True or False. True means that the bean allows loopbacks; False means that the bean throws an exception if a loopback occurs.

<cmp-field> (zero or more; entity beans only)

This element is used in entity beans with container-managed persistence. A cmp-field element must exist for each container-managed field in the bean class. Each cmp-field element may include a description element and must include a field-name element. The description is an optional comment describing the field. The field-name is required and must be the name of one of the bean's fields. The container will manage persistence for the given field. The following portion of a descriptor shows several cmp-field declarations for the Cabin bean:

<cmp-field>
  <description>This is the primary key</description>
  <field-name>id</field-name>
</cmp-field>
<cmp-field>
  <field-name>name</field-name>
</cmp-field>
<cmp-field>
  <field-name>deckLevel</field-name>
</cmp-field>
<cmp-field>
  <field-name>ship</field-name>
</cmp-field>
<cmp-field>
  <field-name>bedCount</field-name>
</cmp-field>
<env-entry> (zero or more)

This element declares an environment entry that is available through the JNDI ENC. The use of environment entries in a bean and a deployment descriptor is discussed further in Section 10.5.3, "Environment Entries".

<ejb-ref> (zero or more)

This element declares a bean reference that is available through the JNDI ENC. The mechanism for making bean references available through the ENC is described in more detail later, in Section 10.5.4, "References to Other Beans".

<resource-ref> (zero or more)

This element declares a reference to a connection factory that is available through the JNDI ENC. An example of a resource factory is the javax.sql.DataSource, which is used to obtain a connection to a database. This element is discussed in detail in Section 10.5.4, "References to Other Beans" later in this chapter.

<security-role-ref> (zero or more)

The security-role-ref element is used to declare security roles in the deployment descriptor, and map them into the security roles in effect for the bean's runtime environment. This element is described in more detail in Section 10.5.6, "Security Roles".

<session-type> (one required; session beans only)

The session-type element declares that a session bean is either stateful or stateless. This element can have one of two values: Stateful or Stateless.

<transaction-type> (one required; session beans only)

The transaction-type element declares that a session bean either manages its own transactions, or that its transactions are managed by the container. This element can have one of two values: Bean or Container. A bean that manages its own transactions will not have container-transaction declarations in the assembly-descriptor section of the deployment descriptor.

10.5.2. Specifying Primary Keys

An entity bean does not always have to use a custom key class as a primary key. If there's a single field in the bean that can serve naturally as a unique identifier, you can use that field as the primary key without having to create a custom key. In the Cabin bean, for example, the primary key type was the CabinPK, which mapped to the bean class field id as shown here (the CabinBean is using bean-managed persistence to better illustrate):

public class CabinBean implements javax.ejb.EntityBean {

    public int id;
    public String name;
    public int deckLevel;
    public int ship;
    public int bedCount;
 
    public CabinPK ejbCreate(int id) {
        this.id = id;
        return new CabinPk(id);
    }
    ...
}

Instead of using the custom CabinPK class, we could have used the appropriate primitive wrapper, java.lang.Integer, and defined the CabinBean as:

public class CabinBean implements javax.ejb.EntityBean {

    public int id;
    public String name;
    public int deckLevel;
    public int ship;
    public int bedCount;
 
    public Integer ejbCreate(int id){
        this.id = id;
        return new Integer(id);
    }
    ...
}

This simplifies things a lot. Instead of taking the time to define a custom primary key like CabinPK, we simply use the appropriate wrapper. To do this, we need to add a primkey-field element to the Cabin bean's deployment descriptor, so that it knows which field to use as the primary key. We also need to change the prim-key-class element to state that the Integer class is being used to represent the primary key. The following code shows how the Cabin bean's deployment descriptor would need to change to use Integer as the primary key field:

  <entity>
      <description>
            This Cabin enterprise bean entity represents a cabin on 
            a cruise ship.
      </description>
      <ejb-name>CabinBean</ejb-name>
      <home>com.titan.cabin.CabinHome</home>
      <remote>com.titan.cabin.Cabin</remote>
      <ejb-class>com.titan.cabin.CabinBean</ejb-class>
      <persistence-type>Bean</persistence-type>
      <prim-key-class>java.lang.Integer</prim-key-class>
      <primkey-field>id</primkey-field>
      <reentrant>False</reentrant>

      <cmp-field><field-name>id</field-name></cmp-field>
      <cmp-field><field-name>name</field-name></cmp-field>
      <cmp-field><field-name>deckLevel</field-name></cmp-field>
      <cmp-field><field-name>ship</field-name></cmp-field>
      <cmp-field><field-name>bedCount</field-name></cmp-field>
   </entity>

Simple primary key fields are not limited to the primitive wrapper classes (Byte, Boolean, Integer, etc.); any container-managed field can be used as a primary key as long as it's serializable. String types are probably the most common, but other types, such as java.lang.StringBuffer, java.util.Date, or even java.util.Hashtable are also valid. Custom types can also be primkey-fields providing that they are serializable. Of course, common sense should be used when choosing a primary key: because it is used as an index to the data in the database, it should be lightweight. Here's code for a bean that uses a Date as its primary key:

// bean class that uses Date as a primary key 
public class HypotheticalBean implements javax.ejb.EntityBean {
    public Date creationDate;
    ...
    public Date ejbCreate() {
      creationDate = new Date();
      return creationDate;
   }
...
}

And here's the corresponding section of the deployment descriptor:

// primkey-field declaration for the Hypothetical bean
...
<entity>
      <ejb-name>HypotheticalBean</ejb-name>
      ...
      <prim-key-class>java.util.Date</prim-key-class>
      <primkey-field>creationDate</primkey-field>
      <reentrant>False</reentrant>

      <cmp-field><field-name>creationDate</field-name></cmp-field>
      ...
</entity>

Throughout the book we use custom compound primary keys, like ShipPK and CabinPK, instead of using simple primary keys. This may seem strange because these custom primary keys only wrap a single field, usually an integer, which could have been represented by an Integer and used as the primkey-field.

The reason we use custom primary keys is simple: encapsulation. If the primary key fields of the beans change over time, using a custom key hides the changes from client applications that use the key. If, for example, the CabinBean changed to use both a String and a long primitive as the primary key fields instead of a single integer field (id), the Cabin bean's custom primary key class (CabinPK) would hide this change from the client application. If, however, we had used a primkey-field of java.lang.Integer, any client applications that use the findByPrimaryKey() method (and other similar operations involving the key) would have to be modified.

10.5.2.1. Deferring primary key definition

With container-managed persistence, it's also possible for the bean developer to defer defining the primary key, leaving key definition to the bean deployer. This feature might be needed if, for example, the primary key is generated by the database and is not a container-managed field in the bean class. Containers that have a tight integration with database or legacy systems that automatically generate primary keys might use this approach. It's also an attractive approach for vendors that sell shrink-wrapped beans because it makes the bean more portable. The following code shows how an entity bean using container-managed persistence defers the definition of the primary key to the deployer:

// bean class for bean that uses a deferred primary key 
public class HypotheticalBean implements javax.ejb.EntityBean {
    ...
    public java.lang.Object ejbCreate(){
      ...
      return null;
   }
...
}

// home interface for bean with deferred primary key
public interface HypotheticalHome extends javax.ejb.EJBHome {
    
    public Hypothetical create() throws ...;
    
    public Hypothetical findByPrimaryKey(java.lang.Object key) throws ...;
}

Here's the relevant portion of the deployment descriptor:

// primkey-field declaration for the Hypothetical bean
...
<entity>
      <ejb-name>HypotheticalBean</ejb-name>
      ...
      <persistence-type>Container</persistence-type>
      <prim-key-class>java.lang.Object</prim-key-class>
      <reentrant>False</reentrant>

      <cmp-field><field-name>creationDate</field-name></cmp-field>
      ...
</entity>

Because the primary key is of type java.lang.Object, the client application's interaction with the bean's key is limited to the Object type and its methods.

10.5.3. Environment Entries

A deployment descriptor can define environment entries, which are values similar to properties that the bean can read when it is running. The bean can use environment entries to customize its behavior, find out about how it is deployed, etc.

The env-entry element is used to define environment entries. This element contains a description element (optional), env-entry-name (required), env-entry-type (required), and env-entry-value (optional). Here is a typical env-entry declaration:

<env-entry>
  <env-entry-name>minCheckNumber</env-entry-name>
  <env-entry-type>java.lang.Integer</env-entry-type>
  <env-entry-value>2000</env-entry-value>
</env-entry>

The env-entry-name is relative to the "java:comp/env" context. For example, the minCheckNumber entry can be accessed using the path "java:comp/env/minCheckNumber" in a JNDI ENC lookup:

InitialContext jndiContext = new InitialContext();
Integer miniumValue = (Integer)
    jndiContext.lookup("java:comp/env/minCheckNumber");

The env-entry-type can be of type String, or one of several primitive wrapper types including Integer, Long, Double, Float, Byte, Boolean, and Short.

The env-entry-value is optional. The value can be specified by the bean developer or deferred to the application assembler or deployer.

The subcontext "java:comp/env/ejb10-properties" can be used to make an entry available via the EJBContext.getEnvironment() method. This feature has been deprecated, but it may help you deploy EJB 1.0 beans within a EJB 1.1 server. The ejb-entry-type must always be java.lang.String for entries in this subcontext. Here's an example:

<env-entry>
  <description>This property is available through
     EJBContext.getEnvironment()</description>
  <env-entry-name>ejb10-properties/minCheckNumber</env-entry-name>
  <env-entry-type>java.lang.String</env-entry-name>
  <env-entry-value>20000</env-entry-value>
</env-entry>

10.5.4. References to Other Beans

The env-ref element is used to define references to other beans within the JNDI ENC. This makes it much easier for beans to reference other beans; they can use JNDI to look up a reference to the home interface for any beans that they are interested in.

The env-ref element contains description (optional), ejb-ref-name (required), ejb-ref-type (required), remote (required), home (required), and ejb-link (optional) elements. Here is a typical env-ref declaration:

<ejb-ref>
    <ejb-ref-name>ejb/CabinHome</ejb-ref-name>
    <ejb-ref-type>Entity</ejb-ref-type>
    <home>com.titan.cabin.CabinHome</home>
    <remote>com.titan.cabin.Cabin</remote>
</ejb-ref>

The ejb-ref-name is relative to the "java:comp/env" context. It is recommended, but not required, that the name be placed under a subcontext of ejb/. Following this convention, the path used to access the Cabin bean's home would be "java:comp/env/ejb/CabinHome". The following code shows how a client bean would use this context to look up a reference to the Cabin bean:

InitialContext jndiContext = new InititalContext();
Object ref = jndiContext.lookup("java:comp/env/ejb/CabinHome");
CabinHome home = (CabinHome)
    PortableRemoteObject.narrow(ref, CabinHome.class);

The ejb-ref-type can have one of two values: Entity or Session, according to whether the bean is an entity or a session bean.

The home element specifies the fully qualified class name of the bean's home interface; the remote element specifies the fully qualified class name of the bean's remote interface.

If the bean referenced by the ejb-ref element is deployed in the same deployment descriptor (it is defined under the same ejb-jar element), the ejb-ref element can be linked to the bean's declaration using the ejb-link element. If, for example, the TravelAgent bean uses references to the ProcessPayment and Customer beans and they are all declared in the same deployment descriptor, then the ejb-ref elements for the TravelAgent bean can use an ejb-link element to map its ejb-ref elements to the ProcessPayment and Customer beans. The ejb-link value must match one of the ejb-name values declared in the same deployment descriptor. Here's a portion of a deployment descriptor that uses the ejb-link element:

<ejb-jar>
<enterprise-beans>
   <session>
      <ejb-name>TravelAgentBean</ejb-name>
      <remote>com.titan.travelagent.TravelAgent</remote>
      ...
      <ejb-ref>
           <ejb-ref-name>ejb/ProcessPaymentHome</ejb-ref-name>
           <ejb-ref-type>Session</ejb-ref-type>
           <home>com.titan.processpayment.ProcessPaymentHome</home>
           <remote>com.titan.processpayment.ProcessPayment</remote>
           <ejb-link>ProcessPaymentBean</ejb-link>
      </ejb-ref>
      <ejb-ref>
           <ejb-ref-name>ejb/CustomerHome</ejb-ref-name>
           <ejb-ref-type>Entity</ejb-ref-type>
           <home>com.titan.customer.CustomerHome</home>
           <remote>com.titan.customer.Customer</remote>
           <ejb-link>CustomerBean</ejb-link>
      </ejb-ref>
   </session>
   <entity>
      <ejb-name>CustomerBean</ejb-name>
      <remote>com.titan.customer.Customer</remote>      
      ...
   </entity>
   <session>
      <ejb-name>ProcessPaymentBean</ejb-name>
      <remote>com.titan.processpayment.ProcessPayment</remote>
      ...
   </session>
   ...
 </enterprise-beans>
 ...
</ejb-jar>

10.5.5. References to External Resources

Beans also use the JNDI ENC to look up external resources, like database connections, that they need to access. The mechanism for doing this is similar to the mechanism used for referencing other beans and environment entries: the external resources are mapped into a name within the JNDI ENC name space. For external resources, the mapping is performed by the resource-ref element.

The resource-ref element contains description (optional), res-ref-name (required), res-type (required), and res-auth (required) elements. Here is a resource-ref declaration used for a DataSource connection factory:

<resource-ref>
   <description>DataSource for the Titan database</description>
   <res-ref-name>jdbc/titanDB</res-ref-name>
   <res-type>javax.sql.DataSource</res-type>
   <res-auth>Container</res-auth>
</resource-ref>

The res-ref-name is relative to the "java:comp/env" context. Although it is not a requirement, it's a good idea to place connection factories under a subcontext that describes the resource type. For example:

Here is how a bean would use JNDI to look up a resource--in this case, a DataSource:

InitialContext jndiContext = new InitialContext();
DataSource source = (DataSource)
    jndiContext.lookup("java:comp/env/jdbc/titanDB");

The res-type is used to declare the fully qualified class name of the connection factory. In this example, the res-type is javax.sql.DataSource.

The res-auth tells the server who is responsible for authentication. It can have one of two values: Container or Application. If Container is specified, authentication (sign-on or login) to use the resource will be performed automatically by the container as specified at deployment time. If Application is specified, the bean itself must perform the necessary authentication before using the resource. The following code shows how a bean might sign on to a connection factory when Application is specified for res-auth:

InitialContext jndiContext = new InitialContext();
DataSource source = (DataSource)
    jndiContext.lookup("java:comp/env/jdbc/titanDB");

String loginName = ejbContext.getCallerPrincipal().getName();
String password = ...; // get password from somewhere

// use login name and password to obtain a database connection
java.sql.Connection con = source.getConnection(loginName, password);

10.5.6. Security Roles

The security-role-ref element is used to define the security roles that are used by a bean and to map them into the security roles that are in effect for the runtime environment. It can contain three subelements: an optional description, a role-name (required), and an optional role-link.

Here's how security roles are defined. When a role name is used in the EJBContext.isCallerInRole(String roleName) method, the role name must be statically defined (it cannot be derived at runtime) and it must be declared in the deployment descriptor using the security-role-ref element:

<-- security-role-ref declaration for Account bean -->
<entity>
  <ejb-name>AccountBean</ejb-name>
  ...
  <security-role-ref>
    <description>
        The caller must be a member of this role in 
        order to withdraw over $10,000
    </description>
    <role-name>Manager</role-name>
    <role-link>Administrator</role-link>
  </security-role-ref>
  ..
</entity>

The role-name defined in the deployment descriptor must match the role name used in the EJBContext.isCallerInRole() method. Here is how the role name is used in the bean's code:

// Account bean uses the isCallerInRole() method
public class AccountBean implements EntityBean {
    int id;
    double balance;
    EntityContext context;

    public void withdraw(Double withdraw)
    throws AccessDeniedException {
        
        if (withdraw.doubleValue() > 10000) {
           boolean isManager = context.isCallerInRole("Manager");
           if (!isManager) {
              // only Managers can withdraw more than 10k
              throw new AccessDeniedException();
           }
        }
        balance = balance - withdraw.doubleValue();

    }
    ...
}

The role-link element is optional; it can be used to map the role name used in the bean to a logical role defined in a security-role element in the assembly-descriptor section of the deployment descriptor. If no role-link is specified, the deployer must map the security-role-ref to an existing security role in the target environment.



Library Navigation Links

Copyright © 2001 O'Reilly & Associates. All rights reserved.