|Dealing with Exception Handling in a distributed environment poses many
challenges, in traditional Client/Server we could extend the system classes and in the
error events deal with the error by showing the user the details of the error and then if
severe enough shut the application down.
This is not an option with distributed
computing. Shutting the server down just because an update could not be performed is not a
good idea and the user never sees the server so they will not benefit from errors
displayed on the server and no matter how hard you train him the monkey never reliably
presses OK on all the messages boxes.....
In our class library all errors are logged to our logging service, which is a
distributed object, if a connection cannot be made to the logging server then it is logged
to the C: Drive. So when our servers encounter a catastrophic error they log it and the
internal state of the server to the logging service then it cleans up all the objects that
have been created by the client application and exists gracefully. The client application
is also shutdown.
But in a fully tested production application catastrophic errors should be few and far
between, the error handling described above is great during development as you have a
complete history of all the errors, in production the log should be monitored by the
maintenance staff to check for strange errors and corrections made as required.
Distributed objects should be able to handle exceptions gracefully without adversely
effecting the client or the server. There are two primary methods for communicating
exceptions between the client and the server, one is to pass some kind of exception object
by reference between the client and the server with every method call. If the server
encounters an error it loads the error object with the details of the error and returns to
the client. The other method is to create an instance of the exception object inside each
server object and allow public read access to the error object. All functions would return
an integer return code which when negative the client would ask the server for the
exception object and deal with is as normal.
The choice of which method is a tough call, and I think it comes down to the discipline
of your developers, the pass with each function call is simpler, and is in the developers
face but incurs more network traffic. The other method is more subtle and easier for
developers not to deal with, but is cleaner and creates less network traffic. Personally I
would use a mix of the two which is probably bad design as it breaks the consistency rule,
but the functions that regularly generate exceptions like Save and Validate I would pass
an exception object, a Retrieve method I would go the other way.
So what type of attributes would an exception object have, we use the same exception
object to deal with validation errors and exceptions. So we include attributes like
ColumnName and Identity as well as the usual, ErrorNumber and ErrorMessage. One of the
more difficult things to deal with is the focus issue. On the server you want to generate
a validation failure message, you know which row and column the error occurred on, but
just because its on row 5 on the server does not mean its on row 5 on the client. The
client may have sorted or filtered the data! So we can deal with the problem by borrowing
some technology from our routine to accept the data changes from the client to the server.
In those functions we have the ability to generate a find string for a given row, the find
string will uniquely identify the row. We can generate this find string and return it to
the client, the client can then use the find string to locate the row in error for the
user. The Identity Attribute is the same identity we use to identify the datawindows for
data transfer, only now we can use the same identity to set focus to the correct
datawindow on the client.
The following is an OMT model of the exception object, in our designs we do not
document the public get/set methods. Therefore there are no methods documented even though
there is a get/set method for each attribute. The method names are exactly the same as the
attribute, one returns a value the other accepts a value: