Martin Wildam wrote: [...] > Maybe we should look at error handling from different point of views. > First, there is the very normal dummy user who don't have a clue on > what is there running in the background. Second there is a system > administrator who - if something goes wrong - could see whether there > might be a problem with the environment or with the configuration he > is responsible for. And then there is the developer who wants/needs > comprehensive detailed information if there is a doubt that an > upcoming error might be due to a bug. > > The dummy user is usually overwhelmed if you present comprehensive > information (or a stack trace for example). He/She wants a simple > message - e.g. like Google Calender's "Ooops, some calenders could not > be loaded. Try again later." - I think basically every IOException > could result in such a final handling. > > The administrator wants some more information, like if a host could > not be reached or name could not be resolved etc. > > The developer is the only person interested in stack traces for > instance. > > I think a solution for the different interests could be a proper > inheritance model of exceptions that you are using in your > applications. This means, if an exception occurs I can handle it using > the first parent exception to generate the "operation failed" thing > and for the developer log the exact detail exception that occurred. So > I could have some common PreCheckException (and children like > InputPreCheckException, ConfigurationPreCheckException and so on). > I think sometimes the same exception can be required to be looked at in different ways. While the user should always get some high-level "we are sorry" message, the admin/developer distinction is not necessarily nicely along a hierarchy you describe. In fact some of the lowest exceptions could be of a nature that administrators have to resolve.
Things get even more complicated if the boundaries are blurry. I've seen that many times with smaller projects and I believe it is still common at Amazon: the developers are also administrators. There can be quite different logging setups depending on your organizational structure, auditing needs and other aspects. That's where external logging configurations come into play. And don't forget that there are at least two dimensions: the hierarchy and the chaining. I tend to chain exceptions through the abstraction layers while using the hierarchies to model different level of detail within a layer. In some way I like the idea of a higher level error reporting system where the user interface is not just plain text but allows drilling down into the information, ideally with structure data in the exceptions. I've seen such systems used in various places (although not with much structured data). In some way the Windows error log is one, but I have also seen logging handlers that log into a full-blown database or a ticket system. The problem is that you really want your logging to be easy and reliable, which those system often are not. But even in text format you can gain a lot by making sure your exception message is written well and with enough information to identify the real issue without wading through stack traces (as a developer) or guru meditation (everyone). > The second problem is the coding efficiency and readability. Within a > method depending on the exception some object variables might not be > set to a valid instance (if you have a longer try-block above and > handling just one parent exception). This may result in a lot of null- > checking necessary. A solution for this could be that the methods you > are calling should always return in a sane way (mostly just avoiding > null-pointer-exceptions) if they receive nulls as arguments. > > Sample: > > try > { > checkInputs(myTask); > checkEnvironment(myTask); > checkConfig(myTask); > con = openConnection(myTask); > } > catch (PreCheckException e) > { > doLoggingAndMessaging(e); > } > > > try > { > doCoreWork(con, myTask); > } > catch (ProcessingException e) > { > doLoggingAndMessaging(e); > } > > closeConnection(con); > > In that sample doCoreWork should fail if con is null or myTask is > missing some important data/status that it should have got during the > prechecks. > The closeConnection method on the other hand should not throw another > exception but silently finish if no (open) connection is given - just > as "well there is nothing to do for me". > > Such a thing looks quite clean to me. > Just my - not verified - ideas This approach can help sometimes, in particular if you can model your system in such a way that the prechecked-objects have only safe methods (modulo the usual candidates you can hardly avoid such as running out of memory). But I don't think this is always feasible, e.g. if you do processing on a database you either have to have a lot of memory to buffer data or you have to live with the fact that the network can cause trouble at any time during your processing. But doing as many checks beforehand is very useful approach IMO. Just adding a whole bunch of assertions at the beginning of your methods can make debugging a lot easier -- if you are protecting yourself against programmer error they can be sufficient. If you need protection against errors in input or other runtime issues, doing sanity checks that throw exceptions is a good thing. By doing them first off you make reasoning about the rest of the code a lot easier and by doing them at all you make understanding errors a lot easier (assuming they are mapped to sensible exceptions and error messages). I truly believe there should be a decent sized book just on this topic :-) Peter --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "The Java Posse" group. To post to this group, send email to javaposse@googlegroups.com To unsubscribe from this group, send email to javaposse+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/javaposse?hl=en -~----------~----~----~----~------~----~------~--~---