Skip to Content

Alright, in chapter 3 we looked at the architecture/design of the Granny application in detail and we have identified several areas that leave room for improvement. Now, while most of us are probably eager to start hacking away, remember our mission statement: we want to make this application enterprise-ready! The way I see it this also implies that we do things in the right order.

The right order

Looking back at 10+ years as a software engineer I got to realize that more often than not projects are overstaffed at the beginning of the development phase. Why? Because usually you want to start with a very small group setting up the general architecture up front! Otherwise, the development team will come up with dozens of different ways to do things resulting in a bloody mess. Sure, it may work… but it makes the solution much harder to maintain, which ultimately leads to a bad TCO. Ironically, exactly those aspects that should help to make the code more manageable and maintainable are usually among those that you see implemented in various ways and style. For those who haven’t guessed yet: I’m talking about logging/tracing and error/exception handling!

I agree, those are not the most exciting features to implement, yet everyone who has ever come across a try-catch-block that simply traces a caught exception or – even worse – features an empty catch-block with nothing but a TODO flag will probably agree with me that there’s a fundamental problem.. and a common one as well!

I’m a big believer of implementing these aspects up front and ensuring that the whole team knows how-to properly make use of these capabilities within the application. It may sound trivial, but implementing these aspects up-front will save you both time and a few headaches in the long run. Let’s face it, we all make mistakes and having a solid exception handling will come in handy to identify the root cause of problems….

Logging/Tracing

As we are about to make changes to the original Granny applications it would help us to understand what is going on behind the scenes. One way to achieve this is by making good usage of logging and tracing.

Note: I’d rather like to avoid a longer discussion about the differences between logging and tracing at this point. One reason (among several) is that traditionally one key aspect of such discussions is the fact that the two cater to different roles: developers and system administrators. The way I see it those roles are getting blurry in the context of cloud development and the broader DevOps movement.)

Enough of the talking now. Let’s take a look at how the original Granny app dealt with these aspects using the HomeController [Source Code] as an example:

@Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
@Autowired
AddressService addressService;
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.debug("in home method");
return "home";
}
...}

In line 4 we can see the declaration of an sl4f Logger as seen across the web. So far, so good. Now, let’s see how it is used within the home() method. Looking at the home() method in general we quickly spot the absence of any error handling code: no try-catch-block and no throwing declaration. Ouch! We’ll make a note about this and continue by looking at the logging statements. Hm, we see one that states: logger.debug(“in home method”);

Now, why is that bad? Well, first of all it would probably much better to use a trace() statement and second, explicitly trace statements like this just clutter our methods. As mentioned earlier in this series, logging and tracing are so called cross-cutting concerns, and as such much better dealt with in a uniform approach outside of the business functionality. One way to achieve this is by using aspects (see AOP).

To accomplish this I did the following things (for a complete change log consult the commit history please):

  • added some dependencies to the pom.xml file (aspectjweaver, spring-aop, spring-aspects, asm, cglib-nodep)
  • created a new aspect called LoggingAdvice in a new package called com.sap.hana.cloud.samples.granny.xcc
  • updated the component-scan path of the spring config (app-context.xml) to include the new cxx package (so that the aspect is applied)
  • added the “aop” schema definition to the app-context.xml file and added aspectj support (line 19)
  • changed the corresponding log level in the log4j.xml file from “info” to “all” (to include the trace)
  • removed all debug() statements from the HomeController class

With that we have automatic tracing for all services. Pretty nifty, isn’t it?

Note: You’d still need to adjust the log level for the corresponding classes in order to see the trace/log messages in the log file. Please read the corresponding chapter in our online documentation for more details.

Exception handling

As said earlier, proper exception handling is one of the very first things you should introduce for your own sake. Too often I see it done at the very end, when the straightforward scenario is already working. Then people start to invoke problems on purpose to see if the exception handling works as expected. That just feels wrong! If you set it upright away it will already help you during development and you can test it on a daily basis.

Looking at the original Granny app we realize that there’s no exception handling at all! If something goes wrong, the user is not even informed about it: the app simply doesn’t work. Developers may be able to introspect the request/response cycle and tracing the HTTP traffic by using browser extensions or web developer tools, yet that’s not really a valid option. That’s a dead-end!

Hence, I introduced a new Exception called ServiceException and introduced a base pattern for business services as follows:

public void createAddress(Address address) throws ServiceException
{
try
{
addressRepository.save(address);
}
catch (Exception ex)
{
this.handleException(ex);
}
}

There are a couple of note-worthy things to point out:

  • ServiceException is a simple sub-class of RuntimeException (Read this article for more details about why I opted for a RuntimeException)
  • I introduced a base class called BaseService (always a good practice to provide base classes to have a central place to add common logic/behavior)
  • The error-handling itself is not really sophisticated yet(!), but at least we provided a common pattern and a central place to deal with exceptions!

All that the handleException() method does (at the moment!) is to wrap the original exception into a ServiceException (if applicable) and then simply re-throwing it.

Please note that I deprecated the original service interface and implementation and introduced new versions in a different package called: com.sap.hana.cloud.samples.granny.srv. The primary reason was that we’ll make plenty of adjustments along the way and as such I really wanted to use our own namespace moving forward.

The last change I want to address today is the ExceptionHandler introduced in the ContactController class:

      /**
           * Handles a {@link ServiceException} and returns some *meaningful* error message.
           *
           * @param ex The {@link ServiceException} to handle
           */
          @ExceptionHandler(ServiceException.class)
          @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
          public  @ResponseBody String handleServiceException(ServiceException ex)
          {
            // do something really sophisticated here!
            return "We screwed up!";
          }

Again, this is not really the most sophisticated approach yet, but at least we established a central place for translating exceptions to error messages. In a nutshell, we just declared an ExceptionHandler for our ServiceException and report it back as an HTTP 500 error code with a human-readable message. Prior to this change the complete stack trace etc. was returned as part of the response, which is definitely not what we want (for security reasons!)

Now, the last missing piece would have been to incorporate that error message handling into the presentation layer by adding an error handler in the jQuery AJAX calls within the home.jsp page. Yet, given the current state of the UI and that we want to re-write it completely anyway I opted against it. To be frank, I think it makes sense to focus on the backend-side of things for now and make sure that the services and the API are clean and ready for consumption before we start working on the UI.

With that, we call it a day! In the next chapter we’ll finally look into the domain model – hope to see you around!

To report this post you need to login first.

2 Comments

You must be Logged on to comment or reply to a post.

  1. Robin van het Hof

    Hi Matthias,

    Very good and concise blog! I fully agree with you about implementing exception handling right from the start, it will turn out to be an enormous payoff in the development end-phase.

    The underlying message is for development teams, small or large, to have proper development and coding guidelines in place, so the end-result is a coherent set of code (and everyone in the team could work with everyone’s code)

    (0) 
  2. Dylan Drummond

    Like this EnterpriseGranny series a lot. Thanks Matthias for this excellent series.

    Also good that the developer has to do a bit of thinking and hunting around by his/herself.

    One timesaver tip would be that, when you come to implement the new Exception Handling described, you can find all the needed classes at the “right Git-moment” from here:

    https://github.com/SAP/cloud-enterprise-granny/tree/25e5dbe8c4fe3e9f06f9ad62bd1d0c7074727f54/src/main/java/com/sap/hana/cloud/samples/granny

    (The current link in the document takes you to further ahead in the EnterpriseGranny project, so you might get confused if you follow the “ServiceException” source code link in the main text, as implementing that involves declaring already dependencies on org.apache.commons (commons-lang3) etc and importing model classes etc.)

    (0) 

Leave a Reply