I've been lately discussing about exception handling in Java with my
workmates. And I've noticed that there's some uncertainty about
Exceptions and how to use them. Currently we're working on a quite
traditional three-tier Spring+Hibernate web app (using way too much of
that disgusting null programming, but that's another story).
Personally I usually regard ... catch (Exception e) ... as a code smell
because it will catch - usually unintentionally - all RuntimeExceptions
too. Not to mention catching Throwable, like there was a lot we could do
with Errors. My current style is catching all checked exceptions on
their own blocks and catching RuntimeException on it's own block where
it makes sense (at least in controllers). But that sometimes makes the
code ugly with a dozen catch blocks doing exactly the same thing. AFAIK
Project Coin is going to fix this annoyance. Would it be better in a
situation where I anyways catch RuntimeException to use Exception as it
is the lowest common denominator?
How do you make the most out of your Exceptions? And how do you do it in
multi-tier architecture?
Best Regards,
Hannu
//do not try this at home...
try{
Connection c = getConn();
query_data();
do_resource_cleanup();
return result;
}catch (SqlException e){
logger.info("hmm i guess this just failed");
return "";
}
code like this was cluttered all over the source. what happened was that
people using the system thought their updates were successful, when in
fact they were not.
also, we leaked tons of connections and preparedstatements.
we changed everything to a catch-rethrow as RuntimeException pattern. a
central Servlet filter catches all exceptions,loggs errors to file and
mail, and redirects to a "submit error description" page.
so the code mostly looks like this now:
Connection c = null;
try{
c = getConn();
return query_data();
}catch (SqlException e){
throw new MyRuntimeException("unexpected error calling XXX using
parameters : PPPP " ,e);
}finally{
do_resource_cleanup(); //check for nulls
}
this is how it looks like the old jdbc-based data access classes, which
are the majority. the hibernate-based data access classes swallowed
runtimeexceptions, their erronous error handling code was entirely
removed, cutting their code footprint to 1/3, most of the methods are
one-liners now.
this refactoring affected about 500 classes. most of the work was done
using the excellent Structural Search and Replace tool inside IntelliJ
Idea. prototyping the changes took about 2 days the actual replace was
done in a couple of minutes after we knew what to change.
--
Fabrizio Giudici - Java Architect, Project Manager
Tidalwave s.a.s. - "We make Java work. Everywhere."
weblogs.java.net/blog/fabriziogiudici - www.tidalwave.it/blog
Fabrizio...@tidalwave.it - mobile: +39 348.150.6941
Only top-level thread handlers should catch these errors and call
System.exit().
> I don't think there is one answer to this problem, but depending on what
> kind of application you are writing the compromises might look
> different. I still stand to the decision of catching Throwable in that
> particular case and I have seen quite a few OutOfMemoryErrors during
> indexing that the tool survived. Also the odd StackOverflowError. In all
> these cases I much preferred the log&continue approach and while I know
> it could go wrong it hasn't failed me so far. If the application ever
> crashes badly it won't hurt anyone -- your average user is much too used
> to restarting programs anyway :-)
>
If you have no shared state or do not mind it potentially being corrupt,
then sure keep plugging along as best you can.
--
Jess Holle
And what happens to other threads? If you call System.exit you should
have global knowledge of all application threads and their current
state. Even in the presence of a VirtualMachineError it is probably
better to try a clean shutdown. Calling System.exit could leave a lot of
things in inconsistent state that will be hard to recover from. Are all
your system's boundaries transactional?
Peter
Proably would use a set of case classes I guess, but would end up
looking like a pretty version of that (someone like Viktor will
probably show us !).
Concrete examples: Runnable does not let you throw Exceptions. Anyone up for defending this grievous API design? The amount of annoyance that this is caused me is almost limitless.
public interface Callable<T,E>I will almost certainly get BGGA's proposed syntax muddled here, but the important thing here is that call() could throw a precise set of checked exceptions and this set would be bubbled up in generic algorithms (rather than Exception as in Callable<T> today), e.g.
{
public T call() throws E;
}
public <T,E> visit( Callable<T,E> visitor ) throws SQLException, E;or some such. The actual list of checked exceptions emanating from visit would be SQLException, IOException, MalformedURLException if E was IOException, MalformedURLException.
What I would really like to see is a meet/or/disjunction/union operator
in the type system, with a case-like construct to resolve the resulting
types. Scala has two things that are halfway there (Either, case
classes), but neither is the full monty.
BGGA's checked exception handling is extremely complex. I don't like it at all.
for ( ... )then you'd expect to have to catch IOException or declare "throws IOException".
{
// some code that throw IOException
}
forEachWombat( ... )You'd get totally different behavior -- which is *not* acceptable in my book.
{
// do some stuff that throws IOException
}
Yeah well, it depends how you look at it I suppose. I would also claim
generics to be a figment of the JVM's imagination, since you can not
implement Comparable<Integer> and Comparable<BigInteger> due to it
being an emulated construct at the parser level.
Other languages with
real generics have no problem with that, in Java all of a sudden you
are going to need anonymous inner classes as dispatch adapters.
Interestingly Java's generics allow the dual construction on interfaces:
public <T extends Interface1 & Interface2> void someMethod(T param) {...}
I really like the idea of having the anonymous unions/joins and
considering that the resulting type system should be a complete lattice
they sound manageable to me -- both for the compiler and the human
reader. Does anyone know reasons why no language seems to have this feature?
don't feel like you gained much in your sample, you could just replace
"String | Integer" with Object and add a "default: throw new
AssertionError();". Obviously you gained type safety,
but it would be
much more useful if unions were structural:
public static void closeSilently(InputStream|OutputStream|Reader|
Writer|Channel preCloseable) {
try {
preCloseable.close();
} catch(IOException e) {}
}
[...]
>> If you just allow any exception to go through (e.g. by letting the
>> methods throw Exception on the API level), programmers will forget to
>> handle things they could handle.
>>
>
> Idiots write idiot code. But, okay, I'll roll with your argument: Then
> why isn't there a mechanism to escape it? Why can't I say: Nono,
> compiler, I did not forget anything, this is what I want. Shut your
> yap. You're not really going to argue with me that there is no
> inconvenience at all here, right?
>
The problem I see is that someone on a lower layer might have done this
and the guy on the layer above is not aware of it. I know you will
counter with the argument that the alternative is bad catches, and I
agree to some extent. But those catches you describe (the
e.printStackTrace() and similar) are known to be bad, adding a mechanism
to the language makes it look much more legit. Of course the new
RuntimeException(e) is much better, but I don't think it is a general
solution either.
>
>
>>> InputStream.close() throws IOException. Nuff said.
>>>
>> That only proves that exceptions are used badly in the JDK.
>>
>
> That "only" proves? The JDK abuses checked exceptions - That's a
> pretty big deal! That's the whole point! That's why the language needs
> an escaping mechanism.
>
It doesn't seem right to add language features to deal with crappy
library design.
> The further damage caused by bad coders abusing the escaping mechanism
> are, in my opinion, much, __much__ smaller than the potential further
> damage that would no longer becaused because the mechanism is
> available. The occasional explicitly supressed checked exception that
> should have been handled is far less of a crime than code littered
> with this abomination:
>
> try {
> something();
> } catch ( IOException e ) {
> e.printStackTrace();
> //TODO Eclipse auto-generated this. The programmer never looked at
> it again, logging to system.err is just about the worst thing you
> could do here, and the original programmer only hit quickfix-add try
> catch block because he couldn't throw it onwards because he's
> implementing a servlet, or a runnable.
> }
>
> Sure, an idiot coded that. But we're all idiots some of the time, and
> you yourself put 'idiots exist' on the table as a legal line of
> reasoning.
>
What you are proposing seems to me like making a mediocre solution easy
to avoid people doing the really bad ones. Call me an idealist, but I
don't really like that.
Just think of the problem one layer above the one that may use the
sneaky throws: you might get some methods you call that pass the
IOException to you, some might do what I consider right and chain it
with their own checked exception and other just do the sneaky throw. To
figure out what's happening I really need to know the internals of each
method down to the bottom. The alternative is catch(Exception).
Admittedly the problem is already there since Java has both checked and
unchecked exceptions.
>>> FilterInputStream and FilterOutputStream's methods throw IOException,
>>> but if I feed it a ByteArrayInputStream, or I let it filter into a
>>> ByteArrayOutputStream, then an IOException can never happen.
>>>
>> Which could be solved by having safe and unsafe versions of the interface.
>>
>>
>
> Do you realize how much code duplication that would require? Exactly
> _BECAUSE_ you can't circumvent checked exception checking, you'd
> actually have to copy/paste every method. I cannot put into words the
> insanity of a system that would _require_ you to copy/paste vast
> tracts of code just to satisfy a system that is principally designed
> to help code maintainance. Talk about bass ackwards. In practice, you
> would most likely implement this by.... employing a sneaky throw hack.
> I can scarcely imagine a better case for enshrining sneaky throwing
> into the JLS, than this line of reasoning.
No implementation would be duplicated, the safe version could be a
subtype of the unsafe one. Not too many interfaces will face that
problem. To me it seems much better than not distinguishing.
Peter