At this point, we have said just
about all that can be said about the bean itself. We 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>
.
The <assembly-descriptor>
element is optional, though it is
difficult to imagine a bean being deployed successfully without one.
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 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 to access the
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 the “Specifying
Security Roles and Method Permissions” section.
<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
Specifying Security Roles and Method
Permissions, 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
the “Identifying Specific Methods”
section.
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
we’ll discuss the rest later.
To see how the <container-transaction>
element is typically used, let’s look again at the
Cabin EJB. 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.
For entity beans, the following methods must be assigned transaction attributes:
And for session beans, the following methods must be assigned transactional attributes:
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.
The ejbSelect( )
methods do not have their own
transaction attributes. ejbSelect( )
methods
always propagate the transaction of the methods that call them.
Two
elements define logical security roles and specify which roles can
call particular bean methods. The
<security-role>
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 is important to realize that the security role names 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 in
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. 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 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. It 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. Once again, we still do not know
what administrator
and everyone
mean. The person deploying the bean, who must map these logical
security roles to real security roles defined in the target
environment, defines them.
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.
A set of methods can be designated as unchecked , which means that security permissions are not checked before the method is invoked. Any client can invoke an unchecked method, 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 the
Principal
under which 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—and 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/>
. 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 to
entity and stateless session beans. Message-driven beans have only a
runAs identity; they never execute under the caller identity, because
there is no “caller.” The messages
that a message-driven bean processes are not considered calls, and
the 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. 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, add the <method-intf>
element to
a method declaration as a modifier. Four values are allowed for a
<method-intf>
element:
Remote
, Home
,
LocalHome
, Local
, and
ServiceEndpoint
.
In practice, it is unlikely that a good developer would use the same
method names in both home and remote interfaces: it would lead to
unnecessarily confusing code. However, you would expect to see the
same names in the local, remote, and possibly endpoint 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
<method-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 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>