12.05.2017 Views

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

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!