The
enterprise
beans contained in a JAR file are laid out
within the deployment descriptor’s
<enterprise-beans>
element. So far, we have
talked about deployment descriptors for 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, Reservation, and ReservationProcessor EJBs in the same JAR
file. The deployment descriptor would look something like this:
<?xml version="1.0" encoding="UTF-8"?> ... <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>
In this descriptor, the <enterprise-beans>
element contains two <session>
elements, one
<entity>
element, and 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.
When CMP entity beans are deployed, all the beans that have
relationships must be deployed in the same EJB-JAR file, using the
same deployment descriptor. The relationships are expressed in the
<relationships>
element, which follows the
<enterprise-beans>
element.
All types of EJBs share assembly information, which is defined in the
<assembly-descriptor>
element that follows
the <relationships>
element (if
it’s present). 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.
The <session>
and <entity>
elements, which describe session and entity beans, usually contain
many nested elements. 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, have the same meanings as when
they appear in 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:
<ejb-name>
(one required)Specifies the name of the enterprise bean component. It is used in
the <methodx>
element to scope method
declarations to the correct enterprise bean. Throughout this book, we
use a name of the form “NameEJB” as
the <ejb-name>
for an enterprise bean. Other
common conventions use names of the form
“NameBean” or
“TheName”.
<home>
(optional)Specifies the fully qualified class name of the enterprise bean’s remote home interface.
<remote>
(optional)Specifies the fully qualified class name of the enterprise bean’s remote interface.
<local-home>
(optional)Specifies the fully qualified class name of the enterprise bean’s local home interface.
<local>
(optional)Specifies the fully qualified class name of the enterprise bean’s local interface.
<service-endpoint>
(EJB 2.1 only; optional)Identifies the JAX-RPC endpoint interface used with stateless session beans that are deployed as EJB Endpoints. Web services and EJB are covered in detail in Chapter 14 and Chapter 15.
<ejb-class>
(one required)Specifies the fully qualified class name of the bean class.
<session-type>
(one required; session beans only)Declares that a session bean is either stateful or stateless. This
element can have one of two values: Stateful
or
Stateless
.
<primkey-field>
(optional; entity beans only)Specifies the primary key field for entity beans that use
container-managed persistence. This element’s value
is the name of the field that is used as the primary key. It is not
used if the bean has a compound primary key or if the entity bean
manages its own persistence. In the Cabin EJB, the
<primkey-field>
is the id
CMP field. This element is discussed in more detail in
Specifying Primary Keys
later in this chapter.
<prim-key-class>
(one required; entity beans only)Specifies the class of the primary key for entity beans. This
element’s value is the fully qualified name of the
primary key class; it makes no difference whether you are using a
custom compound primary key or a simple
<primkey-field>
such as 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)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)Declares that the bean either allows loopbacks (reentrant
invocations) or does 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-version>
(optional; entity beans only)Describes the version of container-managed persistence for which the
entity bean is deployed. EJB containers must support EJB 2.1 CMP, EJB
2.0 CMP, and even EJB 1.1 CMP for backward compatibility. This
element may have one of two values: 2.x
for EJB
2.1 and EJB 2.0 or 1.x
for EJB 1.1.
<abstract-schema-name>
(optional; entity beans only)Uniquely identifies entity beans in a JAR file so that EJB QL statements can reference them. This method is described in more detail in the section “Declaring EJB QL Elements.”
<cmp-field>
(zero or more; entity beans only)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
CMP fields. It must match the method name of the abstract accessor
method (e.g., deckLevel
for getDeckLevel( )
/setDeckLevel( )
). The following
portion of a descriptor shows several
<cmp-field>
declarations for the Cabin EJB:
<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>shipId</field-name> </cmp-field> <cmp-field> <field-name>bedCount</field-name> </cmp-field>
<env-entry>
(zero or more)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 the “Environment Entries” section.
<ejb-ref>
(zero or more)Declares a remote enterprise 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 References to Other Beans.
<
ejb-local-ref>
(zero or more)Declares a local enterprise 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 References to Other Beans.
<security-role-ref>
(zero or more)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 method is described in more detail in the “Security Roles” section.
<security-identity>
(optional)Specifies security identity under which a method will run. This element is described in more detail in the “Specifying Security Roles and Method Permissions” section.
<resource-ref>
(zero or more)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
References to External Resources,
<resource-env-ref>
(zero or more)Describes additional “administered
objects” required by the resource. The
<resource-env-ref>
element and administered
objects are explained in more detail in the
References to External Resources,
later in this chapter.
<message-destination-ref>
(EJB 2.1 only: optional)The <message-destination-ref>
element is new
in EJB 2.1. It describes the type of destination the EJB will send
messages to. The
<message-destination-ref-name>
declares the
JNDI ENC lookup name used by the EJB to access the destination.
<transaction-type>
(one required; session beans only)Declares either that a session bean manages its own transactions or
that the container manages its transactions. 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.
<query>
(zero or more; entity beans only)Contains an EJB QL statement that is bound to a find or a select method. The EJB QL statement defines how the find or select method should execute at runtime. This element is described in more detail later in Section 18.5.9
The <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 the 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 have the same meaning as they did in the
<entity>
and
<session>
elements.
The elements specific to the message-driven bean are different in EJB 2.1 and EJB 2.0.
EJB 2.0 defined a few JMS specific elements, which have been abandoned in EJB 2.1 so that the MDB deployment descriptor can represent JMS-based MDBs as well as Connector-based MDBs. The MDB elements and properties are covered in detail in Chapter 12.
<messaging-type>
Declares the messaging interfaces used by the MDB. For JMS-based
MDBs, the messaging interface is always going to be
javax.jms.MessageListener
, but for other J2EE
Connector-based MDBs it might be something different. If this element
is omitted, the type is assumed to be
javax.jms.MessageListener
.
<message-destination-type>
Designates the type of destination from
which the MDB receives messages. The allowed values for JMS-based
MDBs are javax.jms.Queue
and
javax.jms.Topic
. A J2EE Connector-based MDB might
use some other type.
<activation-config>
Describes the messaging properties of the MDB. The property names and
values used in <activation-config>
depend on
the type of message service used, but EJB 2.1 defines a set of fixed
properties for JMS-based message-driven beans, including:
acknowledgeMode
The container considers this property only if the message-driven bean
uses bean-managed transactions; with container-managed transactions,
it is ignored. It determines which type of acknowledgment it uses;
its value can be either Auto-acknowledge
or
Dups-ok-acknowledge
. The first value acknowledges
messages immediately; the second can delay acknowledgment to benefit
performance but may result in duplicate or redelivered messages.
messageSelector
Message selectors allow an MDB to be more selective about the
messages it receives from a particular topic or queue. Message
selectors use Message
properties as criteria in
conditional expressions.[54] These conditional expressions use Boolean
logic to declare which messages should be delivered to a client. The
syntax of message selectors can cause problems with XML processing.
See CDATA Sections
later
in this chapter.
destinationType
The <message-destination-type>
and the
destinationType
activation configuration property
are redundant for JMS-based MDBs—but not for other types of
J2EE Connector-based MDBs. That’s because the
activation configuration properties of Connector-based MDBs and
JMS-based MDBs are completely different. It’s
important that the
<message-destination-type>
be specified for
both JMS-based and Connector-based MDBs.
subscriptionDurablity
When a JMS-based MDB uses a javax.jms.Topic
, the
subscription must be declared to be either
Durable
or
NonDurable
. A Durable
subscription outlasts an MDB container’s connection
to the JMS provider; if the EJB server suffers a partial failure,
shuts down, or otherwise disconnects from the JMS provider, the
messages that it would have received are not lost. A
NonDurable
subscription means that any messages
the bean would have received while it was disconnected are lost.
EJB 2.0 supports only one type of Message Driven Bean, the JMS-based MDB. Since only one messaging API is supported, EJB 2.0 defined JMS-specific elements, which were replaced in EJB 2.1 with JMS-specific properties.
<message-selector>
Message selectors allow an MDB to be more selective about the
messages it receives from a particular topic or queue. Message
selectors use Message
properties as criteria in
conditional expressions. These conditional expressions use Boolean
logic to declare which messages should be delivered to a client. The
syntax of message selectors can cause problems with XML processing.
See the “CDATA Sections” sidebar
later in this chapter.
<acknowledge-mode>
The container considers this element only if the message-driven bean
uses bean-managed transactions; with container-managed transactions,
it is ignored. It determines which type of acknowledgment it uses;
its value can be either Auto-acknowledge
or
Dups-ok-acknowledge
. The first acknowledges
messages immediately; the second can delay acknowledgment to benefit
performance, but may result in duplicate or redelivered messages.
<message-driven-destination>
This element designates the type of destination to which the MDB
subscribes or listens. The allowed values for this element are
javax.jms.Queue
and
javax.jms.Topic
.
<subscription-durability>
When a MDB uses a javax.jms.Topic
, the
subscription must be declared to be either
Durable
or
NonDurable
. A Durable
subscription outlasts an MDB container’s connection
to the JMS provider; if the EJB server suffers a partial failure,
shuts down, or otherwise disconnects from the JMS provider, the
messages that it would have received are not lost. A
NonDurable
subscription means that any messages
the bean would have received while it was disconnected are lost.
If
a single field in the bean 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, we used the appropriate
primitive
wrapper, java.lang.Integer
, instead of the custom
CabinPK
class, 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. Here’s 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
, and so on); 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 they are serializable. Use common
sense when choosing a primary key: since it is used as an index to
the data in the database, it should be lightweight.
Container-managed persistence makes it 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. Here’s 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.
A deployment descriptor can define environment entries, 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 the subelements
<description>
(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.
In EJB, references to other beans can be
local or remote. In EJB 2.1 you can also reference a web
Service
object that provides access to an EJB
endpoint (see Chapter 15 to learn more about EJB
endpoints).
The <env-ref>
element defines
references to other beans within the
JNDI ENC. This makes it much easier for beans to reference other
beans; a bean can use JNDI to look up a reference to the home
interface for any bean in which it is interested.
The
<env-ref>
element contains the subelements
<description>
(optional),
<ejb-ref-name>
(required),
<ejb-ref-type>
(required),
<remote>
(required),
<home>
(required), and
<ejb-link>
(optional). 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
“.
Here’s 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 it 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 a 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>
In most cases, you are 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. If
that’s the situation, use the
<ejb-link>
element with the
<ejb-ref>
element to get a remote reference
to the enterprise bean.
The 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
it’s 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");
Enterprise 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
namespace. For external resources, the mapping is performed by the
<resource-ref>
element.
The <resource-ref>
element contains the
<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:
jdbc/
for a JDBC DataSource
factory
jms/
for a JMS
QueueConnectionFactory
or
TopicConnectionFactory
factory
mail/
for a JavaMail
session
factory
url/
for a javax.net.URL
factory
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 declares 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, the container will automatically perform authentication
(sign-on or login) to use the resource, as specified at deployment
time. If Application
is specified, the bean itself
must perform authentication before using the resource.
Here’s 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);
In 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 declares the JMS
factory, while the
<resource-env-ref>
element declares 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.
When 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), a strategy that is more efficient than using separate resource connections.
In the TravelAgent EJB, the bookPassage( )
method
uses the ProcessPayment EJB and the Reservation EJB to book a
passenger on a cruise. If the 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 with 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, and having the option to turn off
resource sharing is beneficial. Unless you have a good reason for
turning off resource sharing, I recommend that you use
Shareable
resources.
EJB 2.1 includes a new element,
<service-ref>
, that is used to bind a
JAX-RPC Service to the JNDI ENC. The modified TravelAgent EJB
declares a
<service-ref>
element that looks like this:
<ejb-jar xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:chargeIt="http://charge-it.com/Processor" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd" version="2.1"> <enterprise-beans> <session> <ejb-name>TravelAgentEJB</ejb-name> ...
<service-ref>
<service-ref-name>
service/ChargeItProcessorService</service-ref-name>
<service-interface>
com.charge_it.ProcessorService</service-interface>
<wsdl-file>
META-INF/wsdl/ChargeItProcessor.wsdl</wsdl-file>
<jaxrpc-mapping-file>
META-INF/mapping.xml</jaxrpc-mapping-file>
<service-qname>
chargeIt:ProcessorService</service-qname>
</service-ref>
... </session> </enterprise-beans> ... </ejb-jar>
The <service-ref-name>
element declares the
JNDI ENC lookup name of the JAX-RPC Service: it is always relative to
the "java:comp/env
" context. For more details
about various web service deployment descriptors, see Chapter 15.
The
<security-role-ref>
element defines the
security
roles that are used by a bean and maps 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.
EJB
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 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 of EJB QL may
cause problems for the XML parser; you may need to wrap the query in
a CDATA section. See the sidebar “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 applies to
both methods. The container returns the proper type for each query
method: queries that use the remote home return one or more remote
EJB objects, and queries that use the local home return one or more
local EJB objects. This feature 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 is referenced in an EJB QL statement must have
an abstract schema
name
, which is declared by the
<abstract-schema-name>
element. 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
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.
[54] Message selectors are also based on message headers, which are outside the scope of this chapter.