Primary Services

Many value-added services are available for distributed applications. This book looks at eight value-added services 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, timer, naming, and security. EJB servers automatically manage all the primary services. This capability 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.

Concurrency

Concurrency is important to all the bean types, but it has different meanings for each type.

Concurrency with session and entity beans

Session beans do not support concurrent access. This limitation makes sense if you consider the nature of 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. Because neither stateful nor stateless session beans represent shared data, there is no need for concurrency.

Entity beans represent data that is shared and may be accessed concurrently. Entity beans are shared components. In Titan’s EJB system, for example, there are 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.

Clients sharing access to an EJB object

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

EJB addresses the dangers associated with concurrency in entity beans by prohibiting 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, 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, the EJB specification 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 in order 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 make it impossible for the container to manage the primary services.

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; for example, bean A calls bean B, which in turn calls bean A. In EJB, entity-bean instances are nonreentrant by default, which means that loopbacks like the one just described are not allowed.

Remember that entity and session beans interact using objects that implement each other’s remote and local interfaces, 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 in order to apply security and transaction services.

While most bean-to-bean interactions take place using local reference of enterprise beans that are in the same container, occasionally beans interact using remote references. 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.

Beans access each other through EJB objects

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

A loopback occurs when bean A invokes a method on bean B that then attempts to make a call back to bean A. 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.

A loopback scenario

Figure 3-8. A loopback scenario

Session beans can never be reentrant, and throw an exception if a loopback is attempted. Entity beans can be configured to allow reentrance, although it is discouraged by the specification.

Reentrance is not relevant to message-driven beans because they do not respond to RMI calls, as session and entity beans do. Furthermore, EJB 2.1 endpoint interfaces may only be implemented by stateless session beans, which may not be reentrant.

The problem with reentrance is that client access to a bean is synchronized so that only one client can access any given bean at a 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, 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 affect 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;
    }
}

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

Concurrency with message-driven beans

In message-driven beans, concurrency refers to the processing of more than one message at a time. 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. 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 JMS-MDB that subscribes or listens to that destination can be used to process the messages simultaneously.

Concurrent processing with message-driven beans

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

Message-driven beans that implement APIs other than JMS benefit from the same concurrency controls as JMS-MDBs. Message-driven beans of all kinds are pooled and used to process incoming messages concurrently so that hundreds, possibly thousands, of messages can be handled simultaneously.[11]

Transactions

A transaction is a unit-of-work or a set of tasks 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; as a bean developer, you don’t need to use any APIs to 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.

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 saved in a database. Persistence 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 its own 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.

Object-to-relational persistence

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

In Titan’s system, the CabinBean models the concept of a ship’s cabin. The CabinBean defines three fields: name, deckLevel, and id. 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);
    
}

The abstract accessor methods represent the entity bean’s container-managed fields. When an entity bean is deployed, the container implements these “virtual” fields for the bean, so it is convenient to think of the abstract accessor methods as describing persistent fields. For example, when talking about the state represented by the setName( )/getName( ) abstract accessor method, we refer to it as the name field. Similarly, getId( )/setId( ) represents the id field, and getDeckLevel( )/setDeckLevel( ) represents the deckLevel field.

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.

Object-to-relational mapping of entity beans

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

Many 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 fairly straightforward and usually takes place at deployment time. Figure 3-11 shows Pramati Application Server’s object-to-relational mapping wizard.

Pramati object-to-relational mapping wizard

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

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 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. Sometimes, bean types map to more than one table. These are more complicated mappings, often requiring a SQL join and multiple updates.

In addition, 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 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 result in the 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.

Object database persistence

Object-oriented databases are designed to preserve object types and object graphs, and therefore are a good 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, container-managed persistence provides a programming model that can 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 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.

Legacy persistence

EJB can be 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 an 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.

Container-managed versus bean-managed persistence

Regardless of the type of legacy system, container-managed persistence is preferable to bean-managed persistence. With container-managed persistence, the bean’s state is managed automatically, a process that 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 the developer doesn’t use the automatic persistence service of the EJB server. BMP is also used by third-party persistence providers that support nontraditional database systems. These third-party products will generate BMPs automatically and use J2eeCA to obtain transactionally safe access to a database not normally supported by the EJB vendor. Chapter 6 through Chapter 9 describe container-managed and bean-managed persistence in detail.

Distributed Objects

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 or endpoint interface. Everything else is invisible, including the mechanism used to support distributed objects. 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 (the Java RMI API using the CORBA IIOP protocol). EJB 2.1 also requires support for SOAP 1.1 via the JAX-RPC API.

Regardless of the protocol, the server must support Java clients using the Java EJB client API, which means that the protocol must map to the Java RMI-IIOP or the JAX-RPC programming model. Figure 3-12 illustrates the Java language EJB API supported by different distributed object protocols.

Java EJB client view supported by various protocols

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

EJB also allows servers to support access to beans by clients written in languages other than Java. An example of this capability is the EJB-to-CORBA mapping defined by Sun.[12] 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. Another example is the EJB-to-SOAP mapping based on JAX-RPC. It allows SOAP client applications written in languages such as VisualBasic.NET, C#, and Perl to access stateless session beans. Figure 3-13 illustrates the possibilities for accessing an EJB server from different distributed object clients.

EJB accessed from different distributed clients

Figure 3-13. EJB accessed from different distributed clients

Asynchronous Enterprise Messaging

Prior to EJB 2.0, support for asynchronous enterprise messaging 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 with JMS has become so important that it has been elevated to the status of a primary service.

Support for enterprise messaging requires that the EJB container reliably route messages from JMS clients to JMS-MDBs. 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.[13] 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 JMS-MDB 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 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 JMS-MDB—support for both facilities tends to go hand in hand.

In EJB 2.0, supporting JMS-MDBs required tight coupling between the EJB container and the JMS message router; as a result, many EJB container systems could only support a limited number of JMS providers. This changed in EJB 2.1, which requires support for Java Connector API Version 1.5. J2EE Connectors provides better support for asynchronous communication systems such as JMS, which means that JMS-MDBs have become more of a pluggable service in the EJB platform. Any JMS provider that supports the J2eeCA can send messages to a JMS message-driven bean.

EJB 2.1 : Timer Service

Enterprise JavaBeans 2.1 introduced a new primary service, the Timer Service. The Timer Service can be used to schedule notifications that are sent to enterprise beans at specific times. Timers are useful in many different applications. For example, a banking system may set timers on mortgage accounts to check for past-due payments. A stock-trading system might allow timers to be set on “buy limit orders.” A medical claims system may set timers for automatic fraud audits of individual medical records. Timers can also be used in applications like self-auditing systems and batch processing.

Timers can be set on entity, stateless session, and message-driven beans. With session and entity beans, the bean sets the timers itself. For example, when a mortgage loan is created, the entity bean that represents the loan might set a past-due timer when the loan is created, and reset the timer whenever a payment is made. Some EJB container systems may support message-driven bean timers, which are configured at deployment time and perform batch processing at regular intervals. The Timer Service is covered in detail in Chapter 13.

Naming

All naming services do essentially the same thing: 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. 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 (Java Naming and Directory Interface) supports just about any kind of naming and directory service. Although JNDI can become extraordinarily complex, the way it’s used in J2EE applications is usually fairly simple. 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( );
Object ref = jndiContext.lookup("java:comp/env/ejb/CabinHomeRemote");
CabinHomeRemote cabinHome = (CabinHomeRemote)
    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 vendors must support the CORBA naming service in addition to any other directory services they choose to support.

Security

Enterprise JavaBeans servers can support as many as three kinds of security:

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. 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 encrypting the communication between the client and the server. 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 Sockets 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. For example, 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. When a client invokes a method on a bean, 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.

Role-driven access control

In Enterprise JavaBeans, the security identity is represented by a java.security.Principal object. The Principal acts as a representative for users, groups, organizations, smart cards, and so on 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 mapping 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.[14] 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:

<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> results in an exception. This kind of access control makes for a fairly fine-grained authorization system.

Since a single 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 location of these tags and their relationship to other sections of the deployment descriptor is covered in more detail in Chapter 17.

The person who deploys the bean must examine the <security-role> information and map each logical role to a user group. The deployer need not be concerned with what roles go to which methods; rely on the descriptions given in the <security-role> tags to determine matches based on the description of the logical role. This relieves 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 are mapped to their logical roles in the XML deployment descriptor so that specific user groups have access privileges to specific methods on specific enterprise beans. 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.

Mapping roles in the operational environment to logical roles in the deployment descriptor

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

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. 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.

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. For example, propagation prevents a user in a ReadOnly role from legitimately invoking a method on some bean, which in turn invokes a method that is prohibited to a ReadOnly user. This propagation can be overridden by specifying that the enterprise bean executes under a different security identity, called the runAs security identity (discussed later in this chapter).

Unchecked methods

In EJB, 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.

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, abandoned in EJB 1.1, and then 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—however, 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>CabinEJB</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.

runAs identity

Figure 3-15. runAs identity

Here’s what’s going on in Figure 3-15:

  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, the identity under which the boo( ) method executes.

This protocol applies to entity and stateless session beans equally. 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 messages, and incoming messages don’t have a meaningful identity. These messages are not considered RMI calls, and the JMS clients that send them are not directly associated with the messages. With no caller security identity to propagate, message-driven beans must always have a runAs security identity specified, and always execute under that runAs Principal.

Primary Services and Interoperability

Interoperability is a vital part of EJB. The specification includes the required support for Java RMI-IIOP for remote method invocation, and provides for transaction, naming, and security interoperability. EJB 2.1 also requires support for JAX-RPC, which itself requires support for SOAP 1.1 and WSDL 1.1; these are the standards of the web services industry.

IIOP

EJB requires vendors to provide an implementation of Java RMI that 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. It ensures that transactions started by a J2EE web component propagate to enterprise beans in other containers. The EJB specifications detail how two-phase commits are handed across EJB containers as well as how transactional containers interact with nontransactional containers.

EJB 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 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.

While IIOP has been around for a long time and offers interoperability in a number of areas, the truth is it hasn’t been very successful. There are a variety of reasons why IIOP has not been the silver bullet it was intended to be, but perhaps the biggest reason is complexity. Although IIOP is platform-independent, it’s not trivial for vendors to implement. In addition, there appear to be numerous gaps in the IIOP and other CORBA protocols, which cause interoperability problems when actually deployed in a production environment. It’s rare to hear of real-world systems that have successfully deployed interoperating EJB systems based on IIOP. The solution the industry seems to have latched onto is web services, which depend on SOAP and WSDL as the bases for interoperability.

SOAP and WSDL

SOAP (Simple Object Access Protocol) is the primary protocol used by web services today. It’s based on XML and can be used for both RPC and document (asynchronous) style messaging. The fact that SOAP is based on XML means that it’s fairly easy to support. Any platform (operating system, programming language, software application, etc.) that can create HTTP network connections and parse XML can handle the SOAP protocol. This is why SOAP has gained widespread acceptance in a short period of time. There are over 70 SOAP toolkits (code libraries) available today for just about every modern programming environment, including Java, .NET, JavaScript, C, C++, VisualBasic, Delphi, Perl, Python, Ruby, SmallTalk, and others.

WSDL (Web Service Description Language) is the IDL of the web services. A WSDL document is an XML file that describes what web services a company supports, as well as the protocols, message formats, and network addresses of those web services. WSDL documents are highly structured, so that they can be used to autogenerate RPC stubs and other software interfaces for communicating with web services. Although WSDL documents are open enough to describe any type of service, they are typically used to describe web services that use the SOAP protocol.

WSDL and SOAP are normally used in combination. They form the building blocks for other interoperability standards covering security, transaction, orchestration, enterprise messaging, and a cornucopia of other topics. There is a lot of overlap among different groups that are developing infrastructure protocols based on SOAP and WSDL, and as a result, there are a lot of conflicting and immature standards. SOAP and WSDL have a lot of promise, but it’s still too soon to say whether web services will solve the interoperability problems that have plagued enterprise computing since the beginning. It’s likely that SOAP, WSDL, and the infrastructure protocols based on these standards will go further than IIOP, DCOM, and other predecessors, but they won’t be a silver bullet. Web services are covered in more detail in Chapter 14.



[11] In reality, it’s very difficult to process anything simultaneously without multiple processors, but conceptually this statement is true. Multiple threads in the same VM or multiple VMs on the same processor (computer chip) imitate simultaneous processing.

[12] Sun Microsystems’ Enterprise JavaBeans to CORBA Mapping, Version 1.1, by Sanjeev Krishnan..

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

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

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.157.142