Saturday, October 27, 2018

Spring Boot, Spring Data JPA, jackson-dataformat-xml

At work, we had a RESTful web service application implemented in Groovy/Grails.  This probably started out as a SOAP web service and was then ported to Grails 1.1.1 (2009).  It worked, but had become almost unmaintainable due to the archaic tech stack.  Also, there was a desire to move away from Grails, which not many of the team knew, in favor of Java 8 and Spring.

This was about a one month project.  It built on the experience I got doing RESTful Web Service in Spring.

Since I was using Spring Boot, this web application could have run as a stand-alone Java app using the embedded servlet container support, and this is how I was debugging it at first, but the requirement was to have a drop-in replacement for the old Grails WAR file running in Wildfly, so that was an additional wrinkle.

The original service, which my program had to emulate closely, did not use HTTP methods in a RESTful manner, but instead mostly used GET and POST for everything.  This alone ruled out the use of Spring Data REST, so I needed to write controllers.

I started by creating a suite of integration tests which could be pointed at either the old or the new service.  This was invaluable for verifying that the new service performed identically to the old one.  Many problems in my implementation were exposed in time for me to correct them as a result of running these tests.

The application consists of a collection of endpoints, each providing read/write access to one table in the Oracle database.  Spring Data JPA really shone here.  I rarely needed to give much thought to the database access part and in the one case where I needed to do something outside of the method-name based query model (next value from an Oracle sequence), the native query escape hatch was there.  I think I like this better than GORM.  GORM tries to make everything easy for you but I always wound up spending a lot of time resolving issues with transactions, sessions and cascading... these issues just seem clearer and less hidden in Spring Data JPA.

For rendering the responses in XML, I used jackson-dataformat-xml.  See How to write an XML REST service.  At first I tried using the same classes for the database entities and the POJOs to be rendered into XML in the response, but the complexity of that soon got out of hand and I resorted to creating a separate set of DTO classes for the response POJOs.  This was the most unsatisfying part of the task for me, having this whole array of DTO classes with conversion methods ('from' and 'to')... it added a bunch of just infrastructure coding that had nothing to do with the business logic.

Going into Wildfly was mostly straightforward.  The one exception was authentication/authorization.  We had a custom Wildfly security domain which accessed LDAP for user data and Oracle for group and role data.  I was not able to find a generic way to support any Wildfly security domain with Spring Security.  I think the way to do this is with a custom authentication provider and user details service but the clock ran out on me and we went with something much simpler.

In the Wildfly environment I also needed to switch to using JNDI to resolve an Oracle data source configured in the container, but this is a breeze in Spring boot with

spring:
  datasource:
    jndi-name:    JNDI name of your data source here

in the application.yml file... this didn't take more than half an hour to figure out and make the change.

I had some difficulty at first getting the logging to work the way I wanted it to with the application deployed into a container.  I started out trying to keep the original Log4j 1.2.x configuration, but that doesn't seem to be very well supported in Spring Boot, and eventually I gave up and used Logback with a logback-spring.xml file.  This is one of those areas where there are a hundred different ways to do something and it's not always clear how to proceed.

Similarly, when it comes to configuring your Spring Boot application, there's such a wealth of features to support this, it's not always obvious what's the best way to meet a given requirement.  I wound up with two sets of application-(profile).yml files, one for running the application and one for running the integration tests

The service has been running in production for 3-4 weeks now.  This is the most ambitious Spring Boot and Spring Data project I have tackled and also marks an important milestone in our team's adoption of this technology.