Skip Headers

Oracle Application Server Containers for J2EE Enterprise JavaBeans Developer's Guide
10g (9.0.4)

Part Number B10324-01
Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Go to previous page Go to next page

11
Active Components for Java

Active Components for Java (AC4J) is a framework that extends J2EE to enable applications to interact as peers in a loosely coupled manner. Two or more applications that are participating in a business interaction asynchronously exchange information for the purpose of requesting service and responding with results.

This chapter describes the Oracle solution for managing loosely coupled interactions between autonomous applications. It covers the following topics:

Advantages of AC4J

Often business applications require the ability to perform long-lived interactions between different application services. Applications should be able to communicate with other applications over a long period of time, without limiting resources, and with the ability to survive system crashes. Each application, when communicating with another application, exists as a peer. In other words, both applications can make requests to each other, but neither application can assume control over the resources that its peer application owns. In this environment, the communication between two applications is often disconnected, meaning the applications cannot rely on a constant system connection. The tasks that are performed sometimes may take days, even months, to complete; therefore, the long-lived interactions require asynchronous communication.

A practical example shows how this works. You might want to implement a purchase order (PO) processing system. The system would allow a client or customer to make an asynchronous purchase order request, without having to wait for the possibly rather lengthy purchase order processing to complete. The purchase order processing service itself would make two asynchronous requests, one to a credit service and one to an inventory service, to verify the customer's credit and check the inventory for the purchase order line items. The asynchronous nature of the two parallel requests to the services would allow the purchase order processing service to create an order based on the customer's request and respond with the corresponding order in a timely fashion. After both services returned with their responses, the purchase order processing service could consolidate both results and either cancel the order or continue processing it. (The returns would most likely not happen simultaneously and could potentially take hours, days, or even weeks.) The client could inquire on the status of the order at any given time, based on the order number originally returned by the purchase order processing service.

To implement such a system, the framework must enable applications to perform long-lived interactions as autonomous peers. For any application to exist over an unspecified period of time, the application must be reliable; it must be recoverable and restartable in case of system failures during that time. The application must also be scalable--that is, the long-running application cannot block execution or lock resources for long periods of time. For the application to execute within a reasonable time frame, the framework must provide performance enhancements through concurrently executing computations.

The Java 2 Platform, Enterprise Edition (J2EE), created by Sun Microsystems, provides an excellent environment for building reliable, scalable, tightly coupled applications. One of the main components that aid in this is Enterprise Java Beans (EJBs). It also supplies the building blocks for developing a new framework for satisfying the requirements for long-lived interactions, such as the one outlined in the previous example. EJBs, Java Message Service (JMS), and Java Transaction API (JTA) can be fused together in such a way that applications with support for long-lived interactions can easily be built.

Given the features of J2EE, why do we provide AC4J? Why are existing J2EE technologies, such as EJBs, JMS, and JTA not enough for building such a purchase order processing system?

Unfortunately, the tightly coupled synchronous communication of EJBs does not allow for long-lived interactions, or autonomous peer-to-peer communication. On the other hand, the loosely coupled, asynchronous communication of JMS does not allow the component-based request-response communication that is necessary between application services--at least not in a simple, out of the box manner. In addition, the need for the JTA coordinator to control all resources that are involved in the two-phase commit means that autonomous resources cannot be included in global transactions. Thus, J2EE by itself does not provide the full solution necessary for easily developing applications that use long-lived interactions.

We believe that to support the requirements for long-lived interactions, business solutions require an application component methodology that combines the advantages of EJBs, JMS, and JTA. Specifically, the new methodology must include answers to the following needs.

The application must be able to invoke EJB methods interactively, yet in the disconnected, non-blocking mode that is provided by asynchronous communication; in other words, a true integration of EJB method invocation with JMS messaging properties. This enables requests to be directed to the bean implementation, but in a loosely coupled manner that does not require static connections between the two parties, or the blocking of the requestor until a response is received. Furthermore, exceptions must be handled within the asynchronous environment and propagated back to the client. All services that are executing within the asynchronous environment should still contain a context for data, such as parameters and local variables. The environment should also provide basic time-management capabilities that allow for the programmatic or declarative delay of the execution flow, as well as for the forced execution of certain service operations based on time-out properties.

AC4J Architecture Overview

AC4J beans are known as active EJBs. These are EJBs (stateless session or entity beans) that extend JMS attributes. Thus, active EJBs have all of the properties, services, and Quality of Service of any EJB, plus the asynchronous abilities of JMS. An active EJB contains the business logic. These beans are loosely coupled beans, such that each bean can use either (or both) request-response synchronous or message-driven asynchronous communication to peer objects. Because active EJBs fully comply with the EJB specification, each active EJB uses all EJB features, such as being component-based and reusable and providing access to EJB services, such as security and transactional behavior.

All active EJBs exist within AC4J Interactions. Everything necessary for long-lived interactions is encapsulated within the AC4J Interaction, including all the request-response synchronous and one-way asynchronous communication properties. An AC4J Interaction is a long-lived unit of work that reflects the behavior of a business transaction. The AC4J Interaction replaces global transactions with its own methodology to avoid resource contention, allowing autonomous resources within the transaction, and enabling systems with differing constraints to interact. It groups a series of data exchanges between processes.

Underlying all of the AC4J interactions, the AC4J data bus routes AC4J data tokens (active data and events) between AC4J processes. The AC4J data bus is the fundamental component in AC4J. Applications attach to the AC4J data bus to exchange data and request services. The data bus is responsible for the routing and matching of AC4J data Tokens with registered AC4J reactions and enables transparent load-balancing of the attached application. AC4J data tokens describe a request for service, or a response from a service request, or an exception condition, such as an expiration of a timer.

The AC4J interaction can contain one or more AC4J processes. An AC4J process represents a business task. Each AC4J process provides the transactional and security context within which the application logic executes. It also manages concurrently executing computations, which are known as AC4J reactions. Furthermore, it encapsulates active data (local variables, input parameters, and responses) and matches the incoming active data to its intended recipients--AC4J reactions that are registered with the AC4J process. The AC4J process also maintains the data flow context that determines how to return the response to the caller. The context describes the destination to which the response data are returned.

An application can be dynamically partitioned into concurrently executing AC4J reactions. Each AC4J reaction is executed when specified conditions apply and all required data have arrived. AC4J reactions are the reactive entities that wait for the appropriate active data to arrive before invoking the intended bean method. After the method has processed the data, the AC4J reaction returns responses based on the context. All activities that are executed within the scope of the AC4J reaction execute under the ACID (atomicity, consistency, isolation, durability) properties of a JTA transaction.

AC4J reactions perform the detailed work of a business task by:

AC4J also furnishes certain time-management capabilities: allowing the execution flow to be delayed for a certain amount of time before executing a component, or executing a component after a certain amount of time (the time-out) even if the required data tokens for its execution have not yet arrived.

In summary, AC4J allows EJBs to interact in a loosely coupled fashion by performing the following:

All the preceding features are fully integrated in the EJB framework.

AC4J Components Overview

This section covers the following topics:

Active EJBs

Traditional EJBs are passive--that is, they must be ready to immediately service a request from a client and return results quickly. Failure to do so causes an EJB to be unusable. AC4J allows standard stateless session and entity EJBs to become active. Active EJBs permit requests for service to be decoupled from the actual service execution. The policies that control when and which EJB methods are actually invoked are controlled by the service provider EJB. This decoupling permits service requests and service providers to interact as autonomous peers.


Note:

Within AC4J, you might see JEM, which is an internal name that is equivalent to AC4J, in sample code, file names, and directory paths.


An application can create or look up a JEMHandle and then request service from a business task, which is exposed in the EJB interface.

An active EJB is uniquely identified by a JEMHandle object. A JEMHandle object encapsulates the following:

Interaction

An interaction is a long-lived unit of work that reflects the behavior of a business transaction. A business transaction can span multiple applications that reside in different organizations. The life of a business transaction differs from the life of a local or a global transaction in that the duration of a business transaction in such a disconnected environment can be arbitrarily long.

An interaction represents a business goal that you want to complete. For example, if a customer wants to buy something from a business, all the actions that are necessary to allow the customer to pay for and receive the item he wants are characterized as an interaction. The interaction groups a series of business data exchanges by providing the global execution context of the business transaction.

These applications can run in isolation and commit or roll back their own data without knowledge of other applications. However, these applications should not be considered as different pieces, because the relationships formed among them must be coordinated and their consistency maintained. When a business transaction becomes inconsistent, its participating applications may need to recover. The application recovery can be obtained by registering compensating reactions. For example, when the supplier has confirmed the purchase order request back to the buyer, the buyer must register a compensating reaction. The reaction monitors additional responses from the supplier that might inform him that, for example, the purchase order cannot be fulfilled because the manufacturing department is running late. If the supplier's confirmation of the request is cancelled, then the buyer's compensating reaction is matched and then fired to allow the buyer application to recover its application consistency. This reaction can pick a new supplier and request the item from the supplier or abandon the purchase order process completely.

An interaction is uniquely identified by an interaction identifier (IID). An interaction can contain multiple processes.

Process

A process identifies a business task. In the purchase order example, a process exists for each of the following business tasks: creating a purchase order, checking inventory, and checking customer credit.

Each process does the following:

A process is uniquely identified by a JEMPortHandle object, which encapsulates the process context and the JEMHandle of the active EJB that the process belongs to. The process context is a union of an interaction identifier and the process activation identifier. AC4J automatically creates the interaction and process activation identifiers within a call operation. Alternatively, the application can supply them in the AC4J JEMSession::call operation.

Reaction

A reaction performs the detailed work of a process. Using this construct, an application can specify its persistence interest in the availability of a collection of correlated data tokens that trigger the execution of an active EJB method.

When a process is created as the result of an AC4J call operation, AC4J implicitly creates a base reaction. Additionally, an application can explicitly create a reaction at run time, using the JEMReaction::registerReaction operation to synchronize on data tokens. The implicit or explicit registerReaction operation specifies the active EJB method to be executed when matching succeeds.

The reaction processes incoming requests, returns results based on the request, and enforces business constraints to preserve application consistency. When all data tokens are available and the conditions are matched, the reaction is fired. It can consume one or more input data parameters, process them, and then possibly produces one or more output data tokens for other reactions. Results returned by a reaction (active EJB method) are converted to data tokens by the AC4J infrastructure and routed to the caller. The reaction can request additional services from other active EJBs to complete the business task. These requests result in the creation of new data tokens, which are pushed and routed by the AC4J data bus.

Reactions inside a process context instance can push data tokens to the AC4J data bus in the following ways:

When the timer expires, AC4J pushes a time-out exception data token in the current reaction context instance.

Reactions inside a process context instance can pull data tokens from the AC4J data bus by registering one or more reactions in the current process context instance, using the JEMReaction::registerReaction method.

One or more reactions can exist for each business task. A reaction is used for the request, and another for the response, to support the asynchronous nature in a request-response environment. The number of reactions depends on the number of requests and responses that are necessary.

"AC4J Example" demonstrates how you can receive an asynchronous communication between processes, but still have a request-response environment. The processOrder process is the business task for creating the purchase order. To create the purchase order, you must check the inventory and the customer's credit. Thus, the processOrder reaction invokes the following processes:

After sending the asynchronous requests to the checkINV and checkCRED processes, the processOrder reaction registers another reaction in the same process (processOrderCallback) that waits for the responses back from both the checkCRED and checkINV processes. When all data tokens expected from these processes are available, the processOrderCallback reaction fires and processes the responses.


Note:

To satisfy the AC4J requirement of not locking resources, the call should be an asynchronous AC4J call. However, you can still perform synchronous EJB calls to another bean.


AC4J Data Tokens

The activation of a reaction is triggered by the availability of data tokens. Availability is defined by the arrival of one or more data tokens, with the right conditions and the right access mode.

When an application requests a service by using an AC4J call operation, the system automatically pushes a request data token, which comprises the following:

Later, when a reaction returns a response data token that is automatically generated by AC4J when an active EJB returns or throws an exception, AC4J fills in the routing information. It sends the returned information to the caller process and fills the port handle object of the response data token. In the case in which the caller of the returning process is a client and not another process, then the data bus stores the response data token to a special data bus area from where the client can retrieve it, using the JEMSession::receiveReactionResponseObjectInstance operation.

The data types of the objects that are carried inside an input or output data token can be basic data types (such as integer, string, float, boolean) or constructed class types (such as Java serializable objects).

Data Bus

Improving the autonomy, scalability, and availability of applications requires components that are requesting services to be unaware of the identity, location, and number of components that provide these services. In AC4J, applications are attached to a data bus before starting their operation. The AC4J data bus is responsible for routing and matching data tokens that are pushed and must be pulled by registered reactions. Additionally, the data bus enables scheduling, activation, and execution of the matched reactions.

Matching Reactions

The data bus routing subsystem is responsible for making the different types of data tokens available at the specified destination--the process context instance that comprises the interaction identifier and the process activation identifier--specified by a JEMPortHandle object.

When data tokens are routed and become available in the data bus inside a process context instance, AC4J tries to match these data tokens with all registered reactions that are available in that context instance. The system tries to match the data token tags that are specified in a reaction template, evaluating all constraint conditions against the matched data tokens, to filter and discard the inappropriate ones.

Availability of some data tokens does not mean that a registered reaction matches immediately. Only when all data tokens required by a reaction become available does matching succeed.

For example, inside the processOrder process, the processOrder base reaction has registered the processOrderCallback reaction that is waiting for the checkCRED and checkINV processes to respond. When the checkINV process responds to the takeOrder process, the processOrderCalback reaction is not matched because it is also waiting for the checkCRED process to respond. When the checkCRED process responds to the processOrder process, the processOrderCallback reaction is matched.

Additionally, data tokens that are available in the data bus can be matched with a reaction that will be registered in the future. This matching can be used for sequencing processes, in which the completion of one process can enable another process.

Matching data tokens with reactions triggers the activation of zero, one, or more reactions, which are executed in parallel if they do not conflict for shared resources.

Firing Reactions

Each method of the remote interface of an active EJB implements the application business logic. When the data tokens become available and are matched with a reaction, AC4J verifies that the types (primitive or class types) of the data tokens that are matched on the tags also match the types of active EJB method types of the reaction. Then AC4J verifies that the matched reaction is authorized to pull the available matched data tokens. If everything passes successfully, then AC4J schedules the activation of the reaction.

When the matched reaction is fired, the AC4J container begins a JTA transaction and instantiates the requested active EJB (stateless session bean or entity EJB), using the primary key inside the JEMHandle request object (the primary key is needed only in case of entity beans). Then the EJB method of the fired reaction is executed using the matched data tokens of the reaction.

AC4J automatically commits the current reaction at the end of every active EJB method. A reaction commit marks the end of a JTA transaction so that all its changes to shared data tokens, and all its service requests and responses that have been sent, become visible. The activation of a reaction has "exactly once" semantics (that is, the code specifies that it executes exactly once) if the reaction commits. If a failure occurs after a commit, then the reaction cannot be rolled back and the changes will persist. If a failure occurs before or during a commit, then the container rolls back the current reaction. A reaction rollback reverses all changes to shared data tokens, and the service requests and responses are never sent to any recipient component. In case of failures, the data bus tries again to fire the reaction for a preconfigured number of times. The reaction is marked as completed, with exception completion status if the maximum retry attempts are reached.

In traditional databases, where the duration of a transaction is short, abnormal situations cause the whole transaction to be undone, so all performed work is lost and must be submitted again for execution. Because interactions usually have long duration and contain a large number of reactions, AC4J provides additional mechanisms to handle exceptions.

AC4J automatically makes a reaction persist in the data bus if it completes successfully. The state that is saved (process input variable data, process local variable data, and data flow context information) can be used to continue the application with minimum restart time from the last reaction. When a node becomes unavailable, all reactions that were running and did not end successfully are rolled back. AC4J then again executes the interrupted reactions in another OC4J instance.

AC4J uses a mechanism to capture, propagate, and match the application state and control flow information that are needed for resuming an application after the unexpected error. Additionally, because reaction execution is data-driven, there is no need for the system to keep a volatile or persistent copy of the entire program state (such as program execution stack) to facilitate the storage of the control flow descriptors or the storage of data variables.

Figure 11-1 illustrates how, when all data tokens are available and the conditions are matched, the reaction fires, which causes the method to execute. This method can return results that are converted to data tokens by the AC4J infrastructure and routed to the caller, and can request additional services from other active EJBs to complete the business task. These requests result in the creation of new data tokens, which are pushed and routed by the AC4J data bus.

Figure 11-1 Firing a Reaction

Text description of o_1006.gif follows

Text description of the illustration o_1006.gif

Installing and Configuring AC4J and the Database

Before you can execute any AC4J applications, you must initialize an Oracle9i database as a repository for the AC4J data bus. You must set up the following in the database:

To initialize the AC4J data bus in the database, perform the following steps:


Note:

In this chapter, where the notation J2EE_HOME appears in text, it indicates the full path of the J2EE home directory. This is the directory in which the file oc4j.jar resides. In UNIX syntax, this would be $J2EE_HOME, which is equivalent functionally to the Windows environment variable %J2EE_HOME%. In code examples, $J2EE_HOME appears. These are UNIX examples; in Windows, you would substitute %J2EE_HOME% and use backslashes in path names.


  1. Set the environment variable $AC4J_DEMO_DIR to the path to the top-level directory of the AC4J demo (the directory that contains the files common.xml, purchaseOrder, and README.txt). In UNIX, use this command:

    setenv AC4J_DEMO_DIR path_to_AC4J_demo
    
    
  2. Unpack the AC4J SQL scripts from J2EE_HOME/sql/ac4j-sql.jar, into a new subdirectory AC4J_DEMO_DIR/ac4j/sql:

    cd $AC4J_DEMO_DIR/sql
    mkdir databus
    cd databus
    jar xvf $J2EE_HOME/sql/ac4j-sql.jar
    
    
  3. Before executing the script createall.sql that was unpacked in step 1, make sure that the arguments to the two calls in the script are correct for your database configuration. The two calls are:

    createjem.sql sys_user sys_pwd DB_instance tablespace
    createclient.sql sys_user sys_pwd AC4J_user AC4J_pwd tablespace
    
    

    Table 11-1 contains the meanings and defaults of the arguments.

    Table 11-1 createall Script Call Arguments
    Argument Meaning Script Default

    sys_user

    Database system user

    sys

    sys_pwd

    Database system password

    knl_test7

    DB_instance

    Database instance

    Usually = inst1

    tablespace

    Table space for AC4J tables

    Usually = system

    AC4J_user

    Database AC4J user

    JEMCLIUSER

    AC4J_pwd

    Database AC4J password

    JEMCLIPASSWD

    The argument values may be different for your installation. If so, change them by editing the script.

  4. Execute the script createall.sql:

    cd $AC4J_DEMO_DIR/sql/databus
    sqlplus /nolog @createall.sql
    


    Note:

    The first time you run the script, it is normal to see four error messages, reporting that one synonym and three database links cannot be dropped because they do not exist. You can safely ignore these. You should not see any other error messages.


Data Source Configuration

Configure the following data sources in the data-sources.xml file in J2EE_HOME/config/:

<data-sources>

   <!-- NON-Emulated DataSources: used for JEM server -->
   <data-source
       class="com.evermind.sql.OrionCMTDataSource"
               name="nonEmulatedDS"
               location="jdbc/nonEmulatedDS"
               connection-driver="oracle.jdbc.driver.OracleDriver"
       username="jemuser"
       password="jempasswd"
       url="jdbc:oracle:thin:@host:port:sid"
       inactivity-timeout="30" >
   </data-source>

   <!-- Emulated DataSources: used for JEM client -->
   <data-source
        class="com.evermind.sql.DriverManagerDataSource"
        name="OracleDS"
        location="jdbc/OracleCoreDS"
        xa-location="jdbc/xa/OracleXADS"
        ejb-location="jdbc/OracleDS"
        connection-driver="oracle.jdbc.driver.OracleDriver"
        username="jemuser"
        password="jempasswd"
        url="jdbc:oracle:thin:@host:port:sid"
        inactivity-timeout="30" >
   </data-source>

</data-sources>

In the preceding code, you must replace host, port, and sid with the host name, port number, and database SID of the Oracle database instance on which the AC4J data bus has been installed. Do the replacement for both data sources.

The jemuser is the superuser user name, and the jemcliuser is the default client user name, created with the SQL script createall.sql.

AC4J Example

AC4J is designed for complex applications that interact with each other over long periods of time. This section illustrates the usage of AC4J with a simple purchase order example. To simplify the presentation of the example, it does not show error handling and import statements. The example is packaged as one of the demos for the EJB functionality of OC4J 9.0.4. The demos are available for download from the OTN OC4J sample code site:

http://otn.oracle.com/tech/java/oc4j/demos/904

Running the Example

To run the example, perform the following steps:

  1. Set the environment variable $AC4J_DEMO_DIR to the path to the top-level directory of the AC4J demo (the directory that contains the files common.xml, purchaseOrder, and README.txt). In UNIX, use this command:

    setenv AC4J_DEMO_DIR path_to_AC4J_demo
    
    
  2. Initialize the AC4J data bus.

    Create and populate the database tables required for running the example by executing the createTables.sql script with the following commands:

    cd $AC4J_DEMO_DIR/sql/
    sqlplus /nolog @createTables.sql
    
    
  3. Edit the database configuration files:

    You must set up the data sources used by the demo in the file J2EE_HOME/config/data-sources.xml.

  4. Build the example.

    You must build all three services, by running the default ant rule in each of their directories, as follows:

    cd $AC4J_DEMO_DIR
    ant
    


    Note:

    The ant utility is open-source and portable (between application servers, as well as operating systems), and is therefore ideal for Java-based applications. You can obtain ant and accompanying documentation at the following site:

    http://jakarta.apache.org/ant/

    Some of the sample applications that come with OC4J are set up to use ant. You can study the accompanying build.xml files for models.


    
    

    You can also clean the services in a similar manner:

    cd $AC4J_DEMO_DIR
    ant clean
    
    
  5. Start an OC4J instance, as follows:

    cd $J2EE_HOME
    java -Doracle.aurora.jem.aq.close.interval=2 -jar oc4j.jar
    
    
  6. Deploy the three services.

    Refer to the Oracle Application Server Containers for J2EE User's Guide for information on deploying an EJB on Oracle Application Server, and the Oracle Application Server Containers for J2EE Standalone User's Guide for a standalone implementation. The following example shows standalone usage:

    cd $AC4J_DEMO_DIR
    java -jar $J2EE_HOME/admin.jar ormi://localhost admin welcome -deploy \
     -file src/ejb/purchaseOrderService-ejb/lib/purchaseOrderService.ear \
     -deploymentName purchaseOrderService
    java -jar $J2EE_HOME/admin.jar ormi://localhost admin welcome -deploy \
     -file src/ejb/inventoryService-ejb/lib/inventoryService.ear \
     -deploymentName inventoryService
    java -jar $J2EE_HOME/admin.jar ormi://localhost admin welcome -deploy \
     -file src/ejb/creditService-ejb/lib/creditService.ear \
     -deploymentName creditService
    
    

    Three messages from the application server appear, one for each service that is deployed. Each message reads JEM Server started.

  7. Run the client.

    Make a purchase order request by invoking the ant rule reqpo in the purchaseOrderService subdirectory, as follows:

    cd $AC4J_DEMO_DIR
    ant reqpo
    


    Note:

    The credit and inventory services do not have clients. The user interacts directly only with the PurchaseOrder service, which, in turn, interacts with the other two services.


Application Server Output

The application server produces output similar to the following:

----------- sample of expected app server output from ant reqpo ---------------
=======>PurchaseorderServiceBean.ejbCreate(): begin/end
=======>PurchaseOrderServiceBean.takeOrder(): begin
         : clientName = scott
         : creditCardNumber = 1111-3333-4444-8888
         : (productName, quantity) = pen, 3
         : (productName, quantity) = pencil, 1
=======>PurchaseOrderServiceBean.createPO(): begin
=======>PurchaseOrderBean.ejbCreate(): begin
=======>PurchaseOrderBean.createPONumber(): begin
         : poNum=1
=======>PurchaseOrderBean.createPONumber(): end
=======>PurchaseOrderBean.evaluateTotalCost(): begin
=======>PurchaseOrderBean.evaluateTotalCost(): end---Total Cost = 0.75
=======>PurchaseOrderBean.ejbCreate(): end---poNumber= 1
=======>PurchaseOrderBean.ejbPostCreate(): begin
=======>LineItemBean: ejbCreate:  lineItemName = pen  quantity = 3
=======>LineItemBean: ejbCreate:  lineItemName = pencil  quantity = 1
=======>PurchaseOrderBean.ejbPostCreate(): end---getLineItems().size= 2
       createPO()---poNumber= 1
=======>PurchaseOrderServiceBean.createPO(): end
         : poNumber= 1
=======>PurchaseOrderServiceBean.startProcessingPOrder(): begin
=======>PurchaseOrderServiceBean.startProcessingPOrder(): end
=======>PurchaseOrderServiceBean.takeOrder(): end
=======>PurchaseOrderServiceBean.processOrder(): begin---poNumber= 1
=======>PurchaseOrderServiceBean.callCreditService(): begin
=======>PurchaseOrderServiceBean.callCreditService(): end
=======>PurchaseOrderServiceBean.callInventoryService(): begin
=======>PurchaseOrderServiceBean.callInventoryService(): end
=======>PurchaseOrderServiceBean.registerAsyncRespHandler(): begin
=======>PurchaseOrderServiceBean.registerAsyncRespHandler(): end
=======>PurchaseOrderServiceBean.processOrder(): end---status= STATUS_INPROCESS
=======>CreditServiceBean: ejbCreate: begin/end
=======>CreditServiceBean: checkCRED: begin
         : clientName = SCOTT
         : creditCardNumber = 1111-3333-4444-8888
         : amount = 0.75
=======>InventoryServiceBean: ejbCreate: begin/end
=======>InventoryServiceBean: checkINV: begin
         : (product, quantity) = pen, 3
         : (product, quantity) = pencil, 1
         : CreditorRemote found
         : creditorName = SCOTT
         : availableCredit = 5000.0
=======>CreditServiceBean: checkCRED: end--returning  CREDIT APPROVED
         : BEFORE: availableUnits[0] = 700
         : AFTER: availableUnits[0] = 697
         : BEFORE: availableUnits[1] = 600
         : AFTER: availableUnits[1] = 599
         : Returning: 
         : invCheck[0]=true
         : invCheck[1]=true
=======>InventoryServiceBean: checkINV: end
=======>PurchaseOrderServiceBean.processOrderCallback(): begin.credInfo=CREDIT 
APPROVED
=======>PurchaseOrderServiceBean.callBackMethod(): poNumber= 1
         : invInfo[0] = true
         : invInfo[1] = true
=======>PurchaseOrderServiceBean.callBackMethod(): end---credInfo=CREDIT 
APPROVED  poNumber=1  currentStatus= STATUS_SHIPPED
=======>PurchaseOrderServiceBean.getStatus(): begin---poNumber= 1
=======>PurchaseOrderServiceBean.getStatus(): end---poNumber= 1  
status=STATUS_SHIPPED
-------- (end) sample of expected app server output from ant reqpo ------------

Client Output

The client produces output similar to the following:

---------------  sample of expected client output from ant reqpo --------------
reqpo:
     [java] Getting AC4J Connection and Session...


     [java] sendRequest: begin
     [java]   customer name = IID = scott
     [java]   credit card number = 1111-3333-4444-8888
     [java]   item names = pen,pencil
     [java]   quantities = 3,1


     [java] takeOrder request made. Process Context is:
     [java]   Interaction Identifier (IID) = scott
     [java]   Activation  Identifier (AID) = AE3D71D56E835817E0340003BA137479


     [java] Execute the following to receive PO response and track status:
     [java]   ant -DAID=AE3D71D56E835817E0340003BA137479 resppo
------------- (end) sample of expected client output from ant reqpo ----------

Collecting the Response

The response to the purchase order request is collected by executing a second ant target, resppo. You must supply the activity ID (AID) of the request as a property (ant -DAID=<AID> resppo). As a convenience, reqpo outputs the required ant command line for that request, as the last line of its output. You can use cut-and-paste to copy the text to the command line, for quicker entry.

Here is an example that uses the preceding AID:

cd $AC4J_DEMO_DIR
ant -DAID=AE3D71D56E835817E0340003BA137479 resppo

The program automatically terminates when it receives a STATUS_SHIPPED status.

The exact output depends on how soon after the purchase order request is made that ant resppo is executed.

ant resppo Output

Executing the resppo client of ant produces output similar to the following:

--------------- sample of expected output from ant resppo -------------------
resppo:
     [java] Getting AC4J Connection and Session...

     [java] receiveResponse: begin
     [java] receiveResponse: receiving async response
     [java] receiveResponse: response received
     [java] receiveResponse: Purchase Order number = 1
     [java] receiveResponse: polling for PO status...
     [java]   status = STATUS_SHIPPED

     [java] receiveResponse: Status = SHIPPED. Done.
------------ (end) sample of expected output from ant resppo ------------------

Rerunning the Client

To rerun the client (and submit a different purchase order), rerun ant reqpo, as described in step 6 under "Running the Example".

Explanation of the Example

This section gives an overview of the steps that occur in the execution of the purchase order example. Figure 11-2 illustrates the steps, with the numbers corresponding to the step numbers in the description that follows.

Figure 11-2 Steps in the Purchase Order Example

Text description of o_1007a.gif follows.

Text description of the illustration o_1007a.gif

The "Overview of Steps" is followed by a section for each step. Each step section explains the code for that step in detail.

In Figure 11-2, only the return of the purchase order number from the takeOrder process to the client (steps 2 and 6) is shown as going through the AC4J data bus. This is a simplification, made to emphasize the flow of control between the different AC4J processes and reactions.

In fact, as "AC4J Data Tokens" explains, all AC4J calls (including replies) are mediated by the AC4J data bus. Figure 11-3 illustrates this for two of the steps from the example: the call from the processOrder base reaction to the checkINV process (step 3a) and the return of the reply of the checkINV process to the takeOrder process (step 4).

Figure 11-3 All AC4J Calls Are Mediated by the Data Bus

Text description of o_1055.gif follows.

Text description of the illustration o_1055.gif

Overview of Steps

The steps that occur in the execution of the purchase order example are as follows:

  1. Step 1: Client Sends an Asynchronous Request to the Purchase Order Service Active EJB

    The request (takeOrder) is sent through the data bus and starts a new AC4J process.

  2. Step 2: Purchase Order Service Active EJB Processes the Client's takeOrder Request

    The takeOrder method does the following, to start the processing of the new purchase order:

    1. Creates a purchase order entity bean to represent the new purchase order. The creation of this bean in turn causes the appropriate product and line item beans to be created. This is done using regular EJB entity beans. AC4J is not involved.

    2. Sends an asynchronous request to the processOrder process. This is a method on the same bean (PurchaseOrderService bean), but it forms the base reaction of a new AC4J process.

    3. Returns the purchase order number assigned when the purchase order entity bean was created.

    By initiating another reaction, the takeOrder reaction can finish, and return the purchase order number assigned to this purchase order back to the client as early as possible, so the client can start to poll for the purchase order status.

  3. Step 3: Purchase Order Service Active EJB Processes the processOrder Request

    The processOrder reaction starts a new purchase order. It performs the following:

    1. Sends an asynchronous request to the checkINV process of the InventoryService active EJB, to verify that the items are in inventory

    2. Sends an asynchronous request to the checkCRED process of the CreditService active EJB, to verify that the customer's credit is satisfactory

    3. Registers a processOrderCallback reaction in the current process to receive the results from the preceding two requests.

  4. Step 4: Inventory and Credit Service Active EJBs Process Requests and Make Asynchronous Responses

    Both the checkINV and checkCRED processes return responses.

  5. Step 5: Asynchronous Responses Are Processed by the processOrderCallback Reaction

    The processOrderCallback reaction, within the processOrder process, reacts to the information that is provided by the checkINV and checkCRED processes. If satisfactory, the status of the purchase order entity bean is updated. No confirmation is sent to the client.

  6. Step 6: Client Receives Purchase Order Number Asynchronously and Polls for Purchase Order Status

    The purchase order number returned from step 2 is returned by the AC4J data bus and can be received asynchronously by the client at any time after that. After the client has the purchase order number, it then polls for the status of the purchase order, using a regular EJB call.

Step 1: Client Sends an Asynchronous Request to the Purchase Order Service Active EJB

The code sample in Example 11-1 shows the steps that the client takes in sending an asynchronous request to the PurchaseOrderService active EJB.

Example 11-1 Client Sends an Asynchronous Request to POS Active EJB

static final String DATA_SOURCE_NAME  = "java:comp/env/jdbc/OracleDS";
static final String BEAN_NAME = "java:comp/env/ejb/PurchaseOrderService";
static final String ACTIVE_EJB_NAME = "JEMPurchaseOrderService";
static final String METHOD_NAME = "takeOrder";
static final String DB_USER    = "JEMUSER";
static final String DB_PASSWD  = "JEMPASSWD";

public static void main(String[] args)
{

// 1.0. Create a JNDI Context
Context context = new InitialContext();

// 1.1. Look up the DataSource where AC4J data bus resides
      DataSource client_ds = (DataSource) context.lookup(DATA_SOURCE_NAME);

// 1.2. Obtain a JDBC connection to the DataSource
OracleConnection conn =
(OracleConnection)client_ds.getConnection(DB_USER, DB_PASSWD);

// 1.3. Create an AC4J connection, using the JDBC Connection
ac4j_conn = new JEMConnection(conn);

// 1.4. Create an AC4J session on the data bus
ac4j_sess = new JEMSession(ac4j_conn);

// 1.5. Look up the handle of the PurchaseOrderService Active EJB
activeEJBHandle = (JEMHandle)context.lookup(ACTIVE_EJB_NAME);

// 1.6. Prepare input parameters for asynchronous call

// 1.6a. Make String and int arrays from itemNames and quantities args
String[] itemNames  = getStringArrayFromInput(args[3]);
int[]    quantities = getIntArrayFromInput(args[4]);

// 1.6b. Make a Class array of input parameter types
Class[] inputClassTypes = new Class[] { String.class,
String.class,
itemNames.getClass(),
quantities.getClass() };

// 1.6c. Make an Object array of input parameter values
Object[] inputParams = new  Object[] { (Object) new  String(args[1]),
(Object) args[2],
(Object) itemNames,
(Object) quantities };

// 1.7. Make the asynchronous call
// IID = customer name (args[1]), AID = null = auto-assigned
JEMEmitToken request = ac4j_sess.call(args[1],
null,
activeEJBHandle,
METHOD_NAME,
inputClassTypes,
inputParams,
null, 0, 0);

// 1.8. Commit the transaction
((OracleConnection)ac4j_sess.getJEMConnection().getConnection()).commit();

// 1.9. Get the AID auto-assigned to the request just made
//    Also, Confirm IID (will be value set above - just showing the API)
//    IID + AID = 'Process Context' of the request
JEMPortHandle portHandle = request.getPortHandle();
String iid = portHandle.getIid();
String aid = portHandle.getAid(); 
System.out.println("takeOrder request made. Process Context is:");
System.out.println("  Interaction Identifier (IID) = " + iid);
System.out.println("  Activation  Identifier (AID) = " + aid);

// 1.10 Close AC4J session and connection and JDBC connection
ac4j_sess.close();
ac4j_conn.close();
conn.close();
}

The client exists outside of an AC4J server and is requesting a service from an active EJB through the AC4J data bus. The AC4J data bus is the conduit and controls the asynchronous communication between the client and all reactions. Every client residing outside of an AC4J server must first connect to the AC4J data bus and create a new session for interaction to occur.

After a connection to the AC4J data bus has been retrieved and an AC4J session has been created within it, asynchronous messages can be sent to active EJBs in the same or other AC4J instances. The AC4J data bus coordinates the asynchronous messages and acts as a transactional manager for all active EJBs in the transaction.

The following explanation corresponds to the numbering in Example 11-1.

1.0 to 1.4 Retrieve an AC4J Connection

Because an AC4J connection exists above a JDBC connection, steps 1.0 to 1.4 retrieve that AC4J connection.

1.1 Retrieve the DataSource defined for the database acting as the AC4J conduit.

Define the DataSource to use in the data-sources.xml file as an emulated data source. See "Data Source Configuration" for more information.

Context context = new InitialContext();
DataSource client_ds = (DataSource)
               context.lookup(DATA_SOURCE_NAME);

1.2 Retrieve the JDBC connection from the DataSource object.

OracleConnection conn =
   (OracleConnection)client_ds.getConnection(DB_USER, DB_PASSWD);

1.3 Create an AC4J connection from the JDBC connection object.

ac4j_conn = new JEMConnection(conn);

1.4 Create an AC4J session in a specified data bus.

Providing the name of the data bus, use the AC4J connection to the database and create a session within the data bus in the indicated Oracle database.

ac4j_sess = new JEMSession(ac4j_conn);

1.5 to 1.11 Send an Asynchronous Request

Once an AC4J session has been created on the AC4J data bus, the client can send asynchronous messages to active EJBs. The client must provide the active EJB handle, the process handle, and all the required input parameters to the base reaction. Steps 1.5 to 1.11 explain the details of the call that the client must make to complete the AC4J request.

1.5 Obtain active EJB handle.

In a synchronous EJB environment, you would use a remote EJB handle to make an invocation. In an AC4J asynchronous environment, you must provide a similar handle of class type JEMHandle that identifies an active EJB. You can obtain the active EJB handle by looking up the jem-name that is defined in the orion-ejb-jar.xml file (see "AC4J Active EJB Deployment").

// 1.5. Look up the handle of the PurchaseOrderService Active EJB
activeEJBHandle = (JEMHandle)context.lookup(ACTIVE_EJB_NAME);

1.6 Prepare input parameters for asynchronous call.

The client prepares two arrays--an array of class types, to identify the type of each parameter, and an array of objects, to provide the values. Each has four parameters: two strings (customer name and credit card number), an array of strings (items), and an array of integers (quantities):

Class[] inputClassTypes = new Class[] { String.class,
   String.class,
   itemNames.getClass(),
   quantities.getClass() };

Object[] inputParams = new  Object[] { (Object) new  String(args[1]),
   (Object) args[2],
   (Object) itemNames,
   (Object) quantities };

1.7 Make the asynchronous call.

The JEMSession::call contains the interaction identifier of the EJB, the process activation identifier to identify the process where the method is instantiated, and the JEMHandle of the active EJB.

The interaction and process activation identifier (which together form the process context) are optional and can be omitted or can be null, in which case the system automatically creates them. The data bus identifies the context of the process and routes the data tokens to the intended process. Thus, all EJB calls are invoked asynchronously, through the mediation of the data bus.

// IID = customer name (args[1]), AID = null = auto-assigned
JEMEmitToken request = ac4j_sess.call(args[1],
   null,
   activeEJBHandle,
   METHOD_NAME,
   inputClassTypes,
   inputParams,
   null, 0, 0);

1.8 Commit the transaction.

The client must commit the changes to the AC4J data bus. If the transaction is not committed, then the request is lost and is not visible to the AC4J data bus. To make the request visible to the AC4J data bus, perform the JDBC commit as follows:

((OracleConnection)ac4j_sess.getJEMConnection().getConnection()).commit();

1.9 Get the AID auto-assigned to the request just made.

In this case, the IID was given, but the AID was left null--to be auto-assigned by AC4J. The AID that is assigned must be read from the port handle, and retained to rendezvous asynchronously with the reply later, in Step 6.

JEMPortHandle portHandle = request.getPortHandle();
String aid = portHandle.getAid(); 

1.10 Close session and connections.

Finally, the client must close the AC4J session and connection, and the JDBC connection. This is necessary only because the client does not exist within an AC4J container. For applications running within an AC4J container (such as the active EJBs in this example), the container automatically closes the session and connections.

ac4j_sess.close();
ac4j_conn.close();
conn.close();

Step 2: Purchase Order Service Active EJB Processes the Client's takeOrder Request

The code sample in Example 11-2 shows the steps that the purchase order service active EJB takes in processing the client's takeOrder request.

After the client commits its request, the AC4J data bus matches the data tokens provided by the client with those of the requested reaction and internally schedules the instantiation of the PurchaseOrderBean active EJB and activation of the takeOrder process. The takeOrder process starts a takeOrder base reaction, which starts a new purchase order.

This reaction, takeOrder, processes the client's request by performing the following steps:

  1. Creates a purchase order entity bean to represent the new purchase order. The creation of this bean, in turn, causes the appropriate product and line item beans to be created. This is done using regular EJB entity beans. AC4J is not involved.

  2. Sends an asynchronous request to the processOrder process. This is a method on the same bean (PurchaseOrderService bean), but forms the base reaction of a new AC4J process.

  3. Returns the purchase order number that was assigned when the purchase order entity bean was created.

By initiating another reaction, the takeOrder reaction can finish, and return the purchase order number that was assigned to this purchase order back to the client as early as possible, so the client can start to poll for the purchase order status.

Example 11-2 shows the code that performs these steps.

Example 11-2 Purchase Order Service Active EJB Processes the Client's takeOrder Request

public int takeOrder(String clientName, String creditCardNumber,
String[] productNames, int[] quantities) throws RemoteException, TestException { int poNumber = 0; // 2.1. Create Purchase Order Entity Bean, and get PO Number in return // (Regular EJB Entity Beans code. Not shown) poNumber = createPO(clientName, creditCardNumber, productNames, quantities); // 2.2. Get the current AC4J reaction JEMReaction currentAC4JReaction = (JEMReaction)JEMReaction.getReaction(); // 2.3. Make an asynchronous call to the processOrder reaction on this // Bean, so can return PO Number to client now // 2.3a. Look up the AC4J handle for this Bean (PurchaseOrderService) Context context = new InitialContext(); JEMHandle ac4jPOSBeanHandle = (JEMHandle)context.lookup(jemPurchaseOrderServiceBeanName); // 2.3b. Prepare input parameter Types for processOrder method Class[] inputClassTypes = new Class[] { Integer.TYPE, productNames.getClass(), quantities.getClass() }; // 2.3c. Prepare input parameter Values for processOrder method Object[] inputParams = new Object[] { (Object) (new Integer(poNumber)), (Object) productNames, (Object) quantities }; // 2.3d. Make asynchronous call to processOrder JEMEmitToken emitToken = null; emitToken = currentAC4JReaction.call(null, null, ac4jPOSBeanHandle, "processOrder", inputClassTypes, null, inputParams, null, null, null, 0, 0); // 2.4 Return the PO Number of the new Purchase Order return poNumber; }

The AC4J data bus instantiates the active EJB, JEMPurchaseOrderBean (corresponding to the JEMHandle that is provided by the client), in an AC4J server. The takeOrder process starts a takeOrder base reaction. The implementation of the takeOrder method performs the steps shown in the example. The following explanation corresponds to the numbering in Example 11-2.

2.1 Create purchase order entity bean, and get purchase order number in return.

This step involves regular EJB entity bean programming, and is not shown.

2.2 Get the current AC4J reaction.

The current reaction, takeOrder, is running in an AC4J server. The application code retrieves the current reaction as follows:

JEMReaction currentAC4JReaction = (JEMReaction)JEMReaction.getReaction();

This is done so that the call made in step 2.4 can be made in the context of the current request (that is, as another reaction in the same AC4J process).

The interaction ID (IID) and activity ID (AID) of the current process context could be obtained from the current reaction, as follows:

String iid = currentAC4JReaction.getIid();
String aid = currentAC4JReaction.getAid();

This would allow, among other things, the new call to be made in the context of the same interaction, but with a different activity ID, thus creating a new process in the same interaction.

2.3 Make an asynchronous call to the processOrder reaction on this bean.

The steps taken in making the asynchronous call are the same as those taken by the client in Steps 1.5 to 1.7. The transaction does not need to be committed by the application; AC4J automatically commits the transaction when the method returns.

By initiating another reaction, the takeOrder reaction can finish, and return the purchase order number assigned to this purchase order back to the client as early as possible, so the client can start to poll for the purchase order status.

2.4 Return the purchase order number of the new purchase order.

The application returns the value using the normal synchronous style, as follows:

return poNumber;

How this value is passed back to the client depends on the style of call made by the client.

In this case, the client called the method asynchronously, by way the AC4J data bus. Therefore, AC4J takes the return value and packages it in an AC4J data token, which is placed on the data bus. The client receives the value asynchronously, by registering a reaction that consumes that data token. In this example, this is performed in Step 6. (Step 6 could proceed any time after step 2 completes, that is, once the reply data token is placed on the data bus.)

Had the client called the same active EJB method synchronously, by means of a regular EJB call, the value would have been returned to the client synchronously, in the normal way. The reason is that AC4J-enabled active EJBs can still be called as regular EJBs.

Step 3: Purchase Order Service Active EJB Processes the processOrder Request

The processOrder reaction starts a new purchase order. It performs the following:

  1. Sends an asynchronous request to the checkINV process of the InventoryService active EJB, to verify that the items are in inventory.

  2. Sends an asynchronous request to the checkCRED process of the CreditService active EJB, to verify that the customer's credit is satisfactory.

  3. Registers a processOrderCallback reaction in the current process to receive the results from the preceding two requests.

Example 11-3 shows the code that performs these steps.

Example 11-3 Purchase Order Service Active EJB Processes the processOrder Request

public void processOrder(int poNumber, String[] productNames,
                             int[] quantities)
      throws RemoteException, TestException
{
   PurchaseOrderRemote poRemote = null;
   
   // 3.1. Get the current AC4J reaction
   JEMReaction currentAC4JReaction = (JEMReaction)JEMReaction.getReaction();
   
   // 3.2. Look up purchase order Entity Bean, so can pass reference to
   //    Credit and Inventory Services
   poRemote = lookupPOBean(new Integer(poNumber));
   
   // 3.3. Make an asynchronous call to the Credit Service
   JEMEmitToken creditToken = callCreditService(currentAC4JReaction, poRemote);
   
   // 3.4. Make an asynchronous call to the Inventory Service
   JEMEmitToken inventoryToken = callInventoryService(currentAC4JReaction, 
                                                              productNames,
                                                              quantities);
   
   // 3.5. Register processOrderCallback Reaction
   registerCallback(currentAC4JReaction,
   new JEMEmitToken[] {inventoryToken, creditToken});
   
   // 3.6. Update Purchase Order status to INPROCESS
   poRemote.setStatus(Status.getString(Status.STATUS_INPROCESS));
   }
   

The following explanation corresponds to the numbering in Example 11-3.

3.1 Get the current AC4J reaction.

The purpose of this is to register the new (callback) reaction in 3.6. The callback reaction is registered in the same process as the current reaction so that it can access data that is associated with this process. See Step 5 for details.

JEMReaction currentAC4JReaction = (JEMReaction)JEMReaction.getReaction();

3.2 Look up purchase order entity bean.

This is done so that the reference to the purchase order entity bean can be passed in the request made to the credit service, so that it can query the bean directly.

poRemote = lookupPOBean(new Integer(poNumber));

3.3 Make an asynchronous call to the credit service.

The steps taken in making the asynchronous call are identical to those taken by the client in Steps 1.5 to 1.7. The transaction does not need to be committed by the application; AC4J automatically commits the transaction when the method returns. The checkCRED method of the credit service active EJB is called, starting a new checkCRED process.

JEMEmitToken creditToken = callCreditService(currentAC4JReaction, poRemote);

3.4 Make an asynchronous call to the inventory service.

This step is similar to 3.3. The only difference is that the purchase order entity bean reference is not passed to the service. The product names and quantities are sufficient information. This difference is not an AC4J issue; rather, it is how this example has been implemented.

The checkINV method of the credit service active EJB is called, starting a new checkINV process.

JEMEmitToken inventoryToken = callInventoryService(currentAC4JReaction, 
                                                           productNames,
                                                           quantities);

3.5 Register processOrderCallback reaction.

Register a new reaction in the current process:

registerCallback(currentAC4JReaction,
    new JEMEmitToken[] {inventoryToken, creditToken});

The reaction specifies interest in the tokens returned by the asynchronous calls to the credit and inventory services, made in steps 3.3 and 3.4. The new reaction will be scheduled to execute when the reply tokens from both calls are placed on the AC4J data bus (that is, when both services return from the calls).

The callback reaction is registered in the same process as the current reaction so that it can access data that is associated with this process. See "Step 5: Asynchronous Responses Are Processed by the processOrderCallback Reaction" for details.

3.6 Update purchase order status to INPROCESS.

This state is maintained so that it can be returned to a client that polls to check the status of the purchase order. See "Step 6: Client Receives Purchase Order Number Asynchronously and Polls for Purchase Order Status" for details.

poRemote.setStatus(Status.getString(Status.STATUS_INPROCESS));

The takeOrder base reaction is completed only after the AC4J infrastructure commits the transaction that includes the calls to the other two active EJBs and a registered reaction.

Step 4: Inventory and Credit Service Active EJBs Process Requests and Make Asynchronous Responses

The checkINV and checkCRED processes receive the requests from the AC4J data bus as if they were invoked from any other EJB. The JEMInventoryBean and JEMCreditBean active EJBs are instantiated. The checkINV and checkCRED base reactions are fired when they receive the data tokens, which were initiated from the processOrder reaction, from the AC4J data bus. Both of them receive the request, perform their tasks, and return. The returned values are forwarded by the AC4J data bus to the registered reaction, processOrderCallback.

The code sample in Example 11-4 illustrates the checkINV method. The checkCRED method is the same as in its AC4J responsibilities.

Example 11-4 checkINV Processes Request

public boolean[] checkINV(String[] productNames, int[] quantities)
            throws RemoteException, TestException
{

boolean[] invCheck = new boolean[productNames.length];

// The business logic is not shown
...

return invCheck;
}

The application returns the value using the normal synchronous style, as follows:

return invCheck;

But the value is returned asynchronously, by a data token placed on the data bus. See step 2.4 for details.

Step 5: Asynchronous Responses Are Processed by the processOrderCallback Reaction

Both checkINV and checkCRED processes return their responses to the processOrderCallback reaction through the AC4J data bus. The AC4J data bus ensures that the return data-tokens have valid processOrder process context and match the input parameter types of the processOrderCallback reaction. When both parameters arrive, the processOrcerCallback reaction fires and executes the processOrderCallback method of the JEMPurchaseOrderBean active EJB.

The processOrderCallback reaction reacts to the information provided by the checkINV and checkCRED processes and updates the state of the purchase order entity bean accordingly. Note that no confirmation is sent to the client, which polls for the status of the order--as "Step 6: Client Receives Purchase Order Number Asynchronously and Polls for Purchase Order Status" describes.

The code sample in Example 11-5 shows the processOrderCallback method.

Example 11-5 Asynchronous Responses Are Processed by the processOrderCallback Reaction

public void processOrderCallback(boolean[] invInfo, String credInfo)
      throws RemoteException, TestException
{

PurchaseOrderRemote poRemote = null;
Integer poNumber = null;

//    This Reaction (processOrderCallback) was invoked asynchronously by
//    it's parent Reaction (processOrder) by doing 
//    "JEMReaction.registerReaction".
//
//    This reaction can access all it's parent's variables by using 
//    the JEMProcess concept as shown in the following 3 steps.
//
//    In this case, it is the PO Number that is accessed. 

// 5.1. Obtain the current AC4J process handle
JEMProcess  currentAC4JProcess = (JEMProcess) JEMProcess.getProcess();

// 5.2. Retrieve the first input parameter of parent reaction (processOrder)
JEMTuple inTuple = currentAC4JProcess.getInTupleByIndx(0);

// 5.3. Get PO Number from parent's input parameter
poNumber = (Integer)inTuple.getObjInst();

// 5.4. Look up PurchaseOrder Entity Bean
poRemote = lookupPOBean(poNumber);

// 5.5. Start updating the PO status depending on the replies...
poRemote.setStatus(Status.getString(Status.STATUS_PROCESSED));

// 5.6. Check the credit info
int local_status = Status.STATUS_PROCESSED;
if(credInfo != null)
{
if(credInfo.equalsIgnoreCase("credit approved"))
local_status = Status.STATUS_VALID_CREDIT;
else if(credInfo.equalsIgnoreCase("credit failed"))
local_status = Status.STATUS_CANCELLED_NOCREDIT;
else if(credInfo.equalsIgnoreCase("Invalid Credit Card"))
local_status = Status.STATUS_INVALID_CREDIT_CARD;
} else
local_status = Status.STATUS_CANCELLED_NOCREDIT;

// 5.7. Check the inventory info
for(int i=0; local_status == Status.STATUS_VALID_CREDIT && i<invInfo.length;
i++)
{
if(!invInfo[i])
local_status = Status.STATUS_CANCELLED_NOINV;
} 

// 5.8. Set the final status of the Purchase Order
if(local_status == Status.STATUS_VALID_CREDIT)
poRemote.setStatus(Status.getString(Status.STATUS_SHIPPED));
else
poRemote.setStatus(Status.getString(local_status));
}

The following explanation corresponds to the numbering in Example 11-5.

5.1 Obtain the current AC4J process handle.

The processOrderCallback reaction resides in the same process as the processOrder reaction that created it; processOrder is the base reaction of the processOrder process, and processOrderCallback is a child reaction.

A handle to the process is obtained so that the current reaction can access data that is global to the process - in this case one of the input parameters to the base reaction, the purchase order number.

JEMProcess currentAC4JProcess = (JEMProcess) JEMProcess.getProcess();

5.2 Retrieve the first input parameter of parent reaction.

The parent reaction is the base reaction of this process. The first parameter is the first token in the tuple:

JEMTuple inTuple = currentAC4JProcess.getInTupleByIndx(0);

5.3 Get purchase order number from parent's input parameter.

From the whole set of parameters, the purchase order number is retrieved:

poNumber = (Integer)inTuple.getObjInst();

5.4 Look up purchase order entity bean.

This entity bean is obtained to be able to update the order status steps 5.5 to 5.7:

poRemote = lookupPOBean(poNumber);

5.5 to 5.7 Update the purchase order status, depending on the replies.

These steps are normal entity bean operations, not involving AC4J.

Step 6: Client Receives Purchase Order Number Asynchronously and Polls for Purchase Order Status

The client must know the response to its purchase order request. As stated earlier, each request (or call) is identified by a process context (interaction ID, IID, plus activation ID, AID). Using the process context, the client can pull the response from the AC4J data bus.

The client can then parse the received JEMEmitToken from the response. If the client existed inside the OC4J container, then the container would deconstruct the JEMEmitToken to the required type. Outside the container, the client must parse out the response correctly, as shown in Example 11-6.

Only the purchase order number is returned by the asynchronous reply, not the status of the order itself. After the client has the order number, it polls the purchase order entity bean directly, by means of regular synchronous EJB calls, until the order completes.

Example 11-6 Client Receives Purchase Order Number Asynchronously and Polls for Purchase Order Status

static final String DATA_SOURCE_NAME  = "java:comp/env/jdbc/OracleDS";
static final String BEAN_NAME = "java:comp/env/ejb/PurchaseOrderService";
static final String ACTIVE_EJB_NAME = "JEMPurchaseOrderService";
static final String DB_USER    = "JEMUSER";
static final String DB_PASSWD  = "JEMPASSWD";

public static void main(String[] args) throws  ClassNotFoundException, Exception
{

// 6.0. Create a JNDI Context
Context context = new InitialContext();

// 6.1. Look up the DataSource where AC4J data bus resides
DataSource client_ds = (DataSource) context.lookup(DATA_SOURCE_NAME);

// 6.2. Obtain a JDBC connection to the DataSource
OracleConnection conn =
(OracleConnection)client_ds.getConnection(DB_USER, DB_PASSWD);

// 6.3. Create an AC4J connection, using the JDBC Connection
ac4j_conn = new JEMConnection(conn);

// 6.4. Create an AC4J session on the data bus
ac4j_sess = new JEMSession(ac4j_conn);

// 6.5. Look up the handle of the PurchaseOrderService Active EJB
activeEJBHandle = (JEMHandle)context.lookup(ACTIVE_EJB_NAME);

// 6.6. Receive Response for the specified Process Context
//    (IID + AID) plus reaction (takeOrder)
//    Blocks indefintely, because timeout is 0
JEMEmitToken resptoken =
ac4j_sess.receiveReactionResponse(args[1],         // IID
args[2],         // AID
activeEJBHandle, // Active EJB Handle
"takeOrder",     // Reaction/Method
0);              // Timeout (seconds)

// 6.7. Retrieve data from Reaction Response
Object object = resptoken.getReactionResponseObjectInstance();

// 6.8. Extract PurchaseOrder Number from data
Integer poNumber = retrievePONumber(object);

// 6.9. Commit the transaction
((OracleConnection)ac4j_sess.getJEMConnection().getConnection()).commit();

// 6.10. Look up the PurchaseOrder Service Bean
PurchaseOrderServiceRemote posb = lookupPurchaseOrderServiceBean();

// 6.11. Poll the Purchase Order status - synchronously (regular EJB calls)
//    until the status = shipped
System.out.println("receiveResponse: polling for PO status...");
String status = "";
while(status.compareTo("STATUS_SHIPPED") != 0)
{
status = posb.getStatus(poNumber.intValue());
System.out.println("  status = " + status);
}

if (status.compareTo("STATUS_SHIPPED") == 0)
{
System.out.println("\nreceiveResponse: Status = SHIPPED. Done.");
}
}

The following explanation corresponds to the numbering in Example 11-6.

6.0 to 6.4 Retrieve an AC4J connection.

These steps are identical to steps 1.0 to 1.4.

6.5 Look up the handle of the purchase order service active EJB.

activeEJBHandle = (JEMHandle)context.lookup(ACTIVE_EJB_NAME);

This handle must be provided to the call in step 6.6.

6.6 Receive response for the specified process context:

JEMEmitToken resptoken =
   ac4j_sess.receiveReactionResponse(args[1],         // IID
      args[2],         // AID
      activeEJBHandle, // Active EJB Handle
      "takeOrder",     // Reaction/Method
      0);              // Timeout (seconds)

The IID and AID were output at the end of "Step 1: Client Sends an Asynchronous Request to the Purchase Order Service Active EJB", and are passed in to "Step 6: Client Receives Purchase Order Number Asynchronously and Polls for Purchase Order Status".

6.7 Retrieve data from reaction response.

The return value is extracted from the returned token, as a Java object.

Object object = resptoken.getReactionResponseObjectInstance();

6.8 Extract purchase order number from data.

The return value is then cast to its actual type.

Integer poNumber = retrievePONumber(object);

6.9 Commit the transaction.

This step is necessary because the client is outside of the AC4J container.

((OracleConnection)ac4j_sess.getJEMConnection().getConnection()).commit();

6.10 and 6.11 Look up the purchase order service bean, then poll its status until shipped.

After the purchase order number has been obtained, the client polls the purchase order entity bean directly to get the order status. This process continues until the status changes to shipped. This is regular EJB code and is not shown here.

AC4J Active EJB Deployment

The active EJB is developed as any other EJB. The changes that enable the EJB to be used in an AC4J interaction are in the OC4J-specific deployment descriptor. These are discussed below:

Deploy the EJB with AC4J element specifications in the OC4J-specific deployment descriptor. The following example defines the takeOrder EJB as an active EJB.

Here is the entire orion-ejb-jar.xml file for the three active EJBs:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE orion-ejb-jar PUBLIC "-//Evermind//DTD Enterprise JavaBeans 1.1 
runtime//EN" "http://xmlns.oracle.com/ias/dtds/orion-ejb-jar.dtd">

<orion-ejb-jar>
 <enterprise-beans>
   <entity-deployment name="Products" location="ejb/Product"
       table="PRODUCTS_PURCHASEORDERSERVICE" data-source="jdbc/nonEmulatedDS" >
   </entity-deployment>

   <jem-server-extension data-source-location="jdbc/nonEmulatedDS"
     scheduling-threads="1">
     <description>JEMServer Deployment</description>
   </jem-server-extension>

   <jem-deployment jem-name="JEMPurchaseOrderService" 
ejb-name="PurchaseOrderService" >
     <called-by>
           <caller caller-identity="JEMUSER" />
     </called-by>

     <security-identity>
       <description>using the caller identity</description>
       <use-caller-identity />
     </security-identity>
   </jem-deployment>
 </enterprise-beans>

 <assembly-descriptor>
   <security-role-mapping name="JEMUSER">
     <user name="JEMUSER" />
   </security-role-mapping>
   <default-method-access>
     <security-role-mapping name="&lt;default-ejb-caller-role&gt;" 
impliesAll="true" />
   </default-method-access>
 </assembly-descriptor>
</orion-ejb-jar>

Go to previous page Go to next page
Oracle
Copyright © 2002, 2003 Oracle Corporation.

All Rights Reserved.
Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index