|
The Developer's Resource & Community Site
|
Three-Tier Architecture for a Bank Checking Account - MTS Server Component
Author: Gopalan Suresh Raj
Date Submitted: January 26th 2000
Level of Difficulty: Advanced
Subjects Covered: Microsoft Transaction Server
Pre-required Reading: Microsoft Transaction Server
The source code for this example is available as a zip file for download here.
Previous Page....Developing The Bank Account IDL
To work with any of these samples you will need the following:
- Microsoft Visual J++ ver 6.0
- Windows NT 4.0 Options Pack
The Steps involved in developing the MTS Server component are:
- Set the properties for your project
- Implement the CheckingPK component
- Implement the Checking Server component
- Build and register the MTS server components
- Add MTS aware code to the project's class
- Mark the class as COM/MTS-enabled
- Build the project
- Deploy the DLL into The Microsoft Transaction Server
A Three Tier Architecture for a typical Bank Account
1. Create a new COM DLL project
An MTS component should always be packaged as a DLL. Select New Project and from the File menu, choose the COM DLL project template in Visual J++. Name the project BankServer.
2. Set the properties for your project
- From the Project Explorer | BankServer | BankServer Properties | ClassPath, uncheck the "Merge all Project-specific ........ClassPaths in solution" check box.
- From the Project Explorer | BankServer | BankServer Properties | COM Classes check the "Use existing Type Library" ........radio button.
- Click the "Select" button
- Click the "Browse" button
- Browse to the newly created Bank.tlb file in the project directory
- Select and open the Bank.tlb file
- Click the "OK" button to confirm the list of COM Components
- Click "OK" to confirm Bank Properties form changes
- Notice that the Bank folder is added to the BankServer project
- Notice that 14 java source files have been created:
- Checking.java, IChecking.java, and ICheckingDefault.java
- ICheckingHome.java, and ICheckingHomeDefault.java
- Savings.java, ISavings.java, and ISavingsDefault.java
- ISavingsHome.java, and ISavingsHomeDefault.java
- IAccountKey.java and IAccountKeyDefault.java
- CheckingPK.java and SavingsPK.java
- Rename the generated implementation class:
- Checking.java to CheckingImpl.java
- CheckingPK.java to CheckingPKImpl.java
- Savings.java to SavingsImpl.java
- SavingsPK.java to SavingsPKImpl.java
3. Mark the class as COM/MTS-enabled
To deploy the class in MTS, you need to make it a COM/MTS class. You can easily make it one by right-clicking the mouse on the BankServer class from the Class Outline tab and Selecting Class Properties. Mark the MTS Support as Enabled and say OK.
Mark the Class as COM/MTS enabled
You will immediately notice that some code similar to this is added to the CheckingImpl class.
/** @com.register(clsid=07D690F5-FB95-11D2-97E3-006097A7D34F,
* typelib=07D690F0-FB95-11D2-97E3-006097A7D34F, version="1.0")
* @com.transaction (required)
*/
The final code for the CheckingPKImpl and CheckingPK looks like this:
CheckingPKImpl.java |
package bank; import
com.ms.com.*;
import com.ms.com.IUnknown;
import com.ms.com.Variant;
/**
@com.register(clsid=07D690F4-FB95-11D2-97E3-006097A7D34F,
* typelib=07D690F0-FB95-11D2-97E3-006097A7D34F,
version="1.0")
*/
public class CheckingPKImpl
implements IUnknown, com.ms.com.NoAutoScripting, bank.IAccountKeyDefault
{
public static final int CHECKING_BASE
= 11;
public static final int CHECKING_HIGH
= 10000;
int m_accountNumber;
public
void setKey(int key) {
if ( (key < CHECKING_BASE) ||
(key >
CHECKING_HIGH) ) {
throw new com.ms.com.ComFailException (1,
"Invalid Checking Primary Key");
}
m_accountNumber = key;
}
}
|
The Code for CheckingImpl.java is shown below:
CheckingImpl.java |
package
bank; import com.ms.com.*;
import com.ms.com.IUnknown;
import com.ms.com.Variant;
import com.ms.mtx.*;
import com.ms.wfc.data.*;
/**
@com.register(clsid=07D690F5-FB95-11D2-97E3-006097A7D34F,
* typelib=07D690F0-FB95-11D2-97E3-006097A7D34F,
version="1.0")
* @com.transaction (required)
*/
public class CheckingImpl
implements IUnknown,com.ms.com.NoAutoScripting,
bank.ICheckingHomeDefault,bank.ICheckingDefault
{
static final int ALL_FIELDS
= 0;
static final int NAME_FIELD
= 1;
static final int BALANCE_FIELD= 2;
//odbc
type connection string
static
final String m_strOpenConn =
"FILEDSN=Bank";
////////////////////////////////////////////////////////////////////
//
Home Interface methods
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
public void create(bank.IAccountKey key,
String name, double startingBalance) {
////////////////////////////////////////////////////////////////////
if (startingBalance < 0)
return;
boolean bSuccess = false;
System.out.println("Invoking
Checkings::create");
IObjectContext context = null;
try {
// Get the Object Context
context = (IObjectContext)MTx.GetObjectContext ();
truePut (CheckingImpl.ALL_FIELDS,
(CheckingPKImpl)key,
(new Double (startingBalance)).toString (), name);
bSuccess = true;
}
catch (Exception e) {
bSuccess = false;
e.printStackTrace ();
}
// Upon exit, always call SetComplete
() if happy,
// or SetAbort
() if unhappy
// We do this since we never save state across
method calls.
finally {
if (context!=null) {
if (bSuccess == true)
context.SetComplete ();
else
context.SetAbort ();
}
}
}
////////////////////////////////////////////////////////////////////
//
Remote Interface methods
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
public void credit(double amount,
bank.IAccountKey key) {
////////////////////////////////////////////////////////////////////
double balance = 0.0;
boolean bSuccess = false;
System.out.println("Invoking
Checkings::credit");
try {
balance = getBalance (key);
}
catch (Exception e) {
e.printStackTrace ();
}
System.out.println("changing
Checkings::balance from " + balance);
// Get the Object Context
IObjectContext context =
(IObjectContext)MTx.GetObjectContext ();
try {
if( amount > 0 ) {
balance += amount;
truePut (CheckingImpl.BALANCE_FIELD,
(CheckingPKImpl)key,
(new Double(balance)).toString (), "");
bSuccess = true;
}
}
catch (Exception e) {
bSuccess = false;
e.printStackTrace ();
}
// Upon exit, always call SetComplete
() if happy,
// or SetAbort
() if unhappy
// We do this since we never save state across
method calls.
finally {
if (context!=null) {
if (bSuccess == true)
context.SetComplete ();
else
context.SetAbort ();
}
}
System.out.println(" to " + balance);
}
////////////////////////////////////////////////////////////////////
public void debit(double amount,
bank.IAccountKey key) {
////////////////////////////////////////////////////////////////////
double balance = 0.0;
boolean bSuccess = false;
System.out.println("Invoking
Checkings::debit");
try {
balance = getBalance (key);
}
catch (Exception e) {
e.printStackTrace ();
}
System.out.println("changing
Checkings::balance from " + balance);
// Get the Object Context
IObjectContext context =
(IObjectContext)MTx.GetObjectContext ();
try {
if ( (amount > 0) &&
(balance >= amount) ) {
balance -= amount;
truePut (CheckingImpl.BALANCE_FIELD,
(CheckingPKImpl)key,
(new Double (balance)).toString (), "");
bSuccess = true;
}
}
catch (Exception e) {
bSuccess = false;
e.printStackTrace ();
}
// Upon exit, always call SetComplete
() if happy,
// or SetAbort
() if unhappy
// We do this since we never save state across
method calls.
finally {
if (context!=null) {
if (bSuccess == true)
context.SetComplete ();
else
context.SetAbort ();
}
}
System.out.println(" to " + balance);
}
////////////////////////////////////////////////////////////////////
public double getBalance(bank.IAccountKey
key) {
////////////////////////////////////////////////////////////////////
double balance = 0.0;
boolean bSuccess= false;
System.out.println ("Invoking
Checkings::getBalance");
IObjectContext context = null;
try {
// Get the Object Context
context = (IObjectContext)MTx.GetObjectContext ();
String result = trueGet
(CheckingImpl.BALANCE_FIELD, (CheckingPKImpl)key);
balance = (new Double
(result)).doubleValue();
bSuccess = true;
}
catch (Exception e) {
bSuccess = false;
e.printStackTrace ();
}
// Upon exit, always call SetComplete
() if happy,
// or SetAbort
() if unhappy
// We do this since we never save state across
method calls.
finally {
if (context!=null) {
if (bSuccess == true)
context.SetComplete ();
else
context.SetAbort ();
}
}
System.out.println("Checkings::balance is
" + balance);
return balance;
}
////////////////////////////////////////////////////////////////////
public String
getCustomerName(bank.IAccountKey key) {
////////////////////////////////////////////////////////////////////
String name = null;
boolean bSuccess= false;
System.out.println ("Invoking
Checkings::getCustomerName");
IObjectContext context = null;
try {
// Get the Object Context
context = (IObjectContext)MTx.GetObjectContext ();
name = trueGet
(CheckingImpl.NAME_FIELD, (CheckingPKImpl)key);
bSuccess = true;
}
catch (Exception e) {
bSuccess = false;
e.printStackTrace ();
}
// Upon exit, always call SetComplete
() if happy,
// or SetAbort
() if unhappy
// We do this since we never save state across
method calls.
finally {
if (context!=null) {
if (bSuccess == true)
context.SetComplete ();
else
context.SetAbort ();
}
}
System.out.println ("Checkings::customer_name
is " + name);
return name;
}
////////////////////////////////////////////////////////////////////
//
Other Internal methods
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
String trueGet (int type, CheckingPKImpl
key) {
////////////////////////////////////////////////////////////////////
Connection connection = null;
Recordset rset = null;
Variant rowCount = new Variant
();
String result = null;
String query = null;
String fieldName = null;
switch (type) {
case CheckingImpl.NAME_FIELD:
query = "SELECT CUSTOMER_NAME
FROM Checkings WHERE ACCOUNT_NUMBER=";
fieldName = "CUSTOMER_NAME";
break;
case CheckingImpl.BALANCE_FIELD:
query = "SELECT BALANCE FROM
Checkings WHERE ACCOUNT_NUMBER=";
fieldName = "BALANCE";
break;
}
query += key.m_accountNumber;
try {
//STEP1 : open the connection
connection = new Connection ();
connection.open
(CheckingImpl.m_strOpenConn);
//STEP 2: Obtain the desired recordset with a query
rset = connection.execute (query);
//STEP 3:Get the appropriate fields
if (!rset.getEOF()) {
result = rset.getField
(fieldName).getString ();
}
}
catch (Exception e) {
e.printStackTrace ();
}
finally {
// Cleanup that needs to occur whether we leave via a
return or exception
if (rset != null) {
if (rset.getState () ==
AdoEnums.ObjectState.OPEN)
rset.close ();
ComLib.release (rset);
rset = null;
}
if (connection != null) {
if (connection.getState () ==
AdoEnums.ObjectState.OPEN)
connection.close ();
ComLib.release (connection);
connection = null;
}
}
return result;
}
////////////////////////////////////////////////////////////////////
String truePut (int type, CheckingPKImpl
key, String balance,
String name) {
////////////////////////////////////////////////////////////////////
Connection connection = null;
Recordset rset = null;
Variant rowCount = new Variant
();
String result = null;
String query = null;
switch (type) {
case CheckingImpl.NAME_FIELD:
query = "INSERT INTO Checkings
VALUES ( '"+ key.m_accountNumber
+"','"+ name
+"','"+balance+"')";
break;
case CheckingImpl.BALANCE_FIELD:
query = "UPDATE Checkings SET
BALANCE=" + balance +
" WHERE ACCOUNT_NUMBER="+ key.m_accountNumber;
break;
case CheckingImpl.ALL_FIELDS:
default:
query = "INSERT INTO Checkings
VALUES ( '"+ key.m_accountNumber
+"','"+ name
+"','"+balance+"')";
break;
}
try {
//STEP1 : open the connection
connection = new Connection ();
connection.open
(CheckingImpl.m_strOpenConn);
//STEP 2: Obtain the desired recordset with a query
connection.execute (query);
}
catch (Exception e) {
e.printStackTrace ();
}
finally {
// Cleanup that needs to occur whether we leave via a
return or exception
if
(rset != null) {
if (rset.getState () ==
AdoEnums.ObjectState.OPEN)
rset.close ();
ComLib.release (rset);
rset = null;
}
if (connection != null) {
if (connection.getState () ==
AdoEnums.ObjectState.OPEN)
connection.close ();
ComLib.release (connection);
connection = null;
}
}
return result;
}
////////////////////////////////////////////////////////////////////
public CheckingImpl () {
////////////////////////////////////////////////////////////////////
System.out.println ("Invoking
CheckingImpl::CheckingImpl");
}
}
|
4. Build the project
Select the Build menu and Build the project. This creates an MTS component DLL called BankServer.dll. This contains the Class files and all the appropriate stub code so that the DLL can be registered and deployed in MTS.
5. Deploy the DLL into The Microsoft Transaction Server
Start up the MTS Explorer. Go to the computer you want to install your component on and select "Packages Installed". Right click and create a New empty Package called BankServer. Select the newly created BankServer package and create a New Component. Select the Component through the Browse button into MTS. (Make sure you Install New Component rather than Import Components...) The Checking MTS Server component is now deployed on MTS and is ready for client invocations
Next Page....Developing a Bank Account MTS Client
What do you think of this article?
Have your say about the article. You can make your point about the article by mailing [email protected] (If you haven't allready joined, you can join by going to https://www.onelist.com/community/dev-java).
You can also write a review. We will publish the best ones here on this article. Send your review to [email protected]. Please include the title of the article you are reviewing.
Further Reading
The MTS Series by Gopalan Suresh Raj:
Microsoft Transaction Server By Gopalan Suresh Raj.
Gopalans introductory article on Microsoft Transaction Server intorduces the basics to MTS, and leads in to the example articles included in the series.
Author: Gopalan Suresh Raj
Date Submitted: Jan 26 2000
Level of Difficulty: Beginners / Intermediate
Subjects Covered: MTS
Pre-required Reading: None
Developing a Simple MTS Server Component By Gopalan Suresh Raj.
Part 1 of a two part example.
Author: Gopalan Suresh Raj
Date Submitted: Jan 26 2000
Level of Difficulty: Beginners / Intermediate
Subjects Covered: MTS
Pre-required Reading: Microsoft Transaction Server
Developing a Simple MTS Client Application By Gopalan Suresh Raj.
Part 2 of a two part example.
Author: Gopalan Suresh Raj
Date Submitted: Jan 26 2000
Level of Difficulty: Beginners / Intermediate
Subjects Covered: MTS
Pre-required Reading: Microsoft Transaction Server, Developing a Simple MTS Server Component
Developing The Bank Account IDL By Gopalan Suresh Raj.
A Three-Tier Architecture for a Bank Checking Account - Developing The Bank Account IDL is part 1 of a 3 part example.
Author: Gopalan Suresh Raj
Date Submitted: Jan 26 2000
Level of Difficulty: Beginners / Intermediate
Subjects Covered: MTS
Pre-required Reading: Microsoft Transaction Server
MTS Client By Gopalan Suresh Raj.
A Three-Tier Architecture for a Bank Checking Account - MTS Server Component is the third part of this three part example.
Author: Gopalan Suresh Raj
Date Submitted: Jan 26 2000
Level of Difficulty: Beginners / Intermediate
Subjects Covered: MTS
Pre-required Reading: Microsoft Transaction Server, Developing The Bank Account IDL, MTS Server Component
Author: Gopalan Suresh Raj
You can meet Gopalan, and the other iDevResource authors in Author Central. Gopalan also maintains his own site at https://www.execpc.com/~gopalan/.
Contributors to iDevResource.com get their own site at Author Central. Why not write an article and become a member of the iDevResource community.
© Copyright 1997-2000 Gopalan Suresh Raj. Reproduced with Permission
|