Comments
yourfanat wrote: I am using another tool for Oracle developers - dbForge Studio for Oracle. This IDE has lots of usefull features, among them: oracle designer, code competion and formatter, query builder, debugger, profiler, erxport/import, reports and many others. The latest version supports Oracle 12C. More information here.
Cloud Expo on Google News
SYS-CON.TV
Cloud Expo & Virtualization 2009 East
PLATINUM SPONSORS:
IBM
Smarter Business Solutions Through Dynamic Infrastructure
IBM
Smarter Insights: How the CIO Becomes a Hero Again
Microsoft
Windows Azure
GOLD SPONSORS:
Appsense
Why VDI?
CA
Maximizing the Business Value of Virtualization in Enterprise and Cloud Computing Environments
ExactTarget
Messaging in the Cloud - Email, SMS and Voice
Freedom OSS
Stairway to the Cloud
Sun
Sun's Incubation Platform: Helping Startups Serve the Enterprise
POWER PANELS:
Cloud Computing & Enterprise IT: Cost & Operational Benefits
How and Why is a Flexible IT Infrastructure the Key To the Future?
Click For 2008 West
Event Webcasts
Rebel Without a Clause
Rebel Without a Clause

Do you consider yourself a Java expert? Think you know everything about exception handling? Can you quickly spot the six exception handling problems below?

1: OutputStreamWriter out = ...
2: java.sql.Connection conn = ...
3: try { 5
4: Statement stat = conn.createStatement();
5: ResultSet rs = stat.executeQuery(
6: "select uid, name from user");
7: while (rs.next())
8: {
9: out.println("User ID : " + rs.getString("uid") + 6
10: ", name : " + rs.getString("name"));
11: }
12: conn.close(); 3
13: out.close();
14: }
15: catch(Exception ex) 2
16: {
17: ex.printStackTrace(); 1-4
18: }

Every Java developer should be able to spot at least two. If you can't spot all six, read on.

Rather than provide general guidelines (most of which are well known), we decided to reveal what we call anti-patterns: (unfortunately) common bad programming practices that we've seen time and again in Java code. Our purpose is to familiarize you with these counterexamples so you can quickly spot them and avoid them.

Now for our hall of shame ­ the six most common misuses and abuses of exception handling.

  • Anti-pattern #1: try-catch-bury

    15: catch(Exception ex)
    16: {
    17: ex.printStackTrace();
    18: }

    This is the bane of Java programming ­ catching an exception and not doing anything about it. In terms of frequency and severity, this is probably comparable to the infamous unchecked buffer problem in C and C++. If you see this anti-pattern, you can be 99% sure that it's wrong (there are a few cases where it might make sense, but they should be carefully commented to make it clear to the maintenance developer).

    This is wrong because an exception (almost) always means that something bad (or at least unexpected) has happened, yet we choose to ignore and silence that cry for help. Calling printStackTrace does not qualify as "handling an exception" ­ it's okay for debugging but should not be present past that initial step.

    This problem is frighteningly pervasive. If you look at the documentation for the JDK class ThreadDeath, you'll see the following comment: "The class ThreadDeath is specifically a subclass of Error rather than Exception, even though it's a Œnormal occurrence,' because many applications catch all occurrences of Exception and then discard the exception." This poor programming practice is so common that, in an unhealthy feedback loop, it has affected the design of Java itself. Grunt.

    What should you do instead? You basically have four options:
    1.   Handle the exception: Do something about it. Correct the problem, notify someone, or do some different processing, whatever makes sense in this particular situation. Once again, calling printStackTrace does not qualify as "handling the exception."
    2.   Rethrow the exception: This is not uncommon if your exception handling code examines the exception for additional information, and decides that it can't handle it after all.
    3.   Translate the exception into another exception: Most often this is used to translate a low-level exception into an application-level one.
    4.   Don't catch the exception: If you're not going to do anything about it, why bother catching it? Let it go (and, if necessary, declare it in the signature of your method).

    Solution 1: If you catch an exception, do something about it. Don't bury it.

  • Anti-pattern #2: Pokémon ­ gotta catch 'em all

    15: catch(Exception ex)

    It's often tempting to have a general catch statement. The most common is undoubtedly catch(Exception ex). In almost all cases, that's a bad idea. Why?

    To understand why, we need to step back and consider the purpose of a catch statement. You use a catch statement to indicate that you anticipate certain exceptions and that you're willing to handle them. The class of the exception(s) is how you tell the Java compiler which exceptions you want to handle. Since the vast majority of exceptions inherit directly or indirectly from java.lang.Exception, specifying that in your catch statement effectively says you want to handle almost any exception.

    Let's look at the code sample again. What kind of exceptions are we likely to catch? The most obvious one is SQLException, quite likely to happen any time you deal with JDBC. IOException is another possibility ­ we're dealing with a stream writer. You can already see the problem: it doesn't make sense to handle these two (very different) types of errors in the same catch block. It would make a lot more sense to have two catch blocks, one for SQLException and one for IOException.

    In addition, a large number of other exceptions could occur. What if, for some obscure reason, executeQuery returns null? You'll get a NullPointerException in the next line, yet that (unexpected) exception will be ignored (unless you happen to be looking at the console for your program ­ and even then, unless you wrote that code yourself, what good does that do you?).

    The better approach is to be more specific. In this case, we should have two catch blocks, one for SQLException, the other for IOException. This makes it clear to the next reader of this code that you have thought about these scenarios. But what about all the other exceptions? Let them go. Don't handle them (unless you have a reason to). You cannot (and in fact should not) try to handle all possible exceptions ­ let them bubble up; someone else will deal with them (the JVM if nothing else).

    There are some instances where catching Exception is a good idea, especially when testing or debugging. Once you have isolated the cause of the problem, you need to remove the catch Exception and replace it with the appropriate kind of Exception(s).

    Solution 2: Be as specific as possible in your catch statements and use multiple catch statements if necessary. Don't try to handle all possible exceptions.

  • Anti-pattern #3: The forgotten bathroom key

    3: try {
    4: Statement stat = conn.createStatement();
    5: ResultSet rs = stat.executeQuery(
    6: "select uid, name from user");
    7: while (rs.next())
    8: {
    9: out.println("User ID : " + rs.getString("uid") +
    10: ", name : " + rs.getString("name"));
    11: }
    12: conn.close();
    13: out.close();
    14: }

    One of us used to work in a small office with only one bathroom and you had to use a key. One programmer was notorious for neglecting to return the key to its rightful place, thereby inconveniencing the rest of the office. Often this was because he would get interrupted while coming back to his desk and would forget to return the valuable resource.

    Similarly, when an exception occurs, it affects the execution path of your program. It's easy to overlook that simple fact. If you use resources such as files, sockets, JDBC connections, etc., you need to make sure they're properly closed and released even if an exception occurs. Java has a mechanism designed for such a thing: finally.

    Finally is a wonderful thing; it allows you to guarantee that your cleanup code will be executed before the end of the try/catch/finally block, regardless of whether or not an exception is being thrown. Think of it as a string on your finger that reminds you to return the bathroom key, even if something distracts you. Yet finally is rarely used, even by intermediate programmers.

    Of course, finally blocks should be written with great care. In particular, you should be careful about exceptions being thrown in a finally block ­ this is your final chance to clean things up; don't let it slip. In some cases, it may be acceptable to bury an exception that's thrown in a finally block. Think of it as a best effort. Regardless, use your best judgment, or better yet, ask someone more experienced.

    Solution 3: Make sure all resources are properly released. Use finally liberally.

  • Anti-pattern #4: If you don't know why I am mad, I won't tell you

    3: try {
    4: Statement stat = conn.createStatement();
    5: ResultSet rs = stat.executeQuery(
    6: "select uid, name from user");
    7: while (rs.next())
    8: {
    9: out.println("User ID : " + rs.getString("uid") +
    10: ", name : " + rs.getString("name"));
    11: }
    12: conn.close();
    13: out.close();
    14: }
    15: catch(Exception ex)
    16: {
    17: ex.printStackTrace();
    18: }

    Anyone who has been married for a number of years can relate to this situation. Look at the code again: what happens if an exception occurs during the execution of the loop? Will we get information that can tell us what causes the loop to fail? Not really. All we'll get is an indicator that something is wrong in the area of the class that's doing the processing, but we may not get any input as to what caused the problem.

    The basic "stack trace" that shows the path of execution to the class that caused the exception is cryptic and not easily parsed. It also causes any tester without a Java development background to report application problems with a very general description such as "encountered an ugly Java problem." If such an exception is wrapped to display a message in plain English, testers are more likely to repeat the specific message displayed to them. Not everyone who tests business applications is experienced in the delicate art of stack trace interpretation.

    In addition to displaying a more user-friendly message, there's great benefit in providing a little extra data such as the Class, Method, and a text message. While it's true this information can be obtained from the stack trace, it's much easier to read when it's isolated. Also, taking the extra time to describe the behavior when you're creating the code will greatly expedite resolving the problem later.

    One solution is to embed a snippet like the following one wherever exceptions get logged in the application. You can use the this.getClass().getName() method, and insert the method named and a text message every time the program reports a checked exception. This practice makes exceptions easy to read and to parse.

    this.getClass().getName(), "mymethod", "some message"

    One caveat to this approach is that with a static method call you need to manually insert the class name because the "this" object reference does not have a this to refer to. Simply replace the this.getClass().getName()with your class name "myClass".

    Solution 4: Report a reasonable amount of data in a way that's easy to read.

  • Anti-pattern #5: Overzealous try

    3: try {
    4: Statement stat = conn.createStatement();
    5: ResultSet rs = stat.executeQuery(
    6: "select uid, name from user");
    7: while (rs.next())
    8: {
    9: out.println("User ID : " + rs.getString("uid") +
    10: ", name : " + rs.getString("name"));
    11: }
    12: conn.close();
    13: out.close();
    14: }

    Putting too much activity in a single try block is a bad programming practice, even though it's common. The reason it's common is that it takes extra time to isolate what could go wrong in a particular block and what exceptions could get created as a result of this processing. Wrapping too many statements in a giant catch is like preparing for a move by packing your household goods in refrigerator-sized boxes. Sure, all your goods are in one place, but imagine the sorting task you'll have in the near future. For the inexperienced developer, it's easier to put a bunch of statements in a try block and wrap it up in a generic catch Exception than to isolate the statements. This practice makes troubleshooting Exceptions generated during program execution that much more difficult because there are more variations to check to find out what caused the Exception.

    The code example doesn't really illustrate this fully because there's not enough space to show a complete example, but this is a common problem.

    Solution 5: Keep try blocks as small as possible, but no smaller.

  • Anti-pattern #6: Incomplete execution

    7: while (rs.next())
    8: {
    9: out.println("User ID : " + rs.getString("uid") +
    10: ", name : " + rs.getString("name"));
    11: }

    This is the silent killer of Java systems. Look at the code again and imagine what happens if an exception is thrown in the middle of the loop. The execution of the loop will be interrupted, the catch block will be executed, and that's about it. What about the data that we're writing out? It will be silently incomplete. Whoever or whatever is supposed to use this data will receive incomplete (and hence incorrect) data, but will probably have no idea that it is incomplete. For some systems, that's a problem far more serious than any crash.

    A better approach here would be to attempt to write a message to the output, signaling that the data could not be completely written. Another possibility would be to buffer the output (if possible) and write it out once we have it ready to go.

    Solution 6: Consider all likely exceptions and how they'll affect the flow of execution.

    Example (Rewritten)
    Given these facts, Listing 1 shows what the same code should look like. (Listing 1 can be downloaded from www.sys-con.com/java/sourcec.cfm.) Note that it's quite a bit more verbose. That's not a bad thing ­ good code often includes a lot of error handling.

    Conclusion
    All the anti-patterns in this article were inspired by actual code written by professional programmers. Before you snicker, ask yourself whether your code is really free from these poor practices. Even the most experienced programmers sometimes fall back into them, simply because they're easy (we certainly do). Think of them as bad habits ­ they are notoriously difficult to shake off, but just trying makes you a better person.

    Of course, nothing in this article should be taken as gospel. Use common sense and your experience. For each anti-pattern described here, you could certainly come up with counterexamples. As long as the rules are broken willingly and thoughtfully, you have our blessing, but we do beg for one favor: if what you do is not obvious, please write a nice comment. You'll thank yourself later. Also, share your wealth of knowledge with others on your team; extra time spent on training is a small price to pay in comparison to a massive refactoring effort at the end of the development cycle.

    References

  • Haggar, P. (2000). Practical Java: Programming Language Guide. Addison-Wesley.
  • Ambler, S.W., et al. (2000). The Elements of Java Style. Cambridge University Press.
  • The Sun Java online tutorial: http://java.sun.com/docs/books/ tutorial/essential/exceptions
  • Bloch, J. (2001). Effective Java: Programming Language Guide. Addison-Wesley.
    About Craig Dewalt
    Craig Dewalt is a surfer who likes to lead Java development teams.

    About Max Tardiveau
    Max Tardiveau is the founder of Integrity Logic.max@tardiveau.com

  • In order to post a comment you need to be registered and logged in.

    Register | Sign-in

    Reader Feedback: Page 1 of 1

    You forgot to have a try/catch/finally for the connection itself. Any resource must be allocated inside a try and have it's reference outside the try, so that it can be deallocated if something goes wrong.

    Excellent article on the potential places where a developer would miss or make mistakes in exception handling.

    Like it is said at the end, "nothing in this article should be taken as gospel." I think attention should be paid to those anti-patterns, but how it is done in pratice should be driven by requirements. For example, if it is only required to do the same thing on matter what kinds of errors have happened, I would argue that it is ok to catch just Exception, instead of each of the specific ones. The same can be applied to how big the try/catch block should be.

    As matter of fact, I doubt that many experienced programmers can identify all of the 6 anti-patterns, due to lack of enough requirements for the example.

    You are of course right. It is very difficult to get these minute details right in the world of publishing. Thanks for pointing it out.

    -- Max

    Nice article. I have one minor nit to pick. You are missing a closing brace in listing one. I believe it should go just before "if (out != null) {" to close the one from "if (conn != null) {".

    Heck, while I'm at it, here's a 2nd nit:
    It sure would be nice if the code samples / listings viewed online were formatted using so the indentation was preserved. Actually, that would be really nice in the printed version too.


    Your Feedback
    Infernoz wrote: You forgot to have a try/catch/finally for the connection itself. Any resource must be allocated inside a try and have it's reference outside the try, so that it can be deallocated if something goes wrong.
    David Chen wrote: Excellent article on the potential places where a developer would miss or make mistakes in exception handling. Like it is said at the end, "nothing in this article should be taken as gospel." I think attention should be paid to those anti-patterns, but how it is done in pratice should be driven by requirements. For example, if it is only required to do the same thing on matter what kinds of errors have happened, I would argue that it is ok to catch just Exception, instead of each of the specific ones. The same can be applied to how big the try/catch block should be. As matter of fact, I doubt that many experienced programmers can identify all of the 6 anti-patterns, due to lack of enough requirements for the example.
    Max Tardiveau wrote: You are of course right. It is very difficult to get these minute details right in the world of publishing. Thanks for pointing it out. -- Max
    Rick wrote: Nice article. I have one minor nit to pick. You are missing a closing brace in listing one. I believe it should go just before "if (out != null) {" to close the one from "if (conn != null) {". Heck, while I'm at it, here's a 2nd nit: It sure would be nice if the code samples / listings viewed online were formatted using so the indentation was preserved. Actually, that would be really nice in the printed version too.
    Latest Cloud Developer Stories
    DXWorldEXPO LLC announced today that Telecom Reseller has been named "Media Sponsor" of CloudEXPO | DXWorldEXPO 2018 New York, which will take place on November 11-13, 2018 in New York City, NY. Telecom Reseller reports on Unified Communications, UCaaS, BPaaS for enterprise and S...
    Evan Kirstel is an internationally recognized thought leader and social media influencer in IoT (#1 in 2017), Cloud, Data Security (2016), Health Tech (#9 in 2017), Digital Health (#6 in 2016), B2B Marketing (#5 in 2015), AI, Smart Home, Digital (2017), IIoT (#1 in 2017) and Tele...
    In his keynote at 19th Cloud Expo, Sheng Liang, co-founder and CEO of Rancher Labs, discussed the technological advances and new business opportunities created by the rapid adoption of containers. With the success of Amazon Web Services (AWS) and various open source technologies ...
    Transformation Abstract Encryption and privacy in the cloud is a daunting yet essential task for both security practitioners and application developers, especially as applications continue moving to the cloud at an exponential rate. What are some best practices and processes for ...
    Enterprises are striving to become digital businesses for differentiated innovation and customer-centricity. Traditionally, they focused on digitizing processes and paper workflow. To be a disruptor and compete against new players, they need to gain insight into business data and...
    Subscribe to the World's Most Powerful Newsletters
    Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
    Click to Add our RSS Feeds to the Service of Your Choice:
    Google Reader or Homepage Add to My Yahoo! Subscribe with Bloglines Subscribe in NewsGator Online
    myFeedster Add to My AOL Subscribe in Rojo Add 'Hugg' to Newsburst from CNET News.com Kinja Digest View Additional SYS-CON Feeds
    Publish Your Article! Please send it to editorial(at)sys-con.com!

    Advertise on this site! Contact advertising(at)sys-con.com! 201 802-3021



    SYS-CON Featured Whitepapers
    ADS BY GOOGLE