Chapter 5. Using SOAP for e-Business
4. Place the order only if the total charge is less than a value
The sequence of operations is performed within a transaction context although you see very few instructions related to transactions. If you use UserTransaction class, you invoke the begin() method to start a transaction, the commit() method to complete it, and the rollback() method to abort it. However, explicitly comitting and rolling back transactions is not a good practice. Rather, you are strongly advised to use container- managed transactions instead. If you take this approach, operations are performed automatically in a transactional context. Only if you want to allow rollback should you invoke setRolebackOnly() method of the SessionContext class as shown in the listing.
For example, if the total charge is more than MAX_TOTAL, the update on Product and the creation of the Order object is undone.
ACID and Two-Phase Commit
So far, we have only mentioned atomicity. Now, we will review the four transaction properties known as ACID (Atomicity, Consistency, Isolation, and Durability) , and we will discuss the popular two-phase commit transaction protocol.
Atomicity ensures that a computation will either terminate normally, updating the
involved data resources in the intended way, or abort, updating nothing. In other words, no intermediate situation can exist where data resources are partially updated. This property should be ensured even if there is a system failure. In our example without atomicity, if we encountered a system failure, we would have a situation in which the product database is updated, but the order database is not updated.
Consistency ensures that only consistent-state changes to data resources occur despite concurrent access and system failures. In our case, we have the following constraint over the product and order databases:
([initial stock]-[current stock])*price == [total charge in orders]
The consistency property relies on application programs to some extent, unlike other properties, because constraints on data are not explicitly represented. More specifically, such constraints are embedded in application logic; therefore, a transaction management system cannot monitor them.
Isolation ensures that concurrent computations do not interfere with each other. In other words, the result of concurrent execution of transactions should be equivalent to the case of sequential execution. In our example, the inStock field of Product is critical data, so it requires isolation. Assume that the stock is 10, and two transactions are executed, each of which requires 8 products. Without proper isolation, or a locking mechanism, both transactions would successfully terminate as long as they both satisfy the atomicity constraint.
Durability ensures that once the transaction terminates normally, its result is stored permanently. Generally speaking, termination of a transaction does not mean the completion of a database update; rather, it means that necessary information for updating is recorded. Therefore, this property is related to recovery from failure.
The two-phase commit (TPC)[gl] protocol is a broadly accepted solution for ensuring the ACID properties over distributed data resources. The TPC protocol defines two types of messages: "prepare" and "commit". To complete a transaction, these messages are sent to all data resources. However, the prepare and commit messages are never mixed;
therefore, the completion phase is comprised of two phases: the prepare and commit phases. This simple principle is the basis for various transaction theories.
EJB wraps TPC protocols carefully so that application programmers can develop transaction processing easily and safely. Container-managed transaction is a typical example to simplify development. In summary, EJB lets you develop transaction processing properly without worrying about the details of transaction theories.
Executing EJB from Axis
In this section, we execute the EJB version of the purchase order with Axis. Go to /ch5/ex5/index.jsp in the example navigator. Through the page, you can specify the shipping ID, billing ID, SKU, and quantity, and issue a purchase order (see Figure 5.15).
As long as the database indicates that sufficient quantity exists to fill the order, you receive an invoice. Otherwise, you receive an error message.
Figure 5.15. Example navigator GUI to invoke EJB purchase order.
Listing 5.22 shows a SOAP client program for the purchase order.
Listing 5.22 Client Code for Invoking an EJB Service public class POProcessClient {
private String url;
private String urn;
public POProcessClient(String targetUrl, String serviceUrn) {
url = targetUrl;
urn = serviceUrn;
}
public OrderData order(String shipid, String billid, String sku, int quantity) throws Exception
{
ServiceClient client = new ServiceClient(endpointURL);
OrderData order =
(OrderData)client.invoke(urn, "order",
new Object[] { shipid, billid, sku, new Integer(quantity)} );
return order;
} }
As you can see, there is no difference from ordinary Remote Procedure Call (RPC)
invocations at the client side. At the server side, some Universal Resource Name (URN) such as urn:X-SkatesTown:EJBPOService is bound to an EJB object of the
POProcess class instead of a Java object. If the member variable urn in POProcessClient is urn:X-SkatesTown:EJBPOService, the RPC request from the client is dispatched to the EJB object as in an ordinary RPC invocation.
Using EJB with SOAP Engines
Let's examine how we can use EJB with SOAP engines. Our motivation for introducing EJB was to carry out EAI. EJB objects can be published externally through a SOAP engine. The example in this section has demonstrated that you can integrate multiple databases even when they are distributed in a complicated configuration. However, you might also want to integrate other kinds of applications. For example, you might have an inventory management application instead of the product database in our example. In some cases, you can use a Bean-Managed Persistence (BMP) Entity Bean instead of a CMP Entity Bean.
Furthermore, the J2EE Connector Architecture Specification has been released and is much more sophisticated than the BMP Entity Bean for improving EAI. Although the J2EE Connector Architecture can be viewed as a generalization of JDBC connection pooling, it provides a good framework to integrate legacy applications, addressing connection pooling, transactions, and security. If you're going to perform EAI, you should check out J2EE Connector Architecture.
Once you understand EJB transaction processing capability, a question arises: Can the SOAP engine be improved with EJB? Axis currently provides a SOAP engine that works on top of the servlet container. Let's examine how to move the SOAP engine to an EJB container.
Figure 5.16 illustrates an architecture where a SOAP engine is located within an EJB container. Because requests are sent via various transports, there might be different types of listeners, such as SMTP Listeners, FTP Listeners, and HTTP Listeners. An HTTP Listener is developed as a servlet. Each listener delegates the incoming request to the EJB container via the Remote Method Invocation over Internet Inter-ORB (RMI-IIOP) protocol . Once a request is received, the SOAP engine does not consult the transport listener again in the processing of that request.
Figure 5.16. SOAP Engine within EJB Container
Within the SOAP engine, there are five handlers to process the request. First, the digital signature of the message is verified, then the message is logged, and the POProcess EJB is invoked to process the purchase order in the message. The response from POProcess, an invoice, is digitally signed and logged before being returned to the requestor.
In practice, the SOAP engine would be implemented as a Stateless Session Bean and would invoke the handler chain from the EJB. If you use container managed transactions, a transaction starts automatically when the SOAP engine is invoked. So, atomicity over the update of three databases is ensured within the transaction context. This architecture is especially useful in the case of a system failure. The system might fail during execution of the signature handler in Figure 5.16. In that case, we cannot return an invoice to the requestor, and we might want to roll back the previous operations. This architecture allows us to roll back the transaction automatically so that we do not have to worry about complicated recovery sequences.
A SOAP engine within a servlet can handle normal errors that are typically recognized by Java exceptions. However, it cannot handle system failures properly because such an engine does not record enough information for recovery. On the other hand, the EJB- based architecture is robust against system failure in the sense that a recovery sequence is automatically and properly carried out by the EJB container.
Transactions over the Internet
Dean Caroll of SkatesTown now realizes the advantages of using transactions to integrate legacy applications. Transactions ensure that application data is consistently updated and simplify error handling tremendously. However, he has one lingering concern: Can we use the transaction processing model for B2B collaboration over Internet?
There are some standardization efforts for modeling transaction processing onto B2B.
Transaction Internet Protocol (TIP) provides a simplified two-phase commit protocol.
TIP does not require a very large infrastructure, but it has not garnered broader support because of its complexity. XML Transaction Authority Markup Language (XAML) is another effort for representing transaction protocol in XML. It was announced in October 2000, but so far no specification has been published.
What is the key difficulty in managing transactions over the Internet? We need to keep the two-phase commitment (TPC) protocol in mind. According to the TPC protocol, a transaction manager sends prepare messages to resource managers, and eventually receives OK messages from them. Once a resource manager sends the OK message, it has to wait for a commit or abort message from the transaction manager. This suggests that the resource manager might have to wait a while for the message. Within an intranet, we can expect each system to be stable, and thus the network to be stable.
However, we cannot expect the same level of stability when relying on other companies' systems using the Internet.
A more practical approach is to instead use compensation transactions, which have proper transaction models for the Internet. In our purchase order example, we can reconstruct the architecture to use compensation transactions by including two
transactions (product and order databases updated with different transactions). Assume that the product database is successfully updated, but some problem occurs during the order creation. In that case, we issue a compensation transaction to cancel the update on the product database. Although ACID properties are not ensured here, many real cases can be covered with this protocol.
Reliable Messaging
The EJB architecture integrates object-oriented programming with transaction processing in an elegant way. Its distributed object architecture relies on a client-server model where the client sends a request and waits for a response from a server synchronously.
This synchronous invocation model fits into transaction processing model we have thoroughly reviewed. However, the synchronous model has several limitations that are crucial for EAI.
Mainly, the problems are related to communication between the client and server. With a synchronous model, when a communication failure occurs, the client receives an error message. Accordingly, the client must send the identical request to the server later. In some cases, the client might not want to retry, but would prefer to have someone send the request when communication is restored.
This problem can be resolved by message queuing , where the client puts a message on a queue, and the server gets a message from the queue. The message in the queue is often recorded in persistent storage; therefore, the message is sure to be sent to the server. Thus, message queuing is often called reliable messaging or guaranteed message delivery . Furthermore, message queuing is closely related to transaction processing because the queue can be considered a transaction resource.
Let's examine Java Message Service (JMS), which is a standard API for message queuing systems. First, we will update our purchase order example to include JMS. Then, we will examine the EJB 2.0 Message-driven Bean , which is asynchronously invoked to handle the processing of incoming JMS messages as in JMS applications. Finally, we consider how to adopt JMS as a SOAP transport.
Message Queuing with JMS
We could make our purchase order program more functional by assuming that there is an order management system instead of an order database. Figure 5.17 illustrates this extension including a message queue front-ending the order management system.
POProcess puts order information in the queue, and proceeds to the next operation without blocking. On the other hand, the order management system asynchronously gets the information from the queue to record the order information.
Figure 5.17. Application integration with message queue.
Let's rewrite our purchase order example using JMS. Listing 5.23 is a modification of POProcessBean, namely, POProcessBeanJMS.
Listing 5.23 POProcessBeanJMS Class
public class POProcessBeanJMS implements SessionBean {
public OrderData order(String shipId, String billId, String sku, int quantity)
throws POProcessException {
try {
Product product = productHome.findByPrimaryKey(sku);
if (quantity > product.getInStock()) {
throw new POProcessException("Stock is not enough");
}
product.setInStock(product.getInStock() - quantity);
OrderData order = new OrderData("" + System.currentTimeMillis());
order.setBillTo(billId);
order.setShipTo(shipId);
order.setSKU(sku);
order.setProductName(product.getName());
order.setQuantity(quantity);
int total = quantity * product.getPrice();
order.setTotalPrice(total);
queueConnectionFactory = (QueueConnectionFactory) jndiContext.lookup("QueueConnectionFactory");
queue = (Queue)jndiContext.lookup(queueName);
queueConnection =
queueConnectionFactory.createQueueConnection();
queueSession =
queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
queueSender = queueSession.createSender(queue);
ObjectMessage message = queueSession.createObjectMessage();
message.setObject(order);
queueSender.send(message);
if (total > MAX_TOTAL) {
throw new POProcessException("Exceed the max charge (" + MAX_TOTAL + ")");
}
return order;
} catch(POProcessException e) {
mySessionContext.setRollbackOnly();
throw e;
} catch(RemoteException e) {
throw new EJBException("Fail in Order.order: " +e.getMessage());
} catch(Exception e) {
throw new EJBException("Fail in Order.order: " +e.getMessage());
} } }
Note that the entity bean Order is replaced by a Java class OrderData. The typical way of using JMS, as shown in bold in the program, is as follows: