Development

Moving microservices forward

An important part of building, supporting and maintaining microservices is ensuring that they are kept up to date. Whilst it’s great getting the new features that the upgrade may give your developers, it’s key to ensure you are always able to get the latest security fixes. You don’t want to be in the situation where you are running on an unsupported version of Spring Boot, a major security flaw is found and you need to rework half of your microservice to get the fix!

Because of all this (and because who doesn’t like trying out new technology) I felt that any new microservices my team built should be on the latest version of the technology we use, and we should tackle the challenges as they come. This would remove barriers across the department for other teams that may want to create services on Java 9+, and from a selfish point of view, future me would appreciate not having more work made by current me being lazy.

Features, features, features

Moving from Java 8 to Java 9+ brings more than just security updates and bug fixes. The headliner is the well publicised and debated module system. Whilst we won’t see the main benefits for a while, it will eventually allow us to build applications without dated parts of the JDK that are rarely used these days (think CORBA, swing etc).  There are also some benefits which are more for today; it enables you to build libraries and applications with a proper package structure, only exposing classes that you want people to be able to use, and gives you greater flexibility and confidence when you want to change the internal workings.

There are other small wins though. How many ways can you think of instantiating an ArrayList in your codebase? I can think of these off the top of my head:

List<String> names = new ArrayList();
names.add("bob");
names.add("fred");
 
List<String> names = Lists.newArrayList("bob", "fred");
List<String> names = Arrays.asList("bob", "fred"); 
List<String> names = Collections.singletonList(“bob”);
List<String> names = Collections.emptyList();

This is handled nicely by the new collections factories. Provided are overloaded methods that return an immutable list containing the values you specify Simply:

List.of();
List.of("bob");
List.of(“bob”, “fred”);

Whilst it may seem a simple thing, consistency is always good! It’s also one less reason to have Guava as a dependency.

Other syntactical improvements have been made in the jump to 9, a particular example that I like is the new or method on the Optional type. This allows you to chain together methods that return the same typed Optional<T>.

Say you have three methods to obtain a test score, and a student can only take one of the following technology classes.

private Optional<Integer> getElectronicsTestResult(){
    return Optional.empty();
}
 
private Optional<Integer> getFoodTechnologyTestResult(){
    return Optional.empty();
}
 
private Optional<Integer> getWoodworkTestResult(){
    return Optional.of(80);
}

In java 8, to obtain the value that’s present, you’d need to write something along the lines of:

public Integer getTechnologyTestResult(){
    if(getElectronicsTestResult().isPresent()){
        return getElectronicsTestResult().get();
    }
    else if(getFoodTechnologyTestResult().isPresent()){
        return getFoodTechnologyTestResult().get();
    }
    else if (getWoodworkTestResult().isPresent()){
        return getWoodworkTestResult().get();
    }
    return 0;
}

In java 9+ you can just do this, which is arguably cleaner and more readable:

public Integer getTechnologyTestResult(){
    return getElectronicsTestResult()
            .or(this::getFoodTechnologyTestResult)
            .or(this::getWoodworkTestResult)
            .orElse(0);
}

So, you’re convinced that it’s a good idea, but where do you start?

You need everything to be compatible, but you also want to be able to make gradual changes rather than doing everything in one big bang! In our case, the first step was to get the build tool to be compatible with the version of Java and the version of Spring Boot that you’re aiming to get to. We already had Gradle at the minimum version required (4.5), so my team didn’t have any work to do – a reward for keeping things up to date! The next logical step was to get everything on to Spring Boot 2.X, this was because Java 9 required Spring Boot 2, but Spring Boot 1.X is still only compatible with Java 8. We have a number of internally built libraries that our microservices use (i.e. logging, metrics) and these were all on Java 8 and Spring Boot 1.X. In order to progress, we needed to be in a place where teams could build Java 9+ microservices on Spring Boot 2, without causing issues for everyone else on the older versions.

Upgrading the libraries was generally pain-free, with the larger changes mainly being around the new way in which Spring Boot manages registering custom health checks and metrics (spring-actuator). We are finding little bugs/oddities as we work our way through upgrading the microservices, as different services use different libraries, and in different ways. One particular bug that proved challenging was that the entire management context was not starting up, and after much investigation it proved to be an AutoConfiguration class that should have been removed as part of one of the library upgrades.

Modules – planning ahead

The module system (project Jigsaw) in Java 9+ raises a new issue: each module’s name must be globally unique on the module path. A quick git search and you’d easily come across many examples of com.rightmove.web in our earlier microservices and libraries. We needed a naming convention that would guarantee uniqueness, even with 15 development teams writing new services.

A small group of us discussed the potential options and came up with a proposal to take to the architects. We’d agreed on using reverse DNS, as follows:

//For libraries
com.rightmove.tools.mylibrary.*
  
//For services
com.rightmove.myapplication.*

We felt that it would make sense merging the work to rename packages with the Spring Boot 2 changes. This would mean one major version change and ensured that applications using the Spring Boot 2 versions of the libraries could move to Java 9/10/11 when they were ready. Furthermore, teams would not be tied to running applications on the classpath. The uniqueness of the package structures in the libraries would allow teams to experiment with the module path, as the automatic modules that would be built, would not have conflicting names.

It’ll be easy, they said..

Building a new microservice from scratch using Gradle 4.10.2, Java 10 (at the time) and Spring Boot 2.X. was fairly straight forward once you get the hang of writing module-info.java files, dealing with the split package issue (where two packages with the same name exist in two libraries on your classpath) and ensuring you have the correct Gradle config. Gradle doesn’t really support the JPMS out of the box, so you need to add snippets of your code into your build.gradle for it to work, this is provided in their documentation. In my opinion, at the time of writing, it’s best to avoid the plugins that are listed as alternatives. Development on the ‘experimental’ Jigsaw plugin that is provided by Gradle seemed to have been dropped fairly early on (June 2017). The alternative that initially proved useful, the Chainsaw plugin, is no longer supported and fails as soon as you try to change the version of Java to anything that is newer than 9. Hopefully native support for Jigsaw will come at some point in 2019.

So, what should you do?

Different companies will have different challenges in embracing new versions of Java, but hopefully the quicker release cycle will encourage changes to be ‘little and often’, like everything else in a microservice development environment is moving towards. If you’re able to take the time to look into what would be involved in making the jump, take it. The developers on your team, and future you, will appreciate being able to use the latest technology, and there will come a point where there’s something you need to use, and you’re blocked by being on an unsupported version of Java/Spring Boot so you’ll be kicking yourself for not being proactive.

Leave a Reply