Oracle9iAS Containers for J2EE Enterprise JavaBeans Developer's Guide Release 2 (9.0.3) Part Number A97677-01 |
|
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 the purpose of requesting service and responding with results.
This document describes software Oracle provides to manage loosely coupled interactions between autonomous applications. It also discusses the architecture necessary to run the software.
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. Loosely coupled interactions have the following requirements:
Until AC4J, the following two architectures were available:
The following sections briefly describe these models and show why they do not offer the basis necessary for the six goals presented in "Future Needs of Business Applications". After the two architectures are described, AC4J is shown to be not just a new technology, but one that builds on the two architectures, eliminating their negatives and drawing on their positives. AC4J has the look and feel of Remote Procedure Call and database queueing, which it uses as building blocks.
One of the building blocks of AC4J, the Remote Procedure Call (RPC) programming model, facilitates a tightly coupled environment that provides for request-response communication. Transactional RPC implementations provide for ACID (atomicity, consistency, isolation, and durability) qualities.
Most RPC implementations currently provide two modes of method invocations: synchronous and deferred synchronous.
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.
This model of communication--also known as online or connected--is based on the request-response paradigm, in which the requester and responder of the service are tightly coupled. Tightly coupled applications understand how to reply transparently to the requester.
The programs must be available and running for the application to work. In the event of a network or system 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 roll back to a consistent state through JTA (Java Transaction API). In addition, the application is not autonomous. One application can control resources of other applications for a long time.
JTA is based on the two-phase commit specification. The two-phase commit protocol can cause loss of application autonomy in the case of network disconnection, where the coordinator is incapable of making a coherent global decision over the outcome of the global transaction for a long period of time.
If a purchase order is created and the customer wants to purchase 20 widgets, then the transactional RPC application must do two things:
In this example, an RPC synchronous application would (within a global transaction) do the following:
If both requests 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; of course, to prevent rollback, the application could perform the following alternatives:
If the transaction is rolled back, the purchase order is voided (unless one of the alternatives is performed).
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 (Dynamic Invocation Interface) application.
The client does not need to wait for a reply to the request. Instead, the client continues processing. Then when the client wants to receive a response, it blocks or polls for the availability of the response. A response can be delivered only 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.
If the client is nonexistent, then the response is lost. Thus, for deferred execution to work correctly in the presence of network, system, and application failures, the requests must be stored persistently and processed exactly once.
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 are the same as listed within the RPC synchronous invocation example.
Another of the building blocks, the database transactional queueing model, supports a loosely coupled environment where applications use one-way communication. Oracle AQ is an implementation of a database transactional queueing model.
Applications need to process and deliver each message exactly once, even in the 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 decoupling the applications that send messages from the applications that receive messages and process them, queueing 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 in which a message receiver is temporarily unavailable.
Delivers and processes messages exactly once, no matter whether the network or receiver application is available.
This model is based on sending and receiving messages, not on requesting and responding to service requests. Sending and receiving messages is the foundation of all business protocols for loosely coupled applications. To satisfy this requirement, the application is responsible for 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; this is not true for RPC. This model 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--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 (semantics of undoing a process but not necessarily restoring the original state) occur appropriately in a failure situation.
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 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 roll back the one message that was processed positively.
The RPC and transactional database queueing models both have advantages and disadvantages. The disadvantages keep them from being the best solution. The disadvantages within J2EE application types, and the reasons that previous methods did not work, 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.
Arising out of and building upon the previous two models, 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 allows EJBs to interact in a loosely coupled fashion. It provides the following features:
All the preceding features are integrated in the EJB framework.
AC4J provides a framework for loosely coupled interactions, which are included in the following components, each described more fully after this section:
Figure 10-1 demonstrates the relationship of these components to each other. The sections following (up to, but not including, "Configuring Oracle Databases to Support AC4J") describe each component.
An EJB provides a natural way for describing a business object--such as a customer, purchase order, or invoice. The externally visible business tasks of a business object, which is accessible by other applications, are 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 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 de-coupling permits service request and service providers to interact as autonomous peers.
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:
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. 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, all the actions necessary to allow the customer to pay for and receive the item he 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 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 that monitors additional responses from the supplier that may 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.
A process identifies a business task. In our purchase order example, a process exists 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 10-2 demonstrates an Active EJB, an interaction, and two of its processes.
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.
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. A reaction is a combination of the following:
When a process is created as the result of a 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 reaction template and the Active EJB method to be executed when matching succeeds.
Reactions (EJB methods) can access and 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 data bus.
The reaction processes incoming requests, returns 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 10-3 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. This method 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:
JEMReaction::call
operations that request service from other processes in the same or different interaction context instance
JEMReaction::registerReactionTimer
operation
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 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. To create the purchase order, you must check the inventory and the customer's credit. Thus, the takeOrder
reaction invokes the following processes:
checkINV
--Under the conditions that the customer asks for a new purchase and provides the data of the items wanted, the checkINV
process is activated, its JEMInventoryBean Active EJB is instantiated, and its base reaction--checkINV
--reacts. Later, it returns its results to the takeOrder
process and its JEMPurchaseOrderBean Active EJB.
checkCRED
--This process is activated, its JEMCreditBean Active EJB is instantiated, and its base reaction--checkCRED
--reacts to check the customer's credit. Later, it returns its results to the takeOrder
process and its JEMPurchaseOrderBean Active EJB.
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. When all data tokens expected from these processes are available, the procPO
reaction fires and processes the responses. As Figure 10-4 shows, both the takeOrder
and procPO
reactions exist in the same process, because they are components of the same request-response communication.
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 comprises the following:
takeOrder
)
JEMPortHandle
object of the service provider to whom the request is destined
JEMPortHandle
object, which contains the process context (interaction and process activation identifiers) instance and the JEMHandle
of the requester process that will later receive the results from the service provider
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 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 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).
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.
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 will match immediately. Only when all data tokens required by a reaction become available does matching succeed. For example, inside takeOrder
process the takeOrder
base reaction has registered the procPO
reaction that is waiting for the checkCRED
and checkINV
processes to respond. When the checkINV
process responds to the takeOrder
process, the procPO
reaction is not matched because it is also waiting for the checkCRED
process to respond. When the checkCRED
process responds to the takeOrder
process, the procPO
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 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 the cancelOrder
process can start executing. If the takeOrder
process has not completed but the cancelOrder
process is requested from a client, then 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 the takeOrder
process has completed (thus having already pushed its completion data token), then the 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 do not conflict for shared resources.
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 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, 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, 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 will retry 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. Since interactions have usually long duration and contain a large number of reactions, AC4J provides additional mechanisms to handle exceptions (such as an Oracle9iAS node crash or an Oracle database node crash).
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 crashes, all reactions that were running and did not end successfully are rolled back. AC4J then reexecutes the interrupted reactions in 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) to facilitate the storage of the control flow descriptors or the storage of data variables.
Figure 10-5 demonstrates how data tokens cause reactions to fire, and how reactions send new data tokens to other reactions over the data bus. The data bus coordinates and matches the data tokens with its reactions.
After the method completes, the reaction sends 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 data bus. The AC4J data bus routes the data tokens from a producer reaction to one or more consumer reactions.
Before you can execute any interactions, you must initialize an Oracle9i database as a repository for the AC4J data bus. You must configure it to include the following:
You can add the elements of the preceding list 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, which contains a README.TXT
file that discusses the different SQL command options that are available to you. These commands are also described in the following:
To create AC4J capabilities, you must execute one of the following SQL scripts as a SYS
user on the same system as the database.
createall
: To create all the defaults, including the default data bus, AC4J superuser, and default client user (JEMCLIUSER
), execute the createall
SQL script.
createjemtablespace
: To create the table space for your AC4J system, execute the createjemtablespace
SQL script. You must provide the SYS
username and password and the TNSENTRY
of this database where the data bus is created.
createjem
: To install and create the data bus, execute the createjem
SQL script. This requires the SYS
username and password, TNS_ENTRY
, and an AC4J client username.
createclient
: To create another client on an existing data bus, execute the createclient
SQL script. Provide the SYS
username and password, client username and password, and client tablespace.
recreatedatabus
: To re-create an existing data bus, which deletes the existing data bus and all its contents and then re-creates it, execute the recreatedatabus
script. Provide the SYS
username and password and TNSENTRY
of the database where the data bus resides.
recreateclient
: To re-create an existing client, execute the recreateclient
SQL script. Provide the SYS username and password and the client username and password.
The interaction supports JTA global transactions within the database that the data bus exists in. Thus, you need a nonemulated data source for the superuser to handle the two-phase commit, and a nonemulated data source for the client to send its asynchronous requests to the data bus. 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 defaults with the SQL scripts listed earlier. The jemuser
is the superuser username, and the jemcliuser
is the default client username. The DBLINK
is the link to the database that contains the data bus. For the superuser data source, the DBLINK
is a loopback link.
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 shown in Figure 10-6. To simplify the example, the code sample does not show error handling or import statements. Download the full example off the OTN site.
For the purchase order, the POInteraction
is created. Within the interaction, several business tasks exist as follows:
takeOrder
process)
checkINV
process)
checkCRED
process)
procPO
reaction)
The example includes the following:
takeOrder
reaction, which pushes two data tokens:
procPO
reaction, which acts on the responses from the inventory and credit check processes. If the inventory is available and the credit check goes well, then the procPO
returns the purchase order confirmation to the client.
Figure 10-6 illustrates the information flow inside an interaction. Figure 10-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 the reactions 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.
Here is a summary of the steps in processing the Purchase Order example of Figure 10-6:
takeOrder
process starts a takeOrder
base reaction. This base reaction starts a new purchase order. To complete the purchase order, it must perform three things:
checkINV
process of JEMInventoryBean to verify that the items are in inventory
checkCRED
process of JEMCreditBean to verify that the customer's credit is satisfactory
procPO
reaction in the current process to receive the results from the preceding two processes
checkINV
and checkCRED
processes return responses to the takeOrder
process.
procPO
reaction, within the takeOrder
process, reacts to the information provided by the checkINV
and checkCRED
processes. If satisfactory, the procPO
reaction sends the confirmation to the client through the AC4J data bus.
The following code sample shows the steps in performing loosely coupled interactions in AC4J.
public static void main(String[] args) throws ClassNotFoundException, Exception {// 0. create a JNDI context Context context = new InitialContext(); // 1. look up a datasource where 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(AC4JConn); // 5. Look up 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" = requester'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(); AC4JConn.close(); jemsess.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. Thus, 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 you have retrieved a connection to the AC4J data bus and created an AC4J session within it, you can send asynchronous messages 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 AC4J beans in the transaction. "Connect to the AC4J Data Bus" describes the steps in creating an AC4J-session and completing the client's request.
The following steps detail how to create an AC4J session on the AC4J data bus. These steps are a subset of the steps (those numbered 0 to 4) contained in Example 10-2.
An AC4J connection exists above a JDBC connection. Perform the following:
DataSource
defined for the database acting as the AC4J conduit. The DataSource
you use should be defined in the data-sources.xml
file as a nonemulated data source with a <dblink>
defined to the database where the AC4J data bus resides. See "AC4J Data Bus XML Configuration" for more information.
Context context = new InitialContext(); DataSource clientDS = (DataSource) context.lookup("java:comp/env/jdbc/jemClientDS");
DataSource
object.
OracleConnection conn = (OracleConnection)clientDS.getConnection();
JEMConnection AC4JConn = new JEMConnection(conn);
JEMSession AC4JSess = new JEMSession(AC4Jconn);
After you have created an AC4J session 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. The following steps explain the details of the call that the client must make to complete the AC4J request.
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..);
JEMEmitToken req =
AC4JSess.call
(null, null, ...all other parameters...);
JEMPortHandle portHandle = req.getPortHandle();
String iid = portHandle.getIid();
String p_aid = portHandle.getAid();
JEMHandle
that identifies an active EJB. OU can get the active EJB handle by looking up the jem-name
defined in the orion-ejb-jar.xml
file (see "AC4J Active EJB Deployment").
Context context = new InitialContext(); JMHandle activeEJBHandle = (JEMHandle) context.lookup("JEMPurchaseOrderBean"); JEMEmitToken req = AC4JSess.call(...., activeEJBHandle, ....);
// 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, ...);
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, ...);
// 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, ...);
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 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 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.
conn.commit();
conn.close(); // client as well an application-code must close AC4JConn.close(); // client must close jemsess.close(); // client must close
Once the client commits the 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 JEMPurchaseOrderBean Active EJB and activation of the takeOrder
process. The takeOrder
process starts a takeOrder
base reaction, which starts a new purchase order. As shown in Figure 7-6, this reaction, takeOrder
, processes the client's request by invoking additional services from the other Active EJBs, JEMInventoryBean and JEMCreditBean. This is shown in the following code sample:
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. Look up 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 data bus 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. Here are the steps in the completion of this initiation process:
takeOrder
, is running in an AC4J server. Therefore, 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();
JEMSession::call
calls by co-relating the business transaction.
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, .....);
takeOrder
reaction) provides the base reaction (method) name, the java-class type of the return parameter and all or part of its input parameters that it wishes to call. In the current example (which started at "Connect to the AC4J Data Bus"), 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, ....);
"procPO"
) is waiting for the reply from the JEMInventoryBean, and the second one (procPOInputClassTypes
) 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);
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. 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 from the AC4J data bus, 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 data bus to the registered reaction--procPO
.
The code sample in Example 10-4 shows the checkINV
method. The checkCRED
method is similar in its AC4J responsibilities.
public boolean checkINV(String itemName, int quantity) throws RemoteException, TestException {boolean inventoryExists = false; // The logic in the next step is ommitted inventoryExists = query its own database for the item and quantity; return inventoryExists;}
Both the checkINV
and checkCRED
processes return their responses to the procPO
reaction through the AC4J data bus. The AC4J data bus 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 data bus. The code sample in Example 10-5 shows the procPO
method.
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; }
The client needs to know the response to its 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 data bus.
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:
public static void main(String[] args) throws ClassNotFoundException, Exception {// 0. create a JNDI context Context context = new InitialContext(); // 1. Look up a data source where the 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(AC4JConn); // 5. Look up 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(); AC4JConn.close();}
As seen earlier, the 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 data bus. The client must connect to the AC4J data bus to retrieve its response by providing a proper process context. The steps in connecting to the AC4J data bus were described in Example 10-3. After receiving the response, the client can retrieve a java.lang.Object instance that must be processed further.
After creating an AC4J session on the AC4J data bus, the client can retrieve the response by performing the following steps:
String iid = "user1"; // = customer_name String p_aid = "AID_105_user1"; // = AID_<PO_number>_<customer_name> JEMEmitToken rcvresp = AC4JSess.receiveReactionResponse (iid, p_aid, ...);
jem-name
defined in the orion-ejb-jar.xml
file (see "AC4J Active EJB Deployment").
Context context = new InitialContext(); JMHandle activeEJBHandle = (JEMHandle) context.lookup("JEMPurchaseOrderBean"); JEMEmitToken rcvresp = AC4JSess.receiveReactionResponse (..., activeEJBHandle, ...);
takeOrder
process.
JEMEmitToken rcvresp = AC4JSess.receiveReactionResponse (..., "takeOrder", ...);
receiveReactionResponse
can be used to retrieve the Object instance, as follows:
Object obj = rcvresp.getReactionResponseObjectInstance();
conn.commit();
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.
<jem-server-extension>
element defines the database with the data bus that the active EJBs in this JAR file use for their AC4J communication.
<jem-server-extension data-source-location="jdbc/jemSuperuserDS"> <description>AC4J datasource location</description> </jem-server-extension>
<jem-deployment>
element in the orion-ejb-jar.xml
file identifies the EJB defined in the ejb-jar.xml
file as an active EJB. This element provides an AC4J name (jem-name) that is used to identify the bean within the AC4J calls. For example, this bean is defined as JEMPurchaseOrderBean
, which was used in the JEMHandle
creation. The identity of the caller, which is allowed to request services and retrieve responses from the Active EJB, can be declared in the called-by tag. This caller tag identifies the user in the data bus. For example, JEMCLIUSER is the user name that was used to create a jem-session
,
<jem-deployment jem-name="JEMPurchaseOrderBean" ejb-name="PurchaseOrderBean"> <description>AC4J EJB</description> <called-by> <caller caller-identity="JEMCLIUSER"/> </called-by> </jem-deployment>
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-extensiondata-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>
The remainder of this chapter shows the structure of the AC4J data bus within an Oracle database and refers to administering OC4J to support AC4J.
The createjem
script creates a complete JEM repository in the database; the repository is administered by a PL/SQL package called JEMDatabus
.
The complete AC4J packages are shown in the Javadoc, which can be found on the documentation CD accompanying this product or on OTN.
Under the JEMUSER schema the following package is created:
create or replace package JEMDatabus procedure setDatabusProperties(dfbusname IN VARCHAR2, tokttldiff NUMBER, tokttlcalldiff NUMBER, tokttldatdiff NUMBER, rxnttldiff NUMBER, gccycle NUMBER); procedure setTokttldiff( dfbusname IN VARCHAR2, tokttldiff NUMBER); procedure setTokttlcalldiff( dfbusname IN VARCHAR2, tokttlcalldiff NUMBER); procedure setTokttldatdiff( dfbusname IN VARCHAR2, tokttldatdiff NUMBER); procedure setTokclnttldiff( dfbusname IN VARCHAR2, tokclnttldiff NUMBER); procedure setRxnttldiff( dfbusname IN VARCHAR2, rxnttldiff NUMBER); procedure setGCcycle( dfbusname IN VARCHAR2, gccycle NUMBER); procedure createDatabusTpc( dfbusname IN VARCHAR2 DEFAULT NULL, description IN VARCHAR2 DEFAULT NULL, max_retries IN NUMBER DEFAULT 1, retry_delay IN NUMBER DEFAULT 0, retention_time IN NUMBER DEFAULT 0, gccycle IN NUMBER DEFAULT 10000); procedure dropDatabusTpc(dfbusname IN VARCHAR2 DEFAULT NULL); procedure createAppGroupSubscriber( dbusname IN VARCHAR2, subname IN VARCHAR2 DEFAULT 'JEMSUB'); procedure dropAppGroupSubscriber( dbusname IN VARCHAR2, subname IN VARCHAR2 DEFAULT 'JEMSUB'); end JEMDatabus;
The following PL/SQL procedure creates a databus:
procedure createDatabusTpc( dfbusname IN VARCHAR2 DEFAULT NULL, description IN VARCHAR2 DEFAULT NULL, max_retries IN NUMBER DEFAULT 1, retry_delay IN NUMBER DEFAULT 0, retention_time IN NUMBER DEFAULT 0, gccycle IN NUMBER DEFAULT 10000);
The following PL/SQL procedure drops a databus:
procedure dropDatabusTpc(dfbusname IN VARCHAR2 DEFAULT NULL);
When you create a databus, the following schema objects are created under the JEMUSER schema:
The process also creates unique sequence-numbers and indexes for these tables. It creates multi-consumer queues using DBMS_AQADM package. All the views are created with a public-synonym
and are granted to view for public.
In Table 10-1, the table name consists of the characters in the Table Name column, with an appended name, represented by dfbusname
. This appended name is provided by the user that creates the data bus; the default is the empty string. For example, if the user provides the string _EDB
, then the data bus table would be named TABDFB_EDB
; if the user makes no specification when creating the data bus, then the data bus table would simply be named TABDFB
.
The remainder of this section describes the tables created.
Name = "TABDFB" + dfbusname PRIMARY KEY = Instance-ID (or Oracle SID) Column-Name Column-Type -------------------------------------------- --SID of the database the Databus resides in instid VARCHAR2(50) PRIMARY KEY, --version of the Databus version VARCHAR2(20), --Databus Description description VARCHAR2(2000), --Latest Databus version number scn NUMBER, sysiid VARCHAR2(50), --System generated unique ids sysaid VARCHAR2(150), --System generated unique ids glbiid VARCHAR2(50), --System generated unique ids glbaid VARCHAR2(150), --System generated unique ids ixnaid VARCHAR2(150), --System generated unique ids --Default Retry number of a Reaction maxrxnretry NUMBER, --State of the Databus (not used now): --OPEN: --SUSPENDED: state VARCHAR2(30), --creation date of this DATABUS creatdate VARCHAR2(30), --The following Data Token types can exist in the Databus: --CALL =created as a result of a Session/Reaction call operation --STORE =created as a result of a Reaction storeData operation --RET =created as a result of a Reaction return result operation --EXC =created as a result of a Reaction throw exception operation --SND =created as a result of a Reaction sendData operation --SCHED =System token, used for triggering a scheduling of a Reaction firing --REMATCH=System token, used for triggering the garbage collector to --begin scanning -- for Processes, Reaction and Data Tokens to make unavailable -- and then potentially clean. -- The garbage collector does the following every 'gccycle' period: -- CompactTabTokens: -- Mark as 'INVALID' versions of all Tokens that have passed the -- TimeToLive (TTL) mark; no more reader/writer Reactions will be -- able to be matched -- using this Data Token. -- GarbageCollectTokens: -- Delete all 'INVALID' versions of all Data Tokens when all --reader/writer Reactions -- are gone (the reader reference counts and the exclusive -- modification state of -- the Data Token are nulled) and the tokclnttl has passed -- GarbageCollectRxns: -- Delete all Reactions that are their state is UNMATCHED (no -- suitable Data Tokens) or -- COMPLETED and the rxnttl has passed. Matched Reactions are not -- deleted, since -- the Reaction firing can be scheduled must later of the Reaction -- matching either -- because of resource unavailability or because of user directives -- GarbageCollectProcesses: -- Delete all Processes that encapsulate no Reactions --Default Time a Call Data Token is available for matching in the Databus --after it is stored in the Databus tokttlcalldiff NUMBER, --Default Time a Return, Exception or Stream Data Token --is available for matching will live in the Databus --after it is stored in the Databus tokttldatdiff NUMBER, --Default Time a Serialized Data Token is available for matching in the Databus --after it is stored in the Databus tokttldiff NUMBER, --Default Time a Data Token is retained since having being unavailable for --matching in the Databus tokclnttldiff NUMBER, --Default Time a Reaction is available to be matched with available Data Tokens --in the Databus --after it is created in the Databus rxnttldiff NUMBER, --How often the Databus garbage collector runs: gccycle NUMBER, epoch NUMBER, epochnext NUMBER The TABDBF databus table is initialized with the following values: version = "1.0" sysiid = "JEM$SYSIID" sysaid = "JEM$SYSAID" glbiid = "JEM$GLBIID" glbaid = "JEM$GLBAID" ixnaid = "JEM$IXNAID" state = "DFBST_INIT" dateformat = "DD-MON-YYYY:HH:MI:SS"
Name = "TabPRS"+dfbusname PRIMARY KEY = (iid, aid) Column-Name Column-Type -------------------------------------------- -- prsseq NUMBER, --interaction-ID; identifies the callee interaction iid VARCHAR2(50) NOT NULL, --activation-ID; identifies the callee process activation aid VARCHAR2(150), --return IID; identifies the caller interaction retiid VARCHAR2(50), --return AID; identifies the caller process activation retaid VARCHAR2(150), --JEMHandle; identifies the callee Active EJB handle BLOB, handlelen NUMBER, --JEMHandle; identifies the caller Active EJB rethandle BLOB, rethandlelen NUMBER, --process-name; identifies the callee Active EJB method name of the base --Reaction prspeid VARCHAR2(100), -- funpolytup BLOB, funpolytuplen NUMBER, -- retpolytup BLOB, retpolytuplen NUMBER, excretpolytup BLOB, excretpolytuplen NUMBER, cmpltag VARCHAR2(250), callindx NUMBER, --Not used state VARCHAR2(50), flags NUMBER, --start time for the process prsstart VARCHAR2(30), --end time for the process prsend VARCHAR2(30), --user-ID of starter of the Process activation ixnoriginatorusrid VARCHAR2(50), epoch NUMBER - creates a unique index ("Idx1Prs"+dfbusname) on ("TabPRS"+dfbusname+prsseq)
Name = "TabRXN"+dfbusname PRIMARY KEY = (iid, aid, rid, recursid) Column-Name Column-Type -------------------------------- rxnseq NUMBER, --interaction-ID; identifies the callee interaction iid VARCHAR2(50) NOT NULL, --activation-ID; identifies the callee process activation aid VARCHAR2(150), --reaction-ID; identifies the reaction rid VARCHAR2(100), recursid NUMBER, --JEMHandle; identifies the callee Active EJB handle BLOB, handlelen NUMBER, --process-name; identifies the callee Active EJB method name of the base --Reaction prspeid VARCHAR2(100), --reaction name; identifies the callee Active EJB method name of the fired --Reaction --prspeid is the same as rxnpeid for the base Reaction of a Process rxnpeid VARCHAR2(100), -- grpid VARCHAR2(100), vid NUMBER, -- funpolytup BLOB, funpolytuplen NUMBER, --number of matching tuples (Data Tokens) that must be matched in order for the --Reaction to be --marked as state=MATCHED totmattups NUMBER, mattupsprops BLOB, mattupspropslen NUMBER, --retry count of the Reaction in case of a Reaction rollback; when this count --reaches the --maximum attempts the Reaction is marked state=COMPLETED with status=max --retries reached retrycnt NUMBER, --Reaction states: -- UNMATCHED: when the Data Tokens needed for matching -- are not available (not arrived in the Databus, not visible yet, --have conflict in -- the interest mode) -- MATCHED : when the Data Tokens needed for matching are available -- COMPLETED: when the Reaction commits or rollbacks with exception status --message state VARCHAR2(50), --Reaction statuses: -- PRSUNMATCHED: -- RXNDISCARDED: -- RXNMAXRETRY : -- COMMITED: status VARCHAR2(50), --Type of the Reaction: -- CALL : if it is a base Reaction (implicitly created by the Databus) -- MATCH: if it is not a base Reaction (explicitly created by the application) type VARCHAR2(50), flags NUMBER, --Reaction priority: Reactions registered in a Process when matched are firing --ordered by the --time of registration if they have the same priority. --The firing ordering is: -- order by ReactionPriority descending, ReactionRegistrationTime ascending rxnpri NUMBER, --time-to-live for the reaction rxnttl VARCHAR2(30), --description of this reaction descr VARCHAR2(2000), --date of the reaction registration as Julian date rxndate NUMBER, --date of the reaction registration rxnstart VARCHAR2(30), --date of the reaction commit rxnend VARCHAR2(30), --user-ID of registered this reaction schemausrid VARCHAR2(50), schemamsgid VARCHAR2(50), auxctx BLOB, auxctxlen NUMBER, epoch NUMBER, --Time a Call Data Token is available for matching in the Databus --after it is stored in the Databus tokttlcalldiff NUMBER, --Time a Return, Exception or Stream Data Token --is available for matching will live in the Databus --after it is stored in the Databus tokttldatdiff NUMBER, --Time a Serialized Data Token is available for matching in the Databus --after it is stored in the Databus tokttldiff NUMBER, --Time a Data Token is retained since having being unavailable for matching --in the Databus tokclnttldiff NUMBER, --Time a Reaction is available to be matched with available Data Tokens --in the Databus --after it is created in the Databus rxnttldiff NUMBER - creates a unique index ("Idx1RXN"+dfbusname) on ("TabRXN"+dfbusname+" "+(iid, aid, rid, recursid, prspeid, rxnpeid, grpid, state)) - create a unique index ("Idx2RXN"+dfbusname) 'on ("TabRXN"+dfbusname+" "+rxnseq)
Name = "TabRTL"+dfbusname Column-Name Column-Type -------------------------------------------- mattupseq NUMBER, --every Reaction template needed for matching a Data Token has an index, --starting from 0 --This is used for a composite Reaction matching matindx VARCHAR2(50) NOT NULL, --1. First level of matching: -- The following 3 conditions need to be valid for a template to match with a --Data Token -- --1.1: Interaction-ID that needs to match with a Data Token's Interaction-ID tokiid VARCHAR2(50), --1.2: Activation-ID that needs to match with a Data Token's Activation-ID tokaid VARCHAR2(150), --1.3: tag name that needs to match with a Data Token's tag name tag VARCHAR2(250), -- vid NUMBER, vidtype VARCHAR2(50), --2. Second level of matching: --The filter conditions specified here need to be true for the propereties of --the Data Token matched in the first level polycnd CLOB, polycndlen NUMBER, --Interaction-ID of the Reaction this template belongs to iid VARCHAR2(50) NOT NULL, --Activation-ID of the Reaction this template belongs to aid VARCHAR2(150), --Reaction-ID of the Reaction this template belongs to rid VARCHAR2(100), recursid NUMBER, --java-class type of the object value that is needed for matching objclassname VARCHAR2(1000), --Reaction template states: -- UNMATCHED: when the Data Token needed for matching -- is not available (not arrived in the Databus, not visible yet, --have conflict in the interest mode) -- MATCHED : when the Data Token needed for matching is available state VARCHAR2(50), type VARCHAR2(50), flags NUMBER, --Same as Reaction priority rxnpri NUMBER, timeout NUMBER, --Same as Reaction rxnttl rxnttl VARCHAR2(30), --Same as Reaction rxndate rxndate NUMBER, --points to the Data Token when this template is MATCHED; otherwise null tokseq NUMBER, --Scope Interaction-ID; used to minimize the matching process phase scpiid VARCHAR2(1000), --Scope Activation-ID; used to minimize the matching process phase scpaid VARCHAR2(1000), epoch NUMBER - creates an index ("Idx1RTL"+dfbusname) on ("TabRTL"+dfbusname+" "+(iid, aid, rid, recursid, state)) - creates an index ("Idx2RTL"+dfbusname) on ("TabRTL"+dfbusname+" "+(tokiid, tokaid, tag)) - create a unique index ("Idx3RTL"+dfbusname) 'on ("TabRTL"+dfbusname+" "+mattupseq)
Name = "TabTOK"+dfbusname PRIMARY KEY = sequence-number Column-Name Column-Type -------------------------------------------- --identifies uniquely this Data Token. A matched Reaction template --points to this id tokseq NUMBER PRIMARY KEY, --Data Tokens are chained: --seq-number of the previous version of the Data Token prevtokseq NUMBER, --seq-number of the next version of the Data Token nexttokseq NUMBER, aliastokseq NUMBER, --Interaction-ID of this data-token iid VARCHAR2(50) NOT NULL, --Activation-ID of this data-token aid VARCHAR2(150), -- tag VARCHAR2(250), classtag VARCHAR2(10), -- vid NUMBER, prevvid NUMBER, nextvid NUMBER, -- polycnd CLOB, polycndlen NUMBER, -- objinst BLOB, objinstlen NUMBER, textinst CLOB, textinstlen NUMBER, undoentry BLOB, undoentrylen NUMBER, --used to describe the number of Reactions having matched a query interest --on this Data Token readers NUMBER, consumer VARCHAR2(100), --Data Token states: -- UNMATCHED: when the Data Token is not matched by a Reaction template or --matched in query interest mode -- MATCHEDX : when the Data Token is matched by a Reaction template in --exclusive modification interest mode state VARCHAR2(50), --Data Token statuses: -- VALID : Tok is available; so readers can see it and a writer can reserve it -- INVALID: Tok is unavailable because its time-to-live has expired; nobody can --access it status VARCHAR2(50), -- CALL : -- MATCH : -- STORE : -- RET : -- EXC : -- SND : -- SCHED : -- REMATCH: -- QUIT : -- op VARCHAR2(50), reason VARCHAR2(50), type VARCHAR2(50), flags NUMBER, --Time Data Token is available for matching in the Databus --after it is stored in the Databus tokttl VARCHAR2(30), --Time a Data Token is retained since having being unavailable for matching --in the Databus tokclnttl VARCHAR2(30), --date of the Data Token creation toktime VARCHAR2(50), --user-ID of the who created this Data Token schemausrid VARCHAR2(50), schemamsgid VARCHAR2(50), auxctx BLOB, auxctxlen NUMBER, --description of this Data Token descr VARCHAR2(2000), epoch NUMBER - creates an unique index ("Idx1TOK"+dfbusname) on ("TabTOK"+dfbusname+" "+(iid, aid, tag, vid))
Name = "TabTRK"+dfbusname PRIMARY KEY = sequence-number Column-Name Column-Type -------------------------------------------- trkseq NUMBER PRIMARY KEY, --token seq-number tokseq NUMBER, --instance-ID = Oracle_SID instid VARCHAR2(100), --Interaction-ID; identifies the caller interaction iid VARCHAR2(50), --Activation-ID; identifies the caller process activation aid VARCHAR2(150), --JEMHandle; identifies the caller Active EJB handle VARCHAR2(2000), --reaction ID; identifies the caller Reaction rid VARCHAR2(100), recursid NUMBER, --Data Token push operation; see above op VARCHAR2(50), --Data Token push reason; see above reason VARCHAR2(50), --Interaction-ID; identifies the callee interaction toiid VARCHAR2(50), --Activation-ID; identifies the callee process activation toaid VARCHAR2(150), --JEMHandle; identifies the callee Active EJB tohandle VARCHAR2(100), --process-name; identifies the callee Active EJB method name of the base --Reaction toprspeid VARCHAR2(100), --reaction name; identifies the callee Active EJB method name of the fired --Reaction --prspeid is the same as rxnpeid for the base Reaction of a Process torxnpeid VARCHAR2(100), --description of this tracking info description VARCHAR2(2000), --date of the Data Token push trkdate VARCHAR2(30), --user-ID of the who pushed the Data Token schemausrid VARCHAR2(50), schemamsgid VARCHAR2(50), -- Direction of the push: -- SND: at the sending side (caller with a call operation, callee with a --return operation) -- RCV: at the receiving side (callee with a call operation, caller with a --return operation) direction VARCHAR2(50), epoch NUMBER
In Table 10-2, the queue name consists of the characters in the AQ Topic Name column, plus an appended string, represented by dfbusname
. This appended name is provided by the user that creates the data bus; the default is the empty string. For example, if the user provides the string _EDB
, then the scheduling queue would be named JEMUSER.SQ_EDB
; if the user makes no specification when creating the data bus, then the scheduling queue would simply be named JEMUSER.SQ
. In the case of the exception queue, the character E
is appended at the end.
The remainder of this section describes the AQ schema objects created.
Create AQ Queue table, topics, and default subscriber to all topics using AQ PL/SQL Packages. For every J2EE application deployed to OC4J, the JEM runtime system will add a subscriber to all topics with the J2EE application name deployed used as the subscriber name.
In the following descriptions, dfbusname
is the string provided by the user that creates the data bus; the default is the empty string.
DBMS_AQADM.CREATE_QUEUE_TABLE( Queue_table => quetablename, Multiple_consumers => TRUE, Queue_payload_type => 'SYS.AQ$_JMS_BYTES_MESSAGE', compatible => '8.1.5');
DBMS_AQADM.CREATE_QUEUE( Queue_name => matchquename, Queue_table => quetablename, max_retries => max_retries, retry_delay => retry_delay, retention_time => retention_time); Create and add the subscriber to MATCH-Queue subscriber := sys.aq$_agent('JEMSUB' || dfbusname, null, null); DBMS_AQADM.ADD_SUBSCRIBER( queue_name => matchquename, subscriber => subscriber);
DBMS_AQADM.CREATE_QUEUE( Queue_name => schedquename, Queue_table => quetablename, max_retries => max_retries, retry_delay => retry_delay, retention_time => retention_time); Create and add the subscriber to SCHEDULING-Queue subscriber := sys.aq$_agent('JEMSUB' || dfbusname, null, null); DBMS_AQADM.ADD_SUBSCRIBER( queue_name => schedquename, subscriber => subscriber);
DBMS_AQADM.CREATE_QUEUE( Queue_name => foreignquename, Queue_table => quetablename, max_retries => max_retries, retry_delay => retry_delay, retention_time => retention_time); Create and add the subscriber to FOREIGN-Queue subscriber := sys.aq$_agent('JEMSUB' || dfbusname, null, null); DBMS_AQADM.ADD_SUBSCRIBER( queue_name => foreignquename, subscriber => subscriber);
In Table 10-3, the view name consists of the characters in the View Name column, plus an appended string, represented by dfbusname
. This appended name is provided by the user that creates the data bus; the default is the empty string. For example, if the user provides the string _EDB
, then the data bus view would be named DFB_EDB
; if the user makes no specification when creating the data bus, then the data bus table would simply be named DFB
.
Name = ("DFB"+dfbusname) [ from ("TabDFB"+dfbusname) ] Column-Name ------------ instid, (Instance-ID or Oracle-SID) version, maxrxnretry, state, creatdate, tokttldiff, tokttlcalldiff, tokttldatdiff, tokclnttldiff, rxnttldiff, gccycle
Name = "PRS"+dfbusname Column-Name ------------ iid , (Interaction-ID) aid , (Activation-ID) retiid , (return IID) retaid , (return AID) rethandlelen , prspeid , (process name=method name) cmpltag , state , flags , prsstart , prsend
Name="RXN"+dfbusname [ from ("TabRXN"+dfbusname) ] Column-Name ------------ iid , (Interaction-ID) aid , (Activation-ID) rid , (Reaction-ID) recursid , prspeid , (process name) rxnpeid , (reaction name) grpid , totmattups , retrycnt , state , status , type , flags , rxnpri , rxnttl , descr , rxndate , rxnstart , rxnend
Name = "RTL"+dfbusname [ from ("TabRTL"+dfbusname) ] Column-Name ------------ mattupseq , matindx , tokiid , tokaid , tag , vid , vidtype , iid , (Interaction-ID) aid , (Activation-ID) rid , (Reaction-ID) recursid , polycnd , objclassname , (java-class name of the obj for reaction) state , type , flags , rxnpri , (priority of the reaction) timeout , (timeout for the reaction) tokseq , scpiid , scpaid
Name = "TOK"+dfbusname Column-Name ------------- tokseq , prevtokseq , nexttokseq , aliastokseq , readers , consumer , status , iid , (Interaction-ID) aid , (Activation-ID) tag , vid , prevvid , nextvid , polycnd , textinst , textinstlen , state , op , reason , type , flags , tokttl , tokclnttl , toktime
Name = "TRK"+dfbusname [ from "TabTRK"+dfbusname and from "TabTOK'"+dfbusname ] Condition = trk.tokseq = tok.tokseq AND trk.schemausrid = (select UPPER(user) from dual))) Column-Name ------------ tok.tokseq , (seq-number if the token) tok.prevtokseq , (prev seq-number for the token) tok.nexttokseq , (next seq-number for the token) tok.aliastokseq , tok.readers , tok.consumer , tok.status , tok.iid , (Interaction-ID of the token) tok.aid , (Activation-ID of the token) tok.tag , tok.vid , tok.prevvid , tok.nextvid , tok.polycnd , tok.state , tok.type , tok.flags , tok.tokttl , (time-to-live for the token) tok.tokclnttl , tok.toktime , trk.instid , (instance-ID or Oracle-SID) trk.iid , (Interaction-ID for tracking) trk.aid , (Activation-ID for tracking) trk.rid , (Reaction-ID for tracking) trk.recursid , trk.op , trk.reason , trk.toiid , trk.toaid , trk.tohandle , trk.toprspeid , trk.torxnpeid , trk.description , trk.direction , trk.trkdate
|
![]() Copyright © 2002 Oracle Corporation. All Rights Reserved. |
|