At 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:
<container-transaction>
(zero or more)This element declares which transaction 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 the next section.
<security-role>
(zero or more)This 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 16.7.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>
element, one or more
<role-name>
elements, and one or more
<method>
elements. It is discussed in more
detail in Section 16.7.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 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.
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 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.
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 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.
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>
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.
Security 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.
In 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.
In addition to specifying the Principal
s 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 Principal
s 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.
The 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>
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>
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.
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>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.
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>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; ... }
Specific 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>
There’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>