How the Banking Application is build using JTS interfaces
The bank client is an application program able to manage transactions either in a direct or indirect
management mode, respectively with the interfaces org.omg.CosTransactions.TransactionFactory
and org.omg.CosTransactions.Terminator or with the org.omg.CosTransactions.Current
interface. Transactions created by the client in the Banking application are
done in the indirect mode.
Invoking a remote object within a CORBA environment means that the remote object
implements a CORBA interface defined in a CORBA idl file. The following Bank.idl
describes the interfaces then the possible kind of distributed CORBA objects
involved in the banking application. There is no any interface that inherits
the CosTransactions::TransactionalObject interface, which means that for any
remote invocations the transactional context is normally not propagated. However,
since the Account object may have to register Resource objects that participate
to transaction completion, a context is needed. In the following Bank.idl file
operations defined in the Account interface have explicitly in their signature
the CosTransactions::Control argument meaning that it passed explicitly by the
caller - in this case the Bank Client program.
module arjuna {
module demo {
module jts {
module explicitremotebank {
interface Account :
{
float balance(in CosTransactions::Control ctrl);
void credit( in CosTransactions::Control ctrl, in float value );
void debit( in CosTransactions::Control ctrl, in float value );
};
exception NotExistingAccount
{ };
interface Bank
{
Account create_account( in string name );
Account get_account( in string name )
raises( NotExistingAccount );
};
};
};
};
};
The following portion of code illustrates how a JTS transaction is started
and terminated when the client asks to transfer money from one account to another.
This also describes what are ArjunaTS packages that need to be used in order
to obtain appropriate objects instances (such Current).
Note: The code below is a simplified view of the BankClient.java program. Only
the transfer operation is illustrated; other operations manage transactions
in the same way. (see for details the BankClient.java)
package com.arjuna.demo.jta.remotebank;
import com.arjuna.ats.jts.OTSManager;
public class BankClient
{
private Bank _bank;
....
// This operation is used to make a transfer
//from an account to another account
private void makeTransfer()
{
//get the name of the supplier(name_supplier) and
// the consumer(name_consumer)
// get the amount to transfer (famount)
...
try
{
//the following instruction asks a specific
//ArjunaTS class to obtain a Current instance
Current current = OTSManager.get_current();
System.out.println("Beginning a User transaction to get balance");
current.begin();
Account supplier = _bank.get_account( name_supplier );
Account consumer = _bank.get_account( name_consumer );
supplier.debit( current.get_control(), famount );
//The Control is explicitly propagated
consumer.credit( current.get_control(), famount );
current.commit( );
}
catch (Exception e)
{
...
}
}
Since JTS is used invocations against an ORB are needed, such ORB and Object
Adapter instantiation and initialisation. To ensure a better portability,
the ORB Portability API provides a set of
methods that can be used as described below.
public static void main( String [] args )
{
....
myORB = ORB.getInstance("test");// Create an ORB instance
myORB.initORB(args, null); //Initialise the ORB
org.omg.CORBA.Object obj = null;
try
{
//Read the reference string from a file then convert to Object
....
obj = myORB.orb().string_to_object(stringTarget);
}
catch ( java.io.IOException ex )
{
...
}
Bank bank = BankHelper.narrow(obj);
....
}
The Bank object has mainly two operations: creating an account, which is added
in the account list, and returning an Account object. No transactional instruction
is performed by the Bank object. The following lines decribe the implementation
of the Bank CORBA object
public class BankImpl extends BankPOA {
public BankImpl(OA oa)
{
_accounts = new java.util.Hashtable();
_oa = oa;
}
public Account create_account( String name )
{
AccountImpl acc = new AccountImpl(name);
_accounts.put( name, acc );
return com.arjuna.demo.jts.remotebank.AccountHelper.
narrow(_oa.corbaReference(acc));
}
public Account get_account(String name)
throws NotExistingAccount
{
AccountImpl acc = ( AccountImpl ) _accounts.get( name );
if ( acc == null )
throw new NotExistingAccount("The Account requested does not exist");
return com.arjuna.demo.jts.remotebank.AccountHelper.
narrow(_oa.corbaReference(acc));
}
private java.util.Hashtable _accounts;// Accounts created by the Bank
private OA _oa;
}
After having defined an implementation of the Bank object, we should now create
an instance and make it available for client requests. This is the role of the
Bank Server that has the responsibility to create the ORB and the Object Adapater
instances, then the Bank CORBA object that has its object reference stored in
a file well known by the bank client. The following lines describe how the Bank
server is implemented.
public class BankServer
{
public static void main( String [] args )
{
ORB myORB = null;
RootOA myOA = null;
try
{
myORB = ORB.getInstance("ServerSide");
myOA = OA.getRootOA(myORB);
myORB.initORB(args, null);
myOA.initOA();
....
BankImpl bank = new BankImpl(myOA);
String reference = myORB.orb().
object_to_string(myOA.corbaReference(bank));
//Store the Object reference in the file
...
System.out.println("The bank server is now ready...");
myOA.run();
}
}
The Account object provides mainly three methods balance,
credit and withdraw.
However, in order to provide the transactional behaviour, rather than to modify
the current account directly (according to credit or withdraw) this task is
delegated to an AccountResource object that is able, according to the transaction
outcome, to set the account value either to its initial state or its final state.
The AccountResource object is in fact an object that implements the org.omg.CosTransactions.Resource,
then able to participate to the transaction commitment. For this aim, the Account
object has to register the AccountResource object as a participant, after having
obtaining the reference of the org.omg.CosTransactions.Coordinator object ,
itself obtained via the org.omg.CosTransactions.Control object
package com.arjuna.demo.jta.remotebank;
import org.omg.CosTransactions.*;
import ....
public class AccountImpl extends AccountPOA
{
float _balance;
AccountResource accRes = null;
public Account(String name )
{
_name = name;
_balance = 0;
}
public float balance(Control ctrl)
{
return getResource(ctrl).balance();;
}
public void credit(Control ctrl, float value )
{
getResource(ctrl).credit( value );
}
public void debit(Control ctrl, float value )
{
getResource(ctrl).debit( value );
}
public AccountResource getResource(Control control)
{
try
{
if (accRes == null) {
accRes = new AccountResource(this, _name) ;
//The invocation on the ORB illustrates the fact that the same
//ORB instance created by the Bank Server is returned.
ref = org.omg.CosTransactions.ResourceHelper.
narrow(OA.getRootOA(ORB.getInstance("ServerSide")).
corbaReference(accRes));
RecoveryCoordinator recoverycoordinator =
control.get_coordinator().register_resource(ref);
}
}
catch (Exception e){...}
return accRes;
}
...
}
To be considered as a org.omg.CosTransactions.Resource, the AccountResource
class shall extends the class org.omg.CosTransactions.ResourcePOA generated
by the CORBA IDL compiler. The AccountRessource provides similar methods as
the Account class (credit, withdraw and balance) with the appropriate methods
to participate to the 2PC protocol. The following portion of code describes
how the methods prepare, commit
and rollback are implemented.
public class AccountResource extends org.omg.CosTransactions.ResourcePOA
{
public AccountResource(Account account, String name )
{
_name = name;
_account = account;
_initial_balance = account._balance;
_current_balance = _initial_balance;
}
public float balance()
{
return _current_balance;
}
public void credit( float value )
{
_current_balance += value;
}
public void debit( float value )
{
_current_balance -= value;
}
public org.omg.CosTransactions.Vote prepare()
throws org.omg.CosTransactions.HeuristicMixed,
org.omg.CosTransactions.HeuristicHazard
{
if ( _initial_balance == _current_balance )
return org.omg.CosTransactions.Vote.VoteReadOnly;
if ( _current_balance < 0 )
return org.omg.CosTransactions.Vote.VoteRollback;
return org.omg.CosTransactions.Vote.VoteCommit;
}
public void rollback()
throws org.omg.CosTransactions.HeuristicCommit,
org.omg.CosTransactions.HeuristicMixed,
org.omg.CosTransactions.HeuristicHazard
{
//Nothing to do
}
public void commit()
throws org.omg.CosTransactions.NotPrepared,
org.omg.CosTransactions.HeuristicRollback,
org.omg.CosTransactions.HeuristicMixed,
org.omg.CosTransactions.HeuristicHazard
{
_account._balance = _current_balance;
}
public void commit_one_phase()
throws org.omg.CosTransactions.HeuristicHazard
{
_account._balance = _current_balance;
}
.....
private float _initial_balance;
private float _current_balance;
private Account _account;
}
Sample Application Source Code
Full source code for the banking application is included to provide you with
a starting point for experimentation.