Chapter 16. XML Deployment Descriptors
16.1 What Is an XML Deployment Descriptor?This chapter discusses what goes into an XML deployment descriptor; it teaches you how to write deployment descriptors for your beans. Keep in mind that you may never need to write a deployment descriptor by hand; most vendors of integrated development tools and EJB servers will provide tools for creating the descriptor automatically. Even if you have such a tool available, however, you should be familiar enough with deployment descriptors to be able to read them on your own. This chapter does not attempt to teach you how to read or write correct XML. There are many books on the subject: a good quick reference is XML Pocket Reference by Bob Eckstein (O'Reilly); XML in a Nutshell, by Elliotte Rusty Harold and W. Scott Means (O'Reilly), provides a more detailed treatment. Very briefly, XML looks like HTML, but with different tag names and different attributes inside the tags. You won't see <h1> and <p> inside a deployment descriptor; you'll see tags like <ejb-jar>. But otherwise, if you expect an XML document to look like HTML, you're most of the way toward reading it. The tag names and attribute names for an XML document are defined by a special document called a Document Type Definition (DTD). Therefore, for XML deployment descriptors, there is a DTD that defines the tags and attributes that can be used in the document; the DTDs for deployment descriptors in EJB 2.0 and 1.1 are available online at http://java.sun.com/dtd/ejb-jar_2_0.dtd and http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd. There are a few other important differences between XML and HTML. XML is much more strict; many things that are acceptable in HTML are errors in XML. This should not make a difference if you're just reading a deployment descriptor, but if you're writing one, you have to be careful. Two differences are particularly important. First, XML is case-sensitive. You cannot mix uppercase and lowercase in your tag names. HTML does not care about the difference between <h1> and <H1>, but XML does. All the tags and attributes used in deployment descriptors are lowercase. Second, XML will not forgive you if you fail to supply closing tags. In HTML you can write <p>...<p>, without ever putting in a </p> to end the first paragraph. XML never allows you to be sloppy. Whenever you have an opening tag, there must always be a closing tag. That's about it. These few paragraphs don't even qualify as a quick introduction to XML, but the basic ideas are very simple, and that's really all you should need to get going. 16.2 The Contents of a Deployment DescriptorWe've discussed XML deployment descriptors throughout this book. At this point, you probably know enough to write deployment descriptors on your own. However, it is still worthwhile to take a tour through a complete descriptor. Following are the EJB 2.0 and 1.1 versions of the deployment descriptor for the Cabin EJB, which we created in Chapter 4. The Cabin EJB's deployment descriptor contains most of the tags that are needed to describe entity beans; session and message-driven beans are not much different. The differences between the versions are small but significant. We'll use this deployment descriptor to guide our discussion in the following sections. Here is the EJB 2.0 deployment descriptor: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd"> <ejb-jar> <enterprise-beans> <entity> <description> This Cabin enterprise bean entity represents a cabin on a cruise ship. </description> <ejb-name>CabinEJB</ejb-name> <home>com.titan.cabin.CabinHomeRemote</home> <remote>com.titan.cabin.CabinRemote</remote> <local-home>com.titan.cabin.CabinHomeLocal</local-home> <local>com.titan.cabin.CabinLocal</local> <ejb-class>com.titan.cabin.CabinBean</ejb-class> <persistence-type>Container</persistence-type> <prim-key-class>com.titan.cabin.CabinPK</prim-key-class> <reentrant>False</reentrant> <cmp-version>2.x</cmp-version> <abstract-schema-name>Cabin</abstract-schema-name> <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>shipId</field-name></cmp-field> <cmp-field><field-name>bedCount</field-name></cmp-field> <primkey-field>id</primkey-field> </entity> </enterprise-beans> <assembly-descriptor> <security-role> <description> This role represents everyone who is allowed full access to the Cabin EJB. </description> <role-name>everyone</role-name> </security-role> <method-permission> <role-name>everyone</role-name> <method> <ejb-name>CabinEJB</ejb-name> <method-name>*</method-name> </method> </method-permission> <container-transaction> <method> <ejb-name>CabinEJB</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> </assembly-descriptor> </ejb-jar> Here is the EJB 1.1 deployment descriptor: <?xml version="1.0" encoding="UTF-8"?> <!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> <enterprise-beans> <entity> <description> This Cabin enterprise bean entity represents a cabin on a cruise ship. </description> <ejb-name>CabinEJB</ejb-name> <home>com.titan.cabin.CabinHomeRemote</home> <remote>com.titan.cabin.CabinRemote</remote> <ejb-class>com.titan.cabin.CabinBean</ejb-class> <persistence-type>Container</persistence-type> <prim-key-class>java.lang.Integer</prim-key-class> <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>shipId</field-name></cmp-field> <cmp-field><field-name>bedCount</field-name></cmp-field> <primkey-field>id</primkey-field> </entity> </enterprise-beans> <assembly-descriptor> <security-role> <description> This role represents everyone who is allowed full access to the Cabin EJB. </description> <role-name>everyone</role-name> </security-role> <method-permission> <role-name>everyone</role-name> <method> <ejb-name>CabinEJB</ejb-name> <method-name>*</method-name> </method> </method-permission> <container-transaction> <method> <ejb-name>CabinEJB</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> </assembly-descriptor> </ejb-jar> 16.3 The Document HeaderAn XML document may start with a tag that specifies the version of XML that is in use: <?xml version="1.0" encoding="UTF-8"?> This tag identifies the document as an XML document that adheres to Version 1.0 of the XML specification. The character encoding used (UTF-8) is usually supported by EJB vendors. The next tag specifies the DTD that defines the document. In EJB 2.0, it looks like this: <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd"> In EJB 1.1, it looks like this: <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN" "http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd"> This tag provides the URL from which you (or, more importantly, tools processing the deployment descriptor) can download the document. The DTD can be used to validate the XML document; this means that the EJB server deploying the bean can download the DTD and use it to prove that your deployment descriptor is correct (i.e., that it is organized correctly, it uses the right tag names, and all the tags and attributes have appropriate parameters). This tag also identifies the name of the document's root element, which is <ejb-jar>. The <ejb-jar> tag marks the beginning of the document proper. 16.4 The Descriptor's BodyThe body of any XML document begins and ends with the tag for the document's root element, which is defined by the DTD. For a deployment descriptor, the root element is named <ejb-jar>, and looks like this: <ejb-jar> ... other elements ... </ejb-jar> All other elements must be nested within the <ejb-jar> element. You can place the following kinds of elements within <ejb-jar>:
These elements are quite simple, with the exception of the <enterprise-beans> and <assembly-descriptor> elements. These two elements contain a lot of other material nested within them. We'll look at the <enterprise-beans> element first. 16.5 Describing Enterprise BeansThe enterprise beans contained in a JAR file are described within the deployment descriptor's <enterprise-beans> element. So far we have talked about deployment descriptors for only a single enterprise bean, but it is possible to package several enterprise 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 EJBs in the same JAR file. In EJB 2.0, we could also add a message-driven bean, such as the ReservationProcessor EJB we developed in Chapter 13. The EJB 2.0 deployment descriptor would look something like this: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.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>TravelAgentEJB</ejb-name> <remote>com.titan.travelagent.TravelAgentRemote</remote> ... </session> <entity> <ejb-name>CustomerEJB</ejb-name> <remote>com.titan.customer.CustomerRemote</remote> ... </entity> <session> <ejb-name>ProcessPaymentEJB</ejb-name> <remote>com.titan.processpayment.ProcessPaymentRemote</remote> ... </session> <message-driven> <ejb-name>ReservationProcessorEJB</ejb-name> ... </message-driven> ... </enterprise-beans> <relationships> ... </relationships> <assembly-descriptor> ... </assembly-descriptor> ... </ejb-jar> The EJB 1.1 deployment descriptor would look something like this: <?xml version="1.0" encoding="UTF-8"?> <!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>TravelAgentEJB</ejb-name> <remote>com.titan.travelagent.TravelAgentRemote</remote> ... </session> <entity> <ejb-name>CustomerEJB</ejb-name> <remote>com.titan.customer.CustomerRemote</remote> ... </entity> <session> <ejb-name>ProcessPaymentEJB</ejb-name> <remote>com.titan.processpayment.ProcessPaymentRemote</remote> ... </session> ... </enterprise-beans> <assembly-descriptor> ... </assembly-descriptor> ... </ejb-jar> In this descriptor, the <enterprise-beans> element contains two <session> elements, one <entity> element, and, for EJB 2.0, a <message-driven> element describing the enterprise beans. Other elements within the <entity>, <session>, and <message-driven> elements provide detailed information about the enterprise beans; as you can see, the <ejb-name> element defines the enterprise bean's name. We will discuss all the things that can go into a bean's description later. Multiple-bean deployments have the advantage of being able to 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 is easiest to guarantee that the roles are defined consistently if they are defined in one place. This strategy also makes it easier to ensure that the transaction attributes are applied consistently to all the beans, because you can declare them all at once. 16.5.1 Session and Entity BeansThe <session> and <entity> elements, which describe session and entity beans, usually contain many elements nested within them. The lists of allowable subelements are similar so we'll discuss the <session> and <entity> elements together. Like the <ejb-jar> element, a <session> or <entity> element can optionally contain <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 enterprise bean; the <display-name> is used by deployment tools to represent the enterprise bean; and the two icon elements are used to represent the enterprise bean in visual environments. The icon elements must point to JPEG or GIF images within the JAR file. The other elements are more interesting:
16.5.2 Message-Driven BeansThe <message-driven> element describes message-driven bean deployments. <message-driven> elements occur after <entity> and <session> elements within the <enterprise-bean> element. Like the <entity> and <session> elements, the <message-driven> element can optionally have <description>, <display-name>, <small-icon>, and <large-icon> elements. These elements are used primarily by visual deployment tools to represent the message-driven bean. The <message-driven> element also requires declaration of the <ejb-name>, <ejb-class>, <transaction-type>, and <security-id-entity> elements. In addition, it contains the standard JNDI ENC elements <env-entry>, <ejb-ref>, <ejb-local-ref>, <resource-ref>, and <resource-env-ref>. These are fairly self-explanatory and mean the same as they did in the <entity> and <session> elements. The elements that are specific to the message-driven bean are:
16.5.3 Specifying Primary KeysIf there is a single field in the bean that can serve naturally as a unique identifier, you can use that field as the primary key. Optionally, a custom primary key can be used as a compound primary key. In the Cabin EJB, for example, the primary key type could be the CabinPK, which is mapped to the bean class fields id and name 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, String name) { this.id = id; this.name = name; return null; } ... } In Chapter 4, instead of using the custom CabinPK class, we 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 null; } ... } 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 EJB's deployment descriptor, so 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 EJB'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>CabinEJB</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> <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> <primkey-field>id</primkey-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 is 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 primary keys, provided 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. 16.5.3.1 Deferring primary key definitionWith container-managed persistence, it is 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 is 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>HypotheticalEJB</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. 16.5.4 Environment EntriesA deployment descriptor can define environment entries, which are values similar to properties 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, and so on. The <env-entry> element is used to define environment entries. This element contains <description> (optional), <env-entry-name> (required), <env-entry-type> (required), and <env-entry-value> (optional) subelements. 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 an 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> 16.5.5 References to Other BeansIn EJB 2.0, references to other beans can be either local or remote. In EJB 1.1, references to other beans are always remote references. 16.5.5.1 Remote referencesThe <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 in which they are interested. The <env-ref> element contains <description> (optional), <ejb-ref-name> (required), <ejb-ref-type> (required), <remote> (required), <home> (required), and <ejb-link> (optional) subelements. Here is a typical <env-ref> declaration: <ejb-ref> <ejb-ref-name>ejb/ProcessPaymentHomeRemote</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <home>com.titan.processpayment.ProcessPaymentHomeRemote</home> <remote>com.titan.processpayment.ProcessPaymentHomeRemote</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 ProcessPayment EJB's home would be "java:comp/env/ejb/ProcessPaymentHomeRemote". The following code shows how a client bean would use this context to look up a reference to the ProcessPayment EJB: InitialContext jndiContext = new InititalContext(); Object ref = jndiContext.lookup("java:comp/env/ejb/ProcessPaymentHomeRemote"); ProcessPaymentHomeRemote home = (ProcessPaymentHomeRemote) PortableRemoteObject.narrow(ref, ProcessPaymentHomeRemote.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 (i.e., 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 reference to the ProcessPayment EJB that is declared in the same deployment descriptor, the <ejb-ref> elements for the TravelAgent bean can use an <ejb-link> element to map its <ejb-ref> elements to the ProcessPayment EJB. 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>TravelAgentEJB</ejb-name> <remote>com.titan.travelagent.TravelAgentRemote</remote> ... <ejb-ref> <ejb-ref-name>ejb/ProcessPaymentHome</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <home>com.titan.processpayment.ProcessPaymentHomeRemote</home> <remote>com.titan.processpayment.ProcessPaymentRemote</remote> <ejb-link>ProcessPaymentEJB</ejb-link> </ejb-ref> ... </session> <session> <ejb-name>ProcessPaymentEJB</ejb-name> <remote>com.titan.processpayment.ProcessPaymentRemote</remote> ... </session> ... </enterprise-beans> ... </ejb-jar> If you are an EJB 2.0 developer, you are probably better off using the <ejb-local-ref> element to obtain references to beans in the same JAR file, unless the referenced enterprise bean does not have a set of local component interfaces. In that case, you should use the <ejb-link> element with the <ejb-ref> element to get a remote reference to the enterprise bean. 16.5.5.2 EJB 2.0: Local referencesThe deployment descriptor also provides a special set of tags, the <ejb-local-ref> elements, to declare local EJB references; i.e., references to enterprise beans that are co-located in the same container and deployed in the same EJB JAR file. The <ejb-local-ref> elements are declared immediately after the <ejb-ref> elements: <ejb-local-ref> <ejb-ref-name>ejb/CruiseHomeLocal</ejb-ref-name> <ejb-ref-type>Entity</ejb-ref-type> <local-home>com.titan.cruise.CruiseHomeLocal</local-home> <local>com.titan.cruise.CruiseLocal</local> <ejb-link>CruiseEJB</ejb-link> </ejb-local-ref> <ejb-local-ref> <ejb-ref-name>ejb/CabinHomeLocal</ejb-ref-name> <ejb-ref-type>Entity</ejb-ref-type> <local-home>com.titan.cabin.CabinHomeLocal</local-home> <local>com.titan.cabin.CabinLocal</local> <ejb-link>CabinEJB</ejb-link> </ejb-local-ref> The <ejb-local-ref> element defines a name for the bean within the ENC, declares the bean's type, and gives the names of its local component interfaces. These elements should be linked explicitly to other co-located beans using the <ejb-link> element, but this is not required—the application assembler or deployer can do it later. The value of the <ejb-link> element within the <ejb-local-ref> must equal the <ejb-name> of the appropriate bean in the same JAR file. At deployment time, the EJB container's tools map the local references declared in the <ejb-local-ref> elements to entity beans that are co-located in the same container system. Enterprise beans declared in the <ejb-local-ref> elements are local enterprise beans and so do not require the use of the PortableRemoteObject.narrow() method to narrow the reference. Instead, you can use a simple native cast operation: InitialContext jndiContext = new InititalContext(); CabinHome home = (CabinHome) jndiContext.lookup("java:comp/env/ejb/CabinHomeLocal"); 16.5.6 References to External ResourcesEnterprise beans also use the JNDI ENC to look up external resources, such as 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) subelements. 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 not a requirement, it is 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> element 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> element 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); 16.5.6.1 EJB 2.0: Additional administered objectsIn addition to the resource factory described in the <resource-ref> element, some resources may have other administered objects that need to be obtained from the JNDI ENC. An administered object is a resource that is configured at deployment time and managed by the EJB container at runtime. For example, to use JMS, the bean developer must obtain both a JMS factory object and a destination object: TopicConnectionFactory factory = (TopicConnectionFactory) jndiContext.lookup("java:comp/env/jms/TopicFactory"); Topic topic = (Topic) jndiContext.lookup("java:comp/env/ejb/TicketTopic"); Both the JMS factory and destination are administered objects that must be obtained from the JNDI ENC. The <resource-ref> element is used to declare the JMS factory while the <resource-env-ref> element is used to declare the destination: <resource-ref> <res-ref-name>jms/TopicFactory</res-ref-name> <res-type>javax.jms.TopicConnectionFactory</res-type> <res-auth>Container</res-auth> </resource-ref> <resource-ref> <res-ref-name>jdbc/titanDB</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> <resource-env-ref> <resource-env-ref-name>jms/TicketTopic</resource-env-ref-name> <resource-env-ref-type>javax.jms.Topic</resource-env-ref-type> </resource-env-ref> At deployment time, the deployer maps the JMS TopicConnectionFactory or QueueConnectionFactory and the Topic or Queue declared by the <resource-ref> and <resource-env-ref> elements to a JMS factory and topic. 16.5.6.2 EJB 2.0: Shareable resourcesWhen several enterprise beans in a unit-of-work or transaction all use the same resource, you will want to configure your EJB server to share that resource. Sharing a resource means that each enterprise bean will use the same connection to access the resource (e.g., database or JMS provider), which is more efficient than using separate resource connections. For example, in the TravelAgent EJB, the bookPassage() method uses the ProcessPayment EJB and the Reservation EJB to book a passenger on a cruise. If both of these enterprise beans use the same database, they should share their resource connection for efficiency. Enterprise JavaBeans containers share resources by default, but resource sharing can be turned on or off explicitly through the <resource-ref> element: <resource-ref> <res-ref-name>jdbc/titanDB</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> <res-sharing-scope>Shareable</res-sharing-scope> </resource-ref> <res-sharing-scope> is an optional element that may be declared as either Shareable, indicating that connections should be shared in local transactions, or Unshareable, indicating that they should not. If it is not specified, the default is Shareable. Occasionally, advanced developers may run into situations where resource sharing is not desirable—this is relatively rare, but having the option to turn off resource sharing is beneficial in those circumstances. Unless you have a good reason for turning off resource sharing, I recommend that you use Shareable resources. 16.5.7 Security RolesThe <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>AccountEJB</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. 16.5.8 Declaring EJB QL ElementsEJB QL statements are declared in <query> elements in an entity bean's deployment descriptor. In the following listing, you can see that findByName() and ejbSelectShips() methods were declared in the <query> elements of the Cruise EJB deployment descriptor: <ejb-jar> <enterprise-beans> <entity> <ejb-name>ShipEJB</ejb-name> ... <abstract-schema-name>Ship</abstract-schema-name> ... </entity> <entity> <ejb-name>CruiseEJB</ejb-name> ... <reentrant>False</reentrant> <abstract-schema-name>Cruise</abstract-schema-name> <cmp-version>2.x</cmp-version> <cmp-field> <field-name>name</field-name> </cmp-field> <primkey-field>id</primkey-field> <query> <query-method> <method-name>findByName</method-name> <method-params> <mehod-param>java.lang.String</method-param> </method-params> </query-method> <ejb-ql> SELECT OBJECT(c) FROM Cruise c WHERE c.name = ?1 </ejb-ql> </query> <query> <query-method> <method-name>ejbSelectShips</method-name> <method-params></method-params> </query-method> <result-type-mapping>Remote</result-type-mapping> <ejb-ql> SELECT OBJECT(s) FROM Ship AS s </ejb-ql> </query> </entity> </enterprise-beans> </ejb-jar> The <query> element contains two primary elements. The <query-method> element identifies the find method of the remote and/or local home interface, and the <ejb-ql> element declares the EJB QL statement. The <query> element binds the EJB QL statement to the proper find method. The syntax used in EJB QL may cause problems for the XML parser. See CDATA Sections for more details. When two find methods in the local and remote home interfaces have the same method name and parameters, the query declaration will apply to both of the methods. The container will return the proper type for each query method: the remote home will return one or more remote EJB objects, and the local home will return one or more local EJB objects. This allows you to define the behavior of both the local and remote home find methods using a single <query> element, which is convenient if you want local clients to have access to the same find methods as remote clients. The <result-type-mapping> element can be used to declare whether a select method should return local or remote EJB objects. The value Local indicates that a method should return local EJB objects; Remote indicates remote EJB objects. If the <result-type-mapping> element is not declared, the default is Local. In the <query> element for the ejbSelectShips() method, the <result-type-mapping> is declared as Remote, which means the query should return remote EJB object types (i.e., remote references to the Ship EJB). Every entity bean that will be referenced in an EJB QL statement must have a special designator called an abstract schema name, which is declared by the <abstract-schema-name> element. <abstract-schema-name> elements must have unique names; no two entity beans may have the same abstract schema name. In the entity element that describes the Cruise EJB, the abstract schema name is declared as Cruise, while the Ship EJB's abstract schema name is Ship. The <ejb-ql> element contains an EJB QL statement that uses this identifier in its FROM clause. In Chapter 7 you learned that the abstract persistence schema of an entity bean is defined by its <cmp-field> and <cmr-field> elements. The abstract schema name is also an important part of the abstract persistence schema. EJB QL statements are always expressed in terms of the abstract persistence schemas of entity beans. EJB QL uses the abstract schema names to identify entity bean types, the container-managed persistence (CMP) fields to identify specific entity bean data, and the container-managed relationship (CMR) fields to create paths for navigating from one entity bean to another. 16.6 EJB 2.0: Describing RelationshipsCMP 2.0 entity bean classes are defined using abstract accessor methods that represent virtual persistence and relationship fields. As you learned in Chapter 6, Chapter 7, and Chapter 8, the actual fields themselves are not declared in the entity classes. Instead, the characteristics of these fields are described in detail in the XML deployment descriptor used by the entity bean. The abstract persistence schema is the set of XML elements in the deployment descriptor that describe the relationship and persistence fields. Together with the abstract programming model (i.e., the abstract accessor methods) and some help from the deployer, the container tool has enough information to map the entity and its relationships with other entity beans. The relationships between entity beans are described in the <relationships> section of the XML deployment descriptor. The <relationships> section falls between the <enterprise-beans> and <assembly-descriptor> sections. Within the <relationships> element, each entity-to-entity relationship is defined in a separate <ejb-relation> element: <ejb-jar> <enterprise-beans> ... </enterprise-beans> <relationships> <ejb-relation> ... </ejb-relation> <ejb-relation> ... </ejb-relation> </relationships> <assembly-descriptor> ... </assembly-descriptor> </ejb-jar> Defining relationship fields requires that an <ejb-relation> element be added to the XML deployment descriptor for each entity-to-entity relationship. These <ejb-relation> elements complement the abstract programming model. For each pair of abstract accessor methods that define a relationship field, there is an <ejb-relation> element in the deployment descriptor. EJB 2.0 requires that the entity beans in a relationship be defined in the same XML deployment descriptor. Here is a partial listing of the deployment descriptor for the Customer and Address EJBs, emphasizing the elements that define the relationship: <ejb-jar> ... <enterprise-beans> <entity> <ejb-name>CustomerEJB</ejb-name> <local-home>com.titan.customer.CusomterLocalHome</local-home> <local>com.titan.customer.CustomerLocal</local> ... </entity> <entity> <ejb-name>AddressEJB</ejb-name> <local-home>com.titan.address.AddressLocalHome</local-home> <local>com.titan.address.AddressLocal</local> ... </entity> ... </enterprise-beans> <relationships> <ejb-relation> <ejb-relation-name>Customer-Address</ejb-relation-name> <ejb-relationship-role> <ejb-relationship-role-name> Customer-has-an-Address </ejb-relationship-role-name> <multiplicity>One</multiplicity> <relationship-role-source> <ejb-name>CustomerEJB</ejb-name> </relationship-role-source> <cmr-field> <cmr-field-name>homeAddress</cmr-field-name> </cmr-field> </ejb-relationship-role> <ejb-relationship-role> <ejb-relationship-role-name> Address-belongs-to-Customer </ejb-relationship-role-name> <multiplicity>One</multiplicity> <relationship-role-source> <ejb-name>AddressEJB</ejb-name> </relationship-role-source> </ejb-relationship-role> </ejb-relation> </relationships> </ejb-jar> All relationships between the Customer EJB and other entity beans, such as the CreditCard, Address, and Phone EJBs, require that we define an <ejb-relation> element to complement the abstract accessor methods. Every relationship may have a relationship name, which is declared in the <ejb-relation-name> element. This name identifies the relationship for individuals reading the deployment descriptor or for deployment tools, but it is not required. Every <ejb-relation> element has exactly two <ejb-relationship-role> elements, one for each participant in the relationship. In the previous example, the first <ejb-relationship-role> declares the Customer EJB's role in the relationship. We know this because the <relationship-role-source> element specifies the <ejb-name> as CustomerEJB. CustomerEJB is the <ejb-name> used in the Customer EJB's original declaration in the <enterprise-beans> section. The <relationship-role-source> element's <ejb-name> must always match an <ejb-name> element in the <enterprise-beans> section. The <ejb-relationship-role> element also declares the cardinality, or multiplicity, of the role. The <multiplicity> element can be either One or Many. In this case, the Customer EJB's <multiplicity> element has a value of One, which means that every Address EJB has a relationship with exactly one Customer EJB. The Address EJB's <multiplicity> element also specifies One, which means that every Customer EJB has a relationship with exactly one Address EJB. If the Customer EJB had a relationship with many Address EJBs, the Address EJB's <multiplicity> element would be set to Many. If the bean described by the <ejb-relationship-role> element maintains a reference to the other bean in the relationship, that reference must be declared as a container-managed relationship field in the <cmr-field> element. The <cmr-field> element is declared under the <ejb-relationship-role> element: <ejb-relationship-role> <ejb-relationship-role-name> Customer-has-an-Address </ejb-relationship-role-name> <multiplicity>One</multiplicity> <relationship-role-source> <ejb-name>CustomerEJB</ejb-name> </relationship-role-source> <cmr-field> <cmr-field-name>homeAddress</cmr-field-name> </cmr-field> </ejb-relationship-role> EJB 2.0 requires that the <cmr-field-name> begin with a lowercase letter. For every relationship field defined by a <cmr-field> element, the bean class must include a pair of matching abstract accessor methods. One method in this pair must be defined with the method name set<cmr-field-name>(), with the first letter of the <cmr-field-name> changed to uppercase. The other method is defined as get<cmr-field-name>(), also with the first letter of the <cmr-field-name> in uppercase. In this example, the <cmr-field-name> is homeAddress, which corresponds to the getHomeAddress() and setHomeAddress() methods defined in the CustomerBean class: // bean class code public abstract void setHomeAddress(AddressLocal address); public abstract AddressLocal getHomeAddress(); // XML deployment descriptor declaration <cmr-field> <cmr-field-name>homeAddress</cmr-field-name> </cmr-field> The <cascade-delete> element requests cascade deletion; it can be used with one-to-one or one-to-many relationships. It is always declared as an empty element: <cascade-delete/>. <cascade-delete> indicates that the lifetime of one entity bean in a particular relationship depends upon the lifetime the other entity bean in the relationship. Here's how to modify the relationship declaration for the Customer and Address EJBs to obtain a cascade delete: <relationships> <ejb-relation> <ejb-relationship-role> <multiplicity>One</multiplicity> <role-source> <ejb-name>CustomerEJB</ejb-name> </role-source> <cmr-field> <cmr-field-name>homeAddress</cmr-field-name> </cmr-field> </ejb-relationship-role> <ejb-relationship-role> <multiplicity>One</multiplicity> <cascade-delete/> <role-source> <dependent-name>Address</dependent-name> </role-source> </ejb-relationship-role> </ejb-relation> </relationships> With this declaration, the Address EJB will be deleted automatically when the Customer EJB that refers to it is deleted. 16.7 Describing Bean AssemblyAt this point, we have said just about all that can be said about the bean itself. We have come to the end of the <enterprise-beans> element and are now ready to describe how the beans are assembled into an application. That is, we are ready to talk about the other major element inside the <ejb-jar> element: the <assembly-descriptor> element. The <assembly-descriptor> element is optional, though it is difficult to imagine a bean being deployed successfully without an <assembly-descriptor>. When we say that the <assembly-descriptor> is optional, we really mean that a developer whose only role is to create enterprise beans (for example, someone who is developing beans for use by another party and who has no role in deploying the beans) can omit this part of the deployment descriptor. The descriptor is valid without it—but someone will almost certainly have to fill in the assembly information before the bean can be deployed. The <assembly-descriptor> element serves three purposes: it describes the transaction attributes of the bean's methods; it describes the logical security roles that are used in the method permissions; and it specifies the method permissions (i.e., which roles are allowed to call each of the methods). To this end, an <assembly-descriptor> can contain three kinds of elements, each of which is fairly complex in its own right. These are:
The <container-transaction> and <method-permission> elements both rely on the ability to identify particular methods. This can be a complicated affair, given features of the Java language such as method overloading. The <method> element is used within these tags to identify methods; it is described at length in Section 16.7.3. 16.7.1 Specifying a Bean's Transaction AttributesThe <container-transaction> elements are used to declare the transaction attributes for all the beans defined in the deployment descriptor. A <container-transaction> element maps one or more bean methods to a single transaction attribute, so each <container-transaction> specifies one transaction attribute and one or more bean methods. The <container-transaction> element includes a single <trans-attribute> element, which can have one of six values: NotSupported, Supports, Required, RequiresNew, Mandatory, and Never. These are the transaction attributes we discussed in Chapter 14. In addition to <trans-attribute>, the <container-transaction> element includes one or more <method> elements. The <method> element itself contains at least two subelements: an <ejb-name> element, which specifies the name of the bean; and a <method-name> element, which specifies a subset of the bean's methods. The value of the <method-name> can be a method name or an asterisk (*), which acts as wildcard for all the bean's methods. A lot more complexity is involved in handling overloading and other special cases, but that's enough for now; we'll discuss the rest later. Here is an example that shows how the <container-transaction> element is typically used. Let's look again at the Cabin EJB, which we have used as an example throughout. Let's assume that we want to give the transaction attribute Mandatory to the create() method; all other methods use the Required attribute: <container-transaction> <method> <ejb-name>CabinEJB</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> <container-transaction> <method> <ejb-name>CabinEJB</ejb-name> <method-name>create</method-name> </method> <trans-attribute>Mandatory</trans-attribute> </container-transaction> In the first <container-transaction> , we have a single <method> element that uses the wildcard character (*) to refer to all of the Cabin EJB's methods. We set the transaction attribute for these methods to Required. Then, we have a second <container-transaction> element that specifies a single method of the Cabin EJB: create(). We set the transaction attribute for this method to Mandatory. This setting overrides the wildcard setting; in <container-transaction> elements, specific method declarations always override more general declarations. The following methods must be assigned transaction attributes for each bean declared in the deployment descriptor. For entity beans:
For session beans:
For session beans, only the business methods have transaction attributes; the create and remove methods in session beans do not have transaction attributes. In EJB 2.0, ejbSelect() methods do not have their own transaction attributes. ejbSelect() methods always propagate the transaction of the methods that call them. 16.7.2 Specifying Security Roles and Method PermissionsTwo elements are used to define logical security roles and to specify which roles can call particular bean methods. The <security-role> element can contain an optional <description> element, plus a single <role-name> element that provides the name. An <assembly-descriptor> element can contain any number of <security-role> elements. It is important to realize that the security role names defined here are not derived from a specific security realm. These security role names are logical; they are simply labels that can be mapped to real security roles in the target environment at deployment time. For example, the following <security-role> declarations define two roles—everyone and administrator: <security-role> <description> This role represents everyone who is allowed read/write access to existing Cabin EJBs. </description> <role-name>everyone</role-name> </security-role> <security-role> <description> This role represents an administrator or manager who is allowed to create new Cabin EJBs. This role may also be a member of the everyone role. </description> <role-name>administrator</role-name> </security-role> These role names might not exist in the environment in which the beans will be deployed. There's nothing inherent about everyone that gives it fewer (or greater) privileges than an administrator. It is up to the deployer to map one or more roles from the target environment to the logical roles in the deployment descriptor. So for example, the deployer may find that the target environment has two roles, DBA (database administrator) and CSR (customer service representative), which map to the administrator and everyone roles defined in the <security-role> element. 16.7.2.1 Assigning roles to methodsSecurity roles in themselves would not be worth much if you couldn't specify what the roles were allowed to do. That's where the <method-permission> element comes in. This element maps the security roles to methods in the remote and home interfaces of the bean. A method permission is a flexible declaration that allows a many-to-many relationship between methods and roles. A <method-permission> contains an optional <description>, one or more <method> elements, and one or more <role-name> elements. The names specified in the <role-name> elements correspond to the roles that appear in the <security-role> elements. Here's one way to set method permissions for the Cabin EJB: <method-permission> <role-name>administrator</role-name> <method> <ejb-name>CabinEJB</ejb-name> <method-name>*</method-name> </method> </method-permission> <method-permission> <role-name>everyone</role-name> <method> <ejb-name>CabinEJB</ejb-name> <method-name>getDeckLevel</method-name> </method> </method-permission> In this example, the administrator role has access to all methods in the Cabin EJB. The everyone role has access only to the getDeckLevel() method—it cannot access any of the other methods of the Cabin EJB. Note that the specific method permissions are combined to form a union. The getDeckLevel() method, for example, is accessible by both the administrator and everyone roles, which is the union of the permissions declared in the descriptor. Once again, it is important to note that we still do not know what administrator and everyone mean. They are defined by the person deploying the bean, who must map these logical security roles to real security roles defined in the target environment. All the methods defined in the remote or home interface and all superinterfaces, including the methods defined in the EJBObject and EJBHome interfaces, can be assigned security roles in the <method-permission> elements. Any method that is excluded will not be accessible by any security role. 16.7.2.2 EJB 2.0: Unchecked methodsIn EJB 2.0, a set of methods can be designated as unchecked , which means that the security permissions are not checked before the method is invoked. An unchecked method can be invoked by any client, no matter what role it is using. To designate a method or methods as unchecked, use the <method-permission> element and replace the <role-name> element with an empty <unchecked> element: <method-permission> <unchecked/> <method> <ejb-name>CabinEJB</ejb-name> <method-name>*</method-name> </method> <method> <ejb-name>CustomerEJB</ejb-name> <method-name>findByPrimaryKey</method-name> </method> </method-permission> <method-permission> <role-name>administrator</role-name> <method> <ejb-name>CabinEJB</ejb-name> <method-name>*</method-name> </method> </method-permission> This declaration tells us that all the methods of the Cabin EJB, as well as the Customer EJB's findByPrimaryKey() method, are unchecked. Although the second <method-permission> element gives the administrator permission to access all the Cabin EJB's methods, this declaration is overridden by the unchecked method permission. Unchecked method permissions always override all other method permissions. 16.7.2.3 EJB 2.0: The runAs security identityIn addition to specifying the Principals that have access to an enterprise bean's methods, the deployer can also specify the runAs Principal for the entire enterprise bean. The runAs security identity was originally specified in EJB 1.0, but was abandoned in EJB 1.1. It has been reintroduced in EJB 2.0 and modified so that it is easier for vendors to implement. While the <method-permission> elements specify which Principals have access to the bean's methods, the <security-identity> element specifies under which Principal the method will run. In other words, the runAs Principal is used as the enterprise bean's identity when it tries to invoke methods on other beans—this identity isn't necessarily the same as the identity that's currently accessing the bean. For example, the following deployment descriptor elements declare that the create() method can be accessed only by JimSmith, but that the Cabin EJB always runs under the Administrator security identity: <enterprise-beans> ... <entity> <ejb-name>EmployeeService</ejb-name> ... <security-identity> <run-as> <role-name>Administrator</role-name> </run-as> </security-identity> ... </entity> ... </enterprise-beans> <assembly-descriptor> <security-role> <role-name>Administrator</role-name> </security-role> <security-role> <role-name>JimSmith</role-name> </security-role> ... <method-permission> <role-name>JimSmith</role-name> <method> <ejb-name>CabinEJB</ejb-name> <method-name>create</method-name> </method> </method-permission> ... </assembly-descriptor> To specify that an enterprise bean will execute under the caller's identity, the <security-identity> role contains a single empty element, <use-caller-identity/>. For example, the following declarations specify that the Cabin EJB always executes under the caller's identity, so if Jim Smith invokes the create() method, the bean will run under the JimSmith security identity: <enterprise-beans> ... <entity> <ejb-name>EmployeeService</ejb-name> ... <security-identity> <use-caller-identity/> </security-identity> ... </entity> ... </enterprise-beans> The use of <security-identity> applies equally to entity and stateless session beans. However, message-driven beans have only a runAs identity; they will never execute under the caller identity, because there is no "caller." The asynchronous JMS messages that a message-driven bean processes are not considered calls, and the JMS clients that send them are not associated with the messages. With no caller identity to propagate, message-driven beans must always have a runAs security identity specified. 16.7.2.4 EJB 2.0: Exclude listThe last element of the <assembly-descriptor> is the optional <exclude-list> element. The <exclude-list> element contains a <description> and a set of <method> elements. Every method listed in the <exclude-list> should be considered uncallable, which means that the deployer needs to set up security permissions for those methods so that all calls, from any client, are rejected. Remote clients should receive a java.rmi.remoteException and local clients should receive a javax.ejb.AccessLocalException: <ejb-jar> <enterprise-beans> <entity> <ejb-name>CabinEJB</ejb-name> </entity> </enterprise-beans> <assembly-descriptor> <exclude-list> <method> <ejb-name>CabinEJB</ejb-name> <method-name>getDeckLevel</method-name> </method> <method> ... </method> </exclude-list> </assembly-descriptor> </ejb-jar> 16.7.3 Identifying Specific MethodsThe <method> element is used by the <method-permission> and <container-transaction> elements to specify a specific group of methods in a particular bean. The <method> element always contains an <ejb-name> element that specifies the bean's name and a <method-name> element that specifies the method. It may also include a <description> element, <method-params> elements that specify which method parameters will be used to resolve overloaded methods, and a <method-intf> element that specifies whether the method belongs to the bean's home, remote, local home or local interface. This last element takes care of the possibility that the same method name might be used in more than one interface. 16.7.3.1 Wildcard declarationsThe method name in a <method> element can be a simple wildcard (*). A wildcard applies to all methods of the bean's home and remote interfaces. For example: <method> <ejb-name>CabinEJB</ejb-name> <method-name>*</method-name> </method> Although it's tempting to combine the wildcard with other characters, don't. The value get*, for example, is illegal. The asterisk (*) character can be used only by itself. 16.7.3.2 Named method declarationsNamed declarations apply to all methods defined in the bean's remote and home interfaces that have the specified name. For example: <method> <ejb-name>CabinEJB</ejb-name> <method-name>create</method-name> </method> <method> <ejb-name>CabinEJB</ejb-name> <method-name>getDeckLevel</method-name> </method> These declarations apply to all methods with the given name in both interfaces. They do not distinguish between overloaded methods. For example, if the home interface for the Cabin EJB is modified so that it has three overloaded create() methods, as shown here, the previous <method> declaration would apply to all three methods: public interface CabinHome javax.ejb.EJBHome { public Cabin create() throws CreateException, RemoteException; public Cabin create(int id) throws CreateException, RemoteException; public Cabin create(int id, Ship ship, double [][] matrix) throws CreateException, RemoteException; ... } 16.7.3.3 Specific method declarationsSpecific method declarations use the <method-params> element to pinpoint a specific method by listing its parameters, allowing you to differentiate between overloaded methods. The <method-params> element contains zero or more <method-param> elements that correspond, in order, to each parameter type (including multidimensional arrays) declared in the method. To specify a method with no arguments, use a <method-params> element with no <method-param> elements nested within it. For example, let's look again at our Cabin EJB, to which we have added some overloaded create() methods in the home interface. Here are three <method> elements, each of which unambiguously specifies one of the create() methods by listing its parameters: <method> <description>Method: public Cabin create(); </description> <ejb-name>CabinEJB</ejb-name> <method-name>create</method-name> <method-params></method-params> </method> <method> <description>Method: public Cabin create(int id);</description> <ejb-name>CabinEJB</ejb-name> <method-name>create</method-name> <method-params> <method-param>int</method-param> </method-params> </method> <method> <description> Method: public Cabin create(int id, Ship ship, double [][] matrix); </description> <ejb-name>CabinEJB</ejb-name> <method-name>create</method-name> <method-params> <method-param>int</method-param> <method-param>com.titan.ship.Ship</method-param> <method-param>double [][]</method-param> </method-params> </method> 16.7.3.4 Remote/home/local differentiationThere's one problem left. The same method name can be used in the home interface, the local home interface, the remote interface, and the local interface. To resolve this ambiguity, you can add the <method-intf> element to a method declaration as a modifier. Four values are allowed for a <method-intf> element: Remote, Home, LocalHome, and Local. In reality, it is unlikely that a good developer would use the same method names in both home and remote interfaces; that would lead to unnecessarily confusing code. However, you would expect to see the same names in the local and remote interfaces or the home and local home interfaces. It is also likely that you will need the <method-intf> element in a wildcarded declaration. For example, the following declaration specifies all the methods in the remote interface of the Cabin EJB: <method> <ejb-name>CabinEJB</ejb-name> <method-name>*</method-name> <method-intf>Remote</method-intf> </method> All these styles of method declarations can be used in any combination within any element that uses the <method> element. The <method-permission> elements are combined to form a union of role-to-method permissions. For example, in the following listing, the first <gmethod-permission> element declares that the administrator has access to the Cabin EJB's home methods (create and find methods). The second <method-permission> specifies that everyone has access to the findByPrimaryKey() method. This means that both roles (everyone and administrator) have access to the findByPrimaryKey() method: <method-permission> <role-name>administrator</role-name> <method> <ejb-name>CabinEJB</ejb-name> <method-name>*</method-name> <method-intf>Home</method_intf> </method> </method-permission> <method-permission> <role-name>everyone</role-name> <method> <ejb-name>CabinEJB</ejb-name> <method-name>findByPrimaryKey</method-name> </method> </method-permission> 16.8 The ejb-jar FileThe JAR file format is a platform-independent format for compressing, packaging, and delivering several files together. Based on the ZIP file format and the ZLIB compression standards, the JAR ( Java archive) packages and tool were originally developed to make downloads of Java applets more efficient. As a packaging mechanism, however, the JAR file format is a convenient way to "shrink-wrap" components and other software for delivery to third parties. The original JavaBeans component architecture depends on JAR files for packaging, as does Enterprise JavaBeans. The goal in using the JAR file format is to package all the classes and interfaces associated with one or more beans, including the deployment descriptor, into one file. The JAR file is created using a vendor-specific tool, or using the jar utility that is part of the Java 2, Standard Edition development kit. An ejb-jar file contains:
The XML deployment descriptor must be located in the path META-INF/ejb-jar.xml and must contain all the deployment information for all the beans in the ejb-jar file. For each bean declared in the XML deployment descriptor, the ejb-jar file must contain its bean class, remote and home interfaces, and dependent classes and interfaces. Dependent classes and interfaces are usually things like application-specific exceptions, business interfaces and other supertypes, and dependent objects that are used by the bean. In the ejb-jar file for the TravelAgent bean, for example, we would include the IncompleteConversationalState application exception and the Ticket and CreditCard classes, as well as the remote and home interfaces to other beans referenced by the TravelAgent bean, such as the Customer and ProcessPayment beans.[2] You can use the jar utility from the command line to package a bean in a JAR file. Here is an example of how the jar utility was used to package the Cabin EJB in Chapter 4: \dev % jar cf cabin.jar com/titan/cabin/*.class META-INF/ejb-jar.xml F:\..\dev>jar cf cabin.jar com\titan\cabin\*.class META-INF\ejb-jar.xml You might have to create the META-INF directory first, and copy ejb-jar.xml into that directory. The c option tells the jar utility to create a new JAR file that contains the files indicated in subsequent parameters. It also tells the jar utility to stream the resulting JAR file to standard output. The f option tells jar to redirect the standard output to a new file named in the second parameter (cabin.jar). It is important to get the order of the option letters and the command-line parameters to match. You can learn more about the jar utility and the java.util.zip package in Java™ in a Nutshell by David Flanagan or Learning Java™ by Pat Niemeyer and Jonathan Knudsen, both published by O'Reilly. The jar utility creates the file cabin.jar in the dev directory. If you are interested in looking at the contents of the JAR file, you can use any standard ZIP application (WinZip, PKZIP, etc.) or the command jar tvf cabin.jar. 16.8.1 The client-jar FileEJB 1.1 also allows for a client-jar file, which includes only the interfaces and classes needed by a client application to access a bean. This includes the remote and home interfaces, primary key, and any dependent types to which the client is exposed, such as application exceptions. The specification does not say how this file is delivered to the client, what exactly it contains, or how it is packaged with the ejb-jar file. In other words, the client-jar file is a fairly vendor-specific concept in EJB.
| |||||||
|