On Day 12 "Exploring CORBAservices and CORBAfacilities," you first studied the various CORBAservices and CORBAfacilities and learned what sort of functionality is provided by these specifications. You then got a crack at modifying the Bank application to use the CORBA Naming Service. By now, the Bank application has grown into a complex system.
Today you'll take a step back with the Bank application in terms of functionality, but you'll step out in a completely new direction at the same time. Taking the baseline Bank application from Day 9, "Using Callbacks to Add Push Capability," you'll port the application to Java (not as hard as it might sound). In case you're not familiar with Java already, this chapter first gives you a picture of what Java is all about, along with insights into the relationship between Java and CORBA. Java and CORBA are a very good match for each other, and you'll find out why.
Java is still a relative newcomer to the computer industry, but this language-cum-platform has already seen significant increases in maturity and utility, both in the reliability of the platform itself (in most cases) and in the availability of development tools. Since its introduction in 1995 (when it metamorphosed from its predecessor, known as Oak), Java has enjoyed explosive growth, and despite some legal squabbles at the time of this writing, its growth is continuing.
For those unfamiliar with the Java language/platform, the best way to learn about it is to visit the Web page of the JavaSoft division of Sun Microsystems at http://www.javasoft.com/ or to peruse one of numerous texts available on the language. What these sources will tell you is that Java is characterized by the following:
Java has the potential to make the operating system into a commodity, providing a consistent API across all platforms. Whether this potential will ever be realized remains to be seen. Java is not perfect, of course, and still must face challenges in the way of performance, portability, and, of course, political opposition. For the time being, however, it appears that Java is here to stay, and as it turns out, Java is very well suited for use in CORBA application development.
Why do CORBA and Java make a good match for each other? How is this so? What does the CORBA architecture have to offer the Java language/platform, and vice versa? What symbiosis exists between the two? This section discusses some of the factors that make development with CORBA and Java an attractive proposition.
To Java programmers, developing in CORBA feels surprisingly natural and intuitive, almost as if Java and CORBA were designed for each other. (Of course, this isn't the case; Java and CORBA have completely separate design goals.) Most responsible for the near seamlessness between Java and CORBA are the similarities in architecture:
Those familiar with Java are already aware of its Remote Method Interface (RMI) feature. The functionality provided by RMI is very CORBA-like; indeed, both CORBA and RMI share the common goal of enabling the development of distributed, object-oriented applications. Other than some superficial architectural similarities, CORBA and RMI are quite different, and, thus, each is better suited for different purposes.
One major difference between CORBA and RMI is that, whereas CORBA is language-independent, RMI is limited to Java only. This is a major consideration when choosing an architecture, even for an application implemented entirely in Java. Although such an application can use RMI, an architect must consider the possible need for a system to interact in the future with other, non-Java applications. However, Sun has indicated that later versions of RMI will be interoperable with IIOP (CORBA's Internet Inter-ORB Protocol) so interoperability between the RMI and CORBA architectures might not be far away.
CORBA also holds an advantage over RMI in terms of robustness. Not only has CORBA had a number of years head start on RMI, but also CORBA provides a more enterprise-ready solution. Whereas RMI consists of a communications layer and simple naming services, CORBA is part of an architecture that offers many more services valuable for developing enterprise-class applications. (Review Chapter 12 for an overview of the services and facilities provided by the Object Management Architecture.) Offering capabilities such as hierarchical naming services, transaction management facilities, event services, and a wealth of vertical market facilities, CORBA holds a clear advantage over RMI in terms of robustness.
CORBA does not hold all the cards, though; RMI currently has at least one significant advantage over the CORBA architecture. Because RMI is a Java-only technology, it can take advantage of features of the Java platform. Most notably, RMI integrates seamlessly with Java's Object Serialization technology, enabling objects to be passed by value (as well as by reference) between remote components. In addition, not only can objects be passed by value, but also, because Java employs a platform-independent bytecode, new classes can be sent across the network via RMI for use by client or server components. The capability to dynamically (at runtime) introduce new classes into a system opens up a wealth of potential for new breeds of applications.
Other factors exist that lend credibility to the marriage of Java and CORBA. Perhaps one of the most compelling features of Java is its portability--the capability to run on various types of computer hardware and operating systems. One characteristic you're certain to find in a distributed application is the need to run on a variety of hardware and OS platforms. Also, there is likely greater diversity on the client end of the application, where numerous types of desktop machines abound--from low-end Network Computers, to midrange PCs, to high-end UNIX workstations. Certainly, being able to deliver client applications on all potential end-user platforms is a boon to developers of enterprise-class applications. This is what Java offers to CORBA: the capability to write a client-side application once and run it on a multitude of platforms. (As Java continues to make strides in performance and robustness, it will see more use on the server side of distributed applications, but that's another story.)
Not only does CORBA benefit from Java by gaining cross-platform client applications, but also Java gains from CORBA. The benefit to Java is that CORBA offers cross-language interoperability. Recall that Java's Remote Method Invocation facility works only between Java applications; to communicate with non-Java applications, developers must implement their own communications layer, possibly involving low-level network sockets. CORBA makes this unnecessary, providing an object-oriented abstraction that enables Java applications to communicate with applications written in almost any language.
Developing a CORBA application in Java is not unlike developing the same application in C++. You still use an IDL compiler to translate IDL definitions into server skeletons and client stubs; the only difference is that the compiler generates Java code rather than C++ code. The process, however, remains the same.
To develop the Java version of the Bank application presented in this chapter, you need a few additional tools:
When you have all these items, you're ready to begin developing in Java.
As already mentioned, the use of the IDL compiler is exactly the same when you're writing a CORBA application in C++ or in Java. The command to run the IDL compiler, though, might be different. For the compiler included with VisiBroker for Java, use the following command:
idl2java -package idlGlobal filename
where filename is the name of the IDL file to be compiled. Also, here the -package idlGlobal switch tells the IDL compiler to place the generated code into a directory named idlGlobal, and the generated Java classes will be placed in the idlGlobal package. The VisiBroker IDL compiler will generate code for both servers and clients.
Warning: Before attempting to use the Java IDL compiler, make sure you have installed the product according to the vendor's documentation. In particular, you need to set the PATH and CLASSPATH environment variables. For example, VisiBroker requires VisiBroker-directory/vbj30.jar to be added to the CLASSPATH. If you encounter errors when trying to use the IDL compiler, consult your vendor's documentation.
To prepare for development of the Java Bank application, copy the IDL source files from Day 9 into a new directory, and then compile them with the Java IDL compiler. It's convenient to create a batch file, shell script file, or Makefile to perform this step automatically. When you complete this step, you will have a directory named idlGlobal containing a number of files with the .java extension. You're now ready to proceed with development, starting with the server functionality.
Because you have made no changes to the Bank application, other than using a different development language, the architecture has not altered from previous chapters. Consequently, all the application components will look very familiar to you by now.
The BankServer is now implemented in two files, BankServerImpl.java and BankServerMain. java, appearing in Listings 13.1 and 13.2, respectively. Also, there is the file CORBAAlgorithms. java, which contains some utility methods, appearing in Listing 13.3.
1: // BankServerImpl.java 2: 3: import java.util.Enumeration; 4: import java.util.Vector; 5: 6: import idlGlobal.ATM; 7: import idlGlobal.Bank; 8: 9: import idlGlobal.InvalidATMException; 10: import idlGlobal.InvalidBankException; 11: 12: import util.CORBAAlgorithms; 13: 14: public class BankServerImpl extends idlGlobal._BankServerImplBase { 15: 16: // This BankServer's list of Banks. 17: private Vector myBanks; 18: 19: // This BankServer's list of ATMs. 20: private Vector myATMs; 21: 22: public BankServerImpl(String name) { 23: 24: super(name); 25: 26: myBanks = new Vector(); 27: myATMs = new Vector(); 28: } 29: 30: public void registerBank(Bank bank) throws InvalidBankException 31: { 32: 33: // First, ensure that the given Bank doesn't exist already. 34: if (!CORBAAlgorithms.contains(myBanks, bank)) { 35: 36: // Bank was not found, so add the given Bank to the end 37: // of the list. 38: System.out.println("BankServerImpl: Registering Bank " 39: + "\"" + bank.name() + "\"."); 40: myBanks.addElement(bank._duplicate()); 41: return; 42: } 43: 44: // The Bank was already registered, so throw an exception. 45: System.out.println("BankServerImpl: Attempted to " + 46: "register duplicate Bank."); 47: throw new InvalidBankException(); 48: } 49: 50: public void unregisterBank(Bank bank) throws 51: InvalidBankException { 52: 53: if (!CORBAAlgorithms.contains(myBanks, bank)) { 54: 55: // Invalid Bank; throw an exception. 56: System.out.println("BankServerImpl: Attempted to " + 57: "unregister invalid Bank."); 58: throw new InvalidBankException(); 59: } 60: 61: System.out.println("BankServerImpl: Unregistering Bank \"" 62: + bank.name() + "\"."); 63: 64: // Delete the given Bank. 65: CORBAAlgorithms.removeElement(myBanks, bank); 66: bank._release(); 67: } 68: 69: public Bank[] getBanks() { 70: 71: Bank[] list = new Bank[myBanks.size()]; 72: myBanks.copyInto(list); 73: 74: Enumeration e = myBanks.elements(); 75: while (e.hasMoreElements()) { 76: ((Bank)e.nextElement())._duplicate(); 77: } 78: 79: return list; 80: } 81: 82: public void registerATM(ATM atm) throws InvalidATMException { 83: 84: // First, ensure that the given ATM doesn't exist already. 85: if (!CORBAAlgorithms.contains(myATMs, atm)) { 86: 87: // ATM was not found, so add the given ATM to the end of 88: // the list. 89: System.out.println("BankServerImpl: Registering ATM " 90: + "\"" + atm.name() + "\"."); 91: myATMs.addElement(atm._duplicate()); 92: return; 93: } 94: 95: // The ATM was already registered, so throw an exception. 96: System.out.println("BankServerImpl: Attempted to " + 97: "register duplicate ATM."); 98: throw new InvalidATMException(); 99: } 100: 101: public void unregisterATM(ATM atm) throws InvalidATMException { 102: 103: 104: if (!CORBAAlgorithms.contains(myATMs, atm)) { 105: 106: // Invalid ATM; throw an exception. 107: System.out.println("BankServerImpl: Attempted to " + 108: "unregister invalid ATM."); 109: throw new InvalidATMException(); 110: } 111: 112: System.out.println("BankServerImpl: Unregistering ATM \"" 113: + atm.name() + "\"."); 114: 115: // Delete the given ATM. 116: CORBAAlgorithms.removeElement(myATMs, atm); 117: atm._release(); 118: } 119: 120: public ATM[] getATMs() { 121: 122: ATM[] list = new ATM[myATMs.size()]; 123: myATMs.copyInto(list); 124: 125: Enumeration e = myATMs.elements(); 126: while (e.hasMoreElements()) { 127: ((ATM)e.nextElement())._duplicate(); 128: } 129: 130: return list; 131: } 132: }
1: // BankServerMain.java 2: 3: import org.omg.CORBA.BOA; 4: import org.omg.CORBA.ORB; 5: 6: public class BankServerMain { 7: 8: public static ORB myORB; 9: public static BOA myBOA; 10: 11: public static void main(String[] args) { 12: 13: try { 14: 15: // Initialize the ORB and BOA. 16: myORB = ORB.init(args, null); 17: myBOA = myORB.BOA_init(); 18: 19: // Create a BankServerImpl object and register it with 20: // the ORB. 21: BankServerImpl bankServer = new 22: BankServerImpl("BankServer"); 23: myBOA.obj_is_ready(bankServer); 24: 25: // Wait for CORBA events. 26: System.out.println("BankServer ready."); 27: myBOA.impl_is_ready(); 28: } catch (Exception ex) { 29: 30: // Something failed... 31: System.out.println("BankServerImpl: Unable to bind " + 32: "BankServer:"); 33: System.out.println(ex); 34: return; 35: } 36: 37: // When this point is reached, the application is finished. 38: return; 39: } 40: }
1: // CORBAAlgorithms.java 2: 3: package util; 4: 5: import java.util.Enumeration; 6: import java.util.Vector; 7: 8: public class CORBAAlgorithms { 9: 10: // Return true if the given Vector contains the given CORBA 11: // object, that is, if there is an element in the Vector for 12: // which obj._is_equivalent() returns true. 13: public static boolean contains(Vector vector, org.omg.CORBA. 14: Object obj) { 15: 16: Enumeration e = vector.elements(); 17: 18: while (e.hasMoreElements()) { 19: if (obj._is_equivalent((org.omg.CORBA.Object)e. 20: nextElement())) { 21: 22: // A match was found. 23: return true; 24: } 25: } 26: 27: // A match was not found. 28: return false; 29: } 30: 31: // Remove the first element from the given Vector for which the 32: // obj._is_equivalent() returns true for that element. If no 33: // such element is found, this method does nothing. 34: public static void removeElement(Vector vector, org.omg.CORBA. 35: Object obj) { 36: 37: Enumeration e = vector.elements(); 38: 39: while (e.hasMoreElements()) { 40: org.omg.CORBA.Object cobj = (org.omg.CORBA.Object)e. 41: nextElement(); 42: if (obj._is_equivalent(cobj)) { 43: 44: // A match was found; remove the element. 45: vector.removeElement(cobj); 46: return; 47: } 48: } 49: } 50: }
The logic of the BankServer implementation is identical to its C++ counterpart. Here are some highlights of the Java implementation.
The import statement in Java, as seen in lines 3-12 of Listing 13.1, is a distant cousin to C++'s #include preprocessor directive. The #include directive imports information about other classes into a source file; Java's import statement serves the same purpose. Note that the import statement uses the fully qualified class name of the class--that is, the class's package name plus the class name itself. For instance, the Enumeration class from the preceding listings is in the java.util package.
Note the imports of classes in the idlGlobal package (in lines 6 and 7 of Listing 13.1). These are the classes generated by the IDL compiler in the previous step.
Now notice the class declaration in line 14. In the Java language mapping for IDL, implementation classes for CORBA interfaces extend a certain base class. For the BankServerImpl class, this base class is idlGlobal._BankServerImplBase.
Vector (or more specifically, java.util.Vector) is a built-in Java class that provides the semantics of a resizable array. The BankServerImpl uses Vectors to store the Banks and ATMs that register with the BankServer, as seen in lines 16-20.
Note that in the constructor for BankServerImpl (appearing in lines 22-28), a call is made to super(). This is a special method name denoting a constructor in the superclass (in this case, _BankServerImplBase) that accepts the given arguments (or argument, in this case). Also, note that the BankServerImpl constructor creates the two Vectors declared earlier.
The remainder of the BankServerImpl implementation is self-explanatory. You'll now move on to BankServerMain.java, which creates a BankServerImpl and registers the object in the CORBA environment.
Now turn your attention to Listing 13.2. Note first the import statements appearing in line 3 and 4. These import statements import two important classes in a CORBA application: the Basic Object Adapter (BOA) and the Object Request Broker (ORB). In a moment, you'll see what they are used for.
You might have already guessed this, but static class members in Java (such as those that appear in lines 8 and 9) behave like static members in C++: They are available independently of a particular instance of that class.
Every Java application must have a public static void main() in at least one class (actually, every class can have a main() method, but only one can invoke the application at any given time). This method always takes String[] args as a parameter (or the semantically equivalent String args[]). The main() method of BankServerMain begins in line 11.
Before any CORBA objects are created, the ORB and BOA must be initialized, as shown in lines 13-17.
Then the BankServerImpl object is created and registered with the BOA, through the obj_is_ready() method, as seen in lines 19-23.
The impl_is_ready() method, called in line 27, enters the CORBA event loop, passing incoming CORBA events to the appropriate objects.
Finally, in lines 28-35, a catch handler is added in case exceptions are thrown by any of the methods called. The try...catch mechanism works the same in Java as in C++.
CORBAAlgorithms.java, appearing in Listing 13.3, contains a couple of useful methods.
Note the use of the package statement in line 3. This indicates that the CORBAAlgorithms class belongs to the util package.
Note also that contains(), which begins in line 10, is declared as a static method. Such methods behave the same as in C++--that is, they exist independently of any object instance and are invoked as freestanding functions rather than as methods on objects.
As seen in lines 16-29, this method iterates through the provided Vector, searching for an object for which _is_equivalent() (a standard CORBA method) returns true. The next method, removeElement(), behaves similarly.
Warning: Remember that the _is_equivalent() method returns true if it is known that two object references refer to the same object. It is possible for _is_equivalent() to return false even if two references are in fact equivalent; the only guarantee is that the method will never return true if the references are not equivalent. Use this method with caution.
Listings 13.4-10 contain the definitions of the Java classes that implement the Bank functionality. You'll see that the functionality provided by these classes mirrors their counterparts from previous chapters.
1: // AccountImpl.java 2: 3: import java.util.Enumeration; 4: import java.util.Vector; 5: 6: import idlGlobal.Customer; 7: 8: import idlGlobal.InsufficientFundsException; 9: import idlGlobal.InvalidAmountException; 10: 11: public class AccountImpl extends idlGlobal._AccountImplBase { 12: 13: // This Account's account number. 14: private String myAccountNumber; 15: 16: // This Account's creation date. 17: private String myCreationDate; 18: 19: // This Account's balance. 20: private float myBalance; 21: 22: // This Account's list of owners (which are Customers). 23: private Vector myOwners; 24: 25: public AccountImpl(String accountNumber, String creationDate, 26: float initialBalance, Customer customer) { 27: 28: super(accountNumber); 29: 30: myAccountNumber = new String(accountNumber); 31: myCreationDate = new String(creationDate); 32: myBalance = initialBalance; 33: myOwners = new Vector(); 34: myOwners.addElement(customer._duplicate()); 35: } 36: 37: protected AccountImpl() { 38: 39: myAccountNumber = new String(); 40: myCreationDate = new String(); 41: myBalance = 0.0f; 42: myOwners = new Vector(); 43: } 44: 45: public String accountNumber() { 46: 47: return myAccountNumber; 48: } 49: 50: public String creationDate() { 51: 52: return myCreationDate; 53: } 54: 55: public float balance() { 56: 57: return myBalance; 58: } 59: 60: public Customer[] getCustomers() { 61: 62: Customer[] list = new Customer[myOwners.size()]; 63: myOwners.copyInto(list); 64: 65: Enumeration e = myOwners.elements(); 66: while (e.hasMoreElements()) { 67: ((Customer)e.nextElement())._duplicate(); 68: } 69: 70: return list; 71: } 72: 73: public float withdraw(float amount) throws 74: InvalidAmountException, InsufficientFundsException { 75: 76: // Disallow the withdrawal of negative amounts and throw an 77: // exception if this is attempted. 78: if (amount < 0.0) { 79: System.out.println("AccountImpl: Attempted to " + 80: "withdraw invalid amount."); 81: throw new InvalidAmountException(); 82: } 83: 84: // Disallow withdrawal of an amount greater than the 85: // current balance and throw an exception if this is 86: // attempted. 87: if (amount > myBalance) { 88: System.out.println("AccountImpl: Insufficient funds to" 89: + " withdraw specified amount."); 90: throw new InsufficientFundsException(); 91: } 92: 93: myBalance -= amount; 94: 95: return myBalance; 96: } 97: 98: public float deposit(float amount) throws 99: InvalidAmountException { 100: 101: // Disallow the deposit of negative amounts and throw an 102: // exception if this is attempted. 103: if (amount < 0.0) { 104: System.out.println("AccountImpl: Attempted to deposit " 105: + "invalid amount."); 106: throw new InvalidAmountException(); 107: } 108: 109: myBalance += amount; 110: 111: return myBalance; 112: } 113: }
1: // ATMCardImpl.java 2: 3: import java.util.Enumeration; 4: import java.util.Vector; 5: 6: import idlGlobal.Account; 7: 8: import idlGlobal.InvalidAccountException; 9: 10: import util.CORBAAlgorithms; 11: 12: public class ATMCardImpl extends idlGlobal._ATMCardImplBase { 13: 14: // This ATMCard's Personal Identification Number (PIN). 15: private short myPIN; 16: 17: // The Accounts which are authorized for use with this ATMCard. 18: private Vector myAccounts; 19: 20: public ATMCardImpl(short pin, Account initialAccount) { 21: 22: myPIN = pin; 23: myAccounts = new Vector(); 24: myAccounts.addElement(initialAccount._duplicate()); 25: } 26: 27: private ATMCardImpl() { 28: 29: myPIN = 0; 30: myAccounts = new Vector(); 31: } 32: 33: public void pin(short pin) { 34: 35: myPIN = pin; 36: } 37: 38: public short pin() { 39: 40: return myPIN; 41: } 42: 43: public Account[] getAccounts() { 44: 45: Account[] list = new Account[myAccounts.size()]; 46: myAccounts.copyInto(list); 47: 48: Enumeration e = myAccounts.elements(); 49: while (e.hasMoreElements()) { 50: ((Account)e.nextElement())._duplicate(); 51: } 52: 53: return list; 54: } 55: 56: public void addAccount(Account account) throws 57: InvalidAccountException { 58: 59: if (isAuthorized(account)) { 60: 61: // Account has already been added, so throw an 62: // exception. 63: throw new InvalidAccountException(); 64: } 65: 66: // Add the created Account at the end of the list. 67: myAccounts.addElement(account._duplicate()); 68: } 69: 70: public void removeAccount(Account account) throws 71: InvalidAccountException { 72: 73: if (!isAuthorized(account)) { 74: 75: // Invalid Account; throw an exception. 76: throw new InvalidAccountException(); 77: } 78: 79: // Delete the given Account. 80: CORBAAlgorithms.removeElement(myAccounts, account); 81: account._release(); 82: } 83: 84: public boolean isAuthorized(Account account) { 85: 86: return CORBAAlgorithms.contains(myAccounts, account); 87: } 88: }
1: // CheckingAccountImpl.java 2: 3: import idlGlobal.Customer; 4: 5: import idlGlobal.InsufficientFundsException; 6: import idlGlobal.InvalidAmountException; 7: 8: public class CheckingAccountImpl extends idlGlobal. 9: _CheckingAccountImplBase { 10: 11: // The AccountImpl to which most of this CheckingAccountImpl's 12: // functionality is delegated. 13: private AccountImpl myAccount; 14: 15: public CheckingAccountImpl(String accountNumber, String 16: creationDate, float initialBalance, Customer customer) 17: { 18: 19: myAccount = new AccountImpl(accountNumber, creationDate, 20: initialBalance, customer); 21: } 22: 23: protected CheckingAccountImpl() { 24: 25: } 26: 27: public String accountNumber() { 28: 29: return myAccount.accountNumber(); 30: } 31: 32: public String creationDate() { 33: 34: return myAccount.creationDate(); 35: } 36: 37: public float balance() { 38: 39: return myAccount.balance(); 40: } 41: 42: public Customer[] getCustomers() { 43: 44: return myAccount.getCustomers(); 45: } 46: 47: public float withdraw(float amount) throws 48: InvalidAmountException, InsufficientFundsException { 49: 50: return myAccount.withdraw(amount); 51: } 52: 53: public float deposit(float amount) throws 54: InvalidAmountException { 55: 56: return myAccount.deposit(amount); 57: }
58: }
1: // SavingsAccountImpl.java 2: 3: import idlGlobal.Customer; 4: 5: import idlGlobal.InsufficientFundsException; 6: import idlGlobal.InvalidAmountException; 7: 8: public class SavingsAccountImpl extends idlGlobal. 9: _SavingsAccountImplBase { 10: 11: // The AccountImpl to which most of this SavingsAccountImpl's 12: // functionality is delegated. 13: private AccountImpl myAccount; 14: 15: // This account's interest rate. 16: private float myInterestRate; 17: 18: public SavingsAccountImpl(String accountNumber, String 19: creationDate, float initialBalance, Customer customer, 20: float interestRate) { 21: 22: myAccount = new AccountImpl(accountNumber, creationDate, 23: initialBalance, customer); 24: myInterestRate = interestRate; 25: } 26: 27: protected SavingsAccountImpl() { 28: 29: } 30: 31: public String accountNumber() { 32: 33: return myAccount.accountNumber(); 34: } 35: 36: public String creationDate() { 37: 38: return myAccount.creationDate(); 39: } 40: 41: public float balance() { 42: 43: return myAccount.balance(); 44: } 45: 46: public Customer[] getCustomers() { 47: 48: return myAccount.getCustomers(); 49: } 50: 51: public float withdraw(float amount) throws 52: InvalidAmountException, InsufficientFundsException { 53: 54: return myAccount.withdraw(amount); 55: } 56: 57: public float deposit(float amount) throws 58: InvalidAmountException { 59: 60: return myAccount.deposit(amount); 61: } 62: 63: public float interestRate() { 64: 65: return myInterestRate; 66: } 67: 68: public float setInterestRate(float rate) throws 69: InvalidAmountException { 70: 71: // Disallow negative interest rates and throw an exception 72: // if this is attempted. 73: if (rate < 0.0) { 74: 75: throw new InvalidAmountException(); 76: } 77: 78: float oldInterestRate = myInterestRate; 79: myInterestRate = rate; 80: return oldInterestRate; 81: } 82: }
1: // BankImpl.java 2: 3: import java.text.SimpleDateFormat; 4: import java.util.Date; 5: import java.util.Enumeration; 6: import java.util.Vector; 7: 8: import idlGlobal.Account; 9: import idlGlobal.ATMCard; 10: import idlGlobal.Customer; 11: 12: import idlGlobal.InvalidAccountException; 13: 14: import util.CORBAAlgorithms; 15: 16: public class BankImpl extends idlGlobal._BankImplBase { 17: 18: // This Bank's name. 19: private String myName; 20: 21: // This Bank's address. 22: private String myAddress; 23: 24: // This Bank's list of Accounts. 25: Vector myAccounts; 26: 27: // This Bank's list of Accounts that are subscribed to the 28: // automatic account update service. 29: Vector mySubscribedAccounts; 30: 31: // This Bank's next Account number to assign. 32: int myNextAccountNumber; 33: 34: public BankImpl(String name) { 35: 36: myName = new String(name); 37: myAddress = new String("123 Elm Street, Anywhere USA " 38: + "12345"); 39: myNextAccountNumber = 0; 40: myAccounts = new Vector(); 41: mySubscribedAccounts = new Vector(); 42: 43: Thread updateThread = new 44: UpdateAccountThread(mySubscribedAccounts); 45: updateThread.start(); 46: } 47: 48: private BankImpl() { 49: 50: myName = new String(); 51: myAddress = new String(); 52: myAccounts = new Vector(); 53: mySubscribedAccounts = new Vector(); 54: myNextAccountNumber = 0; 55: } 56: 57: public void name(String name) { 58: 59: myName = new String(name); 60: } 61: 62: public String name() { 63: 64: return myName; 65: } 66: 67: public void address(String address) { 68: 69: myAddress = new String(address); 70: } 71: 72: public String address() { 73: 74: return myAddress; 75: } 76: 77: public Account createAccount(Customer customer, String 78: accountType, float openingBalance) { 79: 80: Account newAccount; 81: 82: if (accountType.compareTo("savings") == 0) { 83: 84: // Create a new SavingsAccountImpl object for the 85: // Account. 86: System.out.println("BankImpl: Creating new " + 87: "SavingsAccount for Customer " + customer. 88: name() + "."); 89: newAccount = new 90: SavingsAccountImpl(getNextAccountNumber(), 91: getCurrentDate(), openingBalance, customer, 92: 10.0f); 93: } else if (accountType.compareTo("checking") == 0) { 94: 95: // Create a new CheckingAccountImpl object for the 96: // Account. 97: System.out.println("BankImpl: Creating new " + 98: "CheckingAccount for Customer " + customer. 99: name() + "."); 100: newAccount = new 101: CheckingAccountImpl(getNextAccountNumber(), 102: getCurrentDate(), openingBalance, customer); 103: } else { 104: 105: // Invalid Account type; do nothing. 106: System.out.println("BankImpl: Customer " + customer. 107: name() + " requested invalid Account type \"" + 108: accountType + "\"."); 109: return null; 110: } 111: 112: // Add the created Account at the end of the list and 113: // return it. 114: BankMain.myBOA.obj_is_ready(newAccount); 115: myAccounts.addElement(newAccount._duplicate()); 116: return newAccount; 117: } 118: 119: public void deleteAccount(Account account) throws 120: InvalidAccountException { 121: 122: if (!CORBAAlgorithms.contains(myAccounts, account)) { 123: 124: // Invalid Account; throw an exception. 125: System.out.println("BankImpl: Attempted to delete " + 126: "invalid Account."); 127: throw new InvalidAccountException(); 128: } 129: 130: System.out.println("BankImpl: Deleting Account \"" + 131: account.accountNumber() + "\"."); 132: 133: // Delete the given Account. 134: CORBAAlgorithms.removeElement(myAccounts, account); 135: account._release(); 136: } 137: 138: public Account[] getAccounts() { 139: 140: Account[] list = new Account[myAccounts.size()]; 141: myAccounts.copyInto(list); 142: 143: Enumeration e = myAccounts.elements(); 144: while (e.hasMoreElements()) { 145: ((Account)e.nextElement())._duplicate(); 146: } 147: 148: return list; 149: } 150: 151: public ATMCard issueATMCard(short pin, Account account) throws 152: InvalidAccountException { 153: 154: // First check to see if the Account is with this Bank. 155: if (!CORBAAlgorithms.contains(myAccounts, account)) { 156: 157: // Invalid Account; throw an exception. 158: System.out.println("BankImpl: Attempted to delete " + 159: "invalid Account."); 160: throw new InvalidAccountException(); 161: } 162: 163: // If we got this far, the Account must exist with this 164: // Bank, so we can proceed. 165: ATMCard newCard = new ATMCardImpl(pin, account); 166: BankMain.myBOA.obj_is_ready(newCard); 167: 168: System.out.println("BankImpl: Issuing new ATMCard on " + 169: "Account " + account.accountNumber()); 170: newCard._duplicate(); 171: return newCard; 172: } 173: 174: public void requestUpdateService(Account account) throws 175: InvalidAccountException { 176: 177: // First check to see if the Account is with this Bank. 178: if (!CORBAAlgorithms.contains(myAccounts, account)) { 179: 180: // Invalid Account; throw an exception. 181: System.out.println("BankImpl: Attempted to delete " + 182: "invalid Account."); 183: throw new InvalidAccountException(); 184: } 185: 186: // If we got this far, the Account must exist with this 187: // Bank, so we can proceed. 188: mySubscribedAccounts.addElement(account._duplicate()); 189: } 190: 191: // Return the next available account number. 192: protected String getNextAccountNumber() { 193: 194: return "Account" + myNextAccountNumber++; 195: } 196: 197: // Return the current date in the form "Mmm DD YYYY". 198: protected String getCurrentDate() { 199: 200: SimpleDateFormat format = new SimpleDateFormat("MMM dd " + 201: "yyyy"); 202: return format.format(new Date()); 203: }
204: }
1: // UpdateAccountThread.java 2: 3: import java.util.Enumeration; 4: import java.util.Vector; 5: 6: import idlGlobal.Account; 7: import idlGlobal.Customer; 8: 9: public class UpdateAccountThread extends Thread { 10: 11: // The Accounts to be updated by this thread. 12: Vector myAccounts; 13: 14: public UpdateAccountThread(Vector accounts) { 15: 16: myAccounts = accounts; 17: } 18: 19: protected UpdateAccountThread() { 20: 21: } 22: 23: public void run() { 24: 25: // Do this forever (or until the thread is killed). 26: while (true) { 27: 28: // Sleep for one minute between updates. 29: try { 30: sleep(60000); 31: } catch (InterruptedException ex) { 32: } 33: 34: // Iterate through each Account... 35: Enumeration e = myAccounts.elements(); 36: while (e.hasMoreElements()) { 37: Account account = (Account)e.nextElement(); 38: 39: // Iterate through each Customer associated with 40: // the Account... 41: Customer[] customers = account.getCustomers(); 42: for (int i = 0; i < customers.length; i++) { 43: 44: try { 45: // Send an update message to each Customer. 46: customers[i].updateAccountBalance(account, 47: account.balance()); 48: } catch (Exception ex) { 49: 50: // Ignore any exceptions that occur. 51: } 52: } 53: } 54: } 55: } 56: }
1: // BankMain.java 2: 3: import org.omg.CORBA.BOA; 4: import org.omg.CORBA.ORB; 5: 6: import idlGlobal.BankServer; 7: import idlGlobal.BankServerHelper; 8: 9: public class BankMain { 10: 11: public static ORB myORB; 12: public static BOA myBOA; 13: 14: public static void main(String[] args) { 15: 16: // Check the number of arguments; there should be exactly 17: // one. 18: if (args.length != 1) { 19: System.out.println("Usage: Bank <bankname>"); 20: return; 21: } 22: 23: // Assign the Bank name to the first argument. 24: String bankName = args[0]; 25: 26: BankServer bankServer; 27: BankImpl bank; 28: 29: try { 30: 31: // Initialize the ORB and BOA. 32: myORB = ORB.init(args, null); 33: myBOA = myORB.BOA_init(); 34: 35: // Create an BankImpl object and register it with the 36: // ORB. 37: bank = new BankImpl(bankName); 38: myBOA.obj_is_ready(bank); 39: 40: // Bind to a BankServer. 41: bankServer = BankServerHelper.bind(myORB); 42: System.out.println("BankImpl: Successfully bound to " + 43: "BankServer."); 44: } catch (Exception ex) { 45: 46: // The bind attempt failed... 47: System.out.println("BankImpl: Unable to bind to " + 48: "BankServer."); 49: return; 50: } 51: 52: try { 53: 54: // Register with the BankServer. 55: bankServer.registerBank(bank); 56: 57: // Wait for CORBA events. 58: myBOA.impl_is_ready(); 59: } catch (Exception ex) { 60: 61: // The registerBank() attempt failed... 62: System.out.println("BankImpl: Unable to register " + 63: "Bank:"); 64: System.out.println(ex); 65: return; 66: } 67: 68: // When this point is reached, the application is finished. 69: return; 70: } 71: }
Here are highlights from the various classes that implement the Bank server application. First, note the implementation for getCustomers() in AccountImpl.java (refer to lines 60-71 of Listing 13.4).
Notice the relative simplicity with which objects are copied from Vectors into arrays. First, an array of the appropriate size is created (in line 62), and the contents of the Vector are copied into it with a single method call: copyInto() (line 63). Because these are outbound CORBA objects, they must be _duplicate()d, which is the reason for the subsequent iteration across the Vector, as in lines 65-68.
Next, note the constructor of BankImpl (lines 34-46 of Listing 13.8).
In the constructor, an UpdateAccountThread object is created (which you'll get a closer look at in a moment) and started. For simple threads, this is all that Java requires: Create a Thread object, and call start() on that object. Now, look at the UpdateAccountThread itself, beginning in line 9 of Listing 13.9.
One way to implement threads in Java is to create a class that derives from java.lang.Thread (or simply Thread), as UpdateAccountThread does. The next step is to implement the run() method. The actual workings of the UpdateAccountThread.run() method are self-explanatory.
Finally, notice in BankMain.java how an application binds to a CORBA object (refer to Listing 13.10). Again, the ORB is initialized (lines 31-33).
Sometime after the ORB and BOA are initialized, BankMain uses the bind() method of BankServerHelper to bind to a BankServer object, as in line 41. (Recall again that bind() is nonstandard and is used here for simplicity.)
The implementation of the ATM server, consisting of ATMImpl.java and ATMMain.java, appears in Listings 13.11 and 13.12. This is clear-cut and contains no surprises.
1: // ATMImpl.java 2: 3: import idlGlobal.Account; 4: import idlGlobal.ATMCard; 5: 6: import idlGlobal.AuthorizationException; 7: import idlGlobal.InsufficientFundsException; 8: import idlGlobal.InvalidAmountException; 9: 10: public class ATMImpl extends idlGlobal._ATMImplBase { 11: 12: private String myName; 13: 14: public ATMImpl(String name) { 15: 16: myName = new String(name); 17: } 18: 19: private ATMImpl() { 20: 21: myName = new String(); 22: } 23: 24: public String name() { 25: 26: return myName; 27: } 28: 29: public void name(String name) { 30: 31: myName = new String(name); 32: } 33: 34: public float withdraw(ATMCard card, Account account, short pin, 35: float amount) throws AuthorizationException, 36: InvalidAmountException, InsufficientFundsException { 37: 38: System.out.println("ATM: Authorizing Account for " + 39: "withdrawal."); 40: if (pin != card.pin() || !card.isAuthorized(account)) { 41: 42: // Incorrect PIN or card not authorized; throw an 43: // exception. 44: System.out.println(" Authorization failed."); 45: throw new AuthorizationException(); 46: } 47: 48: System.out.println(" Authorization succeeded; forwarding " 49: + "withdrawal request to Account."); 50: return account.withdraw(amount); 51: } 52: 53: public float deposit(ATMCard card, Account account, short pin, 54: float amount) throws AuthorizationException, 55: InvalidAmountException { 56: 57: System.out.println("ATM: Authorizing Account for " + 58: "deposit."); 59: if (pin != card.pin() || !card.isAuthorized(account)) { 60: 61: // Incorrect PIN or card not authorized; throw an 62: // exception. 63: System.out.println(" Authorization failed."); 64: throw new AuthorizationException(); 65: } 66: 67: System.out.println(" Authorization succeeded; forwarding " 68: + "deposit request to Account."); 69: return account.deposit(amount); 70: } 71: 72: public float getBalance(ATMCard card, Account account, short 73: pin) throws AuthorizationException { 74: 75: System.out.println("ATM: Authorizing Account for " + 76: "balance."); 77: if (pin != card.pin() || !card.isAuthorized(account)) { 78: 79: // Incorrect PIN or card not authorized; throw an 80: // exception. 81: System.out.println(" Authorization failed."); 82: throw new AuthorizationException(); 83: } 84: 85: System.out.println(" Authorization succeeded; forwarding " 86: + "balance request to Account."); 87: return account.balance(); 88: } 89: }
1: // ATMMain.java 2: 3: import org.omg.CORBA.BOA; 4: import org.omg.CORBA.ORB; 5: 6: import idlGlobal.BankServer; 7: import idlGlobal.BankServerHelper; 8: 9: public class ATMMain { 10: 11: public static ORB myORB; 12: public static BOA myBOA; 13: 14: public static void main(String[] args) { 15: 16: // Check the number of arguments; there should be exactly 17: // one. 18: if (args.length != 1) { 19: System.out.println("Usage: ATM <atmname>"); 20: return; 21: } 22: 23: // Assign the ATM name to the first argument. 24: String atmName = args[0]; 25: 26: BankServer bankServer; 27: ATMImpl atm; 28: try { 29: 30: // Initialize the ORB and BOA. 31: myORB = ORB.init(args, null); 32: myBOA = myORB.BOA_init(); 33: 34: // Create an ATMImpl object and register it with the 35: // ORB. 36: atm = new ATMImpl(atmName); 37: myBOA.obj_is_ready(atm); 38: 39: // Bind to a BankServer. 40: bankServer = BankServerHelper.bind(myORB); 41: System.out.println("ATMImpl: Successfully bound to " + 42: "BankServer."); 43: } catch (Exception ex) { 44: 45: // The bind attempt failed... 46: System.out.println("ATMImpl: Unable to bind to " + 47: "BankServer."); 48: return; 49: } 50: 51: try { 52: 53: // Register with the BankServer. 54: bankServer.registerATM(atm); 55: 56: // Wait for CORBA events. 57: myBOA.impl_is_ready(); 58: } catch (Exception ex) { 59: 60: // The registerATM() attempt failed... 61: System.out.println("ATMImpl: Unable to register ATM:"); 62: System.out.println(ex); 63: return; 64: } 65: 66: // When this point is reached, the application is finished. 67: return; 68: } 69: }
The client pieces--CustomerImpl.java and ATMClientMain.java--carry over almost exactly from their C++ counterparts. The implementations appear in Listings 13.13 and 13.14, respectively.
1: // CustomerImpl.java 2: 3: import java.util.Enumeration; 4: import java.util.Vector; 5: 6: import idlGlobal.Account; 7: 8: public class CustomerImpl extends idlGlobal._CustomerImplBase { 9: 10: // This Customer's name. 11: private String myName; 12: 13: // This Customer's Social Security number. 14: private String mySocialSecurityNumber; 15: 16: // This Customer's address. 17: private String myAddress; 18: 19: // This Customer's mother's maiden name. 20: private String myMothersMaidenName; 21: 22: // This Customer's list of Accounts. 23: private Vector myAccounts; 24: 25: // This Customer's list of ATMCards. 26: private Vector myATMCards; 27: 28: public CustomerImpl(String name, String socialSecurityNumber, 29: String address, String mothersMaidenName) { 30: 31: super(socialSecurityNumber); 32: 33: myName = new String(name); 34: mySocialSecurityNumber = new 35: String(socialSecurityNumber); 36: myAddress = new String(address); 37: myMothersMaidenName = new String(mothersMaidenName); 38: myAccounts = new Vector(); 39: myATMCards = new Vector(); 40: } 41: 42: protected CustomerImpl() { 43: 44: myName = new String(); 45: mySocialSecurityNumber = new String(); 46: myAddress = new String(); 47: myMothersMaidenName = new String(); 48: myAccounts = new Vector(); 49: myATMCards = new Vector(); 50: } 51: 52: public void name(String name) { 53: 54: myName = new String(name); 55: } 56: 57: public String name() { 58: 59: return myName; 60: } 61: 62: public String socialSecurityNumber() { 63: 64: return mySocialSecurityNumber; 65: } 66: 67: public void address(String address) { 68: 69: myAddress = new String(address); 70: } 71: 72: public String address() { 73: 74: return myAddress; 75: } 76: 77: public java.lang.String mothersMaidenName() { 78: 79: return myMothersMaidenName; 80: } 81: 82: public Account[] getAccounts() { 83: 84: Account[] list = new Account[myAccounts.size()]; 85: myAccounts.copyInto(list); 86: 87: Enumeration e = myAccounts.elements(); 88: while (e.hasMoreElements()) { 89: ((Account)e.nextElement())._duplicate(); 90: } 91: 92: return list; 93: } 94: 95: public void updateAccountBalance(Account account, float 96: balance) { 97: 98: System.out.println("CustomerImpl: Received account " + 99: "update:"); 100: System.out.println(" New balance is $" + balance); 101: } 102: }
1: // ATMClientMain.java 2: 3: import org.omg.CORBA.BOA; 4: import org.omg.CORBA.ORB; 5: 6: import idlGlobal.Account; 7: import idlGlobal.ATM; 8: import idlGlobal.ATMCard; 9: import idlGlobal.Bank; 10: import idlGlobal.BankServer; 11: import idlGlobal.BankServerHelper; 12: 13: import idlGlobal.AuthorizationException; 14: import idlGlobal.InsufficientFundsException; 15: import idlGlobal.InvalidAccountException; 16: import idlGlobal.InvalidAmountException; 17: 18: public class ATMClientMain { 19: 20: public static ORB myORB; 21: public static BOA myBOA; 22: 23: public static void main(String[] args) { 24: 25: // Check the number of arguments; there should be exactly 26: // five. 27: if (args.length != 5) { 28: System.out.println("Usage: ATMClient <name> <social " + 29: "security number> <address> <mother's maiden" + 30: " name> <PIN>"); 31: return; 32: } 33: 34: // Assign the command line arguments to the Customer 35: // attributes. 36: String name = args[0]; 37: String socialSecurityNumber = args[1]; 38: String address = args[2]; 39: String mothersMaidenName = args[3]; 40: short pin = Short.decode(args[4]).shortValue(); 41: 42: // Initialize the ORB and BOA. 43: myORB = ORB.init(args, null); 44: myBOA = myORB.BOA_init(); 45: 46: // Create a Customer object. 47: System.out.println("ATMClient: Creating new Customer:"); 48: System.out.println(" name: " + name); 49: System.out.println(" Social Security number: " + 50: socialSecurityNumber); 51: System.out.println(" address: " + address); 52: System.out.println(" mother's maiden name: " + 53: mothersMaidenName); 54: CustomerImpl customer = new CustomerImpl(name, 55: socialSecurityNumber, address, mothersMaidenName); 56: 57: // Notify the BOA that the CustomerImpl object is ready. 58: myBOA.obj_is_ready(customer); 59: 60: // Locate a BankServer object and try to get a list of Banks 61: // and ATMs from it. 62: BankServer bankServer; 63: try { 64: bankServer = BankServerHelper.bind(myORB); 65: } catch (Exception ex) { 66: 67: // The bind attempt failed... 68: System.out.println("ATMClient: Unable to bind to a " + 69: "BankServer:"); 70: System.out.println(ex); 71: return; 72: } 73: System.out.println("ATMClient: Successfully bound to a " + 74: "BankServer."); 75: 76: Bank[] banks; 77: ATM[] ATMs; 78: 79: try { 80: banks = bankServer.getBanks(); 81: ATMs = bankServer.getATMs(); 82: } catch (Exception ex) { 83: 84: // The attempt failed... 85: System.out.println("ATMClient: Unable to get lists of " 86: + "Banks and ATMs:"); 87: System.out.println(ex); 88: return; 89: } 90: 91: // Use the first Bank and the first ATM that appear in the 92: // lists. 93: if (banks.length == 0) { 94: 95: // No Banks were available. 96: System.out.println("ATMClient: No Banks available."); 97: return; 98: } 99: if (ATMs.length == 0) { 100: 101: // No ATMs were available. 102: System.out.println("ATMClient: No ATMs available."); 103: return; 104: } 105: Bank bank = banks[0]; 106: ATM atm = ATMs[0]; 107: System.out.println("ATMClient: Using Bank \"" + bank. 108: name() + "\" and ATM \"" + atm.name() + "\"."); 109: 110: // Do some cool stuff. 111: 112: Account account; 113: ATMCard atmCard; 114: 115: try { 116: account = bank.createAccount(customer, "checking", 117: 0.0f); 118: } catch (Exception ex) { 119: 120: // The createAccount() attempt failed... 121: System.out.println("ATMClient: Unable to create " + 122: "Account:"); 123: System.out.println(ex); 124: return; 125: } 126: 127: try { 128: 129: // Request the automatic account update service from the 130: // Bank. 131: bank.requestUpdateService(account); 132: } catch (Exception ex) { 133: 134: // The requestUpdateService() attempt failed... 135: System.out.println("ATMClient: Unable to request " 136: + "Account update service:"); 137: System.out.println(ex); 138: return; 139: } 140: 141: try { 142: 143: // Print out some Account statistics. 144: System.out.println("ATMClient: Opened new Account:"); 145: System.out.println(" account number: " + account. 146: accountNumber()); 147: System.out.println(" creation date: " + account. 148: creationDate()); 149: System.out.println(" account balance: " + account. 150: balance()); 151: 152: // Ask the Bank to issue an ATMCard for the newly created 153: // Account. 154: System.out.println("ATMClient: Getting ATMCard from " + 155: "Bank."); 156: try { 157: atmCard = bank.issueATMCard(pin, account); 158: } catch (InvalidAccountException ex) { 159: 160: // For some reason, the Account was invalid (this 161: // shouldn't happen). 162: System.out.println("ATMClient: Exception caught: " 163: + "Invalid Account"); 164: return; 165: } 166: 167: // Perform some transactions on the Account through the 168: // ATM. 169: System.out.println("ATMClient: Performing " + 170: "transactions."); 171: try { 172: System.out.println(" Depositing $250.00..."); 173: System.out.println(" New balance is $" + atm. 174: deposit(atmCard, account, pin, 250.00f)); 175: 176: // This will throw an exception since we're trying to 177: // withdraw too much. 178: System.out.println(" Withdrawing $500.00..."); 179: System.out.println(" New balance is $" + atm. 180: withdraw(atmCard, account, pin, 500.00f)); 181: } catch (AuthorizationException ex) { 182: System.out.println("ATMClient: Exception caught:" + 183: " Invalid PIN or No authorization (as " + 184: "expected)"); 185: } catch (InvalidAmountException ex) { 186: System.out.println("ATMClient: Exception caught:" + 187: " Invalid amount"); 188: } catch (InsufficientFundsException ex) { 189: System.out.println("ATMClient: Exception caught:" + 190: " Insufficient funds"); 191: } 192: 193: // Perform some more transactions on the Account 194: // through the ATM. 195: System.out.println("ATMClient: Performing more " + 196: "transactions."); 197: try { 198: System.out.println(" Depositing $500.00..."); 199: System.out.println(" New balance is $" + atm. 200: deposit(atmCard, account, pin, 500.00f)); 201: 202: // This will throw an exception since we're using the 203: // wrong PIN. 204: System.out.println(" Withdrawing $250.00 with " + 205: "incorrect PIN..."); 206: System.out.println(" New balance is $" + atm. 207: withdraw(atmCard, account, (short)(pin + 208: 1), 250.00f)); 209: } catch (AuthorizationException ex) { 210: System.out.println("ATMClient: Exception caught:" + 211: " Invalid PIN or No authorization (as " + 212: "expected)"); 213: } catch (InvalidAmountException ex) { 214: System.out.println("ATMClient: Exception caught:" + 215: " Invalid amount"); 216: } catch (InsufficientFundsException ex) { 217: System.out.println("ATMClient: Exception caught:" + 218: " Insufficient funds"); 219: } 220: 221: // Get rid of the Account. 222: try { 223: System.out.println(" Deleting Account."); 224: bank.deleteAccount(account); 225: 226: // Attempt to delete the Account again, just for kicks. 227: // This should result in an exception being thrown. 228: System.out.println(" Attempting to cause an " + 229: "exception by deleting Account again."); 230: bank.deleteAccount(account); 231: } catch (InvalidAccountException ex) { 232: 233: // Sure enough, the exception was thrown. 234: System.out.println("ATMClient: Exception caught:" + 235: " Invalid Account (as expected)"); 236: } 237: } catch (Exception ex) { 238: 239: // Some operation on the Account failed... 240: System.out.println("ATMClient: Error accessing " + 241: "Account:"); 242: System.out.println(ex); 243: return; 244: } 245: 246: // Sleep for long enough to catch an Account update message or 247: // two. 248: try { 249: Thread.currentThread().sleep(120000); 250: } catch (InterruptedException ex) { 251: } 252: 253: // When this point is reached, the application is finished. 254: return; 255: } 256: }
Except for being ported to Java, the application is precisely the same as on Day 9. The output, save for slight differences between formatting in C++ and Java, is nearly identical. The only difference is the way the application components are invoked (the exception being that, if you're using a Java development tool that produces native executables, there is no difference at all in the way the components are invoked).
Start by running the BankServer application:
java BankServerMain
Again, the output of the BankServer is this:
BankServer ready.
You're now ready to start the Bank application:
java BankMain "First Bank"
which, again, outputs this:
Bank "First Bank" ready.
Meanwhile, the BankServer will have output this:
BankServerImpl: Registering Bank "First Bank".
Then you start the ATM application:
java ATMMain "First Bank ATM"
The ATM application displays the following:
ATM "First Bank ATM" ready.
The BankServer, again, outputs the message:
BankServerImpl: Registering ATM "First Bank ATM".
Finally, you're ready to run the ATMClient application. You can do so by typing the following:
java ATMClientMain "Jeremy Rosenberger" 123456789 "123 Main Street" Doe 1234
The ATMClient again displays the following:
ATMClient: Creating new Customer: name: Jeremy Rosenberger Social Security number: 123456789 address: 123 Main Street mother's maiden name: Doe ATMClient: Successfully bound to a BankServer. ATMClient: Using Bank "First Bank" and ATM "First Bank ATM". ATMClient: Opened new Account: account number: Account0 creation date: Oct 20 1997 account balance: 0.0 ATMClient: Getting ATMCard from Bank. ATMClient: Performing transactions. Depositing $250.00... New balance is $250.0 Withdrawing $500.00... ATMClient: Exception caught: Insufficient funds ATMClient: Performing more transactions. Depositing $500.00... New balance is $750.0 Withdrawing $250.00 with incorrect PIN... ATMClient: Exception caught: Invalid PIN or No authorization (as expected) Deleting Account. Attempting to cause an exception by deleting Account again. ATMClient: Exception caught: Invalid Account (as expected)
At this point, the ATMClient sleeps for two minutes while waiting for messages from the Bank. Be patient, and the ATMClient will eventually output this:
CustomerImpl: Received account update: New balance is $750.0
All this goes by very quickly. After it's over, you can go to the other application windows and see some evidence of what transpired here. Looking first at the BankServer application, you'll see this (with new output messages highlighted in bold):
BankServer ready. BankServerImpl: Returning list of 1 Banks. BankServerImpl: Returning list of 1 ATMs.
The output of the other applications is the same as last time, except for the Bank application. Turn your attention to the window in which the Bank is running and you will see the following familiar output:
Bank "First Bank" ready. BankImpl: Creating new CheckingAccount for Customer Jeremy Rosenberger. AccountImpl: Insufficient funds to withdraw specified amount. BankImpl: Deleting Account "Account00000000". BankImpl: Attempted to delete invalid Account.
Stay tuned for a few moments, and you will see the following (if it took you a minute or so to bring the Bank output window up, this might already be on your screen):
BankImpl: Updating Accounts.
Recall that this message is output just before the second thread in the Bank application sends the update messages to all the Account owners.
Today you were first introduced to the Java language/platform; you then ported the Bank application from C++ to Java. This was a pretty straight port: Most of the code could be translated almost line for line from C++. If you're already familiar with Java, this should have been a fairly trivial exercise. If you're new to Java, you've learned a bit about the language and, more importantly, witnessed the ease with which CORBA applications are implemented in Java.
Because the port of the Bank application from C++ to Java didn't add anything novel, you might have found this exercise uninteresting. However, on Day 14, "Web-Enabling the Bank Example with Java," the Java port will be developed further, so read on.
On Day 14, you'll take the Java version of the Bank application to its next logical step. Because one of Java's strengths is its capability to deploy graphical applications through Web browsers to end users, it makes sense to develop a graphical front end to the Bank application, rather than continue with the (rather boring) character-based interface. This is exactly what you'll do on the next and final day of this book. This exercise will give you insight into what can be accomplished using CORBA and Java.
The following section will help you test your comprehension of the material presented in this chapter and put what you've learned into practice (there are no exercises for this chapter; they'll return in the next). You'll find the answers to the quiz in Appendix A.
© Copyright, Macmillan Computer Publishing. All rights reserved.