Migrating our apps to microservices on Open Liberty
Our journey starts
A year ago we started to migrate our aging applications from a central Java EE platform to a new OpenShift platform. The goals were to promote the autonomy of the development team, to isolate the applications from each other, and to improve our development process as a whole.
We checked several MicroProfile app servers and soon concluded with certainty that Open Liberty has an active and well-structured community, a production-ready product, and also supports our desire for agile work according to SAFe with monthly release cycles.
Our plan was to migrate four existing applications using the appropriate tools to efficiently develop and meet the requirements for productive use. However, we could not start on a greenfield site but had to consider and integrate into the existing landscape.
Our principles during this journey were to design first using OpenAPI v3 and to reuse as much as possible of existing frameworks and platforms as long as it makes sense in a microservice world. Whereever possible, we aimed to use standards, best practices, and a straightforward implementation strategy without compromising well-known enterprise architectural rules (e.g. TOGAF).
Application 1: Using other microservices
Our first application to migrate was a service to check if an employee is still employed by us in order to allow him to buy train ticket in the name of our company. This application provides two web services according to the interface description of our external partner. This was actually the contract of the service which we did not want to break, so we had to use web service technology for providing the service to our partner. It was a good use case to provide web services. Creating web services is very easy using Open Liberty: just add the jaxws-2.2
feature within the server.xml
configuration. The implementation of the web services in turn uses microservices of another application that provides organizational and employee data (Application 2). Great use case for using Microprofile Rest Client as an easy and type-safe approach to invoke other microservices.
Application 2: Using persistence
This second application provides organization and employee data. Actually it was one of the oldest applications, going back almost 30 years. It started as Cobol, later on mixed with Java Business Interfaces. We then added some web services, and finally redesigned the persistence layer using JPA instead of native SQL queries. Last year, we ended up with some new requirements that the underlying data model could not handle well. The app grew and the amount of unused functionality and complexity was far greater than the effort of providing well-designed new services from scratch. So we decided not to migrate this app and, instead, do it all new.
The data storage is in an MSSQL database and Eclipselink/JPA was used for access. The REST interfaces were designed with OpenAPI v3; an OpenAPI generator was used to generate the corresponding Java objects of the interface. Open Liberty provides this functionality with the jpa-2.2
, ejblite-3.2
, and mpOpenAPI-1.1
features. With this last feature, Open Liberty provides a handy UI for developers exploring the microservice functionality. We were able to reuse logging (Apache Log4j 2) and exception handling from the existing framework.
Application 3: Using existing landscape
The third application provides currency rates for other applications. The use of web services for access to our SAP systems and the currency service supplier was a special feature of this application. This time we did not provide web services; instead we reused existing web services. The data is retrieved in batch mode during the night and made available to other legacy applications. Our company central batch scheduler calls a microservice using a curl
command, which then receives a ZIP file containing all the necessary files for further distribution. This last step was already part of the old solution and did not have to be adapted. Finally, because of some security requirements, we used the mpJwt-1.1
and jwt-1.0
features, along with appSecurity-3.0
, to get some authentication and authorization functionality out-of-the-box.
Application 4: Using files
The fourth application was all about files: it checked PDF files for PDF/A-2b conformity, converted any formats (such as MS Office documents, emails, and images) to PDF/A-2b format, and assembled several PDF files into one PDF file. JMS queues were used to meet the high speed and throughput requirements as well as the loose coupling principal, allowing parallel execution of incoming requests; this functionality was provided by using the jms-2.0
, wasJmsClient-2.0
, wasJmsServer-1.0
, and mdb-3.2
features.
The interface was designed with OpenAPI v3. This time we used multipart/form-data
encoding for the documents transfer. We have reached the limits of the JAX-RS standards that we used. Since the use of multipart/form-data
is not yet standardized, the necessary Java objects could not simply be generated with the OpenAPI generator for use by Open Liberty. The JAX-RS community is working on extending the standard to support multipart/form-data. Once this is done, the OpenAPI generator can be adapted accordingly and, hopefully, it will be possible to use a standardized interface. Today, we have to use the IMultipartBody
interface in Open Liberty.
Overall experience so far
During our implementation and testing efforts we noticed the outdated version of OpenAPI UI (aka Swagger UI) provided by Open Liberty. There are newer versions to support the use of multipart/form-data
and the upload of files. This would allow us to give the developers a tool to access the respective microservices to get familiar with the interface using the "Try it out" feature. Reporting this as an issue to the community we got attention and this should be fixed soon.
In addition to the actual migration of the applications, we have built a CI/CD pipeline that allows us to deploy our microservices to an existing OpenShift Container Platform easily and efficiently. Each application consists of an Open Liberty server in a Docker container. Depending on scalability and reliability considerations, one or more pods are started at runtime. The time needed to start a pod in our environment depends how many cores of CPU are defined to a pod. At the moment we manage to start a pod within 10 seconds, quick enough for our use cases. This is thanks to the low memory footprint, the internal parallelized tasks and reduction of path length of Open Liberty.
With the use of MicroProfile Health and MicroProfile Metrics, we get tools that enable us as a team to take over the operational responsibility for our web services. Our experience so far shows that Open Liberty is very stable and easy to use as long as you have Java EE experience. Our applications run for weeks without restarts; previously we had to restart our central Java EE platform weekly. Open Liberty is absolutely production ready.
With our decision to use Open Liberty we did very well. I am confident that we can achieve our goal of migrating the remaining applications that we are responsible for within the coming year.