'; zhtm += ''; zhtm += '

' + pPage + ''; zhtm += '
'; window.popUpWin.document.write(zhtm); window.popUpWin.document.close(); // Johnny Jackson 4/28/98 } //--> Teach Yourself CORBA In 14 Days -- Appendix A --Answers to Quizzes and Exercises

Teach Yourself CORBA In 14 Days

Previous chapterNext chapterContents

Appendix A
Answers to Quizzes and Exercises

Day 1: Getting Familiar with CORBA


1. What does IIOP stand for, and what is its significance?
IIOP (Internet Inter-ORB Protocol) is a protocol that allows ORBs from various CORBA vendors to interoperate with each other, using the TCP/IP protocols. Implementation of IIOP is a requirement for CORBA 2 compliance.

2. What is the relationship between CORBA, OMA, and OMG?
The Object Management Group (OMG) is the organization that controls the OMA and CORBA standards. The Object Management Architecture (OMA) consists of Object Request Broker (ORB) functionality, CORBAservices, CORBAfacilities, domain interfaces, and application objects. Finally, the Common Object Request Broker Architecture (CORBA) is the standard implementation for the ORB functionality of the OMA.

3. What is a client stub?
A client stub is a piece of code, usually generated by an IDL compiler, that allows a client application to interface to CORBA server objects. The interface presented by the client stub is exactly the same as the interface of the server, giving the client the illusion that method calls are performed locally.

4. What is an object reference? An IOR?
An object reference is a pointer to a CORBA object. A client makes all method calls to a CORBA object through a reference to that object. An IOR, or interoperable object reference, is the CORBA/IIOP terminology for an object reference.

Day 2: Understanding the CORBA Architecture


1. What is marshaling?
Marshaling is the process of converting method parameters to a format that can be transmitted across the network. (Unmarshaling is simply the reverse of marshaling.)

2. What are the responsibilities of an ORB?
An ORB locates an object implementation given an object reference, prepares a server to receive requests, and marshals and unmarshals parameters in a method call.

3. Where do server skeletons and client stubs come from?
Server skeletons and client stubs are generated by the IDL compiler.

4. Which server activation policy describes a server that is started and stopped manually?
This is the persistent server policy.

5. How does the use of IDL enhance language independence of CORBA objects?
IDL provides a language-independent mechanism for describing the interfaces of CORBA objects. The language-independent constructs of IDL can then be mapped to language-specific constructs using an IDL compiler for a particular language.

Day 3: Mastering the Interface Definition Language (IDL)


1. Define a type (using typedef), called temperatureSequence, that is a sequence of sequences of floats (yes, this is legal).
The IDL definition would look like this:

typedef sequence<sequence<float>> temperatureSequence;

2. Why might a type like the one described in the preceding question be useful?
Imagine a temperature-measuring system that periodically samples the current temperature. The temperature readings gathered through a single day could potentially be stored in a sequence<float>. Now imagine that at the end of each day, the set of temperature data produced for that day is stored in another sequence. This sequence type would be a sequence<sequence<float>>, which is precisely the answer to question 1.

3. Why are exceptions useful?
Exceptions are useful for at least two reasons. First, consider a method that returns a boolean. If exceptions were not available, the method would have to reserve certain return values to signal error conditions. Of course, doing this eliminates one possible valid return value. In the case of a boolean return value, there are only two possible return values, so giving up one of them to signal an error condition is not practical. Second, when used in languages that directly support exceptions, they can greatly simplify error handling. Rather than check the return value of each method call for an error condition result, a developer can create a more generic error handling mechanism, resulting in cleaner code. Additionally, because unhandled exceptions are passed up the call stack, a hierarchy of exception handlers can be created.

4. Why is the module construct useful?
The module construct is useful because it facilitates the partitioning of a system. A well-partitioned system is easier to define and implement than a monolithic system because the divide-and-conquer approach creates independent or semi-independent components of manageable size. A monolithic design, by comparison, does little to create manageable components of the system.

5. Name some practical uses for the octet data type.
The octet data type is useful for any type of data that should not undergo any sort of translation. For instance, if a bitmapped image is sent across the network, it should arrive in exactly the same format in which it was sent. The image can be sent as an array of octets, which guarantees that there is no translation from the image's source to its destination. Other types of data that can be transmitted in this way include executables, Java class files, and most multimedia files. (By contrast, all other IDL data types can undergo a format conversion when being transmitted between different hardware platforms, operating systems, and/or languages.)

6. Define an enumerated type containing the months of the year.
The type definition would look like this (though you can give it whatever name you like):

enum MonthsOfYear {


7. Why might a nonblocking remote method call be advantageous, compared to a blocking method call?
Consider the case in which a CORBA client is an interactive application. While the client makes calls to CORBA servers, the user might want to interact with the application. If remote method calls, which might be lengthy, were to block, the responsiveness of the client application would be greatly diminished. Nonblocking method calls solve this problem by allowing the client to continue processing during a method invocation so that the client can handle user input in a timely manner. (Again, the use of multithreading in the client is almost always the best solution because oneway method invocations are unreliable.) Day 10, "Learning About CORBA Design Issues," explores this and other issues in greater depth.

8. Imagine a compound data type with a large number of data members. This data type will frequently be used by a client application that needs to access each of the data members. Would it be more efficient to encapsulate this data type into a struct or an interface? Why?

Given that there are a large number of data members in this data type and that the client application needs to access each of these data members, it would be more efficient to send the data to the client as a struct. The reason for this is that accessing each of the data members of an interface (recall that the implementation object is passed by reference) requires a separate method call for each member, resulting in a great deal of overhead. By contrast, a struct is sent by value; therefore, after the struct is returned to the client, the client can then access the member data with its own local copy.

9. Because an IDL method can return a value, what is the purpose of out and inout parameter types?

If a method needs to simultaneously return more than one value of more than one type, out and inout parameters can be used, in addition to (or in lieu of) the return value. This is a similar mechanism to passing parameters by reference in C++. In this way, a single method call can return more than one value.
Why is a oneway method unable to return any value to the caller? Can you think of a mechanism, using oneway calls, to return a result to the caller?
Because a oneway method does not block, the caller does not wait for a result. Consequently, there is no way for a oneway method call to return a result to the client. (Incidentally, exceptions cannot be raised either, for the same reason.) However, a system of oneway calls can be set up as follows: The client makes a oneway call to the server and continues its processing. The server, after it has executed the method, makes a oneway call back to the client (extra credit if you identified this as a callback) with the result information for the previous call.


1. Consider the following classes: Conduit, Faucet, FuseBox, Outlet, Pipe, WaterHeater, WaterPump, and Wire. How would you partition these classes? What relationships, if any, are there between the partitions you have created?
Often, when partitioning a system, there is no clear answer as to which classes belong in what partitions. Many are obvious, but there are those that potentially fall into one of several partitions. In such cases, there is no right or wrong answer. Also, how a particular system uses these classes might affect the partitioning; a partitioning that makes sense for one system might not make sense for another.

Of the classes in this exercise, Conduit, FuseBox, Outlet, and Wire clearly belong in one partition (call it Electrical). Faucet and Pipe clearly belong in another partition (call it Plumbing). WaterHeater and WaterPump, however, could go either way. They are part of the plumbing system, to be sure, but they are also electrical devices, so they could also be considered part of the electrical system. Here there is no right or wrong answer; depending on system requirements, either partition is reasonable.

Assuming you were to place WaterHeater and WaterPump in the Plumbing partition, there would probably be a relationship between WaterHeater and Outlet (or between WaterHeater and FuseBox, if they were connected directly). A similar relationship would exist for WaterPump. Because there are few relationships between the two partitions and close relationships within each partition, you can consider this partitioning scheme a reasonable one.

2. Create an interface that describes a clock radio (which can set hours, minutes, alarm time, and so on).

There are many potential answers to this exercise, but your interface might resemble the following:

// Interface to a clock/radio device. This interface chooses to
// emulate the interface of many clock/radio controls, e.g. a
// "button" to set the hour, another "button" to set the minute,
// and so on.
interface ClockRadio {
// Get the current time. The result is returned in the output
// parameters hour, minute, and second.
void getTime(out short hour, out short minute, out short second);
// Advance the current hour by one; reset to zero if the hour
// exceeds the maximum (23). This method can be used to set the
// time. Returns the new value of the hour.
short IncrementHour();
// Advance the current minute by one; reset to zero if the minute
// exceeds the maximum (59). This method can be used to set the
// time. Returns the new value of the minute.
short IncrementMinute();
// Advance the alarm hour by one; reset to zero if the hour
// exceeds the maximum (23). This method can be used to set the
// alarm time. Returns the new value of the hour.
short IncrementAlarmHour();
// Advance the current alarm minute by one; reset to zero if the
// minute exceeds the maximum (59). This method can be used to set
// the alarm time. Returns the new value of the minute.
short IncrementAlarmMinute();
// Activate the alarm.
void activateAlarm();
// Deactivate the alarm.
void deActivateAlarm();

Day 4: Building a CORBA Application


1. What is the purpose of server skeletons and client stubs?
Server skeletons provide the framework (skeleton) set of classes, which provide skeleton methods for which the developer provides implementations (either through inheritance or through delegation). Client stubs provide the clients with an interface to the server methods.

2. Why does the server need to register the implementation object with the CORBA Naming Service?

The server needs to register with the CORBA Naming Service because, otherwise, there is no way for clients to locate the server. There are other methods of achieving visibility, such as writing a stringified object reference to a well-known and accessible location such as a disk file (assuming a networked file system), a Web server, or an FTP server. The Naming Service provides a standard, convenient method for publishing object references.

3. Why do the client and server need to catch exceptions, especially when none are raised by the IDL operations you defined?

Recall that every remote method can potentially raise an exception and that these exceptions need to be caught and handled. Even if the IDL definition for a method does not specify any exceptions, that method can still raise a CORBA system exception (which would be raised if there were a network error, for example).


1. It was pointed out in the StockMarket example that it would be a good idea to raise an exception in the getStockValue() method if an invalid StockSymbol was passed in. Modify StockMarket.idl so that the method can raise an InvalidStockSymbolException. (You'll also need to add a definition for this exception.)
The resulting StockMarket.idl might look like this (you might have included additional data members in the definition for InvalidStockSymbolException):

// StockMarket.idl

// The StockMarket module consists of definitions useful
// for building stock market-related applications.
module StockMarket {
// The InvalidStockSymbolException is raised when a
// method is passed an invalid stock symbol.
exception InvalidStockSymbolException { };
// The StockSymbol type is used for symbols (names)
// representing stocks.
typedef string StockSymbol;
// The StockServer interface is the interface for a
// server that provides stock market information.
// (See the comments on the individual methods for
// more information.)
interface StockServer {
// getStockValue() returns the current value for
// the given StockSymbol. If the given StockSymbol
// is unknown, the results are undefined (this
// would be a good place to raise an exception).
float getStockValue(in StockSymbol symbol)
raises (InvalidStockSymbolException);
// getStockSymbols() returns a sequence of all
// StockSymbols known by this StockServer.
sequence<StockSymbol> getStockSymbols();


2. In the StockMarket example, an implementation was provided that used the delegation approach. Implement the StockServer to use the inheritance approach. (Extra credit: Include the exception-raising mechanism from the first exercise.)

3. The changes required are to modify the server so that it actually raises the InvalidStockSymbolException when an invalid stock symbol is encountered; similarly, the client must be modified to catch and handle this exception. For the server, here is the new implementation for the getStockValue() method (all other code remains unchanged):

// Return the current value for the given StockSymbol.
public float getStockValue(String symbol) throws
InvalidStockSymbolException {
// Try to find the given symbol.
int stockIndex = myStockSymbols.indexOf(symbol);
if (stockIndex != -1) {
// Symbol found; return its value.
return ((Float)myStockValues.elementAt(stockIndex)).
} else {
// Symbol was not found.
throw new InvalidStockSymbolException();


The changes to the client are limited to the doSomething() method, which now catches the exception and prints a warning message:

// Do some cool things with the StockServer.
protected void doSomething() {
try {
// Get the valid stock symbols from the StockServer.
String[] stockSymbols = myStockServer.getStockSymbols();
// Display the stock symbols and their values.
for (int i = 0; i < stockSymbols.length; i++) {
try {
System.out.println(stockSymbols[i] + " " +
} catch (InvalidStockSymbolException ex) {
System.out.println("Invalid stock symbol.");
} catch (org.omg.CORBA.SystemException ex) {
System.err.println("Fatal error: " + ex);

Day 5: Designing the System: A Crash Course in Object-Oriented Analysis and Design


1. Identify the potential objects in the system described here: An ordering system allows customers to order products from a particular company. Each order consists of one or more line items, each of which identifies a quantity and a particular product. Each product, in turn, has an associated price.

Possible objects in this system can be identified by picking out the nouns: order, line item, quantity, product, and price. (Further analysis might reveal that some of these--particularly quantity and price--do not work well as objects, but that depends on the application.)

2. What is UML, and what is it good for?
The UML, or Unified Modeling Language, is used to facilitate the object-oriented analysis and design process. UML can be used to model a number of aspects of the system, from static design (such as class diagrams) to dynamic (such as use cases and scenarios).

. For an order-processing system design, one requirement given is "must be fast." Is this a reasonable expression of this requirement, or could it be improved? If so, how?

Because fast by itself is not an easily quantifiable term and thus cannot be tested readily, "must be fast" is probably not a reasonably expressed requirement. A more quantifiable requirement would be to require an average system response time of, for example, one second or less.


Modify the system design so that a Bank consists of Branches, each of which owns some of the Customer Accounts. Draw the class diagram for the modified design.

Your modified class diagram should resemble the one shown in Figure A.1. Note that the Account creation responsibility has been moved from the Bank to the Branch, although the Bank and its constituent Branches share the capability to enumerate and close Accounts.

Figure A.1 The modified Bank application class diagram.

Day 6: Implementing Basic Application Capabilities


1. It was noted earlier that _is_equivalent() is not guaranteed to return TRUE when two object references refer to the same object. Can you think of a mechanism that would more reliably determine whether two references refer to the same object? (For simplicity, assume the objects are of the same type.)
A mechanism could assign global unique identifiers (GUIDs) or universal unique identifiers (UUIDs) to objects and make the identifiers available to client applications (through an accessor method such as getGUID()). Such a mechanism could more reliably determine object identity by comparing identifiers; two objects that return the same GUID must be the same object.

. What would happen if _release() were not called on an object that had earlier been _duplicate()d?
If _release() were not called on an object, the object would live forever; its reference count (in a reference counting implementation) would never reach zero.

. Why does NewCustomerMain.cpp have a try ... catch (const CORBA::Exception& ex) block?
The CORBA::Exception is a generic exception that can be thrown by any remote method. Because a number of remote methods are called in NewCustomerMain.cpp, they are done so inside this try ... catch block.


Modify the client application so that it prints the names of the Customers who are associated with the Account that was created. (The single Customer printed should be the same Customer whose information was entered on the command line.)

After line 85 of NewCustomerMain.cpp (refer to Listing 6.21), add the following code. This code gets the Account owners using the getCustomers() method, then iterates through the sequence of Customers returned by that method, printing the name of each Customer (as returned by name()).

// Print out list of Customers owning the Account.
cout << "  Printing list of Account owners:" << endl;
CustomerList* customers = account->getCustomers();
for (CORBA::ULong i = 0; i < customers->length(); i++) {
    cout << "    Owner " << i + 1 << ": " << ((*customers)[i])->

Day 7: Using Exceptions to Perform Error Checking


1. What does it mean to raise (or throw) an exception?
To raise an exception means that a new exception is created and passed to the caller of the method that raised the exception. The caller can then handle the exception or pass it to its caller.

. What does it mean to catch an exception?
To catch an exception means to handle an exception that was raised by a method that was called. Catching an exception might involve displaying an error message to the user, trying to resolve the condition that caused the exception to be raised, or doing nothing at all.

. Why are exceptions useful?
Exceptions are useful because they provide a structured form of error handling. For example, without exceptions, a given method would have to reserve certain return results to signify an error condition; a caller of such a method would have to check for each of these special return codes. Exceptions greatly simplify the complexities that can otherwise arise from error handling mechanisms.


1. Modify the following interface definition so that appropriate exceptions are raised in appropriate places.

exception InvalidNumberException { };
exception NoIncomingCallException { };
exception NotOffHookException { };
interface Telephone {
void offHook();
void onHook();
void dialNumber(in string phoneNumber);
void answerCall();

One potential solution is as follows:
exception InvalidNumberException { };
exception NoIncomingCallException { };
exception NotOffHookException { };
interface Telephone {
void offHook();
void onHook();
void dialNumber(in string phoneNumber)
raises (InvalidNumberException, NotOffHookException);
void answerCall()
raises (NoIncomingCallException);

This solution assumes that it is not valid to dial a number unless the Telephone is already off the hook. It might be equally reasonable for dialNumber() to call offHook() if the Telephone is not already off the hook.

. Implement the interface from exercise 1, raising the appropriate exceptions under the appropriate conditions. (Most of the methods probably won't do anything, except for dialNumber(), which will likely check the validity of the given phone number).
The complete solution is not given here, but the implementation for the exercise 1 solution would look like this (the following is in pseudocode):
void dialNumber(string phoneNumber) {
if (telephone is on hook) {
raise NotOffHookException;
if (phone number is invalid) {
raise InvalidNumberException;
proceed to dial the number...
void answerCall() {
if (no incoming call is being placed) {
raise NoIncomingCallException;
proceed to answer the call...

Day 8: Adding Automated Teller Machine (ATM) Capability


What are the four steps you'll usually follow to make enhancements to a CORBA application?

The steps are to define additional requirements, modify the system design, modify the IDL definitions to reflect the new design, and, finally, implement the new functionality.


Add an operation to the ATM interface that allows funds to be transferred between Accounts. Be sure to provide appropriate exceptions as well.

The IDL should look something like this:

float transfer(in ATMCard card, in Account fromAccount, in Account
        toAccount, in short pin in float amount)
        raises (AuthorizationException, InvalidAmountException,

Note that the implementation of this method will likely need to use the ATMCard to check the authorization for both the fromAccount and toAccount parameters. (Actually, it might be reasonable to allow transfers to any Account, but real ATM cards allow transfers only between accounts on which those cards are authorized.)

Day 9: Using Callbacks to Add Push Capability


1. Why does the issue of thread safety become important in the sample application developed in this chapter?
Thread safety is important because multiple threads might attempt to access the same data simultaneously. To prevent the possible corruption of data, access to such data must be made thread-safe.

. Instead of using oneway methods to notify clients of updates, can you think of another way to efficiently send update messages to clients? (Hint: Multithreading could come in handy here.)
Launching a new thread to send each client update messages significantly enhances the efficiency of non-oneway message delivery. This is because, rather than require a single thread to wait for each client response before delivering the next message, each thread can block while waiting for a response from its respective client.


1. It was noted earlier in the chapter that no facility currently exists to cancel the automatic account update service. Provide an IDL method signature for such an operation. Don't forget to include appropriate exceptions, if any.
The method signature, which would be added to the Bank interface, should resemble the following:

void cancelUpdateService(in Account account)

raises (InvalidAccountException);

The InvalidAccountException would be raised if the Account does not belong to the Bank on which the operation was called. Optionally, you would either raise this exception or ignore the operation if it were called on an Account that was already subscribed to this service.

. Implement the account update cancellation method from exercise 1.
The complete implementation is not given here, but the following pseudocode provides the general algorithm:
void cancelUpdateService(Account account) {
if (account does not belong to this Bank) {
throw InvalidAccountException;
if (account is already subscribed to the auto update service) {
throw InvalidAccountException;
remove the account from list of Accounts subscribed to the auto
update service

Day 10: Learning About CORBA Design Issues


1. What is the major issue associated with mixing client and server functionality in a single-threaded CORBA application?
Depending on what the application is doing, mixing client and server functionality in a single-threaded application introduces the potential for deadlock to occur.

. How can the use of reference counting in a CORBA application lead to problems?
When an application component crashes, none of the reference counts for the objects it referenced will be decremented. Consequently, those objects might not be destroyed when they should be.

. Which version of X11 (the X Window System) would be required to safely run multithreaded X-based applications?
X11R6.1 or later is necessary to safely run multithreaded applications. Earlier versions don't have thread-safe libraries.

. Why is the capability to pass objects by value sometimes useful?
If an application component intends to perform a number of operations on an object, it is often more efficient to use a local copy of that object rather than make numerous method invocations on a remote object.

. Why is it usually inadvisable to use the Exclusive oneway Call design pattern introduced earlier in this chapter?
Because oneway methods are unreliable, the Exclusive oneway Call design pattern is difficult to implement for situations in which reliable message delivery is required.

Day 11: Using the Dynamic Invocation Interface (DII)


1. Would you expect DII to be useful to most CORBA application developers? Why or why not?
As emphasized numerous times in this chapter, DII will probably not be useful to most CORBA application developers. This is primarily because, for most applications, the interfaces are almost always known at compile time anyway. Also, DII adds a great deal of complexity that developers are well advised to avoid altogether.

2. What are the advantages of DII over static method invocation?
DII has two advantages over static method invocation: the flexibility for a client to invoke operations on interfaces that were unknown at the time the client was compiled, and the ability to use one of several options for obtaining the return result from a remote method invocation.

3. What are the disadvantages of DII compared to static method invocation?
The disadvantages to using DII are its complexity, its lack of static-type-checking ability, the additional overhead incurred by its call mechanism, and the overhead associated with interface discovery.

Day 12: Exploring CORBAservices and CORBAfacilities


1. Who defines the specifications for CORBAservices and CORBAfacilities?
The specifications for CORBAservices and CORBAfacilities are defined by the Object Management Group (OMG).

. Who provides the implementations for CORBAservices and CORBAfacilities?
The implementations for CORBAservices and CORBAfacilities are provided by the vendors themselves. The OMG does not provide their implementations, only their specifications.

. What CORBAservices and/or CORBAfacilities, if any, must a vendor provide with an ORB product in order to be considered CORBA 2 compliant?
No CORBAservices or CORBAfacilities implementations are required from a vendor for CORBA 2 compliance. Compliance is determined by a product's ORB capabilities alone.

. Why are vertical market facilities useful?
Vertical market facilities are useful because they can enhance interoperability between applications within a particular industry. In addition, they can facilitate the sharing of data between companies within an industry.


1. Provide an overview of how the Object Trader Service could be used to replace the BankServer in the sample Bank application.
In the sample Bank application, the BankServer component exists solely to allow other application components to locate Banks and ATMs. As it turns out, locating objects by type is precisely the capability provided by the Trader Service. Rather than locate and register with a BankServer component, Banks and ATMs could instead register with the Trader Service. These components would subsequently be available to other application components--namely, Customers--that could locate the components through the same Trader Service. Thus, the functionality of the BankServer component is effectively replaced.

. Describe how the Event Service could be used within the Bank application. (Hint: Consider the automatic account update feature added on Day 9.) What would be the benefit of using this approach?
Currently, the automatic account update feature requires the Bank to iterate through its Accounts that are subscribed to the update service. The Bank invokes a callback method on each of the Customers who are associated with those Accounts. Using the Event Service, the Bank could become a publisher of Account balance update events to which Customers could subscribe. The benefit of this approach is that it eliminates the complexity of delivering update messages to Customers in the Bank application; the details of message delivery are shifted to the Event Service.

. (Extra Credit) If you have any products available to you that implement one or more CORBAservices, try to integrate the functionality provided by a service of your choice with the sample Bank application. (See the section of this chapter labeled "Choosing CORBAservices" to determine which services might integrate well with the sample application.) Because of the numerous possibilities available to you, no answer is provided for this exercise.

Day 13: Developing for the Internet Using CORBA and Java


1. What IDL construct resembles Java's package?
The Java package is very similar to the IDL module.

2. What is an advantage of Java Remote Method Invocation (RMI) over CORBA? Of CORBA over RMI?

One advantage of RMI over CORBA is that RMI allows objects to be passed by value. Some advantages of CORBA over RMI are language independence and robustness.

3. Why might a developer want to use Java to develop a CORBA application?
Java's portability makes it especially attractive for developing CORBA client applications that might be required to run on a variety of platforms.

Day 14: Web-Enabling the Bank Example with Java


1. Why might it be advantageous to deploy a CORBA client as a Java applet?
Two potential advantages for using Java applets for CORBA clients are the Java language's portability and the applet's simplified deployment mechanism through Web browsers.

. Why is it useful for browsers to include built-in CORBA-related classes (for example, Netscape's Communicator includes Visigenic's VisiBroker for Java runtime)?
Because the classes required for CORBA connectivity are included with the browser, they do not have to be downloaded with the applet each time the applet is downloaded, thus reducing the download time required.

. What is a potential disadvantage to the bundling scheme described in question 2?
A possible disadvantage to this scheme is that the CORBA classes provided with the browser might become outdated, thus requiring new versions of the classes to be downloaded with the applet anyway.


Extend the BankApplet to do even more cool stuff. For example, you might add tabbed panels to display the Accounts belonging to a particular Customer. Or, you might extend the applet to allow multiple Customers to be associated with an Account and extend the Account information correspondingly to show all the Customers associated with a given Account. (Because this is an open-ended exercise, no answer is given.)

Previous chapterNext chapterContents

Macmillan Computer Publishing USA

© Copyright, Macmillan Computer Publishing. All rights reserved.