At this point, we’ve said just
about all that can be
said about the bean itself. We’ve 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’s 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 serves three purposes. It describes the
transactional attributes of the bean’s methods; it describes
the logical security roles that are used in the method permissions;
and it specifies 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:
<container-transaction>
(zero or more)This element declares which transactional attributes apply to which
methods. It contains an optional description
element, one or more method
elements, and exactly
one trans-attribute
element.
Entity beans must have
container-transaction
declarations for all remote
and home interface methods. Session beans that manage their own
transactions will not have container-transaction
declarations. This element is discussed in more detail in Section 10.6.1.
<security-role>
(zero or more)The security-role
element defines the security
roles that are used when accessing a bean. These security roles are
used in the method-permission
element. A
security-role
element contains an optional
description
and one role-name
.
This element and the method-permission
element are
described in more detail in Section 10.6.2.
<method-permission>
(zero or more)This element specifies which security roles are allowed to call one
or more of a bean’s methods. It contains an optional
description
, one or more
role-name
elements, and one or more
method
elements. It is discussed in more detail in
the Section 10.6.2 along with the security-role
element.
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 like method overloading.
The method
element is used within these tags to
identify methods; it is described at length in Section 10.6.3.
The container-transaction
elements are used to
declare the transaction
attributes for all the beans
defined in the deployment descriptor. A
container-transaction
element maps many 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 transactional attributes that we discussed in Chapter 8. In addition to the
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. There’s a
lot more complexity to handle overloading and other special cases,
but that’s enough for now; we’ll discuss the rest later.
Here’s an example that shows how the
container-transaction
element is typically used.
Let’s look again at the Cabin bean, which we’ve used as
an example throughout. Let’s assume that we want to give the
transactional attribute Mandatory
to the
create()
method; all other methods use the
Required
attribute:
<container-transaction> <method> <ejb-name>CabinBean</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> <container-transaction> <method> <ejb-name>CabinBean</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
bean’s methods. We set the transactional attribute for these
methods to Required
. Then, we have a second
container-transaction
element that specifies a
single method of the Cabin bean: create()
. We set
the transactional 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:
All Business methods defined in the remote interface (and all superinterfaces)
For session beans, only the business methods have transaction attributes; the create and remove methods in session beans do not have truncation attributes.
Two 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
, plus a single
role-name
that provides the name. An
assembly-descriptor
can contain any number of
security-role
elements.
It’s 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 beans. </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 beans. This role may also be a member or 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’s 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),
that map to the administrator
and
everyone
roles defined in the
security-role
element.
Security roles in themselves wouldn’t 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-role
s to methods in the remote and
home interfaces of the bean. A method-permission
is a very 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 bean:
<method-permission> <role-name>administrator</role-name> <method> <ejb-name>CabinBean</ejb-name> <method-name>*</method-name> </method> </method-permission> <method-permission> <role-name>everyone</role-name> <method> <ejb-name>CabinBean</ejb-name> <method-name>getDeckLevel</method-name> </method> </method-permission>
In this example, the administrator
role has access
to all methods in the Cabin bean. The everyone
role only has access to the getDeckLevel()
method—it cannot access any of the other methods of the Cabin
bean. 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’s
important to note that we still don’t know what
administrator
and everyone
mean. That’s 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.
The 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
,
method-params
elements that specify method
parameters used to resolve overloaded methods, and a
method-intf
element that specifies whether the
method belongs to the bean’s home or remote interface. This
last element takes care of the possibility that the same method name
might be used in both interfaces.
The 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>CabinBean</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 only be used by itself.
Named declarations apply to all methods defined in the bean’s remote and home interfaces that have the specified name. For example:
<method> <ejb-name>CabinBean</ejb-name> <method-name>create</method-name> </method> <method> <ejb-name>CabinBean</ejb-name> <method-name>getDeckLevel</method-name> </method>
These declarations apply to all methods with the given name in both
interfaces. They don’t distinguish between
overloaded
methods. For example, if the home interface for the Cabin bean 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; ... }
Specific method declarations use the
method-params
element to
pinpoint a specific method by listing its parameters. This allows you
to differentiate between overloaded methods. The
method-params
element contains zero or more
method-param
elements which 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 an example, let’s look again at our Cabin bean, to which
we’ve added some overloaded create()
methods
in the home interface. Here are three method
elements, each of which specifies unambiguously one of the
create()
methods by listing its parameters:
<method> <description> Method: public Cabin create(); </description> <ejb-name>CabinBean</ejb-name> <method-name>create</method-name> <method-params> </method-params> </method> <method> <description> Method: public Cabin create(int id); </description> <ejb-name>CabinBean</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>CabinBean</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>
There’s one problem left. The same method name can be used in
both the home and remote interfaces. To resolve this ambiguity, you
can add the
method-intf
element to a method declaration as
a modifier. Only two values are allowed for a
method-intf
element: Remote
and
Home
.
In reality, it’s unlikely that a good developer would use the
same method names in both home and remote interfaces; that would lead
to unnecessarily confusing code. Realistically, it’s more
likely that you’ll need the method-intf
element in a
wildcarded declaration. For example, the
following declaration specifies all of the methods in the remote
interface of the Cabin bean:
<method>
<ejb-name>CabinBean</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
method-permission
element declares that
the administrator
has access to the Cabin
bean’s home methods (create and find methods). The second
method-permission
specifies that the
everyone
role 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>CabinBean</ejb-name> <method-name>*</method-name> <method-intf>Home</method_intf> </method> </method-permission> <method-permission> <role-name>everyone</role-name> <method> <ejb-name>CabinBean</ejb-name> <method-name>findByPrimaryKey</method-name> </method> </method-permission>