Enterprise Application Integration


In real business situations, it is not sufficient to develop Web applications with servlets alone. Integration of existing applications and business processes is more important. SkatesTown has a number of legacy applications such as order management, inventory management, and delivery systems, and is eager to integrate them. This process is called Enterprise Application Integration (EAI).

A basis for EAI must be interaction models that support synchronous and/or asynchronous interactions among applications. The interaction models should include coordination of transactions over applications and a secure execution environment. Robustness, flexibility, and scalability are also required to implement the models.

In this section, we describe Java 2 Enterprise Edition (J2EE) as a vehicle to perform EAI. Enterprise Java Beans (EJBs) provide a message-oriented and object-oriented component interaction model that supports both synchronous and asynchronous interactions. J2EE is a framework to integrate EJBs and other Web application components such as the servlet container. With J2EE, a request to a servlet can be performed within a transaction context in a secure manner.

The rest of this section is structured as follows : First, we review a typical SOAP server architecture for EAI on the basis of J2EE. Transactions and reliable messaging are respectively described in the context of the SOAP server architecture. Finally, we review the J2EE security model, referring to the previous section.

SOAP Server Based on J2EE

The J2EE specification addresses the development of new business services that combine many of the new, best-of-breed applications while leveraging previous investments in legacy systems. Because these services are architected as multitier applications including Web clients , Web servers, servlet containers, application servers, and databases, the main goal of J2EE is to reduce the cost and complexity of developing these multitier services, resulting in services that can be rapidly deployed and easily enhanced. Accordingly , J2EE is a meta-specification over a collection of existing Java standards as follows:

  • Servlet The HTTP server-side API to process HTTP requests and to return HTTP responses.

  • Java Server Page (JSP) A text-based solution to process a request to create a response. More specifically , you can include Java programs in HTML documents.

  • Enterprise Java Beans (EJB) A standard component architecture for building distributed object-oriented applications, especially strongly combined with database systems.

  • Java Database Connectivity (JDBC) The API for connectivity with database systems.

  • Java Message Service (JMS) A standard API for messaging that supports reliable point-to-point messaging, as well as the publish-subscribe model.

  • Java Naming and Directory Interface (JNDI) The standard API for naming and directory access.

  • JavaMail The standard API to send e-mail notifications.

  • JavaBeans Activation Framework (JAF) A framework to handle an arbitrary piece of data, such as MIME byte streams. The JavaMail API uses the JAF API, so it must be included as well.

We will not examine all these specifications. Rather, we'll consider how to architect a SOAP server on top of J2EE. Figure 5.13 shows an initial cut of the SOAP server architecture that takes into account EAI. In this architecture, the front-end is an HTTP server, and a servlet container sits next to it. The server hosts a SOAP engine (for example, Axis), which is the heart of a SOAP server. At the back end, we might have an application serverthat is, an EJB container, database, or collection of business applications.

Figure 5.13. SOAP server on J2EE architecture.

graphics/05fig13.gif

In the simplest case, we only have a Java program within a servlet container as in most examples mentioned so far. The Java program can access a database via JDBC, or invoke a business application via JMS. An application can be provided as an EJB (hosted by the application server). The EJB can invoke a database, a business application, or even another EJB object hosted by another application server.

One of key features of J2EE is transaction support. The next section describes transactions in terms of the path of SOAP engines, EJB objects, and databases. In addition, reliable messaging is discussed, especially taking JMS into account. J2EE provides a security model that includes authorization to EJB. The J2EE security model is also discussed, clarifying further requirements for the SOAP server.

Transaction Processing

So far, our purchase order example had been simplified so that we could focus on addressing SOAP features. However, in reality, the process is much more complicated because we might have to integrate multiple applications, data, and even business processes. In this section, we extend the purchase order example to include a transaction graphics/book.gif , which is a widely used computational model for constructing reliable and available distributed applications.

We include two databases in our purchase order example: Product and Order . The Product database stores product information, such as name , type, and description. Furthermore, inventory management is performed with this database so that only products in stock are sold. The Order database records purchase orders that are accepted.

We have to extend our purchase order program to integrate the Product and Order databases. There are two operations in this new context:

  1. Update the Product database to reduce the amount of inventory on hand based on how many of each product the customer orders.

  2. Update the Order database to add a record of the purchase order.

However, simply executing these operations sequentially is not sufficient because the application might fail just after completing the first operation. In this case, the stock number in the Product database is decreased, but the order is not recorded. The result of the application should be all or nothing that is, all of the operations must complete, or none of the operations should be allowed to complete. Such a requirement is called atomicity and is one of key properties of transaction processing. In addition to atomicity, transactions should have three other properties: consistency, isolation, and durability. These properties will be described more in detail after we review how our program can be implemented with EJB.

With EJB, data resources are represented as Java objects, and transaction processing over the objects is ensured. Figure 5.14 illustrates an overview of the purchase order program, which incorporates databases with EJB. A Product object in the EJB container corresponds to a record in the product table. In the same manner, an Order object corresponds to a record in the order table. These EJB objects are called Entity Beans graphics/book.gif because they represent entities in data resources.

Figure 5.14. Purchase order processing with EJB.

graphics/05fig14.gif

On the other hand, the POProcess object reads and updates data in a database on behalf of the client. A Session Bean graphics/book.gif is an EJB object that performs business logic on behalf of a client in a distributed, transactional context. When the POProcess object receives a client request, it reads and updates databases via Product and Order objects. Although it is complicated to update the date on multiple resources during transaction processing, the EJB container provides a simple way to perform transactions.

Let's review our EJB objects. When providing EJB objects, we must implement a home interface graphics/book.gif , remote interface, and component implementation. As a result, nine Java classes should be provided, as listed in Table 5.2. Note that Container Managed Persistence ( CMP ) graphics/book.gif will be described in more detail in the examples.

Table 5.2. EJB Classes for Purchase Order Program
EJB Type Home Interface Remote Interface Component Implementation
CMP Entity Bean ProductHome Product ProductBean
CMP Entity Bean OrderHome Order OrderBean
Session Bean POProcessHome POProcess POProcessBean

Let's take a closer look at some of these EJBs. Listing 5.18 is the definition of the Product EJB. Product is an interface for a product, so it defines a collection of methods to get and set data fields, such as SKU , Name , Type , and so on. POProcess uses this interface to look up and update the fields.

Listing 5.18 Definition of the Product Interface
 public interface Product extends javax.ejb.EJBObject {     String getSKU() throws java.rmi.RemoteException;     void setSKU(String newValue) throws java.rmi.RemoteException;     String getName() throws java.rmi.RemoteException;     void setName(String newValue) throws java.rmi.RemoteException;     String getType() throws java.rmi.RemoteException;     void setType(String newValue) throws java.rmi.RemoteException;     String getDesc() throws java.rmi.RemoteException;     void setDesc(String newValue) throws java.rmi.RemoteException;     int getPrice() throws java.rmi.RemoteException;     void setPrice(int newValue) throws java.rmi.RemoteException;     int getInStock() throws java.rmi.RemoteException;     void setInStock(int newValue) throws java.rmi.RemoteException; } 

ProductBean is the implementation of Product ; therefore, it defines the set/get methods and data fields for them. In addition, it implements a collection of methods defined by the EntityBean interface, such as ejbCreate , ebjLoad , ejbActivate , and so on (see Listing 5.19). These methods are derived from an EJB object lifecycle model. The lifecycle model is beyond the scope of this book, so interested readers should refer to the EJB specification.

Listing 5.19 ProductBean Class
 public class ProductBean implements EntityBean {     private javax.ejb.EntityContext entityContext = null;     public String sku;     public int price;     public String name;     public String type;     public String desc;     public int inStock;     public void ejbActivate() throws java.rmi.RemoteException {}     public String ejbCreate(java.lang.String sku)         throws javax.ejb.CreateException, java.rmi.RemoteException     {         this.sku = sku;         return sku;     }     public void ejbLoad() throws java.rmi.RemoteException { }     public void ejbPassivate() throws java.rmi.RemoteException { }     public void ejbPostCreate(java.lang.String argProductId)         throws java.rmi.RemoteException { }     public void ejbRemove()         throws java.rmi.RemoteException,                javax.ejb.RemoveException { }     public void ejbStore() throws java.rmi.RemoteException { }     public javax.ejb.EntityContext getEntityContext() {         return entityContext;     }     public void setEntityContext(javax.ejb.EntityContext ctx)         throws java.rmi.RemoteException     {         entityContext = ctx;     }     public void unsetEntityContext() throws java.rmi.RemoteException {         entityContext = null;     }     public String getSKU() {         return sku;     }     public void setSKU(String newValue) {         this.sku = newValue;     }     public String getName() {         return name;     }     public void setName(String newValue) {         this.name = newValue;     }     public String getType() {         return type;     }     public void setType(String newValue) {         this.type = newValue;     }     public String getDesc() {         return this.desc;     }     public void setDesc(String newValue) {         this.desc = newValue;     }     public int getPrice() {         return price;     }     public void setPrice(int newValue) {         this.price = newValue;     }     public int getInStock() {         return inStock;     }     public void setInStock(int newValue) {         this.inStock = newValue;     } } 

As you can see, defining Product and ProductBean is pretty easy, because doing so is almost the same as creating ordinary Java programs. Now, how do you relate Product to the database? You do not have to change the program at all; rather, you can define the mapping to database at deployment time. It is worthwhile to mention that EJB programs are deployed with deployment descriptors, each of which declares all the EJB's external dependencies (such as the names of resources that the EJB uses).

Listing 5.20 is a definition for Product excerpted from the deployment descriptor of our application. As you can see, it defines that Product is a CMP bean (see persisence-type element), specifying which fields of the CMP bean are managed by the EJB container. Note that how these fields are persisted is left to each vendor. It also must be noted that Listing 5.20 is an EJB 1.1 style deployment descriptor that is different from the format defined for EJB 2.0.

Listing 5.20 Deployment Descriptor for the Product EJB
 <entity>   <display-name>Product</display-name>   <ejb-name>Product</ejb-name>   <home>ProductHome</home>   <remote>Product</remote>   <ejb-class>ProductBean</ejb-class>   <persistence-type>Container</persistence-type>   <prim-key-class>java.lang.String</prim-key-class>   <reentrant>False</reentrant>   <cmp-version>1.x</cmp-version>   <cmp-field>     <description>no description</description>     <field-name>name</field-name>   </cmp-field>   <cmp-field>     <description>no description</description>     <field-name>type</field-name>   </cmp-field>   <cmp-field>     <description>no description</description>     <field-name>inStock</field-name>   </cmp-field>   <cmp-field>     <description>no description</description>     <field-name>price</field-name>   </cmp-field>   <cmp-field>     <description>no description</description>     <field-name>desc</field-name>   </cmp-field>   <cmp-field>     <description>no description</description>     <field-name>sku</field-name>   </cmp-field>   <primkey-field>sku</primkey-field>   <security-identity>     <description></description>     <use-caller-identity></use-caller-identity>   </security-identity> </entity> 

Product , ProductHome , and ProductBeans are specified with home , remote , and ejb-class elements. persistence-type specifies a Container to indicate that this entity bean is CMP. The field-name element under cmp-field indicates a data field that is included in ProductBean and will appear as a column name in a database table.

Because the definition of Order is inherently the same as Product , we move on to the POProcess session bean. Its core portion can be found in POProcessBean.order() . Listing 5.21 is a portion of POProcessBean .

Listing 5.21 POProcessBean Class
 public class POProcessBean implements SessionBean {     public Order 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);             Order order  = orderHome.create(""+System.currentTimeMillis());             System.out.println("order class: " + order.getClass() );             order.setBillTo(billId);             order.setShipTo(shipId);             order.setSKU(sku);             order.setProductName(product.getName());             order.setQuantity(quantity);             int total=quantity*product.getPrice();             order.setTotalPrice(total);             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(CreateException e) {             throw new EJBException("Fail in Order.order: " +e.getMessage());         } catch(FinderException e) {             throw new EJBException("Fail in Order.order: " +e.getMessage());         }     } } 

In plain terms, the processes here are as follows:

  1. Find a product based on a given SKU ID.

  2. Update the inStock data field if the order quantity exceeds the stocked products.

  3. Create an order based on the given request and the product information.

  4. Place the order only if the total charge is less than a value ( MAX_TOTAL ).

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) graphics/book.gif , 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.

graphics/05fig15.gif

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 ) graphics/book.gif 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 ) graphics/book.gif Entity Bean instead of a CMP Entity Bean.

Furthermore, the J2EE Connector Architecture graphics/book.gif 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 graphics/book.gif . 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

graphics/05fig16.gif

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) graphics/book.gif 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) graphics/book.gif 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 graphics/book.gif , 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 graphics/book.gif or guaranteed message delivery graphics/book.gif . 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 graphics/book.gif , 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.

graphics/05fig17.gif

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:

  1. Look up QueueConnectionFactory and a queue via JNDI.

  2. Create a connection and a session object to access a queue manager.

  3. Create a queue sender object.

  4. Create a message object and send it.

Because queues can be transaction resources, they adhere to transaction management. More specifically, when the order() method is invoked in Listing 5.23, a transaction starts automatically. Then, only when the method successfully exits is a commit message sent to the queue manager. The message is then placed into the queue where the server can get it.

Let's look at the server side, namely OrderManagementListener , which is the front end of the order management system. Listing 5.24 is an OrderManagementListener class that gets order messages from the queue.

Listing 5.24 OrderManagementListener Class
 public class OrderManagementListener implements MessageListener {     public static void main(String[] args) {         ..................         try {             jndiContext = new InitialContext();             queueConnectionFactory = (QueueConnectionFactory)                 jndiContext.lookup("QueueConnectionFactory");             queue = (Queue) jndiContext.lookup(queueName);             queueConnection =                 queueConnectionFactory.createQueueConnection();             queueSession =                 queueConnection.createQueueSession(false,                     Session.AUTO_ACKNOWLEDGE);  queueReceiver = queueSession.createReceiver(queue);   queueConnection.start();   queueReceiver.setMessageListener(this);  ......         } catch (Exception e) {         } finally {             if (queueConnection != null) {                 try {                     queueConnection.close();                 } catch (JMSException e) {}             }         }     }     public void onMessage(javax.jms.Message msg) {         if(msg instanceof ObjectMessage) {             ObjectMessage message = (ObjectMessage)msg;             OrderData order = (OrderData)message.getObject();             // invoke order management system         }  else {             // do something         }     }} 

Unlike on the client side, a queue receiver object is created to get messages from the queue. We extract an OrderData object from a JMS message, then invoke the order management system with the order data. Again, the message is received only when a transaction is committed at the client side.

You can integrate applications in a loosely coupled and extensible manner with message queuing. First, the client does not have to know who receives the message. Second, even if the server is not available because of a server failure or communication problem, the client can still continue to send requests as long as the queue is available. In addition, load balancing is also possible by simply adding replications of the server.

Message-driven Bean

One of main improvements in EJB 2.0 is the introduction of the Message-driven Bean (MDB). MDB improves the programming of the server side although it does not affect the client side at all.

Let's look at OrderManagementListener again. It is a standalone Java program; therefore, transaction management is not provided at all. You might want to invoke your order management system within a transaction context. MDBs meet such a requirement.

Listing 5.25 is OrderManagementMDB . The functionality is the same as OrderManagementListener , but there is no code to set up a queue connection and session. In other words, basic operations for getting a message from the queue are performed by an EJB platform; accordingly, MDB just receives the dispatched messages. More importantly, the onMessage() method is executed in a transactional context. Note that if transaction rolls back, the message receipt is also rolled back (in other words, the message is put back on the queue).

Listing 5.25 OrderManagementMDB Class
 public class OrderManagementMDB implements MessageDrivenBean {     public void onMessage(Message inMessage) {         ObjectMessage msg = null;         try {             if (inMessage instanceof ObjectMessage) {  msg = (ObjectMessage)inMessage;   OrderData order = (OrderData)msg.getObject();  // Invoke order management system             } else {             }         }  catch (Exception e) { }     } } 

Although OrderManagementMDB defines four methods, only onMessage() is shown here. In contrast to OrderManagementListener , a procedure for receiving messages from the queue is not necessary. So, this class can focus on extracting the OrderData object and invoking the order management system.

MDB is convenient ; however, we need an EJB container for performing it. On the other hand, the JMS client can be a standalone Java program like OrderManagementListener . In some cases, you might use JMS directly (you might not have a proper EJB container in your platform).

JMS As a SOAP Transport

Because SOAP is transport- agnostic , you can use JMS for its transport instead of HTTP. Furthermore, the concept of an intermediary suggests that SOAP messages can be routed to multiple nodes via different transports. Figure 5.18 illustrates a possible configuration that contains both concepts. Inter-company communication is performed via HTTP(S). The receiver company has a SOAP intermediary that receives SOAP messages via HTTP and forwards them to backend applications via JMS. The key idea here is that the external firewall allows HTTP to pass, whereas the internal firewall allows only JMS.

Figure 5.18. SOAP messaging that includes both HTTP and JMS.

graphics/05fig18.gif

This configuration is typical for several reasons. First, you can augment message processing by adding functions to an intermediary without changing the backend applications. For example, you might add digital signature verification and logging functions there. Even in that case, you do not have to change the backend applications. In addition, some companies do not want to accept HTTP through their internal firewall for security reasons. However, strictly speaking, simply eliminating HTTP does not necessarily improve the security level. The intermediary also plays a role of security domain boundary; for example, credential mapping from external IDs to internal IDs is performed.

SOAP JMS transport is necessary especially for enterprise customers. Although Axis does not currently support JMS transport, a prototype implementation is provided in our examples. Listing 5.26 is an excerpt from the JMSSender class for the requestor side.

Listing 5.26 JMSSender Class
 public class JMSSender extends BasicHandler {     private void sendMessage(MessageContext msgContext, String messageTxt)         throws AxisFault {         BytesMessage msg;         try {             queueConnection = queueFactory.createQueueConnection();             queueSession = queueConnection.createQueueSession(false, graphics/ccc.gif Session.AUTO_ACKNOWLEDGE);             queueSender = queueSession.createSender(queue);             msg = queueSession.createBytesMessage();             msg.writeUTF(messageTxt);             TemporaryQueue replyTo = queueSession.createTemporaryQueue();             msg.setJMSReplyTo(replyTo);             queueSender.send(msg);             queueConnection.start();             QueueReceiver receiver = queueSession.createReceiver(replyTo);             javax.jms.Message replyMsg = receiver.receive();             if (replyMsg instanceof BytesMessage) {                 String replyTxt = ((BytesMessage)replyMsg).readUTF();                 org.apache.axis.Message respMsg = new org.apache.axis.Message(replyTxt);                 msgContext.setResponseMessage(respMsg);             }             closeConnection();         } catch (JMSException e) {             throw new AxisFault("JMSSender.sendMessage", "JMSException: " + e, null, graphics/ccc.gif null);         }     }     ...... } 

The sendMessage() method in the class implements a synchronous request/response by combining two JMS messages. It creates a temporary queue for a response, sets it as the reply-to queue in the request message, and sends the message to a request queue. Then, it waits until a response message is delivered to the temporary queue.

Listing 5.27 is a portion of the JMSListener class for the service provider side.

Listing 5.27 JMSListener Class
 public class JMSListener implements MessageListener {     public void onMessage(javax.jms.Message msg) {         BytesMessage bytesMsg = null;         org.apache.axis.Message respMessage = null;         MessageContext msgContext = null;         String msgTxt = null;         AxisEngine engine = AxisServer.getSingleton();         msgContext = new MessageContext(engine);         try {             if (!msg instanceof BytesMessage) {                 // do error handling ......             }             bytesMsg = (BytesMessage) msg;             replyQueue = (Queue)(bytesMsg.getJMSReplyTo());             sender = session.createSender(replyQueue);             msgTxt = bytesMsg.readUTF();             org.apache.axis.Message soapMessage = new org.apache.axis.Message(msgTxt);             msgContext.setRequestMessage(soapMessage);             msgContext.setTransportName(transportName);             engine.invoke(msgContext);             respMessage = msgContext.getResponseMessage();             String respString = respMessage.getAsString();             BytesMessage reply = session.createBytesMessage();             reply.writeUTF(respString);             sender.send(reply);         } catch (Exception e) {             // do error handling         }     } } 

The central method in this class is onMessage() . Here, we get the reply queue from the incoming message and create a queue sender for returning the response. Main processing is invoked with engine.invoke() . Finally, we respond via the temporary queue created by the sender.

To execute SOAP over JMS, visit /ch5/ex6/index.jsp in the example navigator (see Figure 5.19). Like other pages, specify some parameters and click the Submit PO button.

Figure 5.19. Example navigator GUI for SOAP over JMS.

graphics/05fig19.gif

Reliable Messaging on the Internet

In addition to transactions, SkatesTown's Dean Caroll wants to use other means to reliably transmit messages over the Internet. Unfortunately , despite some ongoing efforts, there is still no broadly accepted standard in reliable messaging. As in our previous analysis of security technologies, we can approach this issue at the two levels: messaging and transport.

An example of a messaging-level approach can be found in the ebXML graphics/book.gif Transport, Routing and Packaging (ebXML TRP) specification. It defines the MessageHeader element, which is present as a child of the SOAP header element; this element can contain a collection of parameters to perform reliable messaging in a transport-agnostic manner. Although this specification is well described, it is unfortunately not broadly accepted at this moment.

JMS, as described in this section, is an approach for reliable messaging. However, it requires transport products other than HTTP, such as IBM MQ Series. Recently, IBM announced HTTPR graphics/book.gif to provide HTTP with reliability and asynchronicity features. There is some commonality between TRP and HTTPR models; for example, they both assume message handlers are present at the sender and receiver sides. Accordingly, their protocols are similar. The big difference is whether their parameters are defined as a SOAP header entry or HTTP headers. HTTPR is IBM proprietary, so whether it will be widely accepted is not certain.

J2EE Security Model

So far, we have reviewed transaction processing aspects of J2EE, focusing particularly on EJB and JMS. In addition to transaction processing, J2EE especially addresses security. We have already reviewed security technologies such as SSL, digital signatures, and encryption. In this section, we review an end-to-end security model provided in J2EE.

Figure 5.20 depicts J2EE security architecture, which is based on a role-based access control (RBAC). The HTTP server or servlet container authenticates a requestor, assigning roles to it. Within the servlet container, access to Web resources is authorized based on a URL permission list (not shown in the figure). Within the EJB container, access to EJB objects is authorized based on a method permission list. Permission lists are mappings between roles and target objects: Web resources and EJB objects.

Figure 5.20. J2EE security architecture.

graphics/05fig20.gif

RBAC is flexible because role assignment rules and permission lists can be independently defined. There is a trick here: A credential containing a user ID travels along the method invocation path, which can span multiple EJB containers, and roles are assigned to the requestor at each container for authorization. This concept is especially useful when the system configuration is extremely complex.

J2EE requires compliant platforms to support the following three authentication methods:

  • HTTP basic authentication

  • SSL client authentication

  • Form-based authentication

Note that we will not discuss the third method because the client is not a browser, but a standalone application.

Authorization in J2EE

Using BASIC-AUTH, let's review how user IDs and roles are defined in J2EE. The following is an excerpt from application.xml , which is a deployment descriptor for the overall J2EE application:

 <application>    ...  <security-role>   <role-name>GoodCustomer</role-name>   </security-role>  </application> 

The GoodCustomer role is used in the J2EE application. J2EE does not prescribe any particular means for user definition and user-role mapping. The following is a platform-dependent format extracted from Sun's J2EE Reference Implementation:

 <j2ee-ri-specific-information>    <server-name></server-name>  <rolemapping>   <role name="GoodCustomer">   <principals>   <principal>   <name>ABCRetailer</name>   </principal>   </principals>   </role>   </rolemapping>  ...... </j2ee-ri-specific-information> 

With this format, you can enumerate user IDs within the role element. A principal indicates a user or a user group . In this example, user ABCRetailer can have a role GoodCustomer .

Let's take a look at method permission definition for EJB objects. The following is an excerpt from ejb.xml , which is a deployment descriptor for EJBs:

 <ejb-jar>    <display-name>OrderEjb</display-name>    <enterprise-beans>    </enterprise-beans>    <assembly-descriptor>  <security-role>   <role-name>GoodCustomer</role-name>   </security-role>   <method-permission>   <role-name>GoodCustomer</role-name>  <method>         <ejb-name>  POProcess  </ejb-name>         <method-intf>Remote</method-intf>         <method-name>  order  </method-name>         <method-params>           <method-param>java.lang.String</method-param>           <method-param>java.lang.String</method-param>           <method-param>java.lang.String</method-param>           <method-param>int</method-param>         </method-params>       </method>  </method-permission>  </assembly-descriptor> </ejb-jar> 

The security-role element includes a collection of roles that are referenced somewhere in this file. method-permission indicates who can access the target method with role . role-name specifies the role name for this method permission, in this case, GoodCustomer .

Relation to JAAS

You might notice that there is some commonality between J2EE RBAC and JAAS, which we reviewed in Section "Access Control for Java Classes." A J2EE credential contains a user ID and roles. On the other hand, a JAAS subject contains a user ID and principals. So, we can provide a mapping between roles in J2EE credential and principals in a JAAS subject. Thus, the J2EE credential and the JAAS subject converge in theory.

A potential scenario for using JAAS in J2EE is described as follows (http://www.research.ibm.com/journal/sj/401/koved.html):

"The EJB container issues a Subject.doAs( ) call to establish the credentials before invoking the EJB object. Through the EJB object's internal actions (e.g., through the ORB), AccessController.checkPermission( ) is called to determine whether the caller is authorized for the method."

This indicates that EJB authorization could be implemented with JAAS. Although there is no standard for this purpose, JSR 115 might resolve this J2EE and JAAS migration issue.

Once the issue is resolved, we could provide a security framework that accepts credentials (such as BASIC-AUTH, SSL client authentication, or digital signatures) and authorizes access to Web resources, Java classes, and EJB.



Building Web Services with Java. Making Sense of XML, SOAP, WSDL and UDDI
Building Web Services with Java: Making Sense of XML, SOAP, WSDL and UDDI
ISBN: B000H2MZZY
EAN: N/A
Year: 2001
Pages: 130

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net