Skip Headers

Oracle9iAS Containers for J2EE Enterprise JavaBeans Developer's Guide and Reference
Release 2 (9.0.2)

Part Number A95881-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

8
Active Components For Java

Active Components for Java (AC4J) enables applications to interact as peers in a loosely-coupled manner. Two or more applications participating in a business interaction, exchange information for requesting service and for responding results.

This document describes the architecture needed and the software provided to manage loosely-coupled, interactions between autonomous applications.

Future Needs of Business Applications

The future of business applications requires the ability to perform loosely-coupled interactions. That is, applications should be able to exchange information with other applications over a long period of time, without limiting resources, and by surviving system crashes. The following lists the requirements for loosely-coupled interactions:

  1. Autonomous peer--Each application, when interacting with another application, exists as an autonomous peer. That is, the responding application may choose to ignore the request, or to execute one or more functions on behalf of the requestor (possibly different than the one that the requestor asked for), before responding to the initiating application. As peers, both applications can make requests to each other, but neither can require submission from the other. Neither application can assume control over the resources that its peer application owns.

  2. No time constraints--Because the tasks performed sometimes take days, even months, to complete, the time limits imposed must be longer than what can be accomplished in a short period of time.

  3. Asynchronous exchange of information--Loosely-coupled interactions require that all exchange of information exists within an asynchronous environment. The inter-peer interaction can be one of the following:

    • Distribute information interaction--Sometimes applications must be able to distribute information asynchronously to its peer where no response is required. However, reliability of the delivery for this message must be ensured.

    • Request/response interaction between components in an asynchronous manner--Applications use a request and response mode of communication, where each entity knows where to respond back to with the results.

  4. Reliable, recoverable, and restartable--In order for any application to exist over such a period of time, the application must be reliable--that is, recoverable and restartable--in case of system failures during that time.

  5. Scalable--The application must be scalable. That is, the long-running application cannot block execution or lock resources for long periods of time. In order for the application to execute in a reasonable time frame, the framework must provide performance enhancements through concurrently executing computations.

  6. Tractable--The application must be able to define and track its business processes and their interaction patterns, as follows:

    1. What business processes have started/completed under what business conditions.

    2. What business processes are pending waiting for what business documents.

    3. What is the pattern of interaction of the business processes, where processes can exchange information and what information they are authorized to push/pull to/from other processes.

    4. What is the sequencing of execution of the defined processes.

Current Programming Models

The current architectures available are the following:

The following sections briefly describe these models and show why they do not provide the basis necessary for the five goals presented in "Future Needs of Business Applications".

Remote Procedure Call Model

The Remote Procedure Call (RPC) programming model facilitates a tightly-coupled environment that provides for request/response communication. Transactional RPC implementations provide for ACID qualities.

Most RPC implementations currently provide two modes of method invocations: synchronous and deferred synchronous.

Transactional RPC Synchronous Invocation

The client program blocks when a remote invocation is made and waits until the results arrive or an exception is thrown. Examples of application types that use transactional RPC implementations are EJB, and most CORBA applications. Web services are also based on the RPC model, but are not transactional.

Advantage

This model of communication--also called on-line or connected--is based on the request/response paradigm, where the requester and responder of the service are tightly-coupled. Tightly-coupled applications understand how to reply transparently to the requestor.

Disadvantage

The programs must be available and running for the application to work. In the event of a network or machine failure, or when the application providing the service is busy, the application is not able to continue forward with its processing work. In this case, the state is inconsistent and the application must decide to rollback to a consistent state through JTA. Also, it is not autonomous. One application can control resources of other applications for a long time.

JTA is based on the two-phase commit specification. Two phase commit protocol may cause loss of application autonomy in the case of network disconnection, where the coordinator is unable of making a coherent global decision over the outcome of the global transaction for a long period of time.

Example

If a purchase order is created and the customer wants to purchase 20 widgets, then the transactional RPC application must do two things:

  1. Check inventory for 20 widgets and ask for them to be shipped to the customer.

  2. Check the customer's credit to see if the customer has the ability to purchase these widgets.

In this example, an RPC synchronous application would (within a global transaction) do the following:

  1. Send a request to the inventory database and block until the answer returns.

  2. Send a request to the credit bureau and block until the answer returns.

If all request come back with a satisfactory report, then the transaction is committed and the purchase order is forwarded on to shipping. If one of the two requests fails, the transaction is rolled back. Granted, the application could have the following alternatives that prevents the transaction from being rolled back:

If the transaction is rolled back, the purchase order is voided.

RPC Deferred Synchronous Invocation

An RPC deferred synchronous invocation is queue-oriented. The client places a request in a queue and is then able to continue processing without blocking for the response. An example of this is a CORBA DII application.

Advantages

The client does not need to wait for a reply to the request. Instead, it continues processing. Then when the client wants to receive a response, it blocks or polls for the availability of the response. A response can only be delivered to the same process that made the original deferred request. Thus, if multiple deferred requests are pending, only one response is processed at a time.

Disadvantage

If the client is non-existent, then the response is lost. Thus, for deferred execution to work correctly in the presence of network, machine, and application failures, the requests must be stored persistently and processed exactly once.

Example

In the purchase order example, the requests to the inventory and credit bureau can be made in parallel. After executing both requests, the client can poll for both responses. The disadvantages would be the same as listed within the RPC synchronous invocation example.

Database Transactional Queuing Model

The database transactional queuing model supports a loosely-coupled environment where applications use one-way communication. Oracle AQ is an implementation of a database transactional queuing model.

Applications need to process and deliver each message exactly once, even in presence of multiple failures of the sender or the receiver. Mixing the transactional ACID construct with queue processing creates a model that enables applications to reliably process messages with the ACID guarantees.

Applications can be disconnected for long periods of time and occasionally they can reconnect to communicate, using messages. By de-coupling the applications that send messages from the applications that receive messages and process them, queuing facilitates complex scheduling of autonomous applications. Each message can be durably saved until processed exactly once. Processing of the data is performed in a time independent fashion, even in a situation where a message receiver is temporary unavailable.

Advantages

Delivers and processes messages exactly once, no matter whether the network or receiver application is available or not.

Disadvantages

This model is based on sending and receiving messages. It is not based on requesting and responding to service requests, which is the foundation of all business protocols for loosely-coupled applications. To satisfy this requirement, the application shoulders the burden of creating and parsing each message. Both sides must know the format, security, and headers required for each message. There is no automatic mechanism for routing messages and executing business methods. The implementation of application logic for these mechanisms is the responsibility of the applications. If a response is called for, the application cannot easily reply, because there is no context that captures the relationship between a requester and a responder application, which is the case for RPC. It is not intended for a request/response environment, so if the client needs a response back from the destination object, it must receive and parse a separate message off of its own queue.

Exception handling describes communication failures and not application exceptions.

There is no guarantee for the consistency of the business transactions. Instead, the program itself must guarantee that the application semantic rollbacks occur appropriately in a failure situation.

Example

In the purchase order example, the client would enqueue a message to the inventory queue and another to the credit bureau queue. Both must be reliably processed once in order for the transaction to commit. If either the inventory is not available or the client's credit is not good, the business transaction cannot be successfully completed and another message must be created to semantically rollback the one message that was processed positively.

AC4J Framework

The RPC and transactional database queuing models both have advantages and disadvantages. The disadvantages within J2EE application types are as follows:

The disadvantages prevent each model from solving the business goals laid out in "Future Needs of Business Applications". Thus, a new model is necessary to incorporate the advantages of both models and exclude the disadvantages.

AC4J is a manager of loosely-coupled interactions between autonomous EJB applications. You can partition the application into concurrently executing active units of work--known as Reactions--whose execution is driven by data availability and its purpose is to execute business logic and produce new data. AC4J coordinates the flow of data between Reactions. When data become available on AC4J, the conditions specified by all registered Reactions are checked and if satisfied, then the execution of the methods of all matched Reactions is triggered.

AC4J Architecture

AC4J allows EJBs to interact in a loosely-coupled fashion. It provides the following features:

Introduction to AC4J Components

AC4J provides a framework for loosely-coupled interactions, which are included in the following components:

Figure 8-1 demonstrates the relationship of these components to each other. The sections following describe each component.

Figure 8-1 Relationship of Databus, Interactions, ActiveEJBs, Processes, Data Tokens and Reactions

Text description of jem4.gif follows

Text description of the illustration jem4.gif

Active EJBs

An EJB provides a natural way for describing business object--such as a customer, a purchase order, or an invoice. The externally visible business tasks of a business object, which is accessible by other applications, is separated from their internal implementation details and are described in the EJB interface.

Traditional EJBs are passive, they must be ready to immediately service a request from a client and return results quickly. Failure to deliver on these promises 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 de-coupled from the actual service execution. The policies that control when and which EJB method(s) are actually invoked are controlled by the service provider EJB. This de-coupling permits service request and service providers to interact as autonomous peers.

An application can create or lookup 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 Active EJB name, the J2EE application name, the EJB JAR name, the EJB name, the EJB bean class name, the EJB home interface name, the EJB remote interface name, the instance name (SID) of the database in which the Databus resides on, and the primary key of the EJB that is available only for entity beans.

Interactions

An Interaction is a long-lived unit of work that reflects the behavior of a business transaction. A business transaction may span multiple applications that reside in different organizations. Contrary to the life of a local or a global transaction, the duration of these business transactions in this disconnected environment, can be long.

The Interaction represents a business goal that you want to complete. For example, if a customer wants to buy something from a business, the entire actions necessary to allow the customer to pay for and receive the item he/she wants is 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 may run in isolation and commit/rollback their own data without knowledge of other applications. However these applications should not be considered as different pieces, because the relationships formed amongst 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 Reaction(s). For example, once the supplier has confirmed the purchase order request back to the buyer, the buyer needs to register a compensating reaction that monitors additional responses from the supplier that may inform him that the purchase order cannot be fulfilled because the manufacturing department is running late. If the supplier's promise 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 him or abandon the purchase order process completely.

An Interaction is uniquely identified by an Interaction identifier (IID). An Interaction can contain multiple Processes.

Processes

A Process identifies a business task. In our purchase order example, a Process would exist for each of the following business tasks: creating a purchase order, checking inventory, checking customer credit, and shipping the order.

Each Process does the following:

Figure 8-2 demonstrates an Active EJB, Interaction, and two of its Processes.

Figure 8-2 Relationship of Active EJB, Interaction, and its Processes

Text description of jema.gif follows

Text description of the illustration jema.gif

A Process is uniquely identified by a JEMPortHandle object. A JEMPortHandle object encapsulates the Process Context and the JEMHandle of the Active EJB that the Process belongs to. The Process Context is an 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.

Reactions

A Reaction performs the detailed work of a Process. Using this construct, an application can specify its persistence interest on the availability of a collection of correlated data tokens which trigger the execution of an Active EJB method. A Reaction is a combination of the following:

In every Process, there is a base Reaction, which is implicitly created by AC4J when a Process is created as a result of a AC4J call operation. 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 Reaction template and the Active EJB method to be executed when matching succeeds.

Reactions (EJB methods) can access/modify shared database objects. These objects can be traditional database objects; thus, facilitating coarse grain information sharing in a transactional manner. Similarly, the Reactions exchange fine grain information--such as Active EJB method input parameters and return values--using the AC4J Databus.

The Reaction processes incoming requests, can return results based on the request, and enforces business constraints to preserve application consistency. When a Reaction is fired, it can consume one or more input data parameters, process them, and then possibly produce one or more output data tokens for other Reactions. Figure 8-3 demonstrates how when all data tokens are available and the conditions are matched, the Reaction fires, which causes the method to execute. This method may return results which are converted to data tokens by AC4J infrastructure and routed to the caller. This method may request additional services from other Active EJBs to complete the business task. These requests result in the creation of new data tokens that are pushed and routed by the AC4J Databus.

Figure 8-3 Reaction Pulled Data Tokens, Were Matched, Fired, and had its Active EJB Method Execute

Text description of jem3.gif follows

Text description of the illustration jem3.gif

Reactions inside a Process Context instance can push data tokens to the AC4J Databus in the following ways:

Reactions inside a Process context instance can pull data tokens from the AC4J Databus by registering one or more Reactions in the current Process Context instance using 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 necessary.

The following example demonstrates how one can receive an asynchronous communication between processes, but still have a request/response environment. The takeOrder Process is the business task for creating the purchase order. In order to create the purchase order, you must check the inventory and the customer's credit. Thus, the takeOrder Reaction invokes the following Processes:

After sending the asynchronous requests to the checkINV and checkCRED Processes, the takeOrder Reaction registers another Reaction in the same Process--procPO--that waits for the responses back from both the checkCRED and checkINV Processes. Once all data tokens expected from these Processes are available, the procPO Reaction fires and processes the responses. As shown in Figure 8-4, both the takeOrder and procPO Reactions exist in the same process, as they are components of the same request/response communication.

Figure 8-4 Relationship Of JEMPurchaseOrderBean Interface Methods, Its takeOrder Process (With Its takeOrder And procPO Reactions) And Its cancelOrder Process (With Its cancelOrder Reaction)

Text description of jem5.gif follows

Text description of the illustration jem5.gif


Note:

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


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 is requesting a service by using an AC4J call operation the system automatically pushes a request data token, which consists of 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 needed for sending the returned information to the caller Process and fills the port handle object of the response data token. In the case where the caller of the returning Process is a client and not another Process, then the Databus stores the response data token to a special Databus area from where the client can retrieve it using the JEMSession::receiveReactionResponseObjectInstance operation.

The data types of the objects carried inside an in or out data tokens can be basic data types (such as Integer, String, Float, Boolean) or constructed class types (such as Java serializable objects).

Databus

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

Matching Reactions

The Databus routing subsystem is responsible for making the different types of data tokens available at the specified destination, Process Context instance that is a union of { Interaction identifier and Process Activation identifier }, specified by a JEMPortHandle object.

When data tokens are routed and become available in the Databus inside a Process Context instance, AC4J tries to match these data tokens with all registered Reaction(s) available in that context instance. The system tries to match the data token tags 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 token(s) does not mean that a registered Reaction will match immediately. It is only when all data tokens, required by a Reaction, become available that matching succeeds. For example, inside takeOrder Process the takeOrder base Reaction has registered the procPO Reaction that is the waiting for the checkCRED and checkINV Processes to respond. When checkINV Process responds to the takeOrder Process, procPO Reaction is not matched because it is also waiting for the checkCRED Process to respond. It is when the checkCRED Process responds to the takeOrder Process that the procPO Reaction is matched.

Additionally, data token(s) available in the Databus, may be matched with a Reaction that will be registered in the future. This can be used for sequencing Processes, where the completion of one Process can enable another Process. Inside the same Interaction, the takeOrder Process must be completed before cancelOrder Process can start executing. If takeOrder Process has not completed but cancelOrder Process is requested from a client, its base Reaction, which is implicitly created by the system, will not be matched because it is waiting for the completion data token of the takeOrder Process to be available. If takeOrder Process has completed (already pushed its completion data token), then cancelOrder Process is requested from a client and it will be immediately matched, because the completion data token of the takeOrder Process is already available.

Matching data tokens with Reactions triggers the activation of zero, one, or more Reactions, which are executed in parallel if they don't 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 matched with a Reaction, AC4J verifies that the types (primitive or class types) of the data tokens matched on the tags, also match the types of the Reaction Active EJB method types. 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. Then the EJB method, of the fired Reaction, gets 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/responses that have been sent become visible. The activation of a Reaction has "exactly once" semantics, 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/responses are never sent to any recipient component. In case of failures, the firing of a Reaction will be retried by the Databus for a pre-configured number of attempts. The Reaction is marked as completed, with exception completion status, if the maximum retry attempts are reached.

In traditional database machines, where the duration of a transaction is short, abnormal situations cause the whole transaction to be undone, so all performed work is lost and needs to be submitted again for execution. Since Interactions have usually long duration and contain a large number of Reaction(s), AC4J provides additional mechanisms to handling exceptions (such as an Oracle9iAS node crash or an Oracle database node crash).

A Reaction is automatically persisted in the Databus by AC4J 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 crashes, all Reactions that were running and did not end successfully are rolled back. Then, the interrupted Reactions will be re-executed by another OC4J instance.

AC4J uses a mechanism to capture, propagate, and match the application state and control flow information needed for resuming an application after the crash. 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) in order to facilitate the storage of the control flow descriptors or the storage of data variables.

Relationships of Databus, Data Tokens, and Reactions

Figure 8-5 demonstrates how data tokens cause Reactions to fire, and Reactions send new data tokens to other Reactions over the Databus. The Databus coordinates and matches the data tokens with its Reactions.

Figure 8-5 Databus, Data Tokens, and Reactions

Text description of jem6.gif follows

Text description of the illustration jem6.gif

Once the method completes, the Reaction can send information in the form of a data token to another Reaction. All data tokens are sent asynchronously from one Reaction to another over a data channel known as the AC4J Databus. The AC4J Databus routes the data tokens from a producer Reaction to one or more consumer Reactions.

Set Up Oracle Database For AC4J Support

Before you can execute any Interactions, you must initialize an Oracle9i database as a repository for the AC4J Databus. You must configure it to include the following:

These can be added to your Oracle9i database with scripts that are contained in the ac4j-sql.jar file that was downloaded with your Oracle9iAS installation. Unzip this JAR file. This JAR file contains a README.TXT that discusses the different SQL command options that are available to you. These are also described below:

In order to create AC4J capabilities, you must execute one of the following SQL scripts as a 'SYS' user on the same machine as the database.

AC4J Databus XML Configuration

The Interaction supports JTA global transactions within the database that the Databus exists in. Thus, you need a non-emulated data source for the super user to handle the two-phase commit and a non-emulated data-source for the client to send its asynchronous requests to the Databus. See the DataSource and JTA Chapters in the Oracle9iAS Containers for J2EE Services Guide for a full description of this configuration.

For our purchase order example, the following data sources are configured in the data-sources.xml file for the two-phase commit.


<!--NON-Emulated DataSource for two-phase commit used by super user-->
<data-source
                class="com.evermind.sql.OrionCMTDataSource"
                location="jdbc/jemSuperuserDS"
                username="jemuser"
                password="jempasswd"
                url="jdbc:oracle:thin:@<host>:<port>:<ORCL-SID>"
                inactivity-timeout="60" >
                <property name="dblink"
                    value="JEMLOOPBACKLINK.REGRESS.RDBMS.DEV.US.ORACLE.COM" />
</data-source>

<!--NON-Emulated DataSource for the client user -->
<data-source
                class="com.evermind.sql.OrionCMTDataSource"
                location="jdbc/jemClientDS"
                username="jemcliuser"
                password="jemclipasswd" 
                url="jdbc:oracle:thin:@<host>:<port>:<ORCL-SID>"
                inactivity-timeout="60" >
                <property name="dblink"
                   value="JEMLOOPBACKLINK.REGRESS.RDBMS.DEV.US.ORACLE.COM" />
</data-source>

Both of these users were created as default with the SQL scripts listed earlier. The jemuser is the super username and the jemcliuser is the default client username. The DBLINK is the link to the database that contains the Databus. For the super user data source, this is a loopback link.

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 portion of the purchase order example listed in Figure 7-6. The code sample does not show error handling or import statements to simplify the example. Download the full example off the OTN site.

Example 8-1 Purchase Order Example

For the purchase order, the POInteraction is created. Within the Interaction, several business tasks exist as follows:

Our example is, as follows:

Figure 8-6 illustrates the information flow inside an Interaction. Figure 8-6 also demonstrates how all of the Reactions act on data tokens and provide data tokens to other Processes. This assumes that the customer data has already been made available to the takeOrder Process. The numbers designate the order in which they fire. That is, the procPO is dependent on data tokens from both the checkINV and checkCRED Processes; thus, it cannot fire until both return their responses back to the takeOrder Process.

Figure 8-6 Information Flow Inside An Interaction

Text description of jem2.gif follows

Text description of the illustration jem2.gif

The steps involved in processing the Purchase Order example of Figure 8-6 can be summarized as follows:

  1. Client sends an asynchronous request to an Active EJB: The client requests a service from an Active EJB, JEMPurchaseOrderBean. The client starts a new purchase order by sending an asynchronous request through the Databus to a takeOrder Process.

  2. Active EJB processes the client's request: The takeOrder Process starts a takeOrder base Reaction. This base Reaction starts a new purchase order. To complete the purchase order, it must do three things:

    1. Send an asynchronous request to the checkINV Process of JEMInventoryBean to verify that the items are in inventory.

    2. Send an asynchronous request to the checkCRED Process of JEMCreditBean to verify that the customer's credit is satisfactory.

    3. Register a procPO Reaction in the current Process to receive the results from the above two Processes.

  3. Asynchronous response to the requesting Active-EJB: Both the checkINV and checkCRED Processes return responses to the takeOrder Process.

  4. Asynchronous response to the client: The procPO Reaction, within takeOrder Process, reacts to the information provided by the checkINV and checkCRED Processes. If satisfactory, it sends the confirmation to the client through the AC4J Databus.

  5. Client receives the response: Client retrieves the response from the Databus.

Asynchronous Request to An Active EJB

The following code sample shows steps involved in performing loosely-coupled interactions in AC4J.

Example 8-2 Client Asynchronously Invoking Active EJB

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

// 0. create a JNDI context
Context  context = new  InitialContext();

// 1. lookup a datasource wher Databus exists
DataSource clientDS = (DataSource)
                      context.lookup("java:comp/env/jdbc/jemClientDS");
// 2. Get a JDBC-connection to the database where Databus resides
Connection conn = clientDS.getConnection("jemcliuser", "jemclipasswd");

// 3. Create an AC4J connection using the JDBC connection
JEMConnection AC4JConn = new  JEMConnection(conn);

// 4. Create an AC4J session over an AC4J connection to the Databus
JEMSession AC4JSess = new  JEMSession(jemconn);
// 5. Lookup the Active EJB handle using the jem-name defined
//    in the orion-ejb-jar.xml
JEMHandle activeEJBHandle = 
(JEMHandle)context.lookup("JEMPurchaseOrderBean");

// 6. Gather the base Reaction input parameters. These input parameters are
//    required by the receiving method, takeOrder.
Object[] inputParams = new  Object[] { (Object) new  String("user1"), 
                                       (Object) new  Integer("1234-119"),
                                       (Object) new  String("pens"),
                                       (Object) new  Integer("3")   };

// 7. Create the Process Context, Interaction-ID and Activation ID.
//    NOTE: IID = "user1" = requestor's name
//          AID = AID = AID_105_user1 = AID_<PO_number>_<cust_name>

// 8. Make the call over the AC4J session providing the parameters.
JEMEmitToken req = AC4JSess.call("user1", "AID_105_user1",
                                 activeEJBHandle, "takeOrder",
                                 null, inputParams, null, 0, 0);

// 9. Commit the changes to the Databus by committing the transaction
conn.commit();

// 10. The client must close the AC4J session and connection because it  
//     does not exist within an AC4J container, which would normally
//     close these.
conn.close();
jemconn.close(); 
jemsess.close();
}

The client exists outside of an AC4J server and is requesting a service from an Active EJB through the AC4J Databus. The AC4J Databus is the conduit and controls the asynchronous communication between the client and all Reactions. Thus, every client residing outside of an AC4J server must first connect to the AC4J Databus and create a new session for interaction to occur.

After you have retrieved a connection to the AC4J Databus and created an AC4J session within it, you can send asynchronous messages to Active EJBs in the same or other AC4J instances. The AC4J Databus coordinates the asynchronous messages and acts as a transactional manager for all AC4J beans involved in the transaction. The steps involved in creating a AC4J-session and completing the client's request are described in the following subsection.

Connect To the AC4J Databus

The following steps explain the details of a set of steps involved in creating a AC4J session on the AC4J Databus. These steps are sub-set of steps shown in Example 8-2 (Numbered 0-to-4).

  1. Retrieve an AC4J connection

    An AC4J connection exists above a JDBC connection. Perform the following:

    1. Retrieve the DataSource defined for the database acting as the AC4J conduit. The DataSource used should be defined in the data-sources.xml as a non-emulated datasource with a <dblink> defined to the database where the AC4J Databus resides. See "AC4J Databus XML Configuration" for more information.

      Context context = new InitialContext();
      DataSource clientDS = (DataSource)
                     context.lookup("java:comp/env/jdbc/jemClientDS");
      
      
    2. Retrieve the JDBC connection off of the DataSource object.

      OracleConnection conn = (OracleConnection)clientDS.getConnection();
      
    3. Create an AC4J connection off of the JDBC connection object.

      JEMConnection AC4JConn = new JEMConnection(conn);
      
      
  2. Create an AC4J session in a specified Databus. Using the AC4J connection to the database and providing the name of the Databus you are interested in, create a session within the Databus in the indicated Oracle database.

    JEMSession AC4JSess = new  JEMSession(AC4Jconn);
    

Executing an Asynchronous Request

After you have created an AC4J session on the AC4J Databus, the client can send asynchronous messages to Active EJBs. The client must provide the Active EJB handle, the Process handle, and all of the required input parameters to the base Reaction. The following steps explain the details of the call that client needs to make in order to complete the AC4J request.

  1. Process Context: To identify the context where the Process exists, you must provide both the Interaction identifier and the Process Activation identifier. The combination of both of these identifiers is the Processing Context. There are two ways of providing a Process Context:

    • Client Provides--The AC4J Databus uses the identifiers provided by the client to uniquely identify the Processing Context. The client uses the same identifiers to either retrieve the response to the current request or send additional parameters to the Process. In the current example, the client provides the Interaction Identifier (IID) as a customer's name and Process Activation Identifier (P-AID) as an union of purchase order number and the customer's name as shown:

      String iid = "user1";         // = customer_name 
      String p_aid = "AID_105_user1"; // = AID_<PO_number>_<customer_name>
      JEMEmitToken req = AC4JSess.call(iid, p_aid, ..all other parameters..);
      
    • Automatic context--The Interaction and Process Activation identifiers are optional and can be omitted or can be null, in which case the system automatically creates them. If a client fails to provide either of these identifiers then the AC4J Databus will create them to uniquely identify a processing context. However, the client will have to retrieve these identifiers and use them later to pull the response from the AC4J Databus.

      JEMEmitToken req = 
               AC4JSess.call (null, null, ...all other parameters...);
      JEMPortHandle portHandle = req.getPortHandle();
      String iid = portHandle.getIid();
      String p_aid = portHandle.getAid();
      
      
  2. Active EJB handle: In a synchronous EJB environment, you would use a remote EJB handle for invocation. In an AC4J asynchronous environment, you must provide a similar handle of class type JEMHandle that identifies an active EJB. The Active EJB handle can be obtained by looking up the jem-name defined in the orion-ejb-jar.xml (see Active EJB Deployment pp 7-32).

    Context context = new InitialContext();
    JMHandle activeEJBHandle =
              (JEMHandle) context.lookup("JEMPurchaseOrderBean");
    JEMEmitToken req = AC4JSess.call(...., activeEJBHandle, ....);
    
    
  3. Reaction Name and Input parameters: Client provides the base Reaction (Method) name and all or part of it's input parameters that it wishes to call. In the current example, the client provides all the input parameters to complete the AC4J session call as follows:

    // collect input values for the takeOrder method
    Object[] inputParams = new Object[] { (Object) new String("user1"),
                                          (Object) new Integer("1234-123"),
                                          (Object) new String("pens"),
                                          (Object) new Integer("3")
                                        };
    JEMEmitToken req = AC4JSess.call(..., "takeOrder", null, inputParams,
                                     ...);
    
    
    • If client provides only a part of the parameters to this Reaction then it must provide a set of input parameter types and the indexes of the input parameters as well. In the following example we show how a client can complete a call by providing the first two parameters for takeOrder Process:

      
      // input parameter types (java-class) for takeOrder method
      Class[] takeOrderInputClassTypes =
                  new Class[] { String.class, Integer.TYPE,
                                String.class, Integer.TYPE };
      
      // indexes of input parameters you wish to provide for takeOrder method
      int[] indexOfInputParams = new int[] {0, 1};
      
      // input values corresponding to the indexes for takeOrder method
      Object[] inputParams = new Object[] { (Object) new String("user1"),
                                            (Object) new Integer("1234-123")
                                          };
      // remember the interaction and process-activation ids for this call
      JEMEmitToken req =
             AC4JSess.call(iid, p_aid, ..., "takeOrder",
                                  takeOrderInputClassTypes,
                                  indexOfInputParams, inputParams, ...);
      
      
    • When the client decides to provide the remaining two parameters it must use the same Process Context (Interaction and Process-Activation Identifiers) that it used in the first call it made to the Process. During the second invocation the steps involved will be

      // input parameter types (java-class) for takeOrder method
      Class[] takeOrderInputClassTypes =
                  new Class[] { String.class, Integer.TYPE,
                                String.class, Integer.TYPE };
      
      // indexes of input parameters you wish to provide for takeOrder method
      // NOTE: now, client provides the last 2-input parameters
      int[] indexOfInputParams = new int[] {2, 3};
      
      // input values corresponding to the indexes for takeOrder method
      Object[] inputParams = new Object[] { (Object) new String("pens"),
                                            (Object) new Integer("3")
                                          };
      // use the same interaction and process activation ids as those in the 
      // previous call
      JEMEmitToken req =
             AC4JSess.call(iid, p_aid, ..., "takeOrder",
                                  takeOrderInputClassTypes,
                                  indexOfInputParams, inputParams, ...);
      
      
  4. AC4J Session call: Send all asynchronous requests for any Active EJB to the AC4J Session using the JEMSession::call method.

    When a Reaction wants to provide data to an active EJB method (to the base Reaction of the Process), it executes a JEMSession::call with this information. The JEMSession::call contains the Interaction identifier that the EJB is involved in, 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 are optional and can be omitted or can be null, in which case the system automatically creates them. The Databus identifies the context where the Process can be found and routes the data tokens to the intended Process. Thus, all EJB calls are invoked asynchronously through the mediation of the Databus.

  5. Commit Transaction: The client must commit the changes to the AC4J Databus. If the client forgets to commit the transaction then the request is lost and is not visible to the AC4J Databus. To make the request visible to the AC4J Databus by doing the JDBC-commit as follows:

    conn.commit();
    
    
  6. Finally, the client must close the JDBC-connection, the AC4J session and connection because it does not exist within an AC4J container. The AC4J container would normally close the AC4J session and connection objects.

    conn.close(); // client as well an application-code must close
    jemconn.close(); // client must close
    jemsess.close(); // client must close
    

Active EJB processes the Client's Request

Once the client commits the request, the AC4J Databus matches the data-tokens provided by the client with that of the requested Reaction, and internally schedules the instantiation of the JEMPurchaseOrderBean Active EJB and activation of the takeOrder Process. The takeOrder Process starts a takeOrder base Reaction which starts a new purchase order. As discussed in Figure 7-6, this reaction, takeOrder, processes the client's request by invoking additional services from the other Active EJBs, JEMInventoryBean and JEMCreditBean, as shown in the following code sample:

Example 8-3 Active EJB Asynchronously Invoking Another Active EJB

public void takeOrder(String clientName, int creditCardNumber,
String itemName, int quantity) throws RemoteException, TestException {

// 0. create a JNDI context
Context context = new InitialContext();

// 1. Retrieve the current AC4J Reaction.
JEMReaction currentAC4JReaction = (JEMReaction) JEMReaction.getReaction();

// 2. Lookup the Active EJB handle using the jem-name defined
//    in the orion-ejb-jar.xml
JEMHandle activeInvHandle = (JEMHandle) context.lookup("JEMInventoryBean");

// 3. Gather all input and return parameters for the checkINV Reaction.
//    Define input and return parameter types and the parameter values
Object[] checkINVInputParamValues = 
            new Object[] { (Object)itemName,
                           (Object) new Integer (quantity) };
Class[] checkINVReturnClassType = new Class[] { Boolean.TYPE };

// 4. Request a service from JEMBeancheckINV through Databus
JEMEmitToken inventoryRequest=
               currentAC4JReaction.call(activeInvHandle, "checkINV", null,
                                        checkINVInputParamValues,
                                        checkINVReturnClassType,
                                        null, null, 0,  0);
// 5. Repeat Steps 2-4 above to request a service from another
//    Active EJB, JEMCreditBean. The returned JEMEmitToken is
//    named creditRequest.

// 6. Register a Reaction, procPO, that will be activated when the
//    responses from the above two asynchronous calls to the
//    active-EJBs return
Class[] procPOInputClassTypes = new  Class[] { Boolean.TYPE, String.class };
JEMEmitToken[] requests = new JEMEmitToken[] { inventoryRequest, 
                                               creditRequest };
currentJEMReaction.registerReaction
             ("procPO", procPOInputClassTypes, requests, 1, null, null, 0);
}

The AC4J Databus instantiates the Active EJB, JEMPurchaseOrderBean (corresponding to the JEMHandle provided by the client), in an AC4J server. The takeOrder Process starts a takeOrder base Reaction. The steps involved in the completion of this initiation process are described below:

  1. Process Context: The current Reaction, takeOrder, is running in an AC4J server. Hence, it already has a Process Context and can be used by the application (or Active Bean) code. The application code can retrieve the Process Context through the Demarcation as follows:

    // retrieve the current-Reaction context--a static method
    JEMReaction currentAC4JReaction = (JEMReaction) JEMReaction.getReaction();
    String iid = currentAC4JReaction.getIid();
    String p_aid = currentAC4JReaction.getAid();
    
    
    • The application may use these identifiers to make additional asynchronous JEMSession::call by co-relating the business transaction.

    • Alternatively, the application code may use the currentAC4JReaction to make the additional calls with request/response characteristics. The AC4J Databus then creates a new Process Contexts for the next invocation by using the current Interaction Identifier and a new Process Activation Identifier. The current example uses this approach by using the currentAC4JReaction.

  2. AC4J handle: The base Reaction, takeOrder, starts the purchase order initiation process by requesting services from two other Active EJBs, JEMInventoryBean and JEMCreditBean. The application code needs to retrieve the AC4J handles to these Active EJBs by doing the following:

    Context context = new InitialContext();
    // call to JEMInventoryBean
    JEMHandle activeInvHandle = (JEMHandle) context.lookup("JEMInventoryBean");
    JEMEmitToken inventoryRequest= 
                    currentAC4JReaction.call(activeInvHandle, .....);
    
    // call to JEMCreditBean
    JEMHandle activeCreditHandle = (JEMHandle) context.lookup("JEMCreditBean");
    JEMEmitToken creditRequest= 
                    currentAC4JReaction.call(activeCreditHandle, .....);
    
    
  3. Reaction Name, Return parameter type and Input parameters: Client (now a takeOrder Reaction) provides the base Reaction (Method) name, the return parameter's java-class type and all or part of it's input parameters that it wishes to call. In the current example, the client provides all the input parameters needed by the called Reactions (checkINV, CheckCRED) as follows:

    // collect input values for the checkINV method
    Object[] checkINVInputParamValues = 
                new Object[] { (Object)itemName,
                               (Object) new Integer (quantity)
                             };
    
    // state the return Class type of checkINV method
    Class[] checkINVReturnClassType = new Class[] { Boolean.TYPE };
    
    // make the call to the checkINV method
    JEMEmitToken inventoryRequest=
                   currentAC4JReaction.call(..., "checkINV", null,
                                            checkINVInputParamValues,
                                            checkINVReturnClassType, ....);
    
    // collect input values for the checkCRED method
    Object[] checkCreditInputParamValues = 
                new Object[] { (Object) clientName,
                               (Object) new Integer (creditCardNumber),
                               (Object) new Float (quantity * 1.4) };
    
    // state the return Class type of checkINV method
    Class[] checkCreditReturnClassType = new Class[] { String.class };
    
    // make the call ro checkCRED method
    JEMEmitToken creditRequest=
                   currentAC4JReaction.call(..., "checkCRED", null,
                                            checkCreditInputParamValues,
                                            checkCreditReturnClassType, ....);
    
  4. Register a Return Reaction: The application code then registers a new Reaction, procPO, in the same Process Context of the currentAC4JReaction. This registration of the Reaction requires the Reaction name, procPO, the input parameter types of the new procPO reaction and the JEMEmitTokens retrieved from the call to the currentAC4JReaction. If the new Reaction has multiple input parameters and is receiving them from different Processes then the Array of JEMEmitToken must be constructed in proper order. For example, in the following code the first parameter is waiting for the reply from the JEMInventoryBean and the second one is waiting for the reply from JEMCreditBean.

    Class[] procPOInputClassTypes = new  Class[] { Boolean.TYPE, String.class };
    JEMEmitToken[] requests = new JEMEmitToken[] { inventoryRequest, 
                                                   creditRequest };
    currentJEMReaction.registerReaction
                 ("procPO", procPOInputClassTypes, requests, 1, null, null, 0);
    

Asynchronous Response to the Requesting Active EJB

The takeOrder base Reaction is completed only after the AC4J infrastructure commits the transaction which includes the calls to the other two Active EJBs and a registered Reaction. The "checkINV" and "checkCRED" Processes receive the requests from the AC4J Databus as if it 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 from the AC4J Databus, which were initiated from the takeOrder Reaction. Both of them receive the request, perform their tasks, and return. The returned values are forwarded by the AC4J Databus to the registered Reaction--procPO.

The following code sample shows the checkINV method. The checkCRED method is similar in its AC4J responsibilities.

Example 8-4 checkINV Processes Request

public boolean checkINV(String itemName, int quantity)
            throws RemoteException, TestException
{

boolean inventoryExists = false;
// The logic in the next step is ommitted
inventoryExists = query it's own database for the item and quantity;
return inventoryExists;
}

Asynchronous Response to the Client

Both checkINV and checkCRED Processes return the responses to the procPO Reaction through the AC4J Databus. The AC4J Databus makes sure that the return data-tokens have valid takeOrder Process Context and matches the input parameter types of the procPO Reaction. When both parameters arrive the procPO Reaction fires and executes the procPO method of the JEMPurchaseOrderBean Active EJB, which reacts to the information provided by the checkINV and checkCRED Processes. It completes the client's request by posting the result to the AC4J Databus.

Example 8-5 procPO Reaction Fires

public String procPO(boolean inventoryExists, String creditInfo)
            throws RemoteException, TestException
{
   String poStatus = "Not Shipped";
   if(creditInfo == null)
      return poStatus;

   if (inventoryExists)
   {
      if(creditInfo.equalsIgnoreCase("Credit approved"))
         poStatus = "Shipped";
      else if (creditInfo.equalsIgnoreCase("Credit failed"))
         poStatus = "Credit failed";
   }
   else
      poStatus = "Items unavailable";

   return poStatus;
}

Receive Response by the Client

The client needs to know the response to it's purchase order request. As stated earlier, each request (or call) is identified by a Process Context (Interaction ID and Activation ID). Using the Process Context the client can pull the response from the AC4J Databus.

The received JEMEmitToken from the response can then be parsed by the client. If the client existed inside the OC4J container, the container would deconstruct the JEMEmitToken to the required type. Instead, the client must parse out the response correctly as shown below:

Example 8-6 Client Processes Return

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

// 0. create a JNDI context
Context  context = new  InitialContext();

// 1. lookup a datasource wher Databus exists
DataSource clientDS = (DataSource)
                      context.lookup("java:comp/env/jdbc/jemClientDS");

// 2. Get a JDBC-connection to the database where Databus resides
Connection conn = clientDS.getConnection("jemcliuser", "jemclipasswd");

// 3. Create an AC4J connection using the JDBC connection
JEMConnection AC4JConn = new  JEMConnection(conn);

// 4. Create an AC4J session over an AC4J connection to the Databus
JEMSession AC4JSess = new  JEMSession(jemconn);

// 5. Lookup the Active EJB handle using the jem-name defined
//    in the orion-ejb-jar.xml
JEMHandle activeEJBHandle =
            (JEMHandle) context.lookup("JEMPurchaseOrderBean");

// 6. Retrieve the Response using the Process context with which
//    the initial request was made.
JEMEmitToken  rcvresp = AC4JSess.receiveReactionResponse
          ("user1", "AID_105_user1", activeEJBHandle, "takeOrder", 0);

// 7. The getReactionResponseObjectInstance method parses the returned
//    parameter into an java.lang.Object. 
Object obj = rcvresp.getReactionResponseObjectInstance();
// 8. Print out results
if (obj  instanceof  java.lang.String)
String  ret = (String)  obj;

// 9. The client must commit the transaction 
conn.commit(); 

// 10. The client must close the AC4J session and connection because it  
//     does not exist within an AC4J container, which would normally
//     close these. 
conn.close();
jemsess.close(); 
jemconn.close();
}

As seen earlier, procPO reaction reacts to the information provided by the checkINV and checkCRED Processes. It completes the client's request by posting the result to the AC4J Databus. The client must connect to the AC4J Databus to retrieve it's response by providing a proper Process Context. The steps involved in connecting to the AC4J Databus were described earlier. After receiving the response the client can retrieve a java.lang.Object instance which must be processed further.

Retrieving an Asynchronous Response

After creating a AC4J session on the AC4J Databus, the client can retrieve the response by doing the following steps:

  1. Process Context: The client must provide a proper Process Context that identifies where the request was made. The client must provide both the Interaction identifier and the Process Activation identifier. In the current example the client provides the Interaction Identifier (IID) as a customer's name and Process Activation Identifier (P-AID) as an union of purchase order number and the customer's name as shown:

    String iid = "user1";         // = customer_name 
    String p_aid = "AID_105_user1"; // = AID_<PO_number>_<customer_name>
    JEMEmitToken  rcvresp = AC4JSess.receiveReactionResponse
              (iid, p_aid, ...);
    
    
  2. Active EJB handle: The client must provide the Active EJB handle to which the initial request was made. The Active EJB handle can be obtained by looking up the jem-name defined in the orion-ejb-jar.xml (see "AC4J Active EJB Deployment").

    Context context = new InitialContext();
    JMHandle activeEJBHandle =
              (JEMHandle) context.lookup("JEMPurchaseOrderBean");
    JEMEmitToken  rcvresp = AC4JSess.receiveReactionResponse
              (..., activeEJBHandle, ...);
    
    
  3. Reaction Name: Client may need to provide the process name to which it initiated the call, which, in this case, is the takeOrder Process.

    JEMEmitToken  rcvresp = AC4JSess.receiveReactionResponse
              (..., "takeOrder", ...);
    
  4. Retrieve Object: The JEMEmitToken received from the receiveReactionResponse can be used to retrieve the Object instance as follows:

    Object obj = rcvresp.getReactionResponseObjectInstance();
    
    
  5. Commit Transaction: The client must commit the changes to the AC4J Databus. If the client forgets to commit the transaction then the client can pull the response multiple times. However, it is not a recommended mode of operation. To let the AC4J Databus know that the response was properly retrieved do the following:

    conn.commit();
    

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

The following is the entire orion-ejb-jar.xml file for the three Active EJBs.

<?xml version="1.0"?>
<!DOCTYPE orion-ejb-jar PUBLIC "-//Evermind//DTD Enterprise JavaBeans 1.1 
runtime//EN" "http://www.orionserver.com/dtds/orion-ejb-jar.dtd">

<orion-ejb-jar deployment-version="1.4.5" deployment-time="e60dffcea9">
<enterprise-beans>
   <jem-server-extension 
      data-source-location="jdbc/nonEmulatedDS"  
      scheduling-threads="1">
      <description>AC4J deployment</description>
   </jem-server-extension>

   <jem-deployment jem-name="JEMPurchaseOrderBean"
             ejb-name="PurchaseOrderBean">
      <description>Active Purchase Order bean</description>

      <called-by>
         <caller  caller-identity="JEMCLIUSER"/>  
      </called-by>

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

   <jem-deployment jem-name="JEMInventoryBean"
                ejb-name="InventoryBean">
      <description>Active Inventory bean</description>

      <called-by>
         <caller  caller-identity="JEMCLIUSER"/>
      </called-by>

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

   <jem-deployment jem-name="JEMCreditBean"
             ejb-name="CreditBean">
      <description>Active Credit bean</description>

      <called-by>
         <caller  caller-identity="JEMCLIUSER"/>
      </called-by>

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

</enterprise-beans>


Go to previous page Go to next page
Oracle
Copyright © 2002 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