Applying this to the Account interface

The third isnt a RemoteException at all™the remote method call worked perfectly, but the database was inaccessible. This should result in a message to the user, suggesting that she try again later. Alternatively, the client could intercept this message and automatically try again. The last two cases are both cases of bad data. But they are nonetheless very different. In the first case, the user submitted a request that could conceivably have been valid. For example, the user tried to withdraw 280 when she only had 75 in her account. There is simply no way for the client application to know that this is an invalid request without sending a message to the server. Throwing an instance of OverdraftException seems like a perfectly reasonable way to tell the client what went wrong. The fifth case, on the other hand, might seem a bit contrived. After all, the client application should have been able to detect that the user was attempting to withdraw a negative amount of money without even talking to the server. And, therefore, the request to the server should never even have been sent. The key word here in both of the previous sentences is should. The simple fact is that client programs, especially ones that are undergoing revision as part of the application lifecycle, sometimes inadvertently omit data validation steps that they should have performed. Your goal in building a server is to prevent a minor coding error on the client side from cascading into a major data corruption problem on the server side. The following two rules of thumb will help you do so: • Always explicitly validate data on the server side to make sure a badly written or hostile client application doesnt corrupt important information. • Any time you validate data on the server side, you should make the validation step a checked exception. [ 8] This helps the programmers writing the clients know what sorts of validation are necessary and gives them a good idea of the types of validation they should perform on the client side. [ 8] A checked exception is an exception that must be caught. Of course, in addition, the client should validate information whenever possible. Consider the following two situations: • The user types a negative amount into a textfield and clicks on Deposit. The client application calls the server, the server throws an exception, and the client displays an error dialog to the user. Total elapsed time: 11 seconds. • The user types a negative amount into a textfield and clicks on Deposit. The client attempts to validate the data and immediately displays an error dialog. Total elapsed time: 0.2 seconds. These two applications are functionally equivalent. But the first one relies on the server to do things that ought to be done by the client. As a result, it can feel slow and unresponsive. In general, how much validation should be done by the client is an open question. But there are certain minimal standards. For example, if it is possible for the client application to tell that a piece of data is wrong without using any other information, then the client application ought to do so. Simple client-side checks such as, Is the value a positive number? can noticeably improve an applications responsiveness.

7.1.7.1 Applying this to the Account interface

Given that the arguments will be instances of Money , what could possibly go wrong? There are two main cases: • The user tries to withdraw more money than she has in the account. • A negative amount of money is either deposited or withdrawn. Weve handled both of these cases explicitly. The first leads to an OverdraftException , and the second causes a NegativeAmountException . Since exceptions are simply subclasses of Exception , they can have extra instance variables. Usually, this additional state is just used to give a complete description of the failure. But, in the case of OverdraftException , we also use it to say whether the withdrawal succeeded: public class OverdraftException extends Exception { public boolean _withdrawalSucceeded; public OverdraftExceptionboolean withdrawalSuc ceeded { _withdrawalSucceeded = withdrawalSucceeded; } public boolean didWithdrawalSucceed { return _withdrawalSucceeded; } There are two slightly more marginal cases we didnt handle: • No instance of money is passed in a null argument is passed to the server. • An instance of money worth precisely 0 dollars and 0 cents is either deposited or withdrawn. Whether these really merit their own exception types is a matter of ongoing debate in the programming community. In general, there comes a point at which programmers say that the benefits from the extra precision and information provided by lots of exception types is outweighed by the sheer annoyance of trying to read an interface in which each method can throw four or five different exceptions. My opinion is that these really do merit their own exception types. [ 9] In fact, the best course of action is probably to use five exceptions. The four weve explicitly outlined, as well as an abstract superclass of all of them called BadMoneyArgumentException , which has a descriptive string associated with it. [ 9] The decision not to include lots of different exception types in the books example code is mainly motivated by pedagogical considerations™it prompts this discussion and also includes an example of an exception that contains a success flag. Plus, of course, too many exceptions does make example code harder to read. The interface should then be defined using all the exceptions, with as much precision as is reasonable. The server should throw the most precise exception it can and log the requests and exceptions in an error file. Clients, on the other hand, will probably just catch BadMoneyArgumentException ; most clients dont really use the finer-grained distinctions that the server makes. This compromise preserves most of the benefits of the fine-grained exception hierarchy and allows room for clients to change and take advantage of the extra information the server provides without forcing them to catch four unrelated exceptions.

7.2 Building the Data Objects