Version 2

    Overview

     

    Unlike previous JBoss AS versions, remote transaction propagation between 2 server instances for EJB invocations does not require JTS. EJB client API which was introduced in AS7 uses its own protocol for remote transaction propagation between the client and the server for EJB invocations.

     

    Typical scenario

     

    Let's consider an EJB A hosted on Server 1 and EJB B hosted on Server 2. EJBs are transactional by default. There are ways to disable transactions on EJBs or certain business methods of EJBs. But we won't be going into that part, in this article because our intention is to focus on the transaction propagation and recovery for EJBs, which obviously will require transactional EJBs.

     

    When a client, for example a standalone Java application, invokes on EJB A (on Server 1) a transaction is created. Later, during that invocation request, if EJB A in turn invokes on EJB B (on Server 2), the transaction propagation is carried out between the servers hosting those 2 EJBs. Previous versions of JBoss AS require JTS for the transaction propagation to happen. However in AS7 we have a JBoss specific protocol implemented in EJB client API which does the necessary propagation. The EJB client API interacts with the transaction manager on Server 1 and Server 2 to carry out the necessary transaction propagation and management. Furthermore on the server side of the interaction (i.e. of Server 2 in this case), the AS7 EJB integration layer (heavily) uses JBoss Transaction project (Narayana) APIs to import the remote transaction and further manage it.

     

    Crash and transaction recovery

     

    JBoss Transactions (Narayana) project provides the necessary API infrastructure for libraries like the EJB client API project to integrate for transaction recovery in case of client/server crash scenarios. The design of that API is explained in the JBoss Transactions project manual here http://docs.jboss.org/jbosstm/4.17.3.Final/guides/failure_recovery_guide/.

     

    From a EJB client API perspective, a org.jboss.tm.XAResourceRecovery implementation needs to be registered against the transaction subsystem's recovery manager service. This implementation of XAResourceRecovery is responsible for returning instances of XAResource(s) when the getXAResources() method is invoked by the recovery manager on that implementation. The returned XAResource(s) will then be used by the recovery manager to invoke the:

     

    /**
     * Obtains a list of prepared transaction branches from a resource manager. The transaction manager calls this method 
     * during recovery to obtain the list of transaction branches that are currently in prepared or heuristically completed states.
     */
    javax.transaction.xa.Xid[] recover(int i) throws javax.transaction.xa.XAException;
    

     

    As is evident from the name of the method, the implementation is responsible for returning the Xid(s) which need to be recovered.

     

    XAResourceRecovery implementation for EJB client API

     

    As explained in the JBoss Transactions document, the recovery process involves a background thread which runs at periodic intervals to check for transactions that should be recovered. From a EJB client API perspective, it has to know the target server against which it is issuing a recover request and also the node identifier of the node/server on which the (previously run) transaction(s) originated.

     

    Node identifier of the transaction origin

     

    This is also known as the "parent node name". This is the node on which the transaction, which was propagated, originated. The transaction subsystem integration in AS7 exposes this node identifier via the CoreEnvironmentService:

     

    package org.jboss.as.txn.service;
    
    public class CoreEnvironmentService implements Service<CoreEnvironmentBean> {
    ...
    }
    

     

    package com.arjuna.ats.arjuna.common;
    
    public class CoreEnvironmentBean implements CoreEnvironmentBeanMBean {
    ....
        public String getNodeIdentifier() {
            return nodeIdentifier;
        }
    

     

    So it's straightforward for the EJB client integration in AS7 to know what the node identifier of itself.

     

    Target server(s)/EJBReceiver(s) against which the recover request has to be issued

     

    The EJBClientContext can potentially contain numerous EJBReceiver(s), each of which correspond to a different node. Furthermore, the EJBReceiver(s) within a EJBClientContext can be dynamic - which means at it may not always contain the same set of EJBReceiver(s). Certain receivers may be added or removed as and when such a need arises. As such, there's no fixed set of EJBReceiver(s) which the EJBClientContext can known before hand to issue the recovery request against. So how does the EJB client API know which EJBReceiver(s) should it issue the recovery request when the recovery manager background process asks it for the Xid[] that need to be recovered.

     

    The answer to that question is that whenever a EJBReceiver is registered in a EJBClientContext, it will make that EJBReceiver eligible for issuing a recovery request against. This is a very important thing to remember since if a server/EJBReceiver was being used during EJB invocations and either the server or client crashed, then the recovery against the server/EJBReceiver will not be triggered unless that EJBReceiver gets registered within a EJBClientContext. This however isn't a restriction since a recovery process between 2 servers only makes sense when they are communicating with each other and from a EJB client API perspective, the communication can only happen when the EJBReceiver is registered in a EJBClientContext.

     

    Implementation note

     

    The EJB client project will not have any dependency on the JBoss Transactions (Narayana) project. All of the details being discussed in this article will be implemented in a way such that the AS7 EJB integration part can provide the necessary plugins to the EJB client library for allowing the recovery process to be carried out.