home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


CONTENTS

Chapter 3. Resource Management and the Primary Services

Chapter 2 discussed the basic architecture of Enterprise JavaBeans, including the relationship between the bean class, the component interfaces, the EJB object and EJB home, and the EJB container. These architectural components define a common model for distributed server-side components in component transaction monitors.

One of the reasons why CTMs are such great distributed object platforms is that they do more than just distribute objects: they also manage the resources used by distributed objects. CTMs are designed to manage thousands, even millions, of distributed objects simultaneously. To be this robust, CTMs must be very smart resource managers, managing how distributed objects use memory, threads, database connections, processing power, and more. EJB recognizes that some of the resource-management techniques employed by CTMs are very common, and it defines interfaces that help developers create beans that can take advantage of these common practices.

EJB CTMs are also great distributed object brokers. Not only do they help clients locate the distributed objects they need, but they also provide many services that make it much easier for a client to use the objects correctly. CTMs commonly support six primary services: concurrency, transaction management, persistence, object distribution, naming, and security. These services provide the kind of infrastructure that is necessary for a successful three-tier system.

With the introduction of message-driven beans in EJB 2.0, Enterprise JavaBeans goes beyond most CTMs by expanding the platform's responsibilities to include managing asynchronous messaging components. CTMs have historically been responsible only for managing RMI-based distributed objects. While the method of access is different for message-driven beans, EJB is still responsible for managing the primary services for message-driven beans just as it does for session and entity beans.

This chapter discusses both the resource-management facilities and the primary services that are available to Enterprise JavaBeans.

3.1 Resource Management

One of the fundamental benefits of using EJB servers is that they are able to handle heavy workloads while maintaining a high level of performance. A large business system with many users can easily require thousands of objects—even millions of objects—to be in use simultaneously. As the number of interactions among these objects increases, concurrency and transactional concerns can degrade the system's response time and frustrate users. EJB servers increase performance by synchronizing object interactions and sharing resources.

There is a relationship between the number of clients and the number of distributed objects that are required to service them. As client populations increase, the number of distributed objects and resources required increases. At some point, the increase in the number of clients will impact performance and diminish throughput. EJB explicitly supports two mechanisms that make it easier to manage large numbers of beans at runtime: instance pooling and activation.

3.1.1 Instance Pooling

The concept of pooling resources is nothing new. A commonly used technique is to pool database connections so that the business objects in the system can share database access. This trick reduces the number of database connections needed, which reduces resource consumption and increases throughput. Pooling and reusing database connections is less expensive than creating and destroying connections as needed. Most EJB containers also apply resource pooling to server-side components; this technique is called instance pooling. Instance pooling reduces the number of component instances, and therefore resources, needed to service client requests. It is also less expensive to reuse pooled instances than to frequently create and destroy instances.

As you already know, EJB clients of session and entity beans interact with these types of enterprise beans through the remote and, for EJB 2.0, local interfaces that are implemented by EJB objects. Client applications never have direct access to the actual session or entity bean. Instead, they interact with EJB objects, which wrap bean instances. Similarly, JMS clients in EJB 2.0 never interact with message-driven beans directly. They send messages that are routed to the EJB container system. The EJB container then delivers these messages to the proper message-driven instance.

Instance pooling leverages indirect access to enterprise beans to provide better performance. In other words, since clients never access beans directly, there's no fundamental reason to keep a separate copy of each enterprise bean for each client. The server can keep a much smaller number of enterprise beans around to do the work, reusing each enterprise bean instance to service different requests. Although this sounds like a resource drain, when done correctly, it greatly reduces the resources actually required to service all the client requests.

3.1.1.1 The entity bean life cycle

To understand how instance pooling works for RMI components (session and entity beans), let's examine the life cycle of an entity bean. EJB defines the life cycle of an entity bean in terms of its relationship to the instance pool. Entity beans exist in one of three states:

No state

When a bean instance is in this state, it has not yet been instantiated. We identify this state to provide a beginning and an end for the life cycle of a bean instance.

Pooled state

When an instance is in this state, it has been instantiated by the container but has not yet been associated with an EJB object.

Ready state

When a bean instance is in this state, it has been associated with an EJB object and is ready to respond to business method invocations.

3.1.1.2 Overview of state transitions

Each EJB vendor implements instance pooling for entity beans differently, but all instance-pooling strategies attempt to manage collections of bean instances so that they are quickly accessible at runtime. To create an instance pool, the EJB container creates several instances of a bean class and then holds onto them until they are needed. As clients make business-method requests, bean instances from the pool are assigned to the EJB objects associated with the clients. When the EJB object doesn't need the instance any more, it's returned to the instance pool. An EJB server maintains instance pools for every type of bean deployed. Every instance in an instance pool is equivalent—they are all treated equally. Instances are selected arbitrarily from the instance pool and assigned to EJB objects as needed.

Soon after the bean instance is instantiated and placed in the pool, it's given a reference to a javax.ejb.EJBContext provided by the container. The EJBContext provides an interface that the bean can use to communicate with the EJB environment. This EJBContext becomes more useful when the bean instance moves to the Ready state. Enterprise beans also have a JNDI context called the environment naming context. The function of the environment naming context is not critical to this discussion and will be addressed in more detail later in the chapter.

When a client uses an EJB home to obtain a remote or local reference to a bean, the container responds by creating an EJB object. Once created, the EJB object is assigned a bean instance from the instance pool. When a bean instance is assigned to an EJB object, it officially enters the Ready state. From the Ready state, a bean instance can receive requests from the client and callbacks from the container. Figure 3-1 shows the sequence of events that results in an EJB object wrapping a bean instance and servicing a client.

Figure 3-1. A bean moves from the instance pool to the Ready state

figs/ejb3_0301.gif

When a bean instance moves into the Ready state, the EJBContext takes on new meaning. The EJBContext provides information about the client that is using the bean. It also provides the instance with access to its own EJB home and EJB object, which is useful when the bean needs to pass references to itself to other enterprise beans, or when it needs to create, locate, or remove beans of its own class. So the EJBContext is not a static class; it is an interface to the container, and its state changes as the instance is assigned to different EJB objects.

When the client is finished with a bean's remote reference, either the remote reference passes out of scope or one of the bean's remove methods is called.[1] Once a bean has been removed or is no longer in scope, the bean instance is disassociated from the EJB object and returned to the instance pool. Bean instances can also be returned to the instance pool during lulls between client requests. If a client request is received and no bean instance is associated with the EJB object, an instance is retrieved from the pool and assigned to the EJB object. This is called instance swapping.

After the bean instance returns to the instance pool, it is again available to service a new client request. Figure 3-2 illustrates the life cycle of a bean instance.

Figure 3-2. Life cycle of a bean instance

figs/ejb3_0302.gif

The number of instances in the pool fluctuates as instances are assigned to EJB objects and returned to the pool. The container can also manage the number of instances in the pool, increasing the count when client activity increases and lowering the count during less active periods.

3.1.1.3 Instance swapping

Stateless session beans offer a particularly powerful opportunity to leverage instance pooling. A stateless session bean does not maintain any state between method invocations. Every method invocation on a stateless session bean operates independently, performing its task without relying on instance variables. This means that any stateless session instance can service requests for any EJB object of the proper type, allowing the container to swap bean instances in and out between method invocations made by the client.

Figure 3-3 illustrates this type of instance swapping between method invocations. In Figure 3-3(a), instance A is servicing a business method invocation delegated by EJB object 1. Once instance A has serviced the request, it moves back to the instance pool (Figure 3-3(b)). When a business method invocation on EJB object 2 is received, instance A is associated with that EJB object for the duration of the operation (Figure 3-3(c)). While instance A is servicing EJB object 2, another method invocation is received by EJB object 1 from the client and is serviced by instance B (Figure 3-3(d)).

Figure 3-3. Stateless session beans in a swapping strategy

figs/ejb3_0303.gif

Using this swapping strategy allows a few stateless session bean instances to serve hundreds of clients. This is possible because the amount of time it takes to perform most method invocations is substantially shorter than the pauses between method invocations. The periods in a bean instance's life when it is not actively servicing the EJB object are unproductive; instance pooling minimizes these inactive periods. When a bean instance is finished servicing a request for an EJB object, it is immediately made available to any other EJB object that needs it. This allows fewer stateless session instances to service more requests, which decreases resource consumption and improves performance.

Stateless session beans are declared stateless in the deployment descriptor. Nothing in the class definition of a session bean is specific to being stateless. Once a bean class is deployed as stateless, the container assumes that no conversational state is maintained between method invocations. So a stateless bean can have instance variables, but because bean instances can be servicing several different EJB objects, they should not be used to maintain conversational state.

Implementations of instance pooling vary, depending on the vendor. One way that instance pooling implementations often differ is in how instances are selected from the pool. Two of the common strategies are FIFO (first in, first out) and LIFO (last in, first out). The FIFO strategy places instances in a queue, where they wait in line to service EJB objects. LIFO uses a stack strategy, where the last bean that was added to the stack is the first bean assigned to the next EJB object. Figure 3-3 uses a LIFO strategy.

3.1.1.4 EJB 2.0: Message-driven beans and instance pooling

Message-driven beans, like stateless session beans, do not maintain state specific to a client request, which makes them an excellent component for instance pooling.

In most EJB containers, each type of message-driven bean has its own instance pool that is used to service incoming messages. Message-driven beans subscribe to a specific message destination, which is a kind of address used when sending and receiving messages. When a JMS client sends an asynchronous message to a specific destination, the message is delivered to the EJB containers of message-driven beans that subscribe to that destination. The EJB container will first determine which message-driven bean subscribes to that destination, then choose an instance of that type from the instance pool to process the message. Once the message-driven bean instance has finished processing the message (when the onMessage() method returns), the EJB container will return the instance to its instance pool. An EJB container can process hundreds, possibly thousands, of messages concurrently by leveraging instance pools. Figure 3-4 illustrates how client requests are processed by an EJB container.

Figure 3-4. Message-driven bean instance pooling

figs/ejb3_0304.gif

In Figure 3-4, the top JMS client delivers a message to Destination A and the bottom JMS client delivers a message to Destination B. The EJB container chooses an instance of MessageDrivenBean_1 to process the message intended for Destination A and an instance of MessageDrivenBean_2 to process the message intended for Destination B. The bean instances are removed from the pool and assigned and used to process the messages.

A moment later, the middle JMS client sends a message to Destination B. At this point, the first two messages have already been processed and the container is returning the instances to their respective pools. As the new message comes in, the container chooses a new instance of MessageDrivenBean_2 to process the message.

Message-driven beans are always deployed to process messages from a specific destination. In the above example, instances of MessageDrivenBean_1 process messages only for Destination A, while instances of MessageDrivenBean_2 process messages only for Destination B. Several messages for the same destination can be processed at the same time. If, for example, a hundred messages for Destination A all arrived at the same time from a hundred different JMS clients, the EJB container would simply choose a hundred instances of MessageDrivenBean_1 to process the incoming messages; each instance is assigned a message.

The ability to concurrently process messages makes message-driven beans extremely powerful enterprise beans, on the same level as session and entity beans. They are truly first-class components and are an important addition to the Enterprise JavaBeans platform.

3.1.2 The Activation Mechanism

Unlike other enterprise beans, stateful session beans maintain state between method invocations. This is called conversational state because it represents the continuing conversation with the stateful session bean's client. The integrity of this conversational state needs to be maintained for the life of the bean's service to the client. Stateful session beans, unlike stateless session, entity, and message-driven beans, do not participate in instance pooling. Instead, activation is used with stateful session beans to conserve resources. When an EJB server needs to conserve resources, it can evict stateful session beans from memory. This reduces the number of instances maintained by the system. To passivate the bean and preserve its conversational state, the bean's state is serialized to a secondary storage and maintained relative to its EJB object. When a client invokes a method on the EJB object, a new stateful instance is instantiated and populated from the passivated secondary storage.

Passivation is the act of disassociating a stateful bean instance from its EJB object and saving its state. Passivation requires that the bean instance's state be held relative to its EJB object. After the bean has been passivated, it is safe to remove the bean instance from the EJB object and evict it from memory. Clients are completely unaware of the deactivation process. Remember that the client uses the bean's remote reference, which is implemented by an EJB object, and therefore does not directly communicate with the bean instance. As a result, the client's connection to the EJB object can be maintained while the bean is passivated.

Activating a bean is the act of restoring a stateful bean instance's state relative to its EJB object. When a method on the passivated EJB object is invoked, the container automatically instantiates a new instance and sets its fields equal to the data stored during passivation. The EJB object can then delegate the method invocation to the bean as normal. Figure 3-5 shows activation and passivation of a stateful bean. In Figure 3-5(a), the bean is being passivated. The state of instance B is read and held relative to the EJB object it was serving. In Figure 3-5(b), the bean has been passivated and its state preserved. Here, the EJB object is not associated with a bean instance. In Figure 3-5(c), the bean is being activated. A new instance, instance C, has been instantiated and associated with the EJB object and is in the process of having its state populated with the state held relative to the EJB object.

Figure 3-5. The passivation and activation processes

figs/ejb3_0305.gif

The exact mechanism for activating and passivating stateful beans is up to the vendor, but each stateful bean is serializable and thus provides at least one way of temporarily preserving its state. While some vendors take advantage of the Java serialization mechanism, the exact mechanism for preserving the conversational state is not specified. As long as the mechanism employed follows the same rules as Java serialization with regard to transitive closure of serializable objects, any mechanism is legal. Because Enterprise JavaBeans also supports other ways of saving a bean's state, the transient property is not treated the same when activating a passivated bean as it is in Java serialization. In Java serialization, transient fields are always set back to the initial value for that field type when the object is deserialized. Integers are set to zero, Booleans to false, object references to null, and so on. In EJB, transient fields are not necessarily set back to their initial values but can maintain their original values, or any arbitrary value, after being activated. Care should be taken when using transient fields, since their state following activation is implementation specific.

The activation process is supported by the life-cycle callback methods discussed in Chapter 2. The ejbActivate() and ejbPassivate() methods, respectively, notify the stateful bean instance that it is about to be activated or passivated. The ejbActivate() method is called immediately following the successful activation of a bean instance and can be used to reset transient fields to an initial value if necessary. The ejbPassivate() method is called immediately prior to passivation of the bean instance. These two methods are especially helpful if the bean instance maintains connections to resources that need to be manipulated or freed prior to passivation and reobtained following activation. Because the stateful bean instance is evicted from memory, open connections to resources are not maintained. The exceptions are remote references to other beans and the SessionContext, which must be maintained with the serialized state of the bean and reconstructed when the bean is activated. EJB also requires that the references to the JNDI environment context, component interfaces, and the UserTransaction bean be maintained through passivation.

Unlike stateful beans, entity beans do not have conversational state that needs to be serialized; instead, the state of each entity bean instance is persisted directly to the database. Entity beans do, however, leverage the activation callback methods (ejbActivate() and ejbPassivate()) to notify the instance when it's about to be swapped in or out of the instance pool. The ejbActivate() method is invoked immediately after the bean instance is swapped into the EJB object, and the ejbPassivate() method is invoked just before the instance is swapped out.

3.2 Primary Services

Many value-added services are available for distributed applications. The OMG (the CORBA governing body), for example, has defined 13 services for use in CORBA-compliant ORBs. This book looks at seven value-added services that are called the primary services, because they are required to complete the Enterprise JavaBeans platform. The primary services include concurrency, transactions, persistence, distributed objects, asynchronous messaging (EJB 2.0), naming, and security.

The seven primary services are not new concepts; the OMG defined interfaces for these services that are specific to the CORBA platform some time ago. In most traditional CORBA ORBs, services are add-on subsystems that are explicitly utilized by the application code. This means that the server-side component developer has to write code to use primary service APIs right alongside their business logic. The use of primary services becomes complicated when they are used in combination with resource-management techniques because the primary services are themselves complex. Using them in combination only compounds the problem.

As more complex component interactions are required, coordinating these services becomes a difficult task, requiring system-level expertise unrelated to the task of writing the application's business logic. Application developers can become so mired in the system-level concerns of coordinating various primary services and resource-management mechanisms that their main responsibility, modeling the business, is all but forgotten.

EJB servers automatically manage all the primary services. This relieves the application developers from the task of mastering these complicated services. Instead, developers can focus on defining the business logic that describes the system and leave the system-level concerns to the EJB server. The following sections describe each of the primary services and explain how they are supported by EJB.

3.2.1 Concurrency

The issue of concurrency is important to all the bean types, but it has a different meaning when applied to EJB 2.0 message-driven beans than it does with the RMI-based session and entity beans. This is because of the difference in context: with RMI-based beans, concurrency refers to multiple clients accessing the same bean simultaneously; with message-driven beans, concurrency refers to the processing of multiple asynchronous messages simultaneously. For this reason, we will discuss the importance of concurrency as a primary service separately for these different types of beans.

3.2.1.1 Concurrency with session and entity beans

Session beans do not support concurrent access. This makes sense if you consider the nature of both stateful and stateless session beans. A stateful bean is an extension of one client and serves only that client. It doesn't make sense to make stateful beans concurrent if they are used only by the clients that created them. Stateless session beans don't need to be concurrent because they don't maintain state that needs to be shared. The scope of the operations performed by a stateless bean is limited to the scope of each method invocation. No conversational state is maintained.

Entity beans represent data in the database that is shared and needs to be accessed concurrently. Entity beans are shared components. In Titan's EJB system, for example, there are only three ships: Paradise, Utopia, and Valhalla. At any given moment the Ship entity bean that represents the Utopia might be accessed by hundreds of clients. To make concurrent access to entity beans possible, the EJB container needs to protect the data represented by the shared bean, while allowing many clients to access the bean simultaneously.

In a distributed object system, problems arise when you attempt to share distributed objects among clients. If two clients are both using the same EJB object, how do you keep one client from writing over the changes of the other? If, for example, one client reads the state of an instance just before a different client makes a change to the same instance, the data the first client read becomes invalid. Figure 3-6 shows two clients sharing the same EJB object.

Figure 3-6. Clients sharing access to an EJB object

figs/ejb3_0306.gif

EJB has addressed the dangers associated with concurrency in entity beans by implementing a simple solution: EJB, by default, prohibits concurrent access to bean instances. In other words, several clients can be connected to one EJB object, but only one client thread can access the bean instance at a time. If, for example, one of the clients invokes a method on the EJB object, no other client can access that bean instance until the method invocation is complete. In fact, if the method is part of a larger transaction the bean instance cannot be accessed at all, except within the same transactional context, until the entire transaction is complete.

Since EJB servers handle concurrency automatically, a bean's methods do not have to be made thread-safe. In fact, the EJB specification prohibits use of the synchronized keyword. Prohibiting the use of the thread synchronization primitives prevents developers from thinking that they control synchronization and enhances the performance of bean instances at runtime. In addition, EJB explicitly prohibits beans from creating their own threads. In other words, as a bean developer, you cannot create a thread within a bean. The EJB container has to maintain complete control over the bean to properly manage concurrency, transactions, and persistence. Allowing the bean developer to create arbitrary threads would compromise the container's ability to track what the bean is doing and thus would make it impossible for the container to manage the primary services.

3.2.1.1.1 Reentrance

When talking about concurrency in entity beans, we need to discuss the related concept of reentrance. Reentrance is when a thread of control attempts to reenter a bean instance. In EJB, entity-bean instances are nonreentrant by default, which means that loopbacks are not allowed. Before I explain what a loopback is, it is important that you understand a very fundamental concept in EJB: entity and session beans interact using each other's remote references and do not interact directly. In other words, when bean A operates on bean B, it does so the same way an application client would, by using B's remote or local interface as implemented by an EJB object. This allows the EJB container to interpose between method invocations from one bean to the next to apply security and transaction services.

While most bean-to-bean interactions in EJB 2.0 take place using local interfaces of co-located enterprise beans, occasionally beans may interact using remote interfaces. Remote interfaces enforce complete location transparency. When interactions between beans take place using remote references, the beans can be relocated—possibly to a different server—with little or no impact on the rest of the application.

Regardless of whether remote or local interfaces are used, from the perspective of the bean servicing the call, all clients are created equal. Figure 3-7 shows that, from a bean's point of view, only clients perform business method invocations. When a business method is invoked on a bean instance, it cannot tell the difference between a remote application client and a bean client.

Figure 3-7. Beans access each other through EJB objects

figs/ejb3_0307.gif

A loopback occurs when bean A invokes a method on bean B that then attempts to make a call back to bean A. Figure 3-8 shows this type of interaction. In Figure 3-8, client 1 invokes a method on bean A. In response to the method invocation, bean A invokes a method on bean B. At this point, there is no problem because client 1 controls access to bean A and bean A is the client of bean B. If, however, bean B attempts to call a method on bean A, it is blocked because the thread has already entered bean A. By calling its caller, bean B is performing a loopback. This is illegal by default, because EJB doesn't allow a thread of control to reenter a bean instance.

Figure 3-8. A loopback scenario

figs/ejb3_0308.gif

The nonreentrance policy is applied differently to session beans and entity beans. Session beans can never be reentrant, and they throw an exception if a loopback is attempted. The same is true of a nonreentrant entity bean. Entity beans can be configured in the deployment descriptor to allow reentrance at deployment time. Making an entity bean reentrant, however, is discouraged by the specification. Reentrance is not relevant to EJB 2.0's message-driven beans because they do not respond to RMI calls, as session and entity beans do.

As discussed previously, client access to a bean is synchronized so that only one client can access any given bean at one time. Reentrance addresses a thread of control—initiated by a client request—that attempts to reaccess a bean instance. The problem with reentrant code is that the EJB object—which intercepts and delegates method invocations on the bean instance—cannot differentiate between reentrant code and multithreaded access within the same transactional context. (You'll read more about transactional context in Chapter 14.) If you permit reentrance, you also permit multithreaded access to the bean instance. Multithreaded access to a bean instance can result in corrupted data because threads impact each other's work when they try to accomplish their separate tasks.

It's important to remember that reentrant code is different from a bean instance that simply invokes its own methods at an instance level. In other words, method foo() on a bean instance can invoke its own public, protected, default, or private methods directly as much as it wants. Here is an example of intra-instance method invocation that is perfectly legal:

public HypotheticalBean extends EntityBean {
    public int x;

    public double foo() {
        int i = this.getX();
        return this.boo(i);
    }
    public int getX() {
        return x;
    }
    private double boo(int i) {
        double value = i * Math.PI;
        return value;
    }
}

In the previous code fragment, the business method, foo(), invokes another business method, getX(), and then a private method, boo(). The method invocations made within the body of foo() are intra-instance invocations and are not considered reentrant.

3.2.1.2 EJB 2.0: Concurrency with message-driven beans

Concurrency in message-driven beans refers to the processing of more then one message at a time. As mentioned earlier, concurrent processing of messages makes message-driven beans a powerful asynchronous component model. If message-driven beans could process only a single message at time, they would be practically useless in a real-world application because they couldn't handle heavy message loads.

Many JMS clients may send messages to the same destination at once. The ability of a message-driven bean to process all the messages simultaneously is concurrency. As Figure 3-9 illustrates, if three messages are delivered to a specific destination from three different clients at the same time, three instances of a single message-driven bean that subscribes or listens to that destination can be used to process the messages simultaneously. This is concurrent processing.

Figure 3-9. Concurrent processing with message-driven beans

figs/ejb3_0309.gif

There is actually a lot more to concurrent processing in message-driven beans. For example, topic and queue type destinations are processed differently, although the basic value of concurrent processing is the same. This book will explore the details behind the topic and queue type destinations in Chapter 13.

3.2.2 Transactions

Component transaction monitors were developed to bring the robust, scalable transactional integrity of traditional TP monitors to the dynamic world of distributed objects. Enterprise JavaBeans, as a server-side component model for CTMs, provides robust support for transactions for all the bean types (session, entity, and message-driven).

A transaction is a unit-of-work or a set of tasks that are executed together. Transactions are atomic; in other words, all the tasks in a transaction must be completed together for the transaction to be considered a success. In the previous chapter, we used the TravelAgent bean to describe how a session bean controls the interactions of other beans. Here is a code snippet showing the bookPassage() method described in Chapter 2:

public TicketDO bookPassage(CreditCardDO card,double price)
    throws IncompleteConversationalState {
    if (customer == null ||cruise == null ||cabin == null) {
        throw new IncompleteConversationalState();
    }
    try {
        ReservationHomeRemote resHome = (ReservationHomeRemote)
            getHome("ReservationHome",ReservationHomeRemote.class);
        ReservationRemote reservation =
            resHome.create(customer,cruise,cabin,price,new Date());
        ProcessPaymentHomeRemote ppHome = (ProcessPaymentHomeRemote)
            getHome("ProcessPaymentHome",ProcessPaymentHomeRemote.class);

        ProcessPaymentRemote process = ppHome.create();
        process.byCredit(customer,card,price);

        TicketDO ticket = new TicketDO(customer,cruise,cabin,price);
        return ticket;
    } catch(Exception e) {
        throw new EJBException(e);
    }
}

The bookPassage() method consists of two tasks that must be completed together: the creation of a new Reservation EJB and the processing of the payment. When the TravelAgent EJB is used to book a passenger, the charges to the passenger's credit card and the creation of the reservation must both be successful. It would be inappropriate for the ProcessPayment EJB to charge the customer's credit card if the creation of a new Reservation EJB fails. Likewise, you can't make a reservation if the customer credit card is not charged. An EJB server monitors the transaction to ensure that all the tasks are completed successfully.

Transactions are managed automatically, so as a bean developer you don't need to use any APIs to explicitly manage a bean's involvement in a transaction. Simply declaring the transactional attribute at deployment time tells the EJB server how to manage the bean at runtime. EJB does provide a mechanism that allows beans to manage transactions explicitly, if necessary. Setting the transactional attributes during deployment is discussed in Chapter 14, as is explicit management of transactions and other transactional topics.

3.2.3 Persistence

Entity beans represent the behavior and data associated with real-world people, places, or things. Unlike session and message-driven beans, entity beans are persistent, which means that the state of an entity is stored permanently in a database. This allows entities to be durable, so that both their behavior and their data can be accessed at any time without concern that the information will be lost because of a system failure.

When a bean's state is automatically managed by a persistence service, the container is responsible for synchronizing the entity bean's instance fields with the data in the database. This automatic persistence is called container-managed persistence. When a bean is designed to manage its own state, as is often the case when dealing with legacy systems, it is called bean-managed persistence.

Each vendor gets to choose the exact mechanism for implementing container-managed persistence, but the vendor's implementation must support the EJB callback methods and transactions. The most common mechanisms used in persistence by EJB vendors are object-to-relational persistence and object database persistence.

3.2.3.1 Object-to-relational persistence

Object-to-relational persistence is perhaps the most common persistence mechanism used in EJB servers today. Object-to-relational persistence involves mapping entity bean state to relational database tables and columns.

In Titan's system, the CabinBean models the business concept of a ship's cabin. The CabinBean defines three fields: a String of type name, an int of type deckLevel, and an Integer of type id.

In EJB 2.0, the abbreviated definition of the CabinBean looks like this:

public abstract class CabinBean implements javax.ejb.EntityBean {

    public abstract String getName();
    public abstract void setName(String str);

    public abstract int getDeckLevel();
    public abstract void setDeckLevel(int level);

    public abstract Integer getId();
    public abstract void setId(Integer id);
    
}

In EJB 2.0, the abstract accessor methods represent the entity bean's container-managed fields, which we will just call fields. When an entity bean is deployed, the container will implement these virtual fields for the bean, so it is convenient to think of the abstract accessor methods as describing persistent fields. For example, when we are talking about the state represented by the setName()/getName() abstract accessor method, we will refer to it as the name field. Similarly, the getId()/setId() is the id field, and the getDeckLevel()/setDeckLevel() is the deckLevel field.

In EJB 1.1, the CabinBean definition looks like this:

public class CabinBean implements javax.ejb.EntityBean {

    public int id;
    public String name;
    public int deckLevel;

}

With object-to-relational database mapping, the fields of an entity bean correspond to columns in a relational database. The Cabin's name field, for example, maps to the column labeled NAME in a table called CABIN in Titan's relational database. Figure 3-10 shows a graphical depiction of this type of mapping.

Figure 3-10. Object-to-relational mapping of entity beans

figs/ejb3_0310.gif

Really good EJB systems provide wizards or administrative interfaces for mapping relational database tables to the fields of entity-bean classes. Using these wizards, mapping entities to tables is a fairly straightforward process and is usually performed at deployment time. Figure 3-11 shows Pramati Application Server's object-to-relational mapping wizard.

Figure 3-11. Pramati object-to-relational mapping wizard

figs/ejb3_0311.gif

Once a bean's fields are mapped to the relational database, the container takes over the responsibility of keeping the state of an entity-bean instance consistent with the corresponding tables in the database. This process is called synchronizing the state of the bean instance. In the case of CabinBean, bean instances at runtime will map one-to-one to rows in the CABIN table of the relational database. When a change is made to a Cabin EJB, it is written to the appropriate row in the database. Frequently, bean types will map to more than one table. These are more complicated mappings, often requiring an SQL join. Good EJB deployment tools should provide wizards that make multitable mappings fairly easy.

In addition, EJB 2.0 container-managed persistence defines entity-bean relationship fields, which allow entity beans to have one-to-one, one-to-many, and many-to-many relationships with other beans. Entity beans can maintain collections of other entity beans or single references. The persistence of entity beans in EJB 2.0 is a great deal more complex and powerful than was supported in previous versions of the specification. The new EJB 2.0 container-managed persistence model is covered in Chapter 6, Chapter 7, and Chapter 8.

In addition to synchronizing the state of an entity, EJB provides mechanisms for creating and removing entities. Calls to the EJB home to create and remove entities will result in a corresponding insertion or deletion of records in the database. Because each entity stores its state in a database table, new records (and therefore bean identities) can be added to tables from outside the EJB system. In other words, inserting a record into the CABIN table—whether done by EJB or by direct access to the database—creates a new Cabin entity. It's not created in the sense of instantiating a Java object, but rather in the sense that the data that describes a Cabin entity has been added to the system.

3.2.3.2 Object database persistence

Object-oriented databases are designed to preserve object types and object graphs and therefore are a much better match for components written in an object-oriented language such as Java. They offer a cleaner mapping between entity beans and the database than a traditional relational database. However, this is more of an advantage in EJB 1.1 than it is in EJB 2.0. EJB 2.0 container-managed persistence provides a programming model that is expressive enough to accommodate both object-to-relational mapping and object databases.

While object databases perform well when it comes to very complex object graphs, they are still fairly new to business systems and are not as widely accepted as relational databases. As a result, they are not as standardized as relational databases, making it more difficult to migrate from one database to another. In addition, fewer third-party products (such as products for reporting and data warehousing) exist that support object databases.

Several relational databases support extended features for native object persistence. These databases allow some objects to be preserved in relational database tables like other data types and offer some advantages over other databases.

3.2.3.3 Legacy persistence

EJB is often used to put an object wrapper on legacy systems, systems that are based on mainframe applications or nonrelational databases. Container-managed persistence in such an environment requires a special EJB container designed specifically for legacy data access. Vendors might, for example, provide mapping tools that allow beans to be mapped to IMS, CICS, b-trieve, or some other legacy application.

Regardless of the type of legacy system used, container-managed persistence is preferable to bean-managed persistence. With container-managed persistence, the bean's state is managed automatically, which is more efficient at runtime and more productive during bean development. Many projects, however, require that beans obtain their states from legacy systems that are not supported by the EJB vendor. In these cases, developers must use bean-managed persistence, which means that the developer doesn't use the automatic persistence service of the EJB server. Chapter 6 through Chapter 11 describe container-managed and bean-managed persistence in detail.

3.2.4 Distributed Objects

Three main distributed object services are available today: CORBA IIOP, Java RMI, and Microsoft's .NET. Each of these platforms uses a different RMI network protocol, but they all accomplish basically the same thing: location transparency. Microsoft's .NET platform, which relies on DCOM, is used in the Microsoft Windows environment and is currently not supported by other operating systems. Its tight integration with Microsoft products makes it a good choice for Microsoft-only systems. This may change with the growing support for SOAP (Simple Object Access Protocol), an XML-based protocol that is quickly becoming popular and offers interoperability with non-Microsoft applications. CORBA IIOP is neither operating-system specific nor language specific and has traditionally been considered the most open distributed object service of the three. It's an ideal choice when integrating systems developed in multiple programming languages. Java RMI is a Java language abstraction or programming model for any kind of distributed object protocol. In the same way that the JDBC API can be used to access any SQL relational database, Java RMI is intended to be used with almost any distributed object protocol. In practice, Java RMI has traditionally been limited to the J ava Remote Method Protocol ( JRMP)—known as Java RMI over JRMP—which can be used only between Java applications. Recently, an implementation of Java RMI over IIOP ( Java RMI-IIOP), the CORBA protocol, has been developed. Java RMI-IIOP is a CORBA-compliant version of Java RMI, which allows developers to leverage the simplicity of the Java RMI programming model while taking advantage of the platform- and language-independent CORBA protocol, IIOP.[2]

When we discuss the component interfaces and other EJB interfaces and classes used on the client, we are talking about the client's view of the EJB system. The EJB client view doesn't include the EJB objects, the EJB container, instance swapping, or any of the other implementation specifics. As far as a remote client is concerned, a bean is defined by its remote interface and home interface. Everything else is invisible. As long as the EJB server supports the EJB client view, any distributed object protocol can be used. EJB 2.0 requires that every EJB server support Java RMI-IIOP, but it doesn't limit the protocols an EJB server can support to just Java RMI-IIOP.

Regardless of the protocol used, the server must support Java clients using the Java EJB client API, which means that the protocol must map to the Java RMI-IIOP programming model. Using Java RMI over DCOM seems a little far-fetched, but Java RMI over SOAP is possible. Figure 3-12 illustrates the Java language EJB API supported by different distributed object protocols.

Figure 3-12. Java EJB client view supported by various protocols

figs/ejb3_0312.gif

EJB also allows servers to support access to beans by clients written in languages other than Java. An example of this is the EJB-to-CORBA mapping defined by Sun.[3] This document describes the CORBA Interface Definition Language (IDL) that can be used to access enterprise beans from CORBA clients. A CORBA client can be written in any language, including C++, Smalltalk, Ada, and even COBOL. The mapping also includes details about supporting the Java EJB client view as well as details on mapping the CORBA naming system to EJB servers and distributed transactions across CORBA objects and beans. Eventually, an EJB-to-SOAP mapping may be defined that will allow SOAP client applications written in languages such as Visual Basic, Delphi, and PowerBuilder to access beans. Figure 3-13 illustrates the possibilities for accessing an EJB server from different distributed object clients.

Figure 3-13. EJB accessed from different distributed clients

figs/ejb3_0313.gif

As a mature, platform- and language-independent distributed object protocol, CORBA is currently regarded by many as the superior of the three protocols discussed here. For all its advantages, however, CORBA suffers from some limitations. Pass-by-value, a feature easily supported by Java RMI-IIOP, was only recently introduced in the CORBA 2.3 specification and is not well supported by vendors. Another limitation of CORBA has to do with casting remote proxies. In Java RMI-JRMP, you can cast or widen a proxy's remote interface to a subtype or supertype of the interface, just like any other object. This is a powerful feature that allows remote objects to be polymorphic. In Java RMI-IIOP, you have to call a special narrowing method to change the interface of a proxy to a subtype, which is cumbersome.[4]

However, JRMP has its own limitations. While JRMP may be a more natural fit for Java-to-Java distributed object systems, it lacks inherent support for both security and transactional services—support that is a part of the CORBA IIOP specification. This limits the effectiveness of JRMP in heterogeneous environments where security and transactional contexts must be passed between systems.

3.2.5 EJB 2.0: Asynchronous Enterprise Messaging

Prior to EJB 2.0, support for asynchronous enterprise messaging and specifically the Java Message Service was not considered a primary service because it wasn't necessary in order to have a complete EJB platform. However, with the introduction of message-driven beans in EJB 2.0, asynchronous enterprise messaging has become so important that it has been elevated to the status of a primary service.

Support for this service is complex, but basically it requires that the EJB container system reliably route messages from JMS clients to message-driven beans. This involves more than the simple delivery semantics associated with email or even the JMS API. With enterprise messaging, messages must be reliably delivered, which means that a failure while delivering the message may require the JMS provider to attempt redelivery.[5] What's more, enterprise messages may be persistent, which means they are stored to disk or to a database until they can be properly delivered to their intended clients. Persistent messages also must survive system failures; if the EJB server crashes, these messages must still be available for delivery when the server comes back up.

Most importantly, enterprise messaging is transactional. That means if a message-driven bean fails while processing a message, that failure will abort the transaction and force the EJB container to redeliver the message to another message-driven bean instance.

In addition to message-driven beans, stateless session beans and entity beans can also send JMS messages. Sending messages can be as important to Enterprise JavaBeans as delivery of messages to message-driven beans—support for both facilities tends to go hand in hand.

It's interesting to note that the semantics of supporting message-driven beans requires tight coupling between the EJB container system and the JMS message router, so that many EJB container systems will support a limited number of JMS providers. This means that message-driven beans can't consume messages from any arbitrary JMS provider or MOM product. Only the JMS providers supported explicitly by the EJB vendor will be able to deliver messages to message-driven beans.[6]

3.2.6 Naming

All distributed object services use a naming service of some kind. Java RMI-JRMP and CORBA use their own naming services. All naming services do essentially the same thing, regardless of how they are implemented: they provide clients with a mechanism for locating distributed objects or resources.

To accomplish this, a naming service must provide two things: object binding and a lookup API. Object binding is the association of a distributed object with a natural language name or identifier. The CabinHomeRemote object, for example, might be bound to the name "CabinHomeRemote" or "room." A binding is really a pointer or an index to a specific distributed object, which is necessary in an environment that manages hundreds of different distributed objects. A lookup API provides the client with an interface to the naming system. Simply put, lookup APIs allow clients to connect to a distributed service and request a remote reference to a specific object.

Enterprise JavaBeans mandates the use of JNDI as a lookup API on Java clients. JNDI supports just about any kind of naming and directory service. A directory service is an advanced naming service that organizes distributed objects and other resources—printers, files, application servers, etc.—into hierarchical structures and provides more sophisticated management features. With directory services, metadata about distributed objects and other resources is also available to clients. The metadata provides attributes that describe the object or resource and can be used to perform searches. For example, you can search for all the laser printers that support color printing in a particular building.

Directory services also allow resources to be linked virtually, which means that a resource can be located anywhere you choose in the directory-services hierarchy. JNDI allows different types of directory services to be linked together so that a client can move between various types of services seamlessly. For example, it's possible for a client to follow a directory link in a Novell NetWare directory into an EJB server, allowing the server to be integrated more tightly with other resources of the organization it serves.

Java client applications can use JNDI to initiate a connection to an EJB server and to locate a specific EJB home. The following code shows how the JNDI API might be used to locate and obtain a reference to the EJB home CabinHomeRemote:

javax.naming.Context jndiContext = new javax.naming.InitialContext(properties);
Object ref = jndiContext.lookup("CabinHomeRemote");
CabinHomeRemote cabinHome = (CabinHome)
    PortableRemoteObject.narrow(ref, CabinHomeRemote.class);

Cabin cabin = cabinHome.create(382, "Cabin 333",3);
cabin.setName("Cabin 444");
cabin.setDeckLevel(4);

The properties passed into the constructor of InitialContext tell the JNDI API where to find the EJB server and what JNDI service provider (driver) to load. The Context.lookup() method tells the JNDI service provider the name of the object to return from the EJB server. In this case, we are looking for the home interface to the Cabin EJB. Once we have the Cabin EJB's home interface, we can use it to create new cabins and access existing cabins.

There are many different kinds of directory and naming services; EJB vendors can choose the one that best meets their needs, but all EJB 2.0 platforms must support the CORBA naming service in addition to any other directory services they choose to support.

Enterprise JavaBeans requires the use of the PortableRemoteObject.narrow() method to cast remote references obtained from JNDI into the CabinHomeRemote interface type. This is addressed in more detail in Chapters Chapter 4 and Chapter 5 and is not essential to the content covered here—the use of this facility is not required when enterprise beans use the local component interfaces of other co-located enterprise beans.

3.2.7 Security

Enterprise JavaBeans servers can support as many as three kinds of security: authentication, access control, and secure communication. Only access control is specifically addressed by Enterprise JavaBeans.

Authentication

Simply put, authentication validates the identity of the user. The most common kind of authentication is a simple login screen that requires a username and a password. Once users have successfully passed through the authentication system, they are free to use the system. Authentication can also be based on secure ID cards, swipe cards, security certificates, and other forms of identification. While authentication is the primary safeguard against unauthorized access to a system, it is fairly crude because it doesn't police an authorized user's access to resources within the system.

Access control

Access control (a.k.a. authorization) applies security policies that regulate what a specific user can and cannot do within a system. Access control ensures that users access only those resources for which they have been given permission. Access control can police a user's access to subsystems, data, and business objects, or it can monitor more general behavior. Certain users, for example, may be allowed to update information while others are allowed only to view the data.

Secure communication

Communication channels between a client and a server are frequently the focus of security concerns. A channel of communication can be secured by physical isolation (e.g., via a dedicated network connection) or by encrypting the communication between the client and the server. Physically securing communication is expensive, limiting, and pretty much impossible on the Internet, so we will focus on encryption. When communication is secured by encryption, the messages passed are encoded so that they cannot be read or manipulated by unauthorized individuals. This normally involves the exchange of cryptographic keys between the client and the server. The keys allow the receiver of the message to decode the message and read it.

Most EJB servers support secure communication—usually through the Secure Socket Layer (SSL) protocol—and some mechanism for authentication, but Enterprise JavaBeans specifies only access control in its server-side component models. Authentication may be specified in subsequent versions, but secure communication will probably never be specified because it is independent of the EJB specification and the distributed object protocol.

Although authentication is not specified in EJB, it is often accomplished using the JNDI API. In other words, a client using JNDI can provide authenticating information using the JNDI API to access a server or resources in the server. This information is frequently passed when the client attempts to initiate a JNDI connection to the EJB server. The following code shows how the client's password and username are added to the connection properties used to obtain a JNDI connection to the EJB server:

properties.put(Context.SECURITY_PRINCIPAL, userName );
properties.put(Context.SECURITY_CREDENTIALS, userPassword);

javax.naming.Context jndiContext = new javax.naming.InitialContext(properties);
Object ref= jndiContext.lookup("CabinHomeRemote");
CabinHomeRemote cabinHome = (CabinHome)
    PortableRemoteObject.narrow(ref, CabinHomeRemote.class);

EJB specifies that every client application accessing an EJB system must be associated with a security identity. The security identity represents the client as either a user or a role. A user might be a person, security credential, computer, or even a smart card. Normally, the user is a person whose identity is assigned when she logs in. A role represents a grouping of identities and might be something like "manager," which is a group of user identities that are considered managers at a company.

When a remote client logs on to the EJB system, it is associated with a security identity for the duration of that session. The identity is found in a database or directory specific to the platform or EJB server. This database or directory is responsible for storing individual security identities and their memberships to groups.

Once a remote client application has been associated with a security identity, it is ready to use beans to accomplish some task. The EJB server keeps track of each client and its identity. When a client invokes a method on a component interface, the EJB server implicitly passes the client's identity with the method invocation. When the EJB object or EJB home receives the method invocation, it checks the identity to ensure that the client is allowed to invoke that method.

3.2.7.1 Role-driven access control

In Enterprise JavaBeans, the security identity is represented by a java.security.Principal object. As a security identity, the Principal acts as a representative for users, groups, organizations, smart cards, etc. to the EJB access-control architecture. Deployment descriptors include tags that declare which logical roles are allowed to access which bean methods at runtime. The security roles are considered logical roles because they do not directly reflect users, groups, or any other security identities in a specific operational environment. Instead, security roles are mapped to real-world user groups and users when the bean is deployed. This allows a bean to be portable; every time the bean is deployed in a new system, the roles can be mapped to the users and groups specific to that operational environment.

Here is a portion of the Cabin EJB's deployment descriptor that defines two security roles, ReadOnly and Administrator:

<security-role>
    <description>
        This role is allowed to execute any method on the bean
        and to read and change any cabin bean data.
    </description>
    <role-name>
        Administrator
    </role-name>
</security-role>

<security-role>
    <description>
        This role is allowed to locate and read cabin info.
        This role is not allowed to change cabin bean data.
    </description>
    <role-name>
        ReadOnly
    </role-name>
</security-role>

The role names in this descriptor are not reserved or special names with predefined meanings; they are simply logical names chosen by the bean assembler. In other words, the role names can be anything you want as long as they are descriptive.[7]

How are roles mapped into actions that are allowed or forbidden? Once the <security-role> tags are declared, they can be associated with methods in the bean using <method-permission> tags. Each <method-permission> tag contains one or more <method> tags, which identify the bean methods associated with one or more logical roles identified by the <role-name> tags. The <role-name> tags must match the names defined by the <security-role> tags shown earlier:

<method-permission>
    <role-name>Administrator</role-name>
    <method>
        <ejb-name>CabinEJB</ejb-name>
        <method-name>*</method-name>
    </method>
</method-permission>
<method-permission>
    <role-name>ReadOnly</role-name>
    <method>
        <ejb-name>CabinEJB</ejb-name>
        <method-name>getName</method-name>
    </method>
    <method>
        <ejb-name>CabinEJB</ejb-name>
        <method-name>getDeckLevel</method-name>
    </method>
    <method>
        <ejb-name>CabinEJB</ejb-name>
        <method-name>findByPrimaryKey</method-name>
    </method>
</method-permission>

In the first <method-permission>, the Administrator role is associated with all methods on the Cabin EJB, which is denoted by specifying the wildcard character (*) in the <method-name> of the <method> tag. In the second <method-permission>, the ReadOnly role is limited to accessing only three methods: getName(), getDeckLevel(), and findByPrimaryKey(). Any attempt by a ReadOnly role to access a method that is not listed in the <method-permission> will result in an exception. This kind of access control makes for a fairly fine-grained authorization system.

Since a single XML deployment descriptor can describe more than one enterprise bean, the tags used to declare method permissions and security roles are defined in a special section of the deployment descriptor This allows several beans to share the same security roles. The exact location of these tags and their relationship to other sections of the XML deployment descriptor will be covered in more detail in Chapter 16.

When the bean is deployed, the person deploying the bean will examine the <security-role> information and map each logical role to a corresponding user group in the operational environment. The deployer need not be concerned with what roles go to which methods; he can rely on the descriptions given in the <security-role> tags to determine matches based on the description of the logical role. This unburdens the deployer, who may not be a developer, from having to understand how the bean works in order to deploy it.

Figure 3-14 shows the same enterprise bean deployed in two different environments (labeled X and Z). In each environment, the user groups in the operational environment are mapped to their logical equivalent roles in the XML deployment descriptor so that specific user groups have access privileges to specific methods on specific enterprise beans.

Figure 3-14. Mapping roles in the operational environment to logical roles in the deployment descriptor

figs/ejb3_0314.gif

As you can see from the figure, the ReadOnly role is mapped to those groups that should be limited to the get accessor methods and the find method. The Administrator role is mapped to those user groups that should have privileges to invoke any method on the Cabin EJB.

The access control described here is implicit; once the bean is deployed, the container takes care of checking that users access only those methods for which they have permission. This is accomplished by propagating the security identity, the Principal, with each method invocation from the client to the bean. When a client invokes a method on a bean, the client's Principal is checked to see if it is a member of a role mapped to that method. If it's not, an exception is thrown and the client is denied permission to invoke the method. If the client is a member of a privileged role, the method is invoked.

If a bean attempts to access any other enterprise beans while servicing a client, it will pass along the client's security identity for access-control checks by the other beans. In this way, a client's Principal is propagated from one bean invocation to the next, ensuring that its access is controlled whether or not it invokes a bean method directly. In EJB 2.0, this propagation can be overridden by specifying that the enterprise bean executes under a different security identity, called the runAs security identity, which is discussed later in this chapter.

3.2.7.2 EJB 2.0: Unchecked methods

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.

3.2.7.3 EJB 2.0: The runAs security identity

In addition to specifying the Principals 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 Principals 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>

This kind of configuration is useful when the enterprise beans or resources accessed in the body of the method require a Principal that is different from the one used to gain access to the method. For example, the create() method might call a method in enterprise bean X that requires the Administrator security identity. If we want to use enterprise bean X in the create() method, but we want only Jim Smith to create new cabins, we would use the <security-identity> and <method-permission> elements together to give us this kind of flexibility: the <method-permission> for create() would specify that only Jim Smith can invoke the method, and the <security-identity> element would specify that the enterprise bean always runs under the Administrator security identity.To specify that an enterprise bean will execute under the caller's identity, the <security-identity> role contains a single empty element, the <use-caller-identity> element. 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>CabinEJB</ejb-name>
        ...
        <security-identity>
            <use-caller-identity/>
        </security-identity>
        ...
    </entity>
...
</enterprise-beans>

Figure 3-15 illustrates how the runAs Principal can change in a chain of method invocations. Notice that the runAs Principal is the Principal used to test for access in subsequent method invocations.

Figure 3-15. runAs Identity

figs/ejb3_0315.gif

Here's what's going on in this figure:

  1. The client, who is identified as BillJones, invokes the method foo() on enterprise bean A.

  2. Before servicing the method, enterprise bean A checks to see if BillJones is included in the <method-permission> elements for foo(). It is.

  3. The <security-identity> of enterprise bean A is declared as <use-caller-identity>, so the foo() method executes under the caller's Principal; in this case, it's BillJones.

  4. While foo() is executing, it invokes method bar() on enterprise bean B using the BillJones security identity.

  5. Enterprise bean B checks the foo() method's Principal (BillJones) against the allowed identities for method bar(). BillJones is included in the <method-permission> elements, so the method bar()is allowed to execute.

  6. Enterprise bean B specifies the <security-identity> to be the runAs Principal of Administrator.

  7. While bar() is executing, enterprise bean B invokes the method boo() on enterprise bean C.

  8. Enterprise bean C checks whether bar()'s runAs Principal (Administrator) is included in the <method-permission> elements for method boo(). It is.

  9. The <security-identity> for enterprise bean C specifies a runAs Principal of System, which is the identity under which the boo() method executes.

This protocol 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." Message-driven beans process asynchronous JMS messages. These messages are not considered calls, and the JMS clients that send them are not associated with the messages. With no caller security identity to propagate, message-driven beans must always have a runAs security identity specified and will always execute under that runAs Principal.

3.2.8 EJB 2.0: Primary Services and Interoperability

Interoperability is a vital part of EJB 2.0. The new specification includes the required support for Java RMI-IIOP for remote method invocation and provides for transaction, naming, and security interoperability.

The EJB 2.0 specification requires vendors to provide an implementation of Java RMIthat uses the CORBA 2.3.1 IIOP protocol. The goal of this requirement is that J2EE servers will be able to interoperate, so that J2EE components (enterprise beans, applications, servlets and JSPs) in one J2EE server can access enterprise beans in a different J2EE server. The Java RMI-IIOP specification standardizes the transfer of parameters, return values, and exceptions as well as the mapping of interfaces and value objects to the CORBA IDL.

Vendors may support protocols other than Java RMI-IIOP, as long as the semantics of the RMI interfaces adhere to the types allowed in RMI-IIOP. This constraint ensures that a client's view of EJB is consistent, regardless of the protocol used in remote invocations.

Transaction interoperability between containers for two-phase commits is an optional but important feature of EJB 2.0. It ensures that transactions started by a J2EE web component propagate to enterprise beans in other containers through the implicit propagation mechanism described in the CORBA Object Transaction Service (OTS) v1.2 specification. The EJB 2.0 specification details how two-phase commits are handed across EJB containers as well as how transactional containers interact with non-transactional containers.

The new specification also addresses the need for an interoperable naming service for looking up enterprise beans. It specifies CORBA CosNaming as the interoperable naming service, defining how the service must implement the IDL interfaces of beans in the CosNaming module and how EJB clients use the service over IIOP.

EJB 2.0 provides security interoperability by specifying how EJB containers establish trust relationships and how containers exchange security credentials when J2EE components access enterprise beans across containers. EJB containers are required to support the Secure Sockets Layer (SSL 3.0) protocol and the related IETF standard Transport Layer Security (TLS 1.0) protocol for secure connections between clients and enterprise beans.

3.3 What's Next?

The first three chapters of this book gave you a foundation on which to develop Enterprise JavaBeans components and applications. You should have a better understanding of CTMs and the EJB component model.

Beginning with Chapter 4, you will develop your own beans and learn how to apply them in EJB applications.

[1]  The EJBHome, EJBLocalHome, EJBObject, and EJBLocalObject interfaces all define methods that can be used to remove a bean.

[2]  Java RMI-IIOP is interoperable with CORBA ORBs that support the CORBA 2.3.1 specification. ORBs that support an older specification cannot be used with Java RMI-IIOP because they do not implement the object-by-value portion of the 2.3.1 specification.

[3]  Sun Microsystems' Enterprise JavaBeans™ to CORBA Mapping, Version 1.1, by Sanjeev Krishnan. Copyright 1999 by Sun Microsystems.

[4]  Some vendors actually support native casting in Java clients without the need for the RemotePortableObject.narrow() method—it is not technically that difficult—but the EJB specification requires the use and support of the narrow() method in the remote client API.

[5]  Most EJB vendors will place a limit on the number of times a message can be redelivered. If redelivery occurs too many times, the message might be placed in a "dead message" repository, where it can be reviewed by an administrator.

[6]  This may change as the Java Connector API evolves to better support asynchronous communication systems such as JMS, which could make JMS providers more of a plugable service for EJB platforms.

[7]  For a complete understanding of XML, including specific rules for tag names and data, see Learning XML by Erik Ray (O'Reilly).

CONTENTS