Developing a transactional Advanced Integration Service with IBM Business Process Manager, Part 3: Implementing the Advanced Integration Service

Leveraging the capabilities of the Advanced Integration Services (AIS) in IBM® Business Process Manager V8 Advanced, this series describes how to implement a distributed ACID transactional scenario in IBM Business Process Manager that includes two databases on different machines. The scenario shows the automatic rollback capabilities offered by the SCA-based management in an AIS. Part 3 describes how to implement the AIS for the transaction.

Carlo Randone (carlo_randone@it.ibm.com), Certified IT Architect , IBM

Carlo Randone photoCarlo Randone is a Certified IBM IT Architect and Open Group Master Certified IT Architect in IBM Global Business Services, Italy. Carlo has a deep knowledge of different development platforms and middleware on heterogeneous environments and operating systems. He worked for several years as a Certified Trainer and Solution Developer for a Microsoft® Certified Partner.

Since joining IBM in 2000, Carlo's main job interests are related to SOA and BPM, and their related software engineering methodologies and enabling platforms, and Enterprise Architecture planning and design. He enjoys collecting documentation and hardware pieces related to the historical development of IT, and to support this hobby he is a member of the Charles Babbage Institute.



27 March 2013

Implementing the AIS in Integration Designer

Part 3 of this series guides you through the steps to implement the Advanced Integration Service (AIS) designed to perform the transaction. The workspace for this demo is defined in KBPM-Main.zip provided for download in Part 1 of this series.

As discussed in the article Linking business processes and enterprise services together using IBM Business Process Manager Advanced, the term Advanced Integration Service is a bit of a misnomer: what is really provided is an "AIS operation" because an AIS in IBM BPM V8 provides the ability to define a single operation of a service. In a service design exercise, a logical service may result in a number of related AIS definitions, one for each operation. In the scenario described in this article, the AIS exposes the transactional operation that performs the transfer of funds.

The assembly diagram

Figure 1 shows the assembly diagram of the proposed solution. The main components are:

  • The AIS Inbound Export TxAIS, with the interface TxAIS.
  • The BPEL short-running process (or microflow) TxAIS_Process, with the interface TxAIS and the two references:
    • IChargePartner (interface ICharge, with the operation Charge) to wire the component k1Charge
    • ICreditPartner (interface ICredit, with the operation Credit) to wire the component k2Credit
  • The two components (with a Java™ implementation) for the Credit and Charge operations are:
    • k2Credit (interface ICredit, operation Credit), with the Java implementation k2CreditImpl.java
    • k1Charge (interface ICharge, operation Charge), with the Java implementation k1ChargeImpl.java
Figure 1. The assembly diagram of the AIS implementation
The assembly diagram of the AIS implementation

The TxAIS interface is the interface of the AIS, and is the real interface between the BPMN-based implementation (as modeled in IBM Business Process Manager (BPM) Process Designer) and the technical implementation of the AIS (the BPEL microflow) in the IBM Integration Designer, as shown in Figure 2.

Figure 2. The TxAIS interface
The TxAIS interface

As you can see, there are Input parameters (to drive the transaction), output parameters (the OutputMessage) and a Fault section that supports the transmission of an ErrorMessage string.

You need to configure the following SCA transactional qualifiers to enable the participation of both components (k2Credit and k1Charge) to the same transactional context (initiated by the BPEL short-running microflow), as shown in Figure 3.

  • Set Join transaction to true on the two participant interfaces ICharge and ICredit
  • Set Suspend transaction to false on the two references IChargePartner and ICreditPartner
Figure 3. The transactional qualifiers
The transactional qualifiers

The ICredit and ICharge interfaces, with the related operations Credit and Charge, are shown in Figures 4 and 5, respectively.

Figure 4. The ICredit interface and the Credit operation
The ICredit interface and the Credit operation
Figure 5. The ICharge interface and the Charge operation
The ICharge interface and the Charge operation

The Input and Output parameters of the Credit and Charge operations correspond to the signature of the two Credit and Charge methods in the Java implementation of the two components k2Credit and k1Charge, which are, respectively:

public String Credit(String myConnection, String myTable, String myAccount, 
Integer myAmount)

and

public String Charge (String myConnection, String myTable, String myAccount, 
Integer myAmount)

The Java listings of the k2Credit and k1Charge components are described in the following sections.


Implement the BPEL process in Integration Designer

A typical use of a short-running BPEL process, or microflow, is to string together service invocations within one transaction, or where invocations are not transactional, but the current state of the process need not be retained in the case of a failure. Short-running processes are also preferred for high performance because they don't need to perform persistence in between steps of the process. These concepts were also true in the WebSphere® Process Server.

Business Process Manager Advanced uses the transaction manager in WebSphere Application Server as the basis for its transactions. For the short-running process, the transactional boundary is the entire process.

Following are the primary characteristics of this type of process:

  • It performs lightweight sequences of service invocations and any associated logic.
  • The whole process is fully transactional. For example, the process can receive or initiate a global transaction and can propagate and control further downstream transactional resources. The process may call some non-transactional systems, but internally, the process is always a single transaction.
  • The process is often used to expose services providing real-time responses to graphical user interfaces, or for transactional subprocesses.
  • The process does not need to interact transactionally with back-end systems. The process can also be used to call multiple non-transactional services where the current state of the process need not be retained in a failure; for example, aggregation of multiple sources of data.

A microflow runs in one transaction. However, the services that the microflow invokes can involve more than one transaction. This is because a service that is called through an invoke activity can either participate in the transaction of the microflow, or it can run in its own transaction.

In our case, we need a configuration in which the components k1Charge and k2Credit live in the same transactional context of the microflow, "joining" the microflow transactional context.

As introduced earlier, the SCA transaction qualifiers that are specified for the process and the service that is called are:

  • The suspendTransaction qualifier on the reference of the process component specifies whether the transaction context of the process is propagated to the services to be invoked.
  • The joinTransaction qualifier on the service interface specifies whether a service participates in the transaction of its caller if a transaction is propagated.

Table 1 shows the complete set of the transactional qualifier settings for synchronous invocations. Refer to Transactional behavior of microflows) in the IBM Business Process Manager V8 Information Center for more information.

Table 1. Transactional qualifier settings combinations for synchronous invocation
joinTransactionsuspendTransaction = truesuspendTransaction = false
joinTransaction = true The service does not participate in the microflow transaction The service participates in the microflow transaction
joinTransaction = false The service does not participate in the microflow transaction The service does not participate in the microflow transaction

The interesting case for our demo is the combination of joinTransaction = true and suspendTransaction = false, which enable the service to participate in the microflow transaction. This configuration is in accordance with the qualifier configuration shown in Figure 3.

We'll execute the credit operation to the target account (for example, on Bank2 on DB2 on server B) before the charge operation on the source account (for example, on Bank1 on SQL Server on server A) to show explicitly how a try to set a negative remaining amount on the source account is triggered and how it fires a local rollback on server A. We'll demonstrate how this rollback is propagated to the entire distributed transaction, causing the actual undo of the credit operation already done (or even better, just prepared and still waiting for a final commit) on server B, thanks to the transactional support offered by the SCA-based AIS implementation in IBM BPM.

Figure 6 shows the diagram of the TxAIS_Process microflow in IBM Integration Designer.

Figure 6. The BPEL microflow in Integration Designer
The BPEL microflow in Integration Designer

The left side of the process describes the normal ("happy") execution path, and the right side defines a catch handler to manage the error conditions (such as, transaction abort for insufficient money, but not only that, as you'll see).

The happy path

The first action in the flow is the Receive, implemented with the configuration shown in Figure 7.

Figure 7. The Receive action
The Receive action

The parameters, and the related supporting variables defined in the microflow, are the input parameters exposed by the AIS, and are assigned in the Process Designer when the process variables are used to drive the execution of the AIS. In particular:

  • Connection1Charge, Account1Charge, Table1Charge and Amount are used to drive the Charge operation on the k1Charge component
  • Connection2Credit, Account2Credit, Table2Credit and Amount are used to drive the Credit operation on the k2Credit component

On the left side of the process in Figure 6, after the Receive action, there are the two invokes to drive the Credit and Charge operations, with an intermediate wait snippet, useful in a demo scenario to add some seconds to eventually see the appearance of uncommitted values for non-committed records on one of the two connected databases.

Listing 1 shows the Java snippet SnippetWait.

Listing 1. The Java snippet SnippetWait
// Delay useful only to demonstrate the "dirty records" if you make on DB2 a
// SELECT WITH UC (Uncommitted Read)
// ( select * from accounts with UR)

try{
Thread.sleep(4000);}
catch(Exception ec)
{
}

The value configured in this script as in the provided code is four seconds, but can be changed (keep it below the timeout limits configured for the transactions on WebSphere Application Server and on the connected resources). We'll show how this time window is used later in the article.

Obviously, the wait (inserted here only for demo purposes) must be removed in a production scenario (especially considering that this is a short-running microflow!).

The InvokeCredit and InvokeCharge activities are configured to call, respectively, the Credit operation on k2Credit component and the Charge operation on k1Charge, as shown in Figure 8 and Figure 9, respectively.

Figure 8. InvokeCredit (call Credit)
InvokeCredit (call Credit)
Figure 9. InvokeCharge (call Charge)
InvokeCharge (call Charge)

At the end of the happy path are two final actions: a snippet to build a final output message (OutputMessage on the TxAIS interface) and a Reply to the caller with this message.

The snippet SnippetBuildOutputMessage is implemented, as a visual snippet, to build a final output message concatenating the output messages that come from the k2Credit and k1Charge components. Figure 10 shows the visual representation of this snippet.

Figure 10. The visual snippet to build the output message
The visual snippet to build the output message

The final Replay activity returns the OutputMessage (with the value read from the OutputMessage microflow variable) to the caller, as shown in Figure 11.

Figure 11. The final Reply activity
The final Reply activity

The error path

As you'll see in the following sections, which describe the Java implementations of the k2Credit and k1Charge components, there are at least three main kinds of typical exceptions that the k2Credit and k1Charge components (called by the InvokeCredit and InvokeCharge microflow activities, respectively) can throw. All are managed, or caught, by the MyCacthAll catch handler (of type Catch All) in the BPEL microflow process:

  • The unavailability of the account name of the source (for k1Charge) or target (for k2Credit) bank account. In this case, the components raise an error with the message "Invalid account."
  • The unavailability of sufficient money on the charge account. In this case, the trigger on the database raises an "Insufficient money" error exception that the components can re-throw to the caller.
  • Any other runtime error (for example a wrong table name, an unavailable JDBC connection, a network error to reach the remote database engine, and so on) captured and re-launched by the components, with the related error message.

MyCatchAll intercepts all these kinds of error that can happen on the scope provided around the InvokeCredit and InvokeCharge activities.

Listing 2 shows the SnippetBuildErrorMessage, a Java snippet with the custom code to build the error message.

Listing 2. The snippet to build the error message
com.ibm.bpe.api.BpelException bpelexception = getCurrentFaultAsException();
MyErrorMessage = "From Catch All of the transaction context: getFaultName(): 
" + bpelexception.getFaultName() 
+ "; getMessage(): " + bpelexception.getMessage() + "; getRootCause(): " 
+ bpelexception.getRootCause().getMessage();

In this way, the core elements of the exceptions are passed to the MyErrorMessage variable, and later to the ErrorMessage available on the interface of the AIS.

In the ReplyFault replay, the MyErrorMessage variable is passed to the ErrorMessage fault defined on the error (fault) interface of the AIS, as shown in Figure 12, which shows the details of ReplyFault.

Figure 12. The ReplyFault
The ReplyFault

For a good and technically detailed description of the error handling in IBM BPM V8 with AIS, refer to the developerWorks article Configuring error handling for Advanced Integration Services in IBM Business Process Manager Advanced V8.

Implement the k2Credit component

Listing 3 shows k2CreditImpl.java, the Java implementation of the k2Credit component that exposes the Credit operation. The "k" prefix is the same as the whole demo (KBPM), and the number "2" stands for Bank2, which in this demo (with the default values provided to drive the money transfer) is the bank that hosts the destination account to be credited.

Listing 3. The k2Credit component
import com.ibm.websphere.sca.ServiceManager;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

public class k2CreditImpl {
        /**
        * Default constructor.
        */
        public k2CreditImpl() {
            super();
        }

        /**
        * Return a reference to the component service instance for this implementation
        * class.  This method should be used when passing this service to a partner
        * reference
        * or if you want to invoke this component service asynchronously.    
        *
        * @generated (com.ibm.wbit.java)
        */
       @SuppressWarnings("unused")
       private Object getMyService() {
       	return (Object) ServiceManager.INSTANCE.locateService("self");
       }

       /**
        * Method generated to support implementation of operation "Credit" defined for
        * WSDL port type 
        * named "ICredit".
        * 
        * Please refer to the WSDL Definition for more information 
        * on the type of input, output and fault(s).
        */
       public String Credit(String myConnection, String myTable, String myAccount, 
        Integer myAmount) throws Exception {

          String result = "Result OK from Credit operation";
          int HowManyRows;
      
          // Credit (+) (on Bank2 DB) operation
          
          Context ctx = null;
          DataSource ds = null;
          Connection  con = null;		
          String strSQL;

          strSQL = "UPDATE " + myTable + " SET Amount = Amount + ";
          strSQL = strSQL + String.valueOf(myAmount);
          strSQL = strSQL + " WHERE Description = '" + myAccount + "'";

        try
        {
            ctx = new InitialContext();
            ds = (DataSource) ctx.lookup(myConnection);
            con = ds.getConnection();
            
            Statement stmt = con.createStatement();
            
            HowManyRows = stmt.executeUpdate(strSQL);
            if (HowManyRows!=1)
            {
                   // Invalid Account
                   throw new Exception("Exception from Credit (+): Invalid account.");
            }
            
        }
        catch(Exception ex)
        {   
               result = "Exception from Credit (+): myConnection: " + myConnection +
               "; strSQL: " + strSQL + "; ex.getMessage(): " + ex.getMessage();
               //ex.printStackTrace();
               throw new Exception(result, ex);
        }
        finally
        {
            try
            {
               if(con!=null && !con.isClosed())
               {
                   con.close();     
               }
            }
            catch (SQLException e)
            {
               // ignore
              
            }
        }         
              return(result);
       }

}

The Update operation on the Accounts table is implemented following the traditional Java Enterprise Edition (JEE) execution path to obtain a DataSource by a lookup on the Context, and a Connection by a getConnection on the DataSource, and finally using the createStatement (on the Connection object) and ExecuteUpdate (on the Statement object) methods.

Note that the following statements:

HowManyRows = stmt.executeUpdate(strSQL);
if (HowManyRows!=1)
{
// Invalid Account
throw new Exception("Exception from Credit (+): Invalid account.");
}

are used to check whether the execute command does not return any row, for example, when the statement is not executed on any record (typically for the unavailability of the account passed in the myAccount parameter to the Credit operation). In this case an explicit exception is throw, with the string "Invalid account".

This exception, together with any others (including, eventually, the exception raised by the trigger in the database to check for insufficient money), is caught by the catch(Exception ex) catch handler, which re-throws the exceptions to the caller (in this case, the BPEL microflow).

Implement the k1Charge component

Listing 4 shows k1ChargeImpl.java, the Java implementation of the k1Charge component that exposes the Charge operation. The "k" prefix is the same for the whole demo (KBPM), and the number "1" stands for Bank1, which in this demo (with the default values provided to drive the money transfer), is the bank that hosts the source account to be charged.

Listing 4. The k1Charge component
import com.ibm.websphere.sca.ServiceManager;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

public class k1ChargeImpl {
       /**
        * Default constructor.
        */
       public k1ChargeImpl() {
       	super();
       }

       /**
        * Return a reference to the component service instance for this implementation
        * class.  This method should be used when passing this service to a partner 
        * reference or if you want to invoke this component service asynchronously.    
        *
        * @generated (com.ibm.wbit.java)
        */
       @SuppressWarnings("unused")
       private Object getMyService() {
       	return (Object) ServiceManager.INSTANCE.locateService("self");
       }

       /**
        * Method generated to support implementation of operation "Charge" defined 
        * for WSDL port type named "ICharge".
        * 
        * Please refer to the WSDL Definition for more information 
        * on the type of input, output and fault(s).
        */
       public String Charge (String myConnection, String myTable, String myAccount, 
        Integer myAmount) throws Exception {

          String result = "Result OK from Charge operation";
          int HowManyRows;

          // Charge (-) (on Bank1 DB) operation

          Context ctx = null;
          DataSource ds = null;
          Connection  con = null;

          String strSQL;

          strSQL = "UPDATE " + myTable + " SET Amount = Amount - ";
          strSQL = strSQL + String.valueOf(myAmount);
          strSQL = strSQL + " WHERE Description = '" + myAccount + "'";

        try
        {
            ctx = new InitialContext();
            ds = (DataSource) ctx.lookup(myConnection);
            con = ds.getConnection();
            Statement stmt = con.createStatement();
            
            HowManyRows = stmt.executeUpdate(strSQL);
            if (HowManyRows!=1)
            {
               // Invalid Account
               throw new Exception("Exception from Charge (-): Invalid account.");
            }
            
        }
        catch(Exception ex)
        {
               result = "Exception from Charge (-): myConnection: " + myConnection + 
               "; strSQL: " + strSQL + "; ex.getMessage(): " + ex.getMessage();
               //ex.printStackTrace();
               throw new Exception(result, ex);
        }
        finally
        {
            try
            {
               if(con!=null && !con.isClosed())
               {
                   con.close();     
               }
            }
            catch (SQLException e)
            {
               // ignore
              
            }
        }         

              return(result);
       }

}

Note that in this case, as in the k2Credit component, the statements:

HowManyRows = stmt.executeUpdate(strSQL);
if (HowManyRows!=1)
{
// Invalid Account
throw new Exception("Exception from Charge (-): Invalid account.");
}

are used to check whether the execute command does not return any row, for example, when the statement is not executed on any record (typically for the unavailability of the account passed in the myAccount parameter to the Charge operation). In this case, an explicit exception is thrown, with the string "Invalid account."

This exception, together with any others, is caught by the catch(Exception ex) catch handler, that re-throws the exceptions to the caller (in this case, the BPEL microflow).


Conclusion

Part 3 of this series covered how to implement the transactional AIS in Integration Designer, based on an SCA BPEL microflow with a couple of Java-based SCA components. You've learned how to design the AIS in Integration Designer as an Assembly Diagram, how to design the BPEL microflow, how to configure the interfaces and the transactional qualifiers, and how to develop two components in Java to manipulate the data on the two databases Bank1 and Bank2. In Part 4, you'll learn how to execute and test different transactional scenarios with the components designed and developed in the previous parts of this series, and will learn about the ACID (Atomicity, Consistency, Isolation, Durability) characteristics of the implemented solution.


Acknowledgements

The author would like to thank his colleagues Stefano Angrisano, Marco Antonioni, Giuseppe Bottura, Matteo Franciolli and Daniele Rossi, and his good friends Claudio Cantoni and Alberto Venditti (author, about eight years ago, of a similar demo on a different technological platform) for their reviews and contributions to this article.

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Business process management on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Business process management, WebSphere
ArticleID=861641
ArticleTitle=Developing a transactional Advanced Integration Service with IBM Business Process Manager, Part 3: Implementing the Advanced Integration Service
publish-date=03272013