package examples.cluster.ejb; import javax.ejb.*; import javax.naming.*; import java.rmi.*; import java.util.Hashtable; import examples.cluster.utils.*; import examples.cluster.ejb.teller.*; import examples.cluster.ejb.account.AccountResult; /** * This class illustrates load balancing and failover * using a container-managed JDBC EntityBean. * You'll need to have a properly configured database with * the appropriate tables loaded before running this example. * *

* The client demonstrates: *

* * @author Copyright (c) 1999 by BEA Systems, Inc. All Rights Reserved. */ public class Client { static String url = "t3://localhost:7001"; static String user = null; static String password = null; static String account1 = "10000"; static String account2 = "10005"; static int ITERATIONS = 5; static final int MAXATTEMPTS = 5; static final int SLEEP = 1000; //time to sleep between invocations //to give an opportunity to test failover static TellerHome bank; static Teller teller; static ClusterUtils stats = new ClusterUtils(); static int i; // iteration static int trans; // transaction within iteration Client() { } /** * Runs this example from the command line. Example: *

* java examples.cluster.ejb.Client "t3://localhost:7001" scott tiger *

* The parameters are optional, but if any are supplied, * they are interpreted in this order: *

* @param url URL such as "t3://localhost:7001" of a Server or Cluster * @param user User name, default null * @param password User password, default null */ public static void main(String[] args) { System.out.println("\nBeginning cluster.ejb.Client...\n"); Client client = new Client(); // Parse the argument list if ((args == null) || (args.length == 0)) {} else for (int i = 0; i < args.length; i++) { switch(i) { case 0: url = args[i]; break; case 1: user = args[i]; break; case 2: password = args[i]; break; default: } } double depositAmount = 0; double withdrawalAmount = 0; double transferAmount = 0; try { Context ctx = getInitialContext(); // Get a TellerHome object (bank), from whom we can create Tellers // who'll later help us execute transactions bank = (TellerHome)ctx.lookup("cluster.ejb.TellerHome"); // Repeat each transaction set, doing four transactions in each set for (i = 0 ; i < ITERATIONS; i++) { System.out.println("Start of transaction set " + (i+1)); // Invent amounts for the transactions depositAmount = (100 * (i+1)); transferAmount = depositAmount/2; withdrawalAmount = transferAmount; trans = 0; client.new Balance(account1).transaction(); Thread.sleep(SLEEP); client.new Deposit(account1, depositAmount).transaction(); Thread.sleep(SLEEP); client.new Withdrawal(account1, withdrawalAmount).transaction(); Thread.sleep(SLEEP); client.new Transfer(account1, account2, transferAmount).transaction(); Thread.sleep(SLEEP); System.out.println("End of transaction set " + (i+1) + "\n"); } } catch (TellerException pe) { System.out.println("Processing Error: " + pe); } catch (Exception e) { System.out.println(":::::::::::::: Error :::::::::::::::::"); e.printStackTrace(); } System.out.println("\nStatistics for different servers:\n"); System.out.println(stats.processStatistics()); System.out.println("\nEnd cluster.ejb.Client...\n"); } /** * Gets an initial context for the current user, password and url. * * @return Context * @exception java.lang.Exception if there is * an error in getting the Context */ static public Context getInitialContext() throws Exception { Hashtable h = new Hashtable(); h.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory"); h.put(Context.PROVIDER_URL, url); if (user != null) { System.out.println ("user: " + user); h.put(Context.SECURITY_PRINCIPAL, user); if (password == null) password = ""; h.put(Context.SECURITY_CREDENTIALS, password); } return new InitialContext(h); } /** * Inner class that is used to call the transactions * with the session beans. *

* Subclassed to perform specific transaction types. * */ class Transaction{ TellerResult result; String tellerServer; String transId = getTransactionID(); double amount; String account1Id; String account1Server; double account1Balance; String account2Id; String account2Server; double account2Balance; String tellerMessage; String actionMessage; String account1Message; String account2Message; /** * Loop that attempts to invoke a transaction * on a TellerBean. It will catch any RemoteExceptions * from the TellerBean, and after checking if the * transaction was comitted, will either return, or * -- if the MAXATTEMPTS has not been exceeded -- * attempt the transaction again. * * @exception Exception if the transaction is not * completed in the MAXATTEMPTS * @exception TellerException if there is * an error in performing the transaction * @exception RemoteException if there is * a communications or systems failure * @exception CreateException if there is * a problem creating a teller */ void transaction() throws Exception, TellerException, RemoteException, CreateException { trans++; int attempts = 0; int set = i; boolean committed = false; boolean invoke = true; System.out.println("Transaction " + trans + " of set " + (set + 1) + " (" + transId + ")"); while (attempts < MAXATTEMPTS) { try { attempts++; System.out.println(" Attempt " + attempts); if (teller == null) teller = bank.create(); if (invoke) { invokeTransaction(); buildReport(); printReport(); System.out.println(" End of transaction " + trans + " of set " + (set + 1)); return; } else { // checking transaction committed = teller.checkTransactionId(transId); System.out.println(" Checked transaction " + transId + ": committed = " + committed); if (committed) { return; } else { System.out.println(" Attempting Transaction " + trans + " again"); invoke = true; } } } catch (RemoteException re) { System.out.println(" Error: " + re); // Replace teller, in case that's the problem teller = null; invoke = false; } } throw new Exception(" Transaction " + trans + " of set " + (set + 1) + " ended unsuccessfully"); } /** * Builds a unique identifier for the transaction. * * @return String Transaction ID */ String getTransactionID() { return new String("" + System.currentTimeMillis()); } /** * Calls a teller bean to perform a transaction. * Subclassed in specific transaction types. * * @exception TellerException if there is * an error in performing the transaction * @exception RemoteException if there is * a communications or systems failure */ void invokeTransaction() throws TellerException, RemoteException { } /** * Builds a report of the result of a transaction. * Subclassed in specific transaction types. * */ void buildReport() { tellerServer = result.getTellerServer(); account1Server = result.getAccount1Result().getServer(); account1Balance = result.getAccount1Result().getBalance(); tellerMessage = "Teller (" + tellerServer + "): "; account1Message = "Account " + account1Id + " (on " + account1Server + "): " + "balance: $" + account1Balance; } /** * Prints a report of the result of a transaction. * */ void printReport() { System.out.println(" " + tellerMessage + actionMessage); stats.addClusterMessage("Teller on: " + tellerServer); if (account1Server != null) { stats.addClusterMessage("Account 1 on: " + account1Server); System.out.println(" " + account1Message); } if (account2Server != null) { stats.addClusterMessage("Account 2 on: " + account2Server); System.out.println(" " + account2Message); } } } /** * Performs a balance lookup. * */ class Balance extends Transaction { /** * Constructs a Balance. * * @param accountId String Account ID * @exception TellerException if there is * an error in performing the transaction * @exception RemoteException if there is * a communications or systems failure * @exception CreateException if there is * a problem creating a teller */ Balance(String accountId) throws TellerException, RemoteException, CreateException { this.account1Id = accountId; } /** * Calls a teller bean to perform a balance lookup. * Overshadows Transaction.invokeTransaction(). * * @exception TellerException if there is * an error in performing the transaction * @exception RemoteException if there is * a communications or systems failure */ void invokeTransaction() throws TellerException, RemoteException { result = teller.balance(account1Id); } /** * Builds a report of the result of a transaction. * Overshadows Transaction.buildReport(). * */ void buildReport() { super.buildReport(); actionMessage = "balance of account"; } } /** * Performs a deposit. * */ class Deposit extends Transaction { /** * Constructs a Deposit. * * @param accountId String Account ID * @param amount double Amount * @exception TellerException if there is * an error in performing the transaction * @exception RemoteException if there is * a communications or systems failure * @exception CreateException if there is * a problem creating a teller */ Deposit(String accountId, double amount) throws TellerException, RemoteException, CreateException { this.account1Id = accountId; this.amount = amount; } /** * Calls a teller bean to perform a deposit. * Overshadows Transaction.invokeTransaction(). * * @exception TellerException if there is * an error in performing the transaction * @exception RemoteException if there is * a communications or systems failure */ void invokeTransaction() throws TellerException, RemoteException { result = teller.deposit(account1Id, amount, transId); } /** * Builds a report of the result of a transaction. * Overshadows Transaction.buildReport(). * */ void buildReport() { super.buildReport(); actionMessage = "deposited $" + amount; } } /** * Performs a withdrawal. * */ class Withdrawal extends Transaction { /** * Constructs a Withdrawal. * * @param accountId String Account ID * @param amount double Amount * @exception TellerException if there is * an error in performing the transaction * @exception RemoteException if there is * a communications or systems failure * @exception CreateException if there is * a problem creating a teller */ Withdrawal(String accountId, double amount) throws TellerException, RemoteException, CreateException { this.account1Id = accountId; this.amount = amount; } /** * Calls a teller bean to perform a withdrawal. * Overshadows Transaction.invokeTransaction(). * * @exception TellerException if there is * an error in performing the transaction * @exception RemoteException if there is * a communications or systems failure */ void invokeTransaction() throws TellerException, RemoteException { result = teller.withdraw(account1Id, amount, transId); } /** * Builds a report of the result of a transaction. * Overshadows Transaction.buildReport(). * */ void buildReport() { super.buildReport(); actionMessage = "withdrew $" + amount; } } /** * Performs a transfer. * */ class Transfer extends Transaction { /** * Constructs a Transfer. * * @param accountFrom String Account from ID * @param amount double Amount * @param accountTo String Account to ID * @exception TellerException if there is * an error in performing the transaction * @exception RemoteException if there is * a communications or systems failure * @exception CreateException if there is * a problem creating a teller */ Transfer(String accountFrom, String accountTo, double amount) throws TellerException, RemoteException, CreateException { this.account1Id = accountFrom; this.account2Id = accountTo; this.amount = amount; } /** * Calls a teller bean to perform a transfer. * Overshadows Transaction.invokeTransaction(). * * @exception TellerException if there is * an error in performing the transaction * @exception RemoteException if there is * a communications or systems failure */ void invokeTransaction() throws TellerException, RemoteException { result = teller.transfer(account1Id, account2Id, amount, transId); } /** * Builds a report of the result of a transaction. * Overshadows Transaction.buildReport(). * */ void buildReport() { super.buildReport(); account2Server = result.getAccount2Result().getServer(); account2Balance = result.getAccount2Result().getBalance(); actionMessage = "Transfered $" + amount + " from " + account1Id + " to " + account2Id; account2Message = "Account " + account2Id + " (on " + account2Server + "): " + "balance: $" + account2Balance; } } }