The
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.
The
<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:
<
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>
(EJB 1.1: one required; EJB 2.0: optional)Specifies the fully qualified class name of the enterprise bean’s remote home interface.
<remote>
(EJB 1.1: one required; EJB 2.0: optional)Specifies the fully qualified class name of the enterprise bean’s remote interface.
<local-home>
(EJB 2.0: optional)Specifies the fully qualified class name of the enterprise bean’s local home interface.
<local>
(EJB 2.0: optional)Specifies the fully qualified class name of the enterprise bean’s local interface.
<ejb-class>
(one required)Specifies the fully qualified class name of the bean class.
<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 Section 16.5.3 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>
(EJB 2.0: optional)Describes the version of container-managed persistence for which the
entity bean is deployed. EJB containers must support both EJB 2.0 CMP
and EJB 1.1 CMP for backward compatibility. This element may have one
of two values: 2.x
for EJB 2.0 or
1.x
for EJB 1.1.
<abstract-schema-name>
(EJB 2.0: optional)Uniquely identifies entity beans in a JAR file so that they can be referenced by EJB QL statements. This method is described in more detail in Section 16.5.8.
<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.
In EJB 2.0, it must match the method name of the abstract accessor
method (e.g., deckLevel
for
getDeckLevel()
/setDeckLevel()
).
In EJB 1.1, the <cmp-field>
must match the
field name of one of the bean class’s declared instance fields.
The container will manage persistence for the given CMP field. 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 Section 16.5.4.
<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 Section 16.5.5.
<
ejb-local-ref>
(EJB 2.0: 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 Section 16.5.5.
<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 Section 16.5.6 later in this chapter.
<resource-env-ref>
(EJB 2.0: 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 Section 16.5.6 later in this chapter.
<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 Section 16.5.7.
<security-identity>
(EJB 2.0: optional)Specifies the Principal
under which a method will
run. This element is described in more detail in Section 16.7.2.
<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
.
<transaction-type>
(one required; session beans only)Declares either that a session bean manages its own transactions or
that its transactions are managed by the container. This element can
have one of two values: Bean
or
Container
. A bean that manages its own
transactions will not have container-transaction declarations in the
<assembly-descriptor>
section of the
deployment descriptor.
<query>
(EJB 2.0: zero or more)Contains an EJB QL statement that is bound to a find or 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 16.5.8.
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 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:
<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.[61] 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.
<acknowledge-mode>
This element is considered by the container 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
.
If 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.
With 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.
A 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>
In 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.
The <env-ref>
element is used to define
references
to other beans within the JNDI ENC. This makes it much easier for
beans to reference other beans; they can use JNDI to look up a
reference to the home interface for any beans 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.
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 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");
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
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:
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 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);
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 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.
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), 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.
The
<security-role-ref>
element is used to define the security roles that are used by a bean and
to map them into the security roles that are in effect for the
runtime environment. It can contain three subelements: an optional
<description>
,
a
<role-name>
(required), and an optional
<role-link>
.
Here’s how security roles are defined. When a role name is used
in the EJBContext.isCallerInRole(String
roleName)
method, the role name must be statically
defined (it cannot be derived at runtime) and it must be declared in
the deployment descriptor using the
<security-role-ref>
element:
<-- security-role-ref declaration for Account bean --> <entity> <ejb-name>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 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.
[61] Message selectors are also based on message headers, which are outside the scope of this chapter.