[ Team LiB ] Previous Section Next Section

Other EJB Features

So far, we've seen the major components that make up Enterprise JavaBeans, including the classes, deployment descriptors, and ways to deploy the beans in a WebLogic EJB container. Now let's briefly look over some of the other features that form an integral part of the EJB specifications.

EJB Security

WebLogic Server provides several security features, which are used by your application in conjunction with the application's security model. The security model is defined by using the appropriate attributes of the deployment descriptors we covered earlier in the chapter. WebLogic Server's security features are based on the concept of security roles. A security role represents the function played by a user in the application context. In an airline reservation application, gate agents and ticketing agents could be two of the security roles in the application context.

The EJB deployer can define method permissions (both in the home and the remote interfaces) to security roles. For an airline reservation application example, the business method reserveSeats is accessible to ticketing agents, whereas a specialized business method such as reserveSeatsInLastMinute would be accessible to gate agents for obvious reasons. If the client's identity is associated with a security role that has permissions on a method, the container allows the method to go through. If not, the container throws a RemoteException to the remote client, or an EJBException to the local client, without letting the method call go through.

Defining the application security model involves three steps. First, you define security roles. Next, you declare permissions on a method level to each security role. Finally, you associate users or principals to different security roles.

Defining Security Roles

Security roles can be defined by using the <security-role> stanza within the <assembly-descriptor> block of the ejb-jar.xml deployment descriptor. This stanza can contain a description and a role name for the role that you're defining. Listing 20.3 defines two security roles that will be used in an airline reservation application (gate agents and ticketing agents).

Listing 20.3 Security Role
<!-- At the end of the file -->
<assembly-descriptor>
  <security-role>
    <description>
      Users performing this role are the
      Gate agents for the airline.
    </description>
    <role-name>GateAgent</role-name>
  </security-role>
  <security-role>
    <description>
      Users performing this role are the
      Ticketing agents for the airline.
    </description>
    <role-name>TicketingAgent</role-name>
  </security-role>
<assembly-descriptor>

The role names have no significance, except that they represent a logical business function that individuals playing this role will perform. Role names can be anything as long as they're descriptive. Note that the <assembly-descriptor> is not associated with any single bean. It's shared across all the beans that are defined in the same deployment descriptor. This gives you the benefit of defining the roles once, and defining method permissions across beans for the same role names.

TIP

WebLogic Server provides its own set of security information that can override the deployment descriptor's roles. For a complete understanding of creating these roles and assignment refer to Part VII of this book, and also look at http://edocs.bea.com/wls/docs81/secwlres/wlres.html.


Declarative Security

Let's look at the last step in the EJB declarative security framework. The first step in defining the declarative security model is to create security roles as seen earlier. The next step is to associate permissions to methods for the different security roles. This is done in the ejb-jar.xml deployment descriptor by using the <method-permission> block. One of these blocks maps the role name with the method to which permission is associated. You can specify the wildcard character (*) in the <method-name> attribute to give permissions to all methods in the bean. You may also define attributes to the methods to provide different permissions to different flavors of the same method. The container always chooses the most specific permission definition for any given method. An existing combination indicates a granted permission. More than one role can be given permission to access the same method. To do this, define multiple <method-permission> blocks.

These blocks appear immediately after the <security-role> blocks within the <assembly-descriptor> stanza. Listing 20.4 defines method permissions for the two methods in our example.

Listing 20.4 Method Permissions
 <method-permission>
.   <role-name>GateAgent</role-name>
   <method>
.     <ejb-name>AirlineShoppingCartBean</ejb-name>
.     <method-name>reserveSeatsInLastMinute</method-name>
   </method>
 </method-permission>
 <method-permission>
   <role-name>TicketingAgent</role-name>
  <method>
.     <ejb-name>AirlineShoppingCartBean</ejb-name>
.     <method-name>*</method-name>
.   </method>
 </method-permission>
 <method-permission>
   <unchecked/>
   <method>
     <ejb-name>AirlineShoppingCartBean</ejb-name>
.     <method-name>searchFlights</method-name>
.   </method>
 </method-permission>

In this listing, it's quite evident that we provide access rights to GateAgents to the reserveSeatsInLastMinute method and provide access rights to TicketingAgents to all methods. Now take a look at the third method permission stanza (in the bold lines). Here, we define a method permission to the searchFlights method, but without a role-name. Instead of the role-name, we include an empty attribute called <unchecked> . This attribute tells the container that any user can execute the method. The container does not perform any security checks for such methods. We just covered the basic version of the method element tag. For a detailed description on how method tags can be configured and see all the different ways to use them, refer to the ejb-jar.dtd at http://java.sun.com/dtd/ejb-jar_2_0.dtd.

The next step in declarative security is to associate principals to roles defined earlier. Principals represent actual users and groups that access the system. Assume that John and Jane are gate agents, and Sue and Harry are ticketing agents. So, when John logs in to the system, how does the EJB container know to associate him to the GateAgents role? To do this, the container uses the <security-role-assignment> block within the <weblogic-ejb-jar> stanza in the weblogic-ejb-jar.xml deployment descriptor. This block associates different principal names to each role. For our example, we write this block as follows:


<security-role-assignment>
  <role-name>GateAgent</role-name>
  <principal-name>John</principal-name>
  <principal-name>Jane</principal-name>
</security-role-assignment>
<security-role-assignment>
  <role-name>TicketingAgent</role-name>
  <principal-name>Sue</principal-name>
  <principal-name>Harry</principal-name>
</security-role-assignment>

NOTE

WebLogic Server provides its own security realm that can override the deployment descriptor's roles.


Note that these principals should have been defined as principals in the security realm used by WebLogic Server.

TIP

It's considered best practice not to associate roles directly to users, but rather to the security realm's groups. That way you don't have to configure in two places (security realm and deployment descriptor) when you create users or administer them. The easiest thing is to associate one role for one group.


Programmatic Security

When we discussed security as one of the basic services offered as part of the EJB framework earlier in the chapter, we mentioned programmatic security. Programmatic security is used when the security of your application depends on the business logic. The WebLogic Server EJB container provides some methods that your bean can call to get information about the caller. These methods can be accessed from the EJBContext object in your bean implementation. There are two methods that can come in handy:

  • getCallerPrincipal— This method returns information about the caller as an object of type java.security.Principal. This object contains the name of the principal.

  • isCallerInRole— When invoked with the name of a role, this method returns a boolean indicating whether the caller belongs to that role.

Thus, to provide complex security rules needed by XYZ Airlines, we would not use the default role-based security provided by the container. Instead, we would include the following snippet of code in the reserveSeats method:


public void reserveSeats() {
  if (!(origin.equals("Los Angeles") &&
.    destination.equals("San Francisco"))) {
    // Only certain agents can book these tickets
.    if (context.isCallerInRole("ValidAgentForLASector")) {
      // allow booking
    }
    else {
      // Throw EJBException indicating access denied.
.    }
  }
  else {
    // allow booking for all agents
  }
}

The role name (in the bold line) is an alias to an actual defined role. We check whether the caller is in a role by passing the role alias ValidAgentForLASector (this alias must be mapped to a final role in the deployment descriptor). We did not explicitly use GateAgent. That's because what we use here is a reference, and we leave it to the deployer to decide which role is considered a ValidAgentForLASector. The following XML snippet shows how this link is done using the role-link element:


<session> <!-- also applies to Entity beans -->
.. <!-- other session tags here -->
<security-role-ref>
  <role-name>GateAgent</role-name>
  <role-link>ValidAgentForLASector</role-link>
</security-role-ref>
</session>

Figure 20.14 summarizes the discussion surrounding the WebLogic EJB security features we covered in this section. It illustrates the normal path taken when a user is identified to execute a particular method on the EJB for declarative and programmatic security, respectively.

Figure 20.14. EJB security resolution.

graphics/20fig14.gif

EJB Environment Entries

The J2EE deployment descriptor ejb-jar.xml enables the deployer to define environment entries that can be read at runtime from the bean's environment. The environment entries can be accessed using the JNDI-ENC, as briefly mentioned earlier in the chapter when we talked about the basic services offered by the EJB.

Let's look at the need for environment entries with a help of a simple business case. Consider the following requirement given to us by XYZ Airlines: At any point in time, each flight should have at least five seats reserved for platinum members who want to fly at the last minute. Here, it is obvious that the code should not allow any circumstance that will leave fewer than five seats available for any airline. To achieve this, we coded our bean implementation as given here:


private static int NUM_RESERVED_SEATS = 5;
...
public void reserveSeats(int flightNumber, int numSeats)
    throws AirlineReservationException {
...
  Properties props = getFlightInfo( flightNumber) ;
  int numSeatsInDB = Integer.parseInt((String)props.get("NUM_SEATS"));
  if (numSeatsInDB < NUM_RESERVED_SEATS || numSeatsInDB - numSeats
      < NUM_RESERVED_SEATS) {
    throw new AirlineReservationException
      ("Not enough seats.");
  }
...
}

This works fine as long as XYZ Airlines does not increase the number of reserved seats to 10 from 5. If they do so, we have no other choice but to change the code of the bean implementation and in so doing make the code error-prone. Environment entries can be defined in the ejb-jar.xml file to make the code cleaner by defining properties such as NUM_RESERVED_SEATS. These properties can be read from the bean's environment using the JNDI-ENC. These application properties can be stored at deployment time by using a block called <env-entry> inside each bean's definition. This element applies to all bean types.

Now let's return to the business case we were looking at and decide how we can use <env-entry> to solve our problem. This block contains a description, a name for the environment entry, the type of the entry, and an optional value for the environment entry. The following provides a sample <env-entry> block for our airline reservation bean:


<session> <!-- also applies to Entity beans and Message-Driven beans -->
.. <!-- other session tags here -->
<env-entry>
  <description>
    The number of seats to be reserved
    for platinum passengers for last minute
    reservations.
  </description>
  <env-entry-name>NumReservedSeats</env-entry-name>
  <env-entry-type>java.lang.Integer</env-entry-type>
  <env-entry-value>5</env-entry-value>
</env-entry>
</session>

In this block, we define an environment entry called NumReservedSeats and declare it as type Integer. We then set this variable to a value of 5. The <env-entry-type> attribute can be set to String, or one of the primitive wrapper classes such as Integer, Double, Float, and so on.

The container creates a new environment entry of that type and sets its value to the given value. The variable is loaded into the JNDI ENC context when the bean is deployed. The bean can then retrieve these values form the JNDI tree by looking up the name of the environment entry.

Note that all names are relative to the path java:comp/env/ in the JNDI tree. Thus, our code looks up the value for the number of reserved seats by using the following code snippet. This can be done in the ejbCreate method so that this lookup is done only once.


Context context = new InitialContext();
int numReservedSeats = ((Integer) context.lookup(
  "java:comp/env/NumReservedSeats")).intValue();

That way, the company can easily change the value of the number of reserved seats in the XML file without having to change the code every time.

TIP

Web applications use environment entries in a similar manner in the web.xml deployment descriptor.


EJB and Resource References

References play a key role in removing the hard-wiring that might be done when accessing other EJBs or resources like databases. Such references are declared inside the deployment descriptors that we covered earlier and are used in your bean implementation. One question that's very relevant is why we need references when we can simply define the EJB or resource JNDI name in the bean's environment entries and use that value instead of hard-wiring the JNDI name.

That is certainly possible, but it doesn't leave any room for the container to validate the accuracy of these values. For instance, the container will not be able to tell whether the JNDI name being used is actually tied to the resource that the bean assumes it to be. By using references, the container can perform such validations, as we'll see later on. Moreover, if a resource such as a data source is moved to a new JNDI name, the EJB's code must be modified to reflect the change. Using references, only a deployment descriptor tag will change.

You can define and use three types of references: EJB references (local or remote), resource references, and resource environment references. For a detailed description of how to define these parameters, refer to the DTD defined at http://java.sun.com/dtd/ejb-jar_2_0.dtd.

EJB References

An EJB reference ties a name (let us call this the reference name) to the bean to be referenced within the ejb-jar.xml deployment descriptor. The reference name need not be the same as the JNDI name of the referenced bean. This reference name is then used within the bean implementation to look up the EJB. Inside the weblogic-ejb-jar.xml, this reference name can be mapped to the actual JNDI name of the bean that is being referenced.

You can create two types of EJB references: remote and local references. You can create remote references only if the referenced EJB has a set of remote interfaces. If the referenced EJB has a set of local interfaces, you'll be better off creating a local reference, thus enabling your bean implementation to use the local interfaces of the referenced bean.

The remote reference is created using an <ejb-ref> stanza, and the local reference is created using <ejb-local-ref> in the ejb-jar.xml descriptor. These elements apply for all the bean types and appear under each bean's tag. The following provides a sample remote reference for an AirlineShoppingCart EJB:


<session> <!-- also applies to Entity beans and Message-Driven beans -->
.. <!-- other session tags here -->
<ejb-ref>
  <ejb-ref-name>ejb/ReservationStatelessEJB</ejb-ref-name>
  <ejb-ref-type>Session</ejb-ref-type>
  <home>com.wlsunleashed.ejb.session.Stateless. AirlineReservationRemoteHome</home>
  <remote>com.wlsunleashed.ejb.session.Stateless. AirlineReservationRemoteObject</remote>
</ejb-ref>
</session>

The <ejb-local-ref> element contains the same subelements as defined in the <ejb-ref> block used earlier. However, this should be used for colocated beans only.

How does WebLogic Server resolve this name to the JNDI name of the AirlineReservation EJB? It does so by looking for a <reference-descriptor> stanza within the weblogic-ejb-jar.xml deployment descriptor. This stanza sits inside the <weblogic-enterprise-bean> block. It lists all the reference names used in the ejb-jar.xml descriptor and maps them into the appropriate JNDI names. The following lists a typical reference descriptor for the AirlineShoppingCart EJB, which has a corresponding <ejb-ref> defined in the ejb-jar.xml descriptor:


<weblogic-enterprise-bean>
...
<reference-descriptor>
  <ejb-reference-description>
    <ejb-ref-name>ejb/ReservationStatelessEJB</ejb-ref-name>
    <jndi-name>RemoteAirlineReservationBean</jndi-name>
  </ejb-reference-description>
</reference-descriptor>
</weblogic-enterprise-bean>

When using <ejb-local-ref> in the ejb-jar.xml descriptor, you will use a <ejb-local-reference-description> stanza instead of the <ejb-reference-description> stanza. The contents of both the blocks are exactly the same.

When the bean is deployed into the WebLogic Server container, the container first validates whether the bean that's deployed under the JNDI name matches the description (ejb-ref-type, home/local-home, remote/local interfaces) provided in the ejb-jar.xml descriptor. If this isn't the case, the bean will not be successfully deployed.

Before we move to resource references, let's look at the one of the important optional element; that is, <ejb-link>, which is a subelement of the <ejb-ref> and <ejb-local-ref> elements. This optional element offers an important performance advantage when we refer to another bean residing in the same JAR or EAR file. This element enables WebLogic Server to avoid going to the weblogic-ejb-jar.xml file to resolve the referred EJB's name to the equivalent JNDI name. In other words, the referred EJB can be looked up using the ejb-jar.xml file alone.

The value of the ejb-link element has to be the name of the referred EJB; that is, the value of the <ejb-name> element of the referred EJB. If the referred EJB is in a different ejb-jar.xml file but in the same enterprise application file (EAR), the ejb-link element value should be the name of the referred EJB prefixed with the contained .jar file name and these values should be separated by a hash mark (#); for example, EJB1.jar#bean1. Let's look at an example with the help of the following XML snippet, which defines the Session beans BeanX and BeanY:


<session>
...
<ejb-name>BeanX<ejb-name>
...
<ejb-ref>
  <ejb-ref-name>ejb/BeanXEJB</ejb-ref-name>
  <ejb-ref-type>Session</ejb-ref-type>
  <home>com.wls81unleashed.ejb.session.Stateless.BeanXHome</home>
  <remote>com.wls81unleashed.ejb.session.Stateless.BeanXRemote</remote>
  <ejb-link>BeanY</ejb-link>
</ejb-ref>
...
</session>
<session>
...
<ejb-name>BeanY<ejb-name>
...
<ejb-ref>
  <ejb-ref-name>ejb/BeanYEJB</ejb-ref-name>
  <ejb-ref-type>Session</ejb-ref-type>
  <home>com.wls81unleashed.ejb.session.Stateless.BeanYHome</home>
  <remote>com.wls81unleashed.ejb.session.Stateless.BeanYRemote</remote>
</ejb-ref>
...
</session>

BeanX refers to BeanY and it can directly look up BeanY using the <ejb-link> element definition. If BeanX was not in the same archive as that BeanY, the <ejb-link> element will include the .jar file as given here:


<ejb-link>EJB_Y.jar#BeanY</ejb-link>

All reference names (environment entries, EJBs, and resource references) are relative to the path java:comp/env/ within the JNDI ENC context of the bean. Thus, the bean implementation will use the JNDI ENC name "java:/comp/env/ejb/ReservationStatelessEJB" to look up the AirlineReservation Bean. The ejb/ part of the name is optional, but highly desirable because it qualifies the reference as an EJB, as opposed to other resources within the JNDI ENC. While discussing other resources, you'll see that we use appropriate pathnames when accessing each resource to make it clear.

Resource References

Resource references (also known as resource manager connection factory references in J2EE terms) are conceptually similar to EJB references except that they refer to the resource factories, such as DataSource, JMS connection factories, URLs, and so on. This kind of reference contains a connection factory object (for example, the DataSource), not the actual resource itself.

To define a resource reference, include a <resource-ref> stanza in the bean's tag in ejb-jar.xml. This is applicable for all bean types. This block contains the following parameters:

  • <description>— An optional description of the resource being referred to.

  • <res-ref-name>— The reference name of the resource. Note that this is the suffix to the JNDI name under which the resource is bound. For example, jdbc/ds is accessible through "java:/comp/env/jdbc/ds".

  • <res-type>— The resource type. The type is specified by the fully qualified class or interface name that is to be implemented by this resource. For example, a DataSource resource reference will have a type of javax.sql.DataSource as the <res-type>.

  • <res-auth>— This attribute tells the container who will perform authorization when the resource is used in the bean. If it is set to Container, the container will automatically perform authentication, depending on the permissions set in the WebLogic Server, before deploying the bean. If it is set to Application, the bean is responsible for authenticating itself with the resource.

  • <res-sharing-scope>— Indicates whether the connections obtained using the resource can be shared. By default, connections are shareable. The values this attribute can take are Shareable and Unshareable. Sharable means other EJBs in this application will be able to look up the resource.

The <res-ref-name> specified here is relative to the path java:comp/env/ of the JNDI ENC of the bean. Although it isn't required, it's cleaner to indicate the subcontext under which a resource falls in the reference name. Table 20.6 provides a suggested list of subcontexts for various resource types.

Table 20.6. Naming Resource References in the JNDI-ENC

Resource Type Subcontext

Resource Manager Used (<res-type>)

Suggested Resource

Database connection factory

javax.sql.DataSource

jdbc/

JMS queue connection factory

javax.jms.QueueConnectionFactory

jms/

JMS topic connection factory

javax.jms.TopicConnectionFactory

jms/

JavaMail session factory

javax.mail.Session

mail/

URL connection factory

java.net.URL

url/

As in the case of EJB references, the <resource-ref> block uses the <reference-descriptor> stanza of the weblogic-ejb-jar.xml deployment descriptor to map the reference name to the appropriate JNDI name. The <reference-descriptor> block has a <resource-description> tag within, one for each resource referred to in the ejb-jar.xml descriptor.

Each <resource-description> block should have a corresponding <resource-ref> block defined in the ejb-jar.xml descriptor. A <resource-description> block contains a <res-ref-name> property that identifies the resource reference based on the given reference name. This stanza also contains the <jndi-name> attribute, which links the reference name with the actual JNDI name.

When the bean is deployed, the container verifies whether the resource being referenced is actually of the said type. It also performs necessary authorizations if the <res-auth> property is set to Container.

Let's look at an example of resource references with a real-life case such as an airline reservation system. The business EJB, such as an AirlineReservationEJB, will now have the following block in the ejb-jar.xml deployment descriptor under the <session> or <entity> or <message-driven> stanza depending on the bean type:


<resource-ref>
  <description>The XYZ DataSource</description>
  <res-ref-name>jdbc/AirlineData</res-ref-name>
  <res-type>javax.sql.DataSource</res-type>
  <res-auth>Container</res-auth>
</resource-ref>

We also have a corresponding block in the weblogic-ejb-jar.xml deployment descriptor, as shown here:


<resource-description>
  <res-ref-name>jdbc/AirlineData</res-ref-name>
  <jndi-name>XYZAirlineDataSource</jndi-name>
</resource-description>

Figure 20.15 summarizes the discussion surrounding the EJB and resource references we covered in this section. In this figure, (a) and (b) are the normal path when an EJB or a resource is looked up. But, as we described earlier, ejb-link offers a more efficient lookup mechanism for EJBs colocated in the same JAR or EAR file.

Figure 20.15. Resource References Resolution

graphics/20fig15.gif

One obvious advantage in using references is that you can reconfigure the EJB or resources at any time without going back to change the code. There is a very clear demarcation between what the bean has to know and what it does not. Actual JNDI names under which the EJB or resources are loaded can be abstracted by using EJB and resource references, thus making your bean portable.

Resource Environment References

The resource environment references are another example of environment entries available as part of the ejb-jar.xml configuration file. These are used to associate logical names for administered objects that are associated with resources such as JMS destinations, database connections, and so on. The following XML snippet gives an idea of this environment entry:


<resource-env-ref>
  <resource-env-ref-name>jms/myQueue<resource-env-ref-name>
  <resource-env-ref-type>javax.jms.Queue<resource-env-ref-type>
</resource-env-ref>

The administered objects that are configured using the preceding environment entry are bound in the target WebLogic Server environment using the matching resource-env-description element in the weblogic-ejb-jar.xml file as given here:


<resource-env-description>
  <resource-env-ref-name>jms/myQueue<resource-env-ref-name>
  <jndi-name>wls81unleashed.jms.destn.myQueue<jndi-name>
</resource-env-description>

After this configuration is done for these administered objects, they can be used in any J2EE component; that is, any EJB, servlet, and so on. The following code snippet demonstrates how to use this environment entry:


InitialContext ic = new InitialContext();
Queue myWLS81Queue = (Queue)ic.lookup("java:comp/env/jms/myQueue");

This environment binds the JMS destination into the consistent java:comp/env JNDI tree and in so doing makes the application more portable.

EJB-QL

Enterprise JavaBeans Query Language (EJB-QL) was one of the major features presented in EJB 2.0. It was introduced to standardize the behavior of custom finder methods in Entity beans—CMP. Additionally, it addresses the new select methods that are actually data queries and are private to one Entity bean. EJB QL is similar to its RDBMS equivalent: SQL. The queries are defined by mapping to the Entity bean schema. Because EJB-QL queries are abstract and independent of the underlying data store, they're portable across data stores and EJB containers. This is covered in detail in Chapter 22, later in the book.

Entity Relationships

Entity relationships formed one of the major features that was missing in the EJB 1.1 specification because Entity beans encapsulate only the persistence fields and had no idea about relationships that exist in the real world. The EJB 2.0 relationship fields address complex relationships between Entity beans, thereby providing a closer mapping of Entity beans to their real-world equivalents. Based on cardinality, there are four major types of relationships: one-to-one, one-to-many, many-to-one, and many-to-many. All these can be unidirectional or bi-directional. The EJB relationships are controlled using the deployment elements in the ejb-jar.xml and their related entries in the WebLogic-specific deployment descriptors. For a detailed description of how to manage relationships, see Chapter 22, "Working with Entity Beans."

    [ Team LiB ] Previous Section Next Section