Skip to content

Extend Base Services in Projects

Heiko Scherrer edited this page Sep 16, 2019 · 1 revision

Issue

The challenge in a customer project is often to extend an existing service with functionality but without touching the sources of an particular OpenWMS.org base microservice. In an ideal scenario the existing microservice binary bundle remains untouched and just needs a bit of external configuration - like a 12-factor microservices promises to be.

Solution

The solution is to implement project specific logic in an own custom module and package it as a separate JAR file. The OpenWMS.org microservice is started up and the project specific module is added to the classpath of the microservice, so that all custom beans are registered as part of the main application.

Usually SpringBoot microservices are packages in a ZIP file with a SpringBoot specific directory layout, hence I don't call it JAR file here even it has the jar extension. Only classes and jar files packaged inside this ZIP file are accessible at startup time. This is realised with a special starter class and a classloader switch that happens when starting up the process behind the scenes and you don't take notice of.

A lot is possible with SpringBoot, so is the startup mechanism extensible. By using a different Loader implementation and setting some startup parameters we can get SpringBoot to what we want to.

Example

A project extension should heavily rely on the persistent model and use the services and repositories of the OpenWMS.org COMMON Service. It is not an option to use the COMMON service over the REST API because integration needs to be done on entity level. Additionally the custom services extends the REST API of the service and provides a couple of operations to new provided resources. Those operations should be available for Java clients as part of a Feign interface.

We define a new project module that defines a Maven provided dependency to the OpenWMS.org COMMON Service. Now we have all service implementations, repositories and entities available at compile time and we can implement project specific functionality.

At runtime we need to start the OpenWMS.org COMMON service differently like we did before:

java -cp openwms-common-service-exec.jar -Dloader.main=org.openwms.common.CommonStarter -Dloader.path=project.jar org.springframework.boot.loader.PropertiesLauncher

We basically don't start the process in the standard way as JAR but point to the PropertiesLauncher class that contains the main method to start. As classpath parameter we just point to the OpenWMS.org COMMON Service fat jar because it has all classes and jars needed to start and add two additional parameters:

loader.main: The SpringBootApplication annotated class of the COMMON Service loader.path: Points to the project specific JAR file or a directory that contains files to include on the classpath

By doing so the project specific services, repositories and other beans are picked up by the root application and custom controllers are exposed over the COMMON API.

But Feign?

Exposing Feign clients is not that simple, because Feign client configuration does not support wildcards in component scanning and requires a listing of the packages to scan for FeignClients. It would be nice to define a pattern inside a client application with wildcards like org.openwms.**.api.*. Then our project specific modules could easily follow this convention and put its additional FeignClient interfaces onto the classpath - well a client can still scan for all classes in org.openwms but this is probably not the best solution.

But wildcards are not supported and the extension must therefor put its FeignClients into a package that is picked up by the hosting COMMON Service, for example into org.openwms.common.location.api.