Chapter 6. Transactions and security

This chapter covers

  • Overview of transaction concepts
  • Container and bean-managed transactions
  • Security concepts
  • Declarative and programmatic security

Transaction and security management are important aspects of any serious enterprise development effort. By the same token, both are system-level concerns rather than true business application development concerns, which is why they often become an afterthought. In the worst-case scenario, these critical aspects of application development are overlooked altogether. Given these facts, you’ll be glad to know that EJB 3 provides functionality in both realms that is robust enough for the most demanding environments, and yet simple enough for those who prefer to focus on developing business logic. Although we have briefly mentioned these features in previous chapters, we haven’t dealt with them in any detail until this chapter.

The first part of this chapter is devoted to exploring the rich transaction management features of EJB 3. We’ll briefly discuss transactions and explore container-managed and bean-managed transactions support in EJB. The remainder of the chapter deals with EJB security features, and you’ll learn about declarative and programmatic security support.

6.1. Understanding transactions

We engage in transactions almost every day—when withdrawing money from an ATM or paying a phone bill, for example. Transactions in computing are a closely related concept but differ slightly and are a little harder to define. In the most basic terms, a transaction is a grouping of tasks that must be processed as an inseparable unit. This means every task that is part of the transaction must succeed in order for the transaction to succeed. If any of the tasks fail, the transaction fails as well. You can think of a transaction as a three-legged wooden stool. All three legs must hold for the stool to stand. If any of them break, the stool collapses. In addition to this all-or-nothing value proposition, transactions must guarantee a degree of reliability and robustness. We will come back to what this last statement means when we describe what are called the ACID (atomicity, consistency, isolation, and durability) properties of transactions. A successful transaction is committed, meaning its results are made permanent, whereas a failed transaction is rolled back, as if it never happened.

To explore transaction concepts further, let’s take a look at a sample problem in the ActionBazaar application. Before exploring transaction support in EJB, we’ll briefly discuss ACID properties, transaction management concepts such as resource and transaction managers, and two-phase commits.

6.1.1. A transactional solution in ActionBazaar

Some items on ActionBazaar have a “Snag-It” ordering option. This option allows a user to purchase an item on bid at a set price before anyone else bids on it. As soon as the first bid is placed on an item, the Snag-It option disappears. This feature has become popular because neither the buyer nor the seller needs to wait for bidding to finish as long as they both like the initial Snag-It price tag. As soon as the user clicks the Snag-It button, the ActionBazaar application makes sure no bids have been placed on the item, validates the buyer’s credit card, charges the buyer, and removes the item from bidding. Imagine what would happen if one of these four actions failed due to a system error, but the rest of the actions were allowed to succeed. For example, assume that we validate and charge the customer’s credit card successfully. However, the order itself fails because the operation to remove the item from bid fails due to a sudden network outage and the user receives an error message. Since the credit card charge was already finalized, the customer is billed for a failed order! To make matters worse, the item remains available for bidding. Another user can put a bid on the item before anyone can fix the problem, creating an interesting situation for the poor customer support folks to sort out. We can see this situation in figure 6.1.

Figure 6.1. Because the ordering process is not covered by a transaction, ActionBazaar reaches a strange state when a Snag-It order fails halfway through. The customer is essentially billed for a failed order.

While creating ad hoc application logic to automatically credit the customer back in case of an error is a Band-Aid for the problem, transactions are ideally suited to handle such situations. A transaction covering all of the ordering steps ensures that no actual ordering operation changes are finalized until the entire operation finishes successfully. If any errors occur, all pending data changes, including the credit card charge, are aborted. On the other hand, if all the operations succeed the transaction is marked successful and all ordering changes are made permanent.

Although this all-or-nothing value proposition is a central theme of transactional systems, it is not the only attribute. A number of properties apply to transactional systems; we’ll discuss them next.

6.1.2. ACID properties

The curious acronym ACID stands for atomicity, consistency, isolation, and durability. All transactional systems are said to exhibit these four characteristics. Let’s take a look at each of these characteristics.

Atomicity

As we’ve seen in our ActionBazaar scenario, transactions are atomic in nature; they either commit or roll back together. In coding terms, you band together an arbitrary body of code under the umbrella of a transaction. If something unexpected and irrecoverable happens during the execution of the code, the result of the attempted execution is completely undone so that it has no effect on the system. Otherwise, the results of a successful execution are allowed to become permanent.

Consistency

This is the trickiest of the four properties because it involves more than writing code. This is the most common way of describing the consistency property: if the system is in a state consistent with the business rules before a transaction begins, it must remain in a consistent state after the transaction is rolled back or committed. A corollary to this statement is that the system need not be in a consistent state during the transaction. Think of a transaction as a sandbox or sanctuary—you are temporarily protected from the rules while inside it. As long as you make sure all the business rules in the system remain intact after the last line of code in a transaction is executed, it doesn’t matter if you are in an inconsistent state at an arbitrary point in the transaction. Using our example, it is fine if we charge the customer even though we really haven’t removed the item from bidding yet, because the results of our code will have no impact on the system until and unless our transaction finishes successfully. In the real world, setting up rules and constraints in the database (such as primary keys, foreign key relationships, and field constraints) ensures consistency so that transactions encountering error conditions are rejected and the system is returned to its pretransactional state.

Isolation

If you understand thread synchronization or database locking, you already know what isolation is. The isolation property makes sure transactions do not step on one another’s toes. Essentially, the transaction manager (a concept we’ll define shortly) ensures that nobody touches your data while you are in the transaction. This concept is especially important in concurrent systems where any number of processes can be attempting to manipulate the same data at any given time. Usually isolation is guaranteed by using low-level database locks hidden away from the developer. The transaction manager places some kind of lock on the data accessed by a transaction so that no other processes can modify them until the transaction is finished.

In terms of our example, the transaction isolation property is what guarantees that no bids can be placed on the item while we are in the middle of executing the Snag-It ordering steps since our “snagged” item record would be locked in the database.

Durability

The last of the four ACID properties is durability. Transaction durability means that a transaction, once committed, is guaranteed to become permanent. This is usually implemented by using transaction logs in the database server. (The application server can also maintain a transaction log. However, we’ll ignore this fact for the time being.) Essentially, the database keeps a running record of all data changes made by a transaction before it commits. This means that even if a sudden server error occurs during a commit, once the database recovers from the error changes can be reverted to be properly reapplied (think of untangling a cassette tape and rewinding it to where the tape started tangling). Changes made during the transaction are applied again by executing the appropriate entries from the transaction log (replaying the rewound tape to finish). This property is the muscle behind transactions ensuring that commit really does mean commit.


Isolation levels

The concept of isolation as it pertains to databases is not as cut and dried as we just suggested. As you might imagine, making transactions wait for one another’s data locks limits the number of concurrent transactions that can run on a system. However, different isolation strategies allow for a balance between concurrency and locking, primarily by sacrificing lock acquisition strictness. Each isolation strategy corresponds to an isolation level. Here are the four most common isolation levels, from the highest level of concurrency to the lowest:

  • Read uncommitted— At this isolation level, your transaction can read the uncommitted data of other transactions, also known as a “dirty” read. You should not use this level in a multithreaded environment.
  • Read committed— Your transaction will never read uncommitted changes from another transaction. This is the default level for most databases.
  • Repeatable read— The transaction is guaranteed to get the same data on multiple reads of the same rows until the transaction ends.
  • Serializable— This is the highest isolation level and guarantees that none of the tables you touch will change during the transaction, including adding new rows. This isolation level is very likely to cause performance bottlenecks.

A good rule of thumb is to use the highest isolation level that yields an acceptable performance level. Generally, you do not directly control isolation levels from EJBs—the isolation level is set at the database resource level instead.


In the next section, we’ll examine the internals of transaction management and define such concepts as distributed transactions, transaction managers, and resource managers.

6.1.3. Transaction management internals

As you have probably already guessed, application servers and enterprise resources like the database management system do most of the heavy lifting in transaction management. Ultimately, everything that you do in code translates into low-level database operations such as locking and unlocking rows or tables in a database, beginning a transaction log, committing a transaction by applying log entries, or rolling back a transaction by abandoning the transaction log. In enterprise transaction management, the component that takes care of transactions for a particular resource is called a resource manager. Remember that a resource need not just be a database like Oracle. It could be a message server like IBM MQSeries or an enterprise information system (EIS) like PeopleSoft CRM.

Most enterprise applications involve only a single resource. A transaction that uses a single resource is called a local transaction. However, many enterprise applications use more than one resource. If you look carefully at our Snag-It order example, you’ll see that it most definitely involves more than one database: the credit card provider’s database used to charge the customer, as well as the ActionBazaar database to manage bids, items, and ordering. It is fairly apparent that for sane business application development some kind of abstraction is needed to manage multiple resources in a single transaction. This is exactly what the transaction manager is—a component that, under the hood, coordinates a transaction over multiple distributed resources.

From an application’s view, the transaction manager is the application server or some other external component that provides simplified transaction services. As figure 6.2 shows, the application program (ActionBazaar) asks the transaction manager to start, commit, and roll back transactions. The transaction manager coordinates these requests among multiple resource managers, and each transaction phase may translate to numerous low-level resource commands issued by the resource managers.

Figure 6.2. Distributed transaction management. The application program delegates transaction operations to the transaction manager, which coordinates between resource managers.

Next, we’ll discuss how transactions are managed across multiple resources. In EJB, this is done with two-phase commits.

6.1.4. Two-phase commit

How transactions are managed in a distributed environment involving more than one resource is extremely interesting. The protocol commonly used to achieve this is called the two-phase commit.

Imagine what would happen if no special precautions were taken while attempting to commit a transaction involving more than one database. Suppose that the first database commits successfully, but the second fails. It would be difficult to go back and “undo” the finalized changes to the first database. To avoid this problem, the two-phase commit protocol performs an additional preparatory step before the final commit. During this step, each resource manager involved is asked if the current transaction can be successfully committed. If any of the resource managers indicate that the transaction cannot be committed if attempted, the entire transaction is abandoned (rolled back). Otherwise, the transaction is allowed to proceed and all resource managers are asked to commit. As table 6.1 shows, only distributed transactions use the two-phase commit protocol.

Table 6.1. A transaction may be either local or global. A local transaction involves one resource and a global transaction involves multiple resources.

Property

Local

Global Transaction

Number of resources

One

Multiple

Coordinator

Resource Manager

Transaction manager

Commit protocol

Single-Phase

Two-phase

We have just reviewed how transactions work and what makes them reliable; now let’s take a look at how EJB provides these services for the application developer.


The XA protocol

To coordinate the two-phase commit across many different kinds of resources, the transaction manager and each of the resource managers must “talk the same tongue,” or use a common protocol. In the absence of such a protocol, imagine how sophisticated even a reasonably effective transaction manager would have to be. The transaction manager would have to be developed with the proprietary communication protocol of every supported resource.

The most popular distributed transaction protocol used today is the XA protocol, which was developed by the X/Open group. Java EE uses this protocol for implementing distributed transaction services.


6.1.5. Transaction management in EJB

Transaction management support in EJB is provided through the Java Transaction API (JTA). JTA is a small, high-level API exposing functionality at the distributed transaction manager layer, typically provided by the application server. As a matter of fact, for the most part, as an EJB developer you will probably need to know about only one JTA interface: javax.transaction.UserTransaction. This is because the container takes care of most transaction management details behind the scenes. As an EJB developer, you simply tell the container where the transaction begins and ends (called transaction demarcation or establishing transaction boundaries) and whether to roll back or commit.

There are two ways of using transactions in EJB. Both provide abstractions over JTA, one to a lesser and one to a greater degree. The first is to declaratively manage transactions through container-managed transaction (CMT); this can be done through annotations or the deployment descriptor. On the other hand, bean-managed transaction (BMT) requires you to explicitly manage transactions programmatically. It is important to note that in this version of EJB, only session beans and MDBs support BMT and CMT. The EJB 3 Java Persistence API is not directly dependent on either CMT or BMT but can transparently plug into any transactional environment while used inside a Java EE container. We’ll cover this functionality when we discuss persistence in upcoming chapters. In this chapter, we’ll explore CMT and BMT as they pertain to the two bean types we discussed in chapter 3 (session beans) and chapter 4 (MDBs).


JTS vs. JTA

These like-sounding acronyms are both related to Java EE transaction management. JTA defines application transaction services as well as the interactions among the application server, the transaction manager, and resource managers. Java Transaction Service (JTS) deals with how a transaction manager is implemented. A JTS transaction manager supports JTA as its high-level interface and implements the Java mapping of the OMG Object Transaction Service (OTS) specification as its low-level interface.

As an EJB developer, there really is no need for you to deal with JTS.


Container-managed transactions are by far the simplest and most flexible way of managing EJB transactions. We’ll take a look at them first.

6.2. Container-managed transactions

In a CMT, the container starts, commits, and rolls back a transaction on our behalf. Transaction boundaries in declarative transactions are always marked by the start and end of EJB business methods. More precisely, the container starts a JTA transaction before a method is invoked, invokes the method, and depending on what happened during the method call, either commits or rolls back the managed transaction. All we have to do is tell the container how to manage the transaction by using either annotations or deployment descriptors and ask it to roll back the transaction when needed. By default, the container assumes that you will be using CMT on all business methods. This section describes CMT in action. We’ll build the Snag-It ordering system using CMT and you’ll learn how to use the @TransactionManagement and @TransactionAttribute annotations. Also, you’ll learn both how to roll back a transaction using methods of EJBContext and when an application exception is raised.

6.2.1. Snag-It ordering using CMT

Listing 6.1 implements the Snag-It ordering scenario as the method of a stateless session bean using CMT. This is fine since the user can order only one item at a time using the Snag-It feature and no state information has to be saved between calls to the OrderManagerBean. The bean first checks to see if there are any bids on the item, and if there are none, it validates the customer’s credit card, charges the customer, and removes the item from bidding. To keep the code sample as simple as possible, we’ve omitted all details that are not directly necessary for our explanation of CMT.

Listing 6.1. Implementing Snag-It using CMT

First we tell the container that it should manage the transactions for this bean . If we do not specify the @TransactionManagement annotation or the transactiontype element in the deployment descriptor, the container assumes that we intend to use a CMT. The EJB context is injected into the bean . A transaction is required for the placeSnagItOrder method and one should be started by the container when needed. If an exception stops us from completing the Snag-It order, we ask the container to roll back the transaction using the injected EJBContext object’s setRollbackOnly method .

Let’s take a closer look at the TransactionManagement annotation .

6.2.2. The @TransactionManagement annotation

The @TransactionManagement annotation specifies whether CMT or BMT is to be used for a particular bean. In our case, we specify the value TransactionManagementType.CONTAINER—meaning the container should manage transactions on the bean’s behalf. If we wanted to manage transactions programmatically instead, we’d specify TransactionManagementType.BEAN for the TransactionManagement value. Notably, although we have explicitly included the annotation in our example, if we leave it out the container will assume CMT anyway. When we explore BMT, it will be more obvious why a CMT is the default and most commonly used choice.

Next, we’ll look at the second transaction-related annotation in listing 6.1: @TransactionAttribute.

6.2.3. The @TransactionAttribute annotation

Although the container does most of the heavy lifting in CMT, you still need to tell the container how it should manage transactions. To understand what this means, consider the fact that the transaction which wraps around your bean’s method could be started by the container specifically when calling your method, or it could be inherited from a client calling your method (otherwise called joining a transaction). Let’s explore this idea a little more using our example. The placeSnagItOrder method in listing 6.1 calls a number of methods such as bidsExisting, validateCredit, chargeCustomer, and removeItemFromBidding. As figure 6.3 shows, these method calls could simply be forwarded to other session bean invocations, such as BidManagerBean.bidsExist, BillingManagerBean.validateCredit, BillingManagerBean.chargeCustomer, and ItemManagerBean.removeFromBidding.

Figure 6.3. The method invocations from the CMT session bean is actually forwarded to other session beans that may be using various transaction attributes.

We already know that the placeSnagItOrder method is managed by a transaction. What if all the session beans we are invoking are also managed by CMT? Should the container reuse the transaction created for our method to invoke the other methods? Should our existing transaction be independent of the other session beans’ transactions? What happens if any of the methods cannot support transactions? The @TransactionAttribute annotation tells the container how to handle all these situations. The annotation can be applied either to individual CMT bean methods or to the entire bean. If the annotation is applied at the bean level, all business methods in the bean inherit the transaction attribute value specified by it. In listing 6.1, we specify that the value of the @TransactionAttribute annotation for the placeSnagItOrder method should be TransactionAttributeType.REQUIRED. There are six choices for this annotation defined by the enumerated type TransactionAttributeType. Table 6.2 summarizes their behavior.

Table 6.2. Effects of transaction attributes on EJB methods

Transaction Attribute

Caller Transaction Exists?

Effect

REQUIRED

No

Container creates a new transaction.

 

Yes

Method joins the caller’s transaction.

REQUIRES_NEW

No

Container creates a new transaction.

 

Yes

Container creates a new transaction and the caller’s transaction is suspended.

SUPPORTS

No

No transaction is used.

 

Yes

Method joins the caller’s transaction.

MANDATORY

No

javax.ejb.EJBTransactionRequiredException is thrown.

 

Yes

Method joins the caller’s transaction.

NOT_SUPPORTED

No

No transaction is used.

 

Yes

The caller’s transaction is suspended and the method is called without a transaction.

NEVER

No

No transaction is used.

 

Yes

javax.ejb.EJBException is thrown.

Let’s take a look at what each value means and where each is applicable.

REQUIRED

REQUIRED is the default and most commonly applicable transaction attribute value. This value specifies that the EJB method must always be invoked within a transaction. If the method is invoked from a nontransactional client, the container will start a transaction before the method is called and finish it when the method returns. On the other hand, if the caller invokes the method from a transactional context, the method will join the existing transaction. In case of transactions propagated from the client, if our method indicates that the transaction should be rolled back, the container will not only roll back the whole transaction but will also throw a javax.transaction.RollbackException back to the client. This lets the client know that the transaction it started has been rolled back by another method. Our placeSnagItOrder method is most likely invoked from a nontransactional web tier. Hence, the REQUIRED value in the @TransactionAttribute annotation will cause the container to create a brand-new transaction for us when the method is executed. If all the other session bean methods we invoke from our bean are also marked REQUIRED, when we invoke them they will join the transaction created for us. This is just fine, since we want the entire ordering action to be covered by a single “umbrella” transaction. In general, you should use the REQUIRED value if you are modifying any data in your EJB method and you aren’t sure whether the client will start a transaction of its own before calling your method.

REQUIRES_NEW

The REQUIRES_NEW value indicates that the container must always create a new transaction to invoke the EJB method. If the client already has a transaction, it is temporarily suspended until our method returns. This means that the success or failure of our new transaction has no effect on the existing client transaction. From the client’s perspective:

  1. Its transaction is paused.
  2. Our method is invoked.
  3. Our method either commits or rolls back its own transaction.
  4. The client’s transaction is resumed as soon as our method returns.

The REQUIRES_NEW attribute has limited uses in the real world. You should use it if you need a transaction but don’t want a rollback to affect the client. Also use this value when you don’t want the client’s rollback to affect you. Logging is a great example. Even if the parent transaction rolls back, you want to be able to record the failure in your logs. On the other hand, failing to log a minor debugging message should not roll back your entire transaction and the problem should be localized to the logging component.

SUPPORTS

The SUPPORTS attribute essentially means the EJB method will inherit whatever the transactional environment of the caller is. If the caller does not have a transaction, the EJB method will be called without a transaction. On the other hand, if the caller is transactional, the EJB method will join the existing transaction and won’t cause the exiting transaction to be suspended. This approach avoids any needless overhead in suspending or resuming the client transaction. The SUPPORTS attribute is typically useful for methods that perform read-only operations such as retrieving a record from a database table. In our Snag-It example, the session bean method for checking whether a bid exists on the item about to be ordered can probably have a SUPPORTS attribute since it modifies no data.

MANDATORY

MANDATORY really means requires existing— that is, the caller must have a transaction before calling an EJB method and the container should never create a transaction on behalf of the client. If the EJB method using the MANDATORY attribute is invoked from a nontransactional client, the container throws an EJBTransactionRequiredException. This value is also very rarely used. You should use this value if you want to make sure the client fails if you request a rollback. We can make a reasonable case to require a MANDATORY transaction on a session bean method that charges the customer. After all, we want to make sure nothing is accidentally given away for free if the client neglects to detect a failure in the method charging the customer, and the invoker’s transaction can be forcibly rolled back by us when necessary.

NOT_SUPPORTED

If we assign NOT_SUPPORTED as the transaction attribute, the EJB method cannot be invoked in a transactional context. If a caller with an associated transaction invokes the method, the container will suspend the transaction, invoke the method, and then resume the transaction when the method returns. This attribute is typically useful only for an MDB supporting a JMS provider in nontransactional, auto-acknowledge mode. To recap from chapter 5, in such cases the message is acknowledged as soon as it is successfully delivered and the MDB has no capability or apparent need to support rolling back message delivery.

NEVER

In a CMT, NEVER really means never. In other words, this attribute means that the EJB method can never be invoked from a transactional client. If such an attempt is made, a javax.ejb.EJBException is thrown. This is probably the least-used transaction attribute value. It could be used if your method is changing a nontransactional resource (such as a text file) and you want to make sure the client knows about the nontransactional nature of the method.

Transaction attributes and MDBs

As we mentioned in chapter 4, MDBs don’t support all of the six transaction attributes we just discussed. Although you can apply any of the attributes to a stateful or stateless session bean, MDBs only support the REQUIRED and NOT_SUPPORTED attributes. This relates to the fact that no client ever invokes MDB methods directly; it is the container that invokes MDB methods when it receives an incoming message. Since there is no existing client transaction to suspend or join, REQUIRES_NEW, SUPPORTS, and MANDATORY make no sense (refer to table 6.2). NEVER makes no sense either, since we don’t need that strong a guard against the container. In effect, depending on message acknowledgment on method return, we need only tell the container of two conditions: we need a transaction (REQUIRED) that encapsulates the message listener method, or we do not need transaction support (NOT_SUPPORTED).

So far, we’ve taken a detailed look at how transactions are created and managed by the container. We know that the successful return of a CMT method causes the container to commit a method or at least not roll it back if it is a joined transaction. We’ve explained how a CMT method can mark an available transaction as rolled back, but we’ve not yet discussed the actual mechanics. Let’s dig into the underpinnings next.

6.2.4. Marking a CMT for rollback

If the appropriate business conditions arise, a CMT method can ask the container to roll back a transaction as soon as possible. The important thing to note here is that the transaction is not rolled back immediately, but a flag is set for the container to do the actual rollback when it is time to end the transaction. Let’s go back to a snippet of our scenario in listing 6.1 to see exactly how this is done:

@Resource
private SessionContext context;
...
public void placeSnagItOrder(Item item, Customer customer){
try {
...
validateCredit(customer);
...
} catch (CreditValidationException cve) {
context.setRollbackOnly();
...

As this snippet shows, the setRollbackOnly method of the injected javax.ejb.EJBContext marks the transaction to be rolled back when we are unable to validate the user’s credit card, a CreditValidationException is thrown, and we cannot allow the order to complete. If you go back and look at the complete listing, we do the same thing to head off other serious problems, such as the database server goes down or if we have trouble charging the credit card.

To keep things simple, assume that the container starts a new transaction because the placeSnagItOrder method is invoked from a nontransactional web tier. This means that after the method returns, the container will check to see if it can commit the transaction. Since we set the rollback flag for the underlying transaction through the setRollbackOnly method, the container will roll back instead. Because the EJB context in this case is really a proxy to the underlying transaction, you should never call the setRollbackOnly method unless you are sure there is an underlying transaction to flag. Typically, you can only be sure of this fact if your method has a REQUIRED, REQUIRES_NEW, or MANDATORY transaction attribute. If your method is not invoked in a transaction context, calling this method will throw java.lang.IllegalStateException.

Another EJBContext method you should know about is getRollbackOnly. The method returns a boolean telling you whether the underlying CMT transaction has already been marked for rollback.


Note

The setRollbackOnly and getRollbackOnly methods can only be invoked in an EJB using CMT with these transaction attributes: REQUIRED, REQUIRES_NEW, or MANDATORY. Otherwise, the container will throw an IllegalStateException.


If you suspect that this method is used very infrequently, you are right. There is one case in particular when it is useful to check the status of the transaction you are participating in: before engaging in a very long, resource-intense operation. After all, why expend all that effort for something that is already going to be rolled back? For example, let’s assume that ActionBazaar checks a potential Power Seller’s creditworthiness before approving an account. Since this calculation involves a large set of data collection and business intelligence algorithms that potentially involve third parties, it is undertaken only if the current transaction has not already been rolled back. The code could look like this:

@Resource
private SessionContext context;
... checkCreditWorthiness(Seller seller) { ...
if (!context.getRollbackOnly()) {
DataSet data = getDataFromCreditBureauRobberBarons(seller);
runLongAndConvolutedBusinessAnalysis(seller, data);
...
} ...

If the model of catching exceptions just to call the setRollbackOnly method seems a little cumbersome, you’re in luck. EJB 3 makes the job of translating exceptions into transaction rollback almost transparent using the ApplicationException paradigm. We’ll examine the role of exception handling in transaction management next.

6.2.5. Transaction and exception handling

The subject of transactions and exception handling in EJB 3 is intricate and often confusing. However, properly used, exceptions used to manage transactions can be extremely elegant and intuitive.

To see how exceptions and transactions work together, let’s revisit the exception-handling code in the placeSnagItOrder method:

try {
// Ordering code throwing exceptions.
if (!bidsExisting(item)){
validateCredit(customer);
chargeCustomer(customer, item);
removeItemFromBidding(item);
}
} catch (CreditValidationException cve) {
context.setRollbackOnly();
} catch (CreditProcessingException cpe){
context.setRollbackOnly();
} catch (DatabaseException de) {
context.setRollbackOnly();
}

As you can see, the CreditValidationException, CreditProcessingException, and DatabaseException exceptions being thrown are essentially the equivalent of the managed transaction being rolled back. To avoid this all-too-common mechanical code, EJB 3 introduces the idea of controlling transactional outcome through the @javax.ejb.ApplicationException annotation. The best way to see how this works is through an example. Listing 6.2 reimplements the placeSnagItOrder method using the @ApplicationException mechanism to roll back CMTs.

Listing 6.2. Using @ApplicationException to roll back CMTs

The first change from listing 6.1 you’ll notice is the fact that the try-catch blocks have disappeared and have been replaced by a throws clause in the method declaration . However, it’s a good idea for you to gracefully handle the application exceptions in the client and generate appropriate error messages. The various nested method invocations still throw the three exceptions listed in the throws clause . The most important thing to note, however, is the three @ApplicationException specifications on the custom exceptions. The @ApplicationException annotation identifies a Java checked or unchecked exception as an application exception.


Note

In EJB, an application exception is an exception that the client is expected to handle. When thrown, such exceptions are passed directly to the method invoker. By default, all checked exceptions except for java.rmi.RemoteException are assumed to be application exceptions. On the other hand, all exceptions that inherit from either java.rmi.RemoteExceptions or java.lang.RuntimeException are assumed to be system exceptions(as you might already know, all exceptions that inherit from java.lang.RuntimeException are unchecked). In EJB, it is not assumed that system exceptions are expected by the client. When encountered, such exceptions are not passed to the client as is but are wrapped in a javax.ejb.EJBException instead.


In listing 6.2, the @ApplicationException annotations on CreditValidationException and CreditProcessingException do not change this default behavior since both would have been assumed to be application exceptions anyway. However, by default, DatabaseException would have been assumed to be a system exception. Applying the @ApplicationException annotation to it causes it to be treated as an application exception instead.


Session synchronization

Although using CMT doesn’t give you full control over when a transaction is started, committed, or rolled back, you can be notified about the transaction’s lifecycle events. This is done simply by having your CMT bean implement the javax.ejb.SessionSynchronization interface. This interface defines three methods:

  • void afterBegin() Called right after the container creates a new transaction and before the business method is invoked.
  • void beforeCompletion() Invoked after a business method returns but right before the container ends a transaction.
  • void afterCompletion(boolean committed) Called after the transaction finishes. The boolean committed flag indicates whether a method was committed or rolled back.

Implementing this interface in a stateful session bean can be considered close to having a poor man’s persistence mechanism, because data can be loaded into the bean when the transaction starts and unloaded right before the transaction finishes, while the afterCompletion callback can be used to reset default values. However, you can make a valid argument that since session beans are supposed to model processes, if it makes sense to cache some data and synchronize with the database as a natural part of a process, then this practice is just fine, if not fairly elegant.

Note this facility doesn’t make much sense in a stateless session bean or MDB where data should not be cached anyway; therefore, the interface is not supported for those bean types.


More than the @ApplicationException annotation itself, the rollback element changes default behavior in profound ways. By default, application exceptions do not cause an automatic CMT rollback since the rollback element is defaulted to false. However, setting the element to true tells the container that it should roll back the transaction before the exception is passed on to the client. In listing 6.2, this means that whenever a CreditValidationException, CreditProcessingException, or DatabaseException is thrown, the transaction will be rolled back and the client will receive an exception indicating the cause for failure, accomplishing exactly the same thing as the more verbose code in listing 6.1 aims to do. If the container detects a system exception, such as an ArrayIndexOutOfBounds or NullPointerException that you didn’t plan for, it will still roll back the CMT. However, in such cases the container will also assume that the bean is in inconsistent state and will destroy the instance. Because unnecessarily destroying bean instances is costly, you should never deliberately use system exceptions.

Although the simplified code is very tempting, we recommend that you use application exceptions for CMT rollback carefully. Using the setRollbackOnly method, however verbose, removes the guesswork from automated transaction management, especially for junior developers who might have a hard time understanding the intricacies of exception handling in EJB. However, don’t interpret this to mean you should avoid using custom application exceptions in general. In fact, we encourage the use of this powerful and intuitive errror-handling mechanism widely used in the Java realm.

As you can clearly see, CMT relieves you from all but the most unavoidable details of EJB transaction management. However, for certain circumstances, CMT may not give you the level of control you need. BMT gives you this additional control while still providing a powerful, high-level API, as you’ll see next.

6.3. Bean-managed transactions

The greatest strength of CMT is also its greatest weakness. Using CMT, you are limited to having the transaction boundaries set at the beginning and end of business methods and relying on the container to determine when a transaction starts, commits, or rolls back. BMT, on the other hand, allows you to specify exactly these details programmatically, using semantics similar to the JDBC transaction model with which you might already be familiar. However, even in this case, the container helps you by actually creating the physical transaction as well as taking care of a few low-level details. With BMT, you must be much more aware of the underlying JTA transaction API, primarily the javax.transaction.UserTransaction interface, which we’ll introduce shortly. But first, we’ll redevelop the Snag-It ordering code in BMT so that we can use it in the next few sections. You’ll learn more about the javax.transaction.UserTransaction interface and how to use it. We’ll also discuss the pros and cons of using BMT over CMT.

6.3.1. Snag-It ordering using BMT

Listing 6.3 reimplements the code in listing 6.1 using BMT. It checks if there are any bids on the item ordered, validates the user’s credit card, charges the customer, and removes the item from bidding. Note that the import statements are omitted and error handling trivialized to keep the code sample short.

Listing 6.3. Implementing Snag-It using BMT

Briefly scanning the code, you’ll note that the @TransactionManagement annotation specifies the value TransactionManagementType.BEAN as opposed to TransactionManagementType.CONTAINER, indicating that we are using BMT this time . The TransactionAttribute annotation is missing altogether since it is applicable only for CMT. A UserTransaction, the JTA representation of a BMT, is injected and used explicitly to begin , commit , or roll back a transaction. The transaction boundary is much smaller than the entire method and includes only calls that really need to be atomic. The sections that follow discuss the code in greater detail, starting with getting a reference to the javax.transaction.UserTransaction.

6.3.2. Getting a UserTransaction

The UserTransaction interface encapsulates the basic functionality provided by a Java EE transaction manager. JTA has a few other interfaces used under different circumstances. We won’t cover them, as most of the time you’ll be dealing with UserTransaction. (For full coverage of JTA, check out http://java.sun.com/products/jta/.) As you might expect, the UserTransaction interface is too intricate under the hood to be instantiated directly and must be obtained from the container. In listing 6.3, we used the simplest way of getting a UserTransaction: injecting it through the @Resource annotation. There are a couple of other ways to do this: using JNDI lookup or through the EJBContext.

JNDI lookup

The application server binds the UserTransaction to the JNDI name java:comp/UserTransaction. You can look it up directly using JNDI with this code:

Context context = new InitialContext();
UserTransaction userTransaction =
(UserTransaction) context.lookup("java:comp/UserTransaction");
userTransaction.begin();
// Perform transacted tasks.
userTransaction.commit();

This method is typically used outside of EJBs—for example, if you need to use a transaction in a helper or a nonmanaged class in the EJB or web tier where dependency injection is not supported. If you find yourself in this situation, you might want to think long and hard about moving the transactional code to an EJB where you have access to greater abstractions.

EJBContext

You can also get a UserTransaction by invoking the getUserTransaction method of the EJBContext. This approach is useful if you’re using a SessionContext or MessageDrivenContext for some other purpose anyway, and a separate injection just to get a transaction instance would be redundant. Note that you can only use the getUserTransaction method if you’re using BMT. Calling this in a CMT environment will cause the context to throw an IllegalStateException. The following code shows the getUserTransaction method in action:

@Resource
private SessionContext context;
...
UserTransaction userTransaction = context.getUserTransaction();
userTransaction.begin();
// Perform transacted tasks.
userTransaction.commit();

On a related but relevant note, you cannot use the EJBContext getRollbackOnly and setRollbackOnly methods in BMT, and the container will throw an IllegalStateException if accessed. Next, let’s see how the obtained UserTransaction interface is actually used.

6.3.3. Using UserTransaction

You’ve already seen the UserTransaction interface’s most frequently used methods: begin, commit, and rollback. The UserTransaction interface has a few other useful methods we should take a look at as well. The definition of the entire interface looks like this:

public interface UserTransaction {
void begin() throws NotSupportedException, SystemException;
void commit() throws RollbackException,
HeuristicMixedException, HeuristicRollbackException, SecurityException,
IllegalStateException, SystemException;

void rollback()
throws IllegalStateException,
SecurityException, SystemException;

void setRollbackOnly() throws IllegalStateException,
SystemException;

int getStatus() throws SystemException;
void setTransactionTimeout(int seconds) throws SystemException;
}

The begin method creates a new low-level transaction behind the scenes and associates it with the current thread. You might be wondering what would happen if you called the begin method twice before calling rollback or commit. You might think this is possible if you want to create a nested transaction, a paradigm supported by some transactional systems. In reality, the second invocation of begin would throw a NotSupportedException since Java EE doesn’t support nested transactions. The commit and rollback methods, on the other hand, remove the transaction attached to the current thread by using the begin method. While commit sends a “success” signal to the underlying transaction manager, rollback abandons the current transaction. The setRollbackOnly method on this interface might be slightly counterintuitive as well. After all, why bother marking a transaction as rolled back when you can roll it back yourself?

To understand why, consider the fact that we could call a CMT method from our BMT bean that contains a lengthy calculation and checks the transactional flag before proceeding (like our Power Seller credit validation example in section 6.2.4). Since our BMT transaction would be propagated to the CMT method, it might be programmatically simpler, especially in a long method, to mark the transaction rolled back using the setRollbackOnly method instead of writing an involved if-else block avoiding such conditions. The getStatus method is a more robust version of getRollbackOnly in the CMT world. Instead of returning a boolean, this method returns an integer-based status of the current transactions, indicating a more fine-tuned set of states a transaction could possibly be in. The javax.transaction.Status interface defines exactly what these states are, and we list them in table 6.3.

Table 6.3. The possible values of the javax.transaction.Status interface. These are the status values returned by the UserTransaction.getStatus method.

Status

Description

STATUS_ACTIVE

The associated transaction is in an active state.

STATUS_MARKED_ROLLBACK

The associated transaction is marked for rollback, possibly due to invocation of the setRollbackOnly method.

STATUS_PREPARED

The associated transaction is in the prepared state because all resources have agreed to commit (refer to the two-phase commit discussion in section 6.1.4).

STATUS_COMMITTED

The associated transaction has been committed.

STATUS_ROLLEDBACK

The associated transaction has been rolled back.

STATUS_UNKNOWN

The status for associated transaction is not known (very clever, don’t you agree?).

STATUS_NO_TRANSACTION

There is no associated transaction in the current thread.

STATUS_PREPARING

The associated transaction is preparing to be committed and awaiting response from subordinate resources (refer to the two-phase commit discussion in section 6.1.4).

STATUS_COMMITTING

The transaction is in the process of committing.

STATUS_ROLLING_BACK

The transaction is in the process of rolling back.

The setTransactionTimeout method specifies the time, in seconds, in which a transaction must finish. The default transaction timeout value is set to different values for different application servers. For example, JBoss has a default transaction timeout value of 300 seconds whereas Oracle Application Server 10g has a default transaction timeout value of 30 seconds. You might want to use this method if you’re using a very long-running transaction. Typically, it is better to simply set the application server-wide defaults using vendor-specific interfaces, however. At this point, you are probably wondering how to set a transaction timeout when using CMT instead. This is only supported by containers using either an attribute in the vendor-specific deployment descriptor or vendor-specific annotations.

Comparing listings 6.1 and 6.3, you might ask if the additional complexity and verbosity associated with BMT is really worth it. Let’s explore this issue in detail next.

6.3.4. The pros and cons of BMT

CMT is the default transaction type for EJB transactions. In general, BMT should be used sparingly because it is verbose, complex, and difficult to maintain. There are some concrete reasons to use BMT, however. BMT transactions need not begin and end in the confines of a single method call. If you are using a stateful session bean and need to maintain a transaction across method calls, BMT is your only option. Be warned, however, that this technique is complicated and error prone and you might be better off rewriting your application rather than attempting this. Can you spot a bug in listing 6.3? The last catch block did not roll back the transaction as all the other catch blocks did. But even that is not enough; what if the code throws an error (rather than an exception)? Whichever way you do it, it is error prone and we recommend using CMT instead.

Another argument for BMT is that you can fine-tune your transaction boundaries so that the data held by your code is isolated for the shortest time possible. Our opinion is that this idea indulges in premature optimization, and again, you are probably better off refactoring your methods to be smaller and more specific anyway. Another drawback for BMT is the fact that it can never join an existing transaction. Existing transactions are always suspended when calling a BMT method, significantly limiting flexible component reuse.

This wraps up our discussion of EJB transaction management. It is now time to turn our attention to another critical aspect of enterprise Java development: security.

6.4. Exploring EJB security

Securing enterprise data has always been a primary application development concern. This is especially true today in the age of sophisticated cyber-world hackers, phishers, and identity/data thieves. Consequently, security is a major concern in developing robust Java EE solutions. EJB has a security model that is elegant, flexible, and portable across heterogeneous systems.

In the remainder of this chapter, we’ll explore some basic security concepts such as authentication and authorization, users, and groups, and we’ll investigate the Java EE/EJB security framework. We’ll also take a look at both declarative and programmatic security in EJB 3.

Let’s start with two of the most basic ideas in security: authentication and authorization.

6.4.1. Authentication vs. authorization

Securing an application involves two primary functions: authentication and authorization. Authentication must be done before authorization can be performed, but as you’ll see, both are necessary aspects of application security. Let’s explore both of these concepts.

Authentication

Authentication is the process of verifying user identity. By authenticating yourself, you prove that you are who you say you are. In the real world, this is usually accomplished through visual inspection/identity cards, signature/handwriting, fingerprint checks, and even DNA tests. In the computer world, the most common method of authentication is by checking username and password. All security is meaningless if someone can log onto a system with a false identity.

Authorization

Authorization is the process of determining whether a user has access to a particular resource or task, and it comes into play once a user is authenticated. In an open system, an authenticated user can access any resource. In a realistic security environment, this all-or-nothing approach would be highly ineffective. Therefore, most systems must restrict access to resources based on user identity. Although there might be some resources in a system that are accessible to all, most resources should be accessed only by a limited group of people.

Both authentication and authorization, but especially authorization, are closely tied to other security concepts, namely users, groups, and roles, which we’ll look at next.

6.4.2. Users, groups, and roles

To perform efficient and maintainable authorization, it is best if you can organize users into some kind of grouping. Otherwise, each resource must have an associated list of all the users that can access it. In a nontrivial system, this would easily become an administrator’s nightmare. To avoid this problem, users are organized into groups and groups as a whole are assigned access to resources, making the access list for an individual resource much more manageable.

The concept of role is closely related to the concept of group, but is a bit tricky to understand. For an EJB application, roles are much more critical than users and groups. To understand the distinction, consider the fact that you might not be building an in-house solution but a packaged Java EE application. Consequently, you might not know the exact operational environment your application will be deployed in once it is purchased by the customer. As a result, it’s impossible for you to code for the specific group names a customer’s system administrator will choose. Neither should you care about groups. What you do care about is what role a particular user in a group plays for your application. In the customer system, user Joe might belong to the system group called peons. Now assume that an ActionBazaar integrated B2B Enterprise Purchasing System is installed on the customer’s site. Among other things, this type of B2B installation transparently logs in all existing users from the customer system into the ActionBazaar site through a custom desktop shortcut. Once logged in, from ActionBazaar’s perspective, Joe could simply be a buyer who buys items online on behalf of the B2B customer company. To another small application in the operational environment, user Joe might be an administrator who changes system-wide settings. For each deployed application in the operational environment, it is the responsibility of the system administrator to determine what system group should be mapped to what application role. In the Java EE world, this is typically done through vendor-specific administrative interfaces. As a developer, you simply need to define what roles your application’s users have and leave the rest to the assembler or deployer. For ActionBazaar, roles can be buyers, sellers, administrators, and so on.

Let’s solidify our understanding of application security in EJB using an ActionBazaar example.

6.4.3. A security problem in ActionBazaar

At ActionBazaar, customer service representatives (CSRs) are allowed to cancel a user’s bid under certain circumstances (for example, if the seller discloses something in answer to an e-mail question from the bidder that should have been mentioned on the item description). However, the cancel bid operation doesn’t check if the user is actually a CSR as long as the user can locate the functionality on the ActionBazaar site—for example, by typing in the correct URL. Figure 6.4 illustrates the security problem in ActionBazaar.

Figure 6.4. A security breach in ActionBazaar allows a hacker to shill bids by posting an item, starting a bidding war from a fake account and then at the last minute canceling the highest fake bid. The end result is that an unsuspecting bidder winds up with an overpriced item.

A clever hacker breaks into the ActionBazaar web server logs and figures out the URL used by CSRs to cancel bids. Using this knowledge, he devises an even cleverer “shill bidding” scheme to incite users to overpay for otherwise cheap items. The hacker posts items on sale and uses a friend’s account to incite a bidding war with genuine bidders. If at any point genuine bidders give up bidding and a fake bid becomes the highest bid, the hacker avoids actually having to pay for the item and losing money in posting fees by canceling his highest fake bid through the stolen URL. No one is any wiser as the genuine bidders as well as the ActionBazaar system think the highest bid was canceled for legitimate reasons. The end result is that an honest bidder is fooled into overpaying for otherwise cheap items.

After a while, ActionBazaar customer service finally catches onto the scheme thanks to a few observant users and makes sure the bid canceling action is authorized for CSRs only. Now if a hacker tries to access the functionality, the system simply denies access, even if the hacker has a registered ActionBazaar account and accesses the functionality through the URL or otherwise. As we discuss how security is managed by EJB in the next section, you will begin to see what an actual solution looks like.

6.4.4. EJB 3 and Java EE security

Java EE security is largely based on the Java Authentication and Authorization Service (JAAS) API. JAAS essentially separates the authentication system from the Java EE application by using a well-defined, pluggable API. In other words, the Java EE application need only know how to talk to the JAAS API. The JAAS API, in contrast, knows how to talk to underlying authentication systems like Lightweight Directory Access Protocol (LDAP), such as Microsoft Active Directory or Oracle Internet Directory (OID) using a vendor plug-in. As a result, you can easily swap between authentication systems simply by swapping JAAS plug-ins without changing any code. In addition to authentication, the application server internally uses JAAS to perform authorization for both the web and EJB tiers. When we look at programmatic EJB security management, we’ll directly deal with JAAS very briefly when we discuss the JAAS javax.security.Principal interface. Feel free to explore JAAS at http://java.sun.com/products/jaas/ since our discussion is limited to what is needed for understanding EJB security.

JAAS is designed so that both the authentication and authorization steps can be performed at any Java EE tier, including the web and EJB tiers. Realistically, however, most Java EE applications are web accessible and share an authentication system across tiers, if not across the application server. JAAS fully leverages this reality and once a user (or entity, to use a fancy security term) is authenticated at any Java EE tier, the authentication context is passed through tiers whenever possible, instead of repeating the authentication step. The Principal object we already mentioned represents this sharable, validated authentication context. Figure 6.5 depicts this common Java EE security management scenario.

As shown in figure 6.5, a user enters the application through the web tier. The web tier gathers authentication information from the user and authenticates the supplied credentials using JAAS against an underlying security system. A successful authentication results in a valid user Principal. At this point, the Principal is associated with one or more roles. For each secured web/EJB tier resource, the application server checks if the principal/role is authorized to access the resource. The Principal is transparently passed from the web tier to the EJB tier as needed.

Figure 6.5. Most common Java EE security management scenario using JAAS

A detailed discussion of web tier authentication and authorization is beyond the scope of this book, as is the extremely rare scenario of standalone EJB authentication using JAAS. However, we’ll give you a basic outline of web tier security to serve as a starting point for further investigation before diving into authorization management in EJB 3.

Web tier authentication and authorization

The web tier servlet specification (http://java.sun.com/products/servlet/) successfully hides a great many low-level details for both authentication and authorization. As a developer, you simply need to tell the servlet container what resources you want secured, how they are secured, how authentication credentials are gathered, and what roles have access to secured resources. The servlet container, for the most part, takes care of the rest. Web tier security is mainly configured using the login-config and security-constraint elements of the web.xml file. Listing 6.4 shows how securing the administrative module of ActionBazaar might look using these elements.

Listing 6.4. Sample web.xml elements to secure order canceling and other ActionBazaar admin functionality

Listing 6.4 specifies how the web container should gather and validate authentication . In our case, we have chosen the simplest authentication mechanism, BASIC. BASIC authentication uses an HTTP header–based authentication scheme that usually causes the web browser to gather username/password information using a built-in prompt. Other popular authentication mechanisms include FORM and CLIENT-CERT. FORM is essentially the same as BASIC except for the fact that the prompt used is an HTML form that you create. CLIENT-CERT, on the other hand, is an advanced form of authentication that bypasses username/password prompts altogether. In this scheme, the client sends a public key certificate stored in the client browser to the web server using Secured Socket Layer (SSL) and the server authenticates the contents of the certificate instead of a username/password. The credentials are then validated by the JAAS provider.

Next we specify the realm the container should authenticate against . A realm is essentially a container-specific abstraction over a JAAS-driven authentication system. We then specify that all URLs that match the pattern /admin/* should be secured . Finally, we specify that only validated principals with the CSR role can access the secured pages . In general, this is all there really is to securing a web application using JAAS, unless you choose to use programmatic security, which essentially follows the same pattern used in programmatic EJB security.

EJB authentication and authorization

At the time of writing, authenticating and accessing EJBs from a standalone client without any help from the servlet container is still a daunting task that requires you to thoroughly understand JAAS. In effect, you’d have to implement all of the authentication steps that the servlet container nicely abstracts away from you. Thankfully, this task is not undertaken very often and most application servers provide a JAAS login module that can be used by applications.

On the other hand, the authorization model in EJB 3 is simple yet powerful. Much like authorization in the web tier, it centers on the idea of checking whether the authenticated Principal is allowed to access an EJB resource based on the Principal’s role. Like transaction management, authentication can be either declarative or programmatic, each of which provides a different level of control over the authentication process. In addition, like the transaction management features discussed in this chapter, security applies to session beans and MDBs, and not the JPA entities.

We’ll first explore declarative security management by coding our bid-canceling scenario presented in 6.4.3 and then move on to exploring programmatic security management.

6.4.5. Declarative security

Listing 6.5 applies authentication rules to the BidManagerBean that includes the cancelBid method our clever hacker used for his shill-bidding scheme. Now, only CSRs are allowed to use this method. Note that we have omitted method implementation since this is not relevant to our discussion.

Listing 6.5. Securing bid cancellation using declarative security management

Listing 6.5 features some of the most commonly used security annotations defined by common metadata annotations for Java Platform Specification JSR-250, javax.annotation.security.DeclareRoles, javax.annotation.security.RolesAllowed, and javax.annotation.security.PermitAll. Two other annotations that we have not used but will discuss are javax.annotation.security.DenyAll and javax.annotation.security.RunAs. Let’s start our analysis of the code and security annotations with the @DeclareRoles annotation.

Declaring roles

We highly recommend that you declare the security roles to be employed in your application, EJB module, EJB, or business methods. There are a few ways of declaring roles, one of which is through the @DeclareRoles annotation, which we use in listing 6.5 . This annotation applies at either the method or the class level and consists of an array of role names. We are specifying that the BidManagerBean use the roles of BIDDER, CSR, and ADMIN. Alternatively, we could have specified roles for the entire enterprise application or EJB module through deployment descriptors. The ActionBazaar application could use the roles of guests, bidders, sellers, Power Sellers, CSRs, admins, and so on. If we never declare roles, the container will automatically build a list of roles by inspecting the @RolesAllowed annotation. Remember, when the application is deployed, the local system administrator must map each role to groups defined in the runtime security environment.

Specifying authenticated roles

The @RolesAllowed annotation is the crux of declarative security management. This annotation can be applied to either an EJB business method or an entire class. When applied to an entire EJB, it tells the container which roles are allowed to access any EJB method. On the other hand, we can use this annotation on a method to specify the authentication list for that particular method. The tremendous flexibility offered by this annotation becomes evident when you consider the fact that you can override class-level settings by reapplying the annotation at the method level (for example, to restrict access further for certain methods). However, we discourage such usage because at best it is convoluted and at worst it can cause subtle mistakes that are hard to discern. In listing 6.5, we specify that only CSR and ADMIN roles be allowed to cancel bids through the cancelBid method . The @PermitAll and @DenyAll annotations are conveniences that perform essentially the same function as the @RolesAllowed annotation.

@PermitAll and @DenyAll

We can use the @PermitAll annotation to mark an EJB class or a method to be invoked by any role. We use this annotation in listing 6.5 to instruct the container that any user can retrieve the current bids for a given item. You should use this annotation sparingly, especially at the class level, as it is possible to inadvertently leave security holes if it is used carelessly. The @DenyAll annotation does exactly the opposite of @PermitAll. That is, when used at either the class or the method level, it renders functionality inaccessible by any role. You might be wondering why you would ever use this annotation. Well, the annotation makes sense when you consider the fact that your application may be deployed in wide-ranging environments that you did not envision. You can essentially invalidate methods or classes that might be inappropriate for a particular environment without changing code by using the @DenyAll annotation. Just as with the @RolesAllowed annotation, when applied at the method level these annotations will override bean-level authorization settings.


Note

The three security annotations, @PermitAll, @DenyAll, and @RoleAllowed, cannot simultaneously be applied to the same class or the same method.


Let’s now wrap up our discussion of declarative security management by discussing our final annotation, @RunAs.

@RunAs

The @RunAs annotation comes in handy if you need to dynamically assign a new role to the existing Principal in the scope of an EJB method invocation. You might need to do this, for example, if you’re invoking another EJB within your method but the other EJB requires a role that is different from the current Principal’s role. Depending on the situation, the new “assumed” role might be either more restrictive, lax, or neither. For example, the cancelBid method in listing 6.5 might need to invoke a statistics-tracking EJB that manages historical records in order to delete the statistical record of the canceled bid taking place. However, the method for deleting a historical record might require an ADMIN role. Using the @RunAs annotation, we can temporarily assign a CSR an ADMIN role so that the statistics-tracking EJB thinks an admin is invoking the method:

@RunAS("ADMIN")
@RolesAllowed("CSR")
public class BidManagerBean implements BidManager{
public void cancelBid(Bid bid, Item item) {...}
}

You should use this annotation sparingly since like the @PermitAll annotation, it can open up security holes you might not have foreseen.

As you can see, declarative security gives you access to a powerful authentication framework while staying mostly out of the way. The flexibility available to you through the relatively small number of relevant annotations should be apparent as well. If you have ever rolled out your own security or authentication system, one weakness might have crossed your mind already. The problem is that although you can authenticate a role using declarative security, what if you need to provide security settings specific to individuals, or even simple changes in method behavior based on the current Principal’s role? This is where programmatic EJB security steps onto the stage.

6.4.6. Using EJB programmatic security

In effect, programmatic security provides direct access to the Principal as well as a convenient means to check the Principal’s role in the code. Both of these functions are made available through the EJB context. We’ll begin exploring programmatic security by redeveloping the bid-canceling scenario as a starting point. Listing 6.6 implements the scenario.

Listing 6.6. Securing bid cancellation using programmatic security

Listing 6.6 first injects the EJB context . We use the isCallerInRole method of the EJBContext to see if the underlying authenticated principal has the CSR role . If it does not, we throw a java.lang.SecurityException notifying the user about the authorization violation . Otherwise, the bid cancellation method is allowed to proceed normally. We discuss both the security management related methods provided in the EJB context next, namely isCallerInRole and getCallerPrincipal.

isCallerInRole and getCallerPrincipal

Programmatic security is made up solely of the two previously mentioned security-related methods. The methods are defined in the javax.ejb.EJBContext interface as follows:

public interface EJBContext {
...
public java.security.Principal getCallerPrincipal();
public boolean isCallerInRole(java.lang.String roleName);
...
}

You’ve already seen the isCallerInRole method in action; it is fairly self-explanatory. Behind the scenes, the EJB context retrieves the Principal associated with the current thread and checks if any of its roles match the name you provided. The getCallerPrincipal method gives you direct access to the java. security.Principal representing the current authentication context. The only method of interest in the Principal interface is getName, which returns the name of the Principal. Most of the time, the name of the Principal is the login name of the validated user. This means that just as in the case of a homemade security framework, you could validate the individual user if you needed to.

For example, let’s assume that we had a change of heart and decided that in addition to the CSRs, bidders can cancel their own bids as long as the cancellation is done within a minute of putting in the bid. We could implement this using the getCallerPrincipal method as follows:

public void cancelBid(Bid bid, Item item) {
if (!context.isCallerInRole("CSR")
&& !(context.getCallerPrincipal().getName().equals(
bid.getBidder().getUsername()) && (bid.getTimestamp() >=
(getCurrentTime() - 60*1000))))) {
throw new SecurityException(
"No permissions to cancel bid");
}
...
}

Note, though, that there is no guarantee exactly what the Principal name might return. In some environments, it can return the role name, group name, or any other arbitrary String that makes sense for the authentication system. Before you use the Principal.getName method, you should check the documentation of your particular security environment. As you can see, the one great drawback of programmatic security management is the intermixing of security code with business logic as well as the potential hard-coding of role and Principal names. In previous versions of EJB, there was no way of getting around these shortfalls. However, in EJB 3 you can alleviate this problem somewhat by using interceptors. Let’s see how to accomplish this next.

Using interceptors for programmatic security

As you know, in EJB 3 you can set up interceptors that are invoked before and after (around) any EJB business method. This facility is ideal for crosscutting concerns that should not be duplicated in every method, such as programmatic security (discussed in chapter 5). We could reimplement listing 6.6 using interceptors instead of hard-coding security in the business method (see listing 6.7).

Listing 6.7. Using interceptors with programmatic security

The SecurityInterceptor class method checkUserRole is designated as AroundInvoke, meaning it would be invoked whenever a method is intercepted . In the method, we check to see if the Principal is a CSR . If the role is not correct, we throw a SecurityException. Our BidManagerBean, on the other hand, specifies the SecurityInterceptor class as the interceptor for the cancelBid method .

Note that although using interceptors helps matters a bit in terms of removing hard-coding from business logic, there is no escaping the fact that there is still a lot of hard-coding going on in the interceptors themselves. Moreover, unless you’re using a simple security scheme where most EJB methods have similar authorization rules and you can reuse a small number of interceptors across the application, things could become complicated very quickly. In effect, you’d have to resort to writing ad hoc interceptors for method-specific authentication combinations (just admin, CSR and admin, everyone, no one, and so on). Contrast this to the relatively simple approach of using the declarative security management annotations or deployment descriptors. All in all, declarative security management is the scheme you should stick with, unless you have an absolutely unavoidable reason not to do so.

6.5. Summary

In this chapter, we discussed the basic theory of transactions, transaction management using CMT and BMT, basic security concepts, as well as programmatic and declarative security management. Both transactions and security are crosscutting concerns that ideally should not be interleaved with business logic. The EJB 3 take on security and transaction management tries to reflect exactly this belief, fairly successfully in our opinion, while allowing some flexibility.

An important point to consider is the fact that even if you specify nothing for transaction management in your EJB, the container still assumes default transactional behavior. However, the container applies no default security settings if you leave it out. The assumption is that at a minimum, an application server would be authenticated and authorized at a level higher than EJB (for example, the web tier). Nevertheless, we definitely recommend that you not leave yourself vulnerable by ignoring security at the mission-critical EJB layer where most of your code and data is likely to reside. Security vulnerabilities are insidious and you are better safe than sorry. Most importantly, the security features of EJB 3 are so easy to use that there is no reason to risk the worst by ignoring them.

The discussion on security and transactions wraps up our coverage of session and message-driven beans. Neither feature is directly applied to the EJB Persistence API as they were for entity beans in EJB 2. You’ll see why this is the case as we explore the persistence API in the next few chapters.

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

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