Writing Enterprise Software Error Checking
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
<strong>Writing</strong> <strong>Enterprise</strong> <strong>Software</strong> <strong>Error</strong><br />
<strong>Checking</strong><br />
"There is no substitute for hard work." -Thomas A. Edison<br />
Recap: The need for error checking<br />
In my previous article (http://www.riversand.com/blog/writing-enterprisesoftware/),<br />
I introduced the topic of error handling in enterprise software. More<br />
accurately, I introduced the topic of error checking. <strong>Error</strong> handling includes checking<br />
for errors and catching exceptions, but it also involves doing something about an<br />
error when it happens.<br />
I will set aside the "what to do when an error occurs" topic for now. In this article I<br />
will recap why error checking is important, present a common use case where error<br />
checking is needed, and then show some code that performs thorough (and<br />
thoroughly ugly) error checking. In the next article, I will present a design pattern for<br />
making the error checking much cleaner.<br />
In short, the main reasons to thoroughly check for errors (and catch exceptions that<br />
can be thrown) are to write code that behaves predictably, and to accurately and<br />
usefully report errors. If you are writing code that does not check every return code<br />
for an error, then you run the risk that your code fails later in some way that is<br />
seemingly unrelated to the original failure. You also lose critical information about<br />
the error, information that might help the user of your software solve the problem<br />
and avoid a support call.<br />
Many software developers implement the main functionality of their code, thinking<br />
they will go back and add error checking/reporting/handling, but time pressures<br />
often get in the way of such follow up activities.<br />
©2017 Riversand Technologies Inc. All rights reserved.
Performing thorough error checking in your code is hard work. It takes discipline.<br />
But it's well worth the effort.<br />
Aside: A case for useful error messages<br />
I mentioned above that errors should be reported both accurately and usefully. The<br />
distinction is important. How many times have you performed some operation on<br />
your computer, such as copying a file or updating your personal information on a<br />
web site, only to receive an error message along the lines of "Unknown error" or<br />
"<strong>Error</strong> 0xC0081004 occurred"? Both of those may be accurate error messages, but<br />
they are far from useful.<br />
<strong>Writing</strong> an accurate and useful error message (such as "Unable to copy file.<br />
Destination is full." or "Unable to update user information because the account is<br />
locked. Please contact customer support.") requires effort. But think how happy<br />
your users will be when they understand why an error happened. They'll be ecstatic<br />
if they have enough information to correct the error themselves.<br />
Case study: Getting a configuration value from JSON<br />
I have made a general, sort of hand-wavy argument for the importance of error<br />
checking. Let's take a look at an example. Suppose we want to store our software's<br />
configuration data in a JSON file [0]. Suppose also that we've organized the JSON<br />
into a collection of nested objects, so that the stored configuration data reflects the<br />
organization of modules and their UI elements in our software.<br />
For example, if we want to store the configuration value for the layout of UI element<br />
1 in module A, we might have a JSON object that looks like the following snippet:<br />
{<br />
"modules":<br />
{<br />
"moduleA":<br />
{<br />
"element1":<br />
{<br />
"layout":<br />
{...}<br />
}<br />
}<br />
}<br />
©2017 Riversand Technologies Inc. All rights reserved.
}<br />
A common approach to accessing this information in Java would be to load it into a<br />
hierarchy of Java objects whose classes model the structure of the JSON object. This<br />
is easily accomplished using the (very handy) Gson library<br />
(https://en.wikipedia.org/wiki/Gson). If you're not familiar with Gson, that's okay.<br />
All you really need to know is that we can write a set of Java classes that allows us to<br />
load the JSON object above into a collection of related Java objects and access the<br />
layout for element 1 using code that looks something like:<br />
JsonObject layout =<br />
config.getModule("moduleA").getElement("element1").layout;<br />
During development of the software, this works just fine. We control the contents of<br />
the JSON configuration object, which we store in a file as part of our software's<br />
configuration. Everything works fine during testing, too. All is good.<br />
Then we ship our software and much happiness ensues. It's finally time to take that<br />
much delayed, and desperately needed, vacation. Margaritaville beckons.<br />
Support call #1<br />
Until we get our first support call, and the customer is irritated because she gets a<br />
message about a "NullPointerException" when she tries to run our software. It ran<br />
just fine yesterday. This morning, she started our software, just like she does every<br />
morning, but now it's giving her this cryptic message and she can't do her job. All is<br />
no longer good.<br />
Thoughts of Margaritaville are wastin' away. [1]<br />
Analysis: The customer did what?<br />
After several long hours on the phone with the irate customer, we finally determine<br />
that she had inadvertently changed the JSON configuration file while poking around<br />
in our installation directory. If this seems far-fetched, you've never supported an<br />
enterprise software product.<br />
Even though the error is due to the customer's action, we don't win any points for<br />
selling her software that gives her a completely useless (albeit accurate) error<br />
message.<br />
©2017 Riversand Technologies Inc. All rights reserved.
We can do better. So we roll up our sleeves and get back to work. We decide that<br />
we need to add error checking to the code that retrieves the layout information for<br />
element 1.<br />
It turns out that the customer accidently added a space to the name of the<br />
"modules" JSON object, so when we parsed the JSON file, we didn't find a<br />
"modules" object, and we don't know what to do with an object named "module s".<br />
The end result is that our code that accesses the settings for "moduleA" accesses a<br />
non-existent modules object. That is, the code Config.getModule("moduleA")<br />
returns a null pointer, so the code<br />
Config.modules("moduleA").getElement("element1") generates a<br />
NullPointerException.<br />
Fix #1: <strong>Checking</strong> for missing configuration data<br />
Now that we know what can go wrong with the line of code<br />
JsonObject layout =<br />
config.getModule("moduleA").getElement("element1").layout;<br />
we change it to the following:<br />
JsonObject layout = null;<br />
String errorMessage = null;<br />
Module module = config.getModule("moduleA");<br />
if (module == null) {<br />
errorMessage = "configuration section missing for moduleA";<br />
}<br />
else {<br />
Element element = module.getElement("element1");<br />
if (element == null) {<br />
errorMessage = "configuration section missing for moduleA:element1";<br />
}<br />
else {<br />
layout = element.layout;<br />
}<br />
}<br />
if (layout == null || errorMessage != null) {<br />
if (errorMessage == null) {<br />
errorMessage = "missing configuration for moduleA:element1:layout";<br />
©2017 Riversand Technologies Inc. All rights reserved.
}<br />
}<br />
log.error(errorMessage);<br />
throw new Exception(errorMessage); // caller must handle exception<br />
Sheesh! We went from one line of easy to read code to 23 lines of code that are, to<br />
put it charitably, less than elegant.<br />
Admittedly, we added checks for more than just the missing "modules" JSON object,<br />
since we rightly surmised that if the "modules" JSON object can get corrupted, the<br />
same can happen to "moduleA", "element1", or "element1:layout". But at least<br />
we're learning from our mistakes and we're proactively adding some much needed<br />
error checks along with some useful error messages.<br />
But still, that's some really ugly code. The whole flow of the code is ruined. Anyone<br />
reading this code will be unhappy encountering 23 lines of code, most of which are<br />
almost never needed (assuming our configuration file is not usually corrupted).<br />
But wait, there's more! [2]<br />
Most likely, we have more than one element in module A whose layout we need to<br />
retrieve.<br />
One approach would be to copy the block of 23 lines, paste it in the necessary<br />
location(s), change the names to protect the innocent, and get on with our lives. I've<br />
seen things like that done many times over the years. This practice leads to code<br />
bloat, copy/paste errors, and maintenance nightmares.<br />
A much better approach would be to move the 23-line code block to a function that<br />
takes parameters for the module name and element name. The 23 lines of code<br />
would be even harder to read, since they must be reworked to be generic enough to<br />
handle arbitrary module and element names. However, this new function would add<br />
some significant code goodness benefits: the error checking logic would be removed<br />
from the main flow of the code, any bug fixes or updates to the function would<br />
benefit all users of the function, and getting the configuration layout for other<br />
elements would be a breeze.<br />
Next time<br />
Still, it seems like we can do better. What's bothersome is the need to do a<br />
thorough check on the JSON configuration object, even though the object is usually<br />
©2017 Riversand Technologies Inc. All rights reserved.
valid. It would be nice to have the error checking performed on-demand, which<br />
could save processing time. After all, nobody likes waiting for an application that's<br />
slow to start. [3]<br />
We might not mind paying the processing cost when getting a few layout values<br />
from a JSON configuration object during application initialization, but suppose we<br />
have an application that communicates via internally-generated messages. Most of<br />
the time, these messages are valid (since we are generating them ourselves), but<br />
what if we update the application's modules independently of each other and we're<br />
concerned about changes in message formats across versions of modules?<br />
We want to validate a message's format, but only if the message is invalid, in which<br />
case we need to report the issue accurately so that we can fix it quickly. Performing<br />
thorough error checking on every message could be a performance killer.<br />
Furthermore, it would be nice if when the error checking kicks in, it also generates<br />
the corresponding error report automatically. Hey, since we're wishing, why not<br />
wish big?<br />
In the next installment, I will describe an error checking/reporting design pattern<br />
that I call the Validator Pattern. The goals of this pattern are:<br />
<br />
<br />
<br />
<br />
Perform thorough error checking and error reporting<br />
Only perform the error checking when there is an error<br />
Perform the error checking and reporting automatically<br />
Remove the code that performs error checking and reporting from the main<br />
flow of the code<br />
I make no claim that I am the first person to think of this design pattern, but when I<br />
was looking for a solution with the above requirements, I didn't find anything that<br />
was exactly what I wanted. So here we are.<br />
©2017 Riversand Technologies Inc. All rights reserved.
Notes:<br />
[0] In case you're getting irritated about the fact that I'm rolling my own<br />
configuration mechanism, yes, I'm aware that there are plenty of libraries that assist<br />
in managing an application's persistence. This is simply an illustration of a common<br />
scenario where error checking is necessary and useful error reporting is extremely,<br />
well, useful.<br />
[1] I'm referring to the Jimmy Buffett song "Margaritaville"<br />
(https://en.wikipedia.org/wiki/Margaritaville), but I'm also describing a situation<br />
known to many software developers where a vacation is interrupted by some urgent<br />
customer issue. I have a good friend who has never (to my knowledge) taken a<br />
vacation without being required to work on code at some point during the vacation.<br />
Talk about vacation interruptus.<br />
[2] https://en.wikipedia.org/wiki/Ron_Popeil<br />
[3] I truly enjoyed playing Sid Meier's Civilization V, but every time I started it, I had<br />
to leave the room and get a cup of coffee. Waiting for it to initialize and be ready for<br />
me to play was agonizing.<br />
Riversand is an innovative leader in Master Data Management, powering industries from the world's<br />
largest to SMBs. Riversand's single integrated, scalable and robust multi-domain MDM platform caters<br />
to a variety of use cases across multiple verticals. In addition Riversand offers specific solutions such as<br />
Vendor Portal, Spare Parts Management, Material Master, GDSN on-boarding, Media Assets<br />
Management, Print Publishing etc. Business value which Riversand provides include accelerated timeto-market,<br />
increased sales, improved order accuracy, reduced costs and enhanced customer service.<br />
Customer satisfaction is at the heart of Riversand's innovation.<br />
For<br />
©2017<br />
more<br />
Riversand<br />
information,<br />
Technologies<br />
visit<br />
Inc.<br />
Riversand.com<br />
All rights reserved.<br />
and follow @RiversandMDM on Twitter.<br />
LEARN MORE