Looking at the technology choices involved in using the new Liberty Starter to build a microservice from scratch to evolve a monolithic app to microservices

In my previous article, An evolved image service for Plants by WebSphere, I outlined a series of evolutionary stages that the new Image microservice will go through as it moves to being hosted in the cloud.

The stages are not prescriptive and neither is it mandated that the final service will reside in the cloud. The breakdown is intended to allow you to see the approach being taken in more detail and to fully understand the costs and benefits involved at each stage. The idea is to provide you with a series of re-usable concepts and code that can quick start your own microservice evolution. This article is going to examine the first stage.

New service, same data

This first stage will involve creating a new service. The service is a new API that will be called by the existing application but it will still reference the same data. This allows us to focus on the external characteristics of the service, what technology to choose and how the service is exposed, but leave internal considerations such as data management to a later stage.

It is important to get this stage right because these externals are what you are going to wire the monolith up to. Even more so if it will be a general-purpose service that new clients will connect to as well. Microservices bring the benefit of rapid deployment but remember that one of the costs is in operational complexity. Changing all those service API calls at a later stage can be an expensive operation. Some time spent here will potentially save you a lot of time later on.

The diagram below shows at a high level how we are going to split out the Image service but leave it sharing the same database as the monolith uses. The Plants By WebSphere application has explicit knowledge of where this new service can be found (something that we change in a later evolutionary stage) but is no longer responsible for serving images. The Image service will contain the business logic taken from the existing application and add a new REST API, but it will reference the existing database.


PbWMonolithToMS-EvolvedImageService

REST or WebSockets? Synchronous or asynchronous?

When exposing services, API invocations can either be of a synchronous or asynchronous nature, and that tends to favour one or other of these technologies. Synchronous calls are those where the client of the service will make a request, wait for a response and, once that is received, it will move on with some more processing with the answer. It doesn’t mean that this invocation is necessarily a blocking one because it can be carried out by a worker thread in a pool, but from a process flow perspective the client is waiting for a response.

Asynchronous calls, on the other hand, is a more event-based or long-running, ad-hoc interaction with the service. The client invokes the API but is not necessarily waiting for the response and handles it when it comes. Furthermore, these calls may be part of a longer-running conversation that involves a series of exchanges over a period of time.

In the absence of any other deciding factors, such as technology constraints or mandates to use specific libraries, REST is a good choice for synchronous scenarios and WebSockets for asynchronous or long-running ones. With regards to the Image service, client interaction is of a synchronous nature and so it will be provided as a REST API using JAX-RS.

Quick start with Liberty Starter

The new Image service will run on WebSphere Liberty. The modular design and small footprint of the Liberty server makes it ideal for microservices. It is very quick to test and deploy a new service, is packaged with only what your service needs, but still provides access to all the latest Java EE 7 technologies.

Life has also just got even easier! We’ve just released the Liberty Starter running in Bluemix. This new service allows you to get a fully working Liberty server, configured with just the technologies you want, delivered in a simple ZIP file to your desktop. See Introducing the Liberty Starter: A tool to get you writing microservices quickly for a more detailed explanation on to use Liberty Starter to generate an application.

For the Image service, select the REST and Persistence technology options, as shown below:


PbWMonolithToMS-LibertyStarter

If you want to complete this service using the artifacts provided by Liberty Starter, see Creating the Image Service for Plants by WebSphere using Liberty Starter for a full tutorial.

Defining the REST API

The REST API for the Image service will initially be based on the URLs that are currently embedded in the client HTML pages. These effectively dictate the minimum set of operations that will be required, and are used to form the basis of a new JAX-RS application. In general, if your evolved service is already being directly accessed by clients, then you will probably need to support these existing interactions. Microservices are deployed and updated separately, so you may need to expand the API to support management functions or extend it to provide new functions. If you don’t want to keep the existing API, you need to consider what happens to existing clients that are using the service and whether or not this new API will break them.

Updating the monolithic application

Once the new Image service is up and running, some changes will need to be made to the existing monolithic application to take advantage of this new service. One option is to modify the existing code directly but this may not always be possible. Being an older application, the source code may no longer be available or your organisation no longer has the business and technical knowledge to make any changes whilst ensuring that everything is still working.

Another option is to use the interception, filter, and listener capabilities that are provided by Java EE to intercept and redirect calls to the new service. These can often be deployed without code changes. Similarly, they can be removed without code changes, making it very easy to revert back to the working, albeit monolithic, system.

This raises an interesting question about how to test your evolved application to make sure that both the monolithic part and new microservice part are functioning correctly. In summary, it involves creating a test baseline for the monolithic application. This can be either using existing tests or where you write a series of tests that either pass or fail. The important point is that these tests accurately describe the current behaviour of the monolith.

As you start to introduce an evolution to microservices, then you run these tests as part of your continuous integration build and take action if you see any deviation. Remember, these tests are not necessarily intended to ensure correct behaviour; if something doesn’t pass then that’s how the application behaves today and you want to make sure that it still behaves that way when a microservice is introduced. You are going to want to fix these, but given that you may not know which clients are currently using your application and how many rely on actual behaviour, rather than documented behaviour, the first step is parity with the existing system. The article Testing out Plants by WebSphere gives an overview of how we’ve approached testing the monolithic Plants by WebSphere application and the Image service, with more detailed articles on testing to follow.

Redirection filter

One of the simplest interceptions that we can start with is a filter which is deployed in front of the monolithic application. It redirects client requests for images to the new Image service. It makes use of the redirection mechanism built into the HTTP protocol whereby a browser can be told that a resource has moved and is provided with a new location from which to retrieve it. Using a filter in this manner means that we don’t need to update any image links that are currently being embedded in the HTML served to the client.

When you consider that within a monolithic application a lot of the referencing is relative (e.g. URLs are often relative to the context root of the application), then not having to go through your entire application and change them will save a lot of time. There is also additional safety for your existing users because you’re not risking not updating something that is dynamically generated, or a bookmarked link no longer working. The diagram below shows an outline of how this filter works (the numbers indicate the HTTP verbs and response codes that are exchanged):


PbWMonolithToMS-RedirectFilter

The flow between the Plants by WebSphere and the browser is as follows:

  1. Browser requests an image (HTTP GET)
  2. Filter intercepts and sends back a 302 response code and the location of the Image service
  3. Browser makes new request to the Image service
  4. Image service returns the image

Externalisation of configuration

When redirecting the client we have to know where the new service is, or at least we do in this evolutionary stage; other stages will introduce techniques to address this such as service registries (see An evolved image service for Plants by WebSphere for a recap on the evolutionary stages for the Image service).

Given that we want to allow for this service to eventually reside in the cloud, externalising the configuration means that we can easily change the location of the Image service. It also starts to evolve the monolithic application by introducing 12-factor application concepts (see Creating a 12-factor application with WAS Liberty for more on what a 12-factor application is) because it is entirely feasible that the monolithic application may at some point also reside in the cloud.

The code snippet below from the redirection filter shows the configuration being supplied via an initialisation parameter.

 @WebFilter(filterName="RedirectFilter", 
            servletNames={"com.ibm.websphere.samples.pbw.war.ImageServlet", "FacesServlet"},
            urlPatterns="*",
            initParams={@WebInitParam(name="PBW_SERVICES_IMAGE", value="http://localhost:9081")})
  
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if(response.isCommitted() || (serviceEndpoint == null)) {
            //can't do anything as the response has already been committed
            chain.doFilter(request, response);
            return;
        }
        HttpServletResponse resp = (HttpServletResponse) response;
        HttpServletRequest req = (HttpServletRequest) request;
        if("ln=images".equals(req.getQueryString())) {
            String path = req.getServletPath();
            if(path.endsWith(".jsf")) {
                String resource = path.substring(path.lastIndexOf('/'), path.lastIndexOf('.'));
                resp.sendRedirect(serviceEndpoint + "/images/resources" + resource);
                return;
            }
        }
        if(req.getServletPath().endsWith("/ImageServlet")) {
            resp.sendRedirect(serviceEndpoint + "/images/product/inventory/" + req.getParameterMap().get("inventoryID")[0]);
            return;
        }
        chain.doFilter(request, response);
    }
  

Although this is a start to externalise the configuration, it is still bound up in the Java EE deployment mechanisms for web applications, i.e. web.xml. Future evolutions of this service will look to decouple this further through the use of JNDI and environment variables.

Java Server Faces (JSF) considerations

One of the features of JSF that Plants by WebSphere uses is libraries. In practical terms, this means that there are a number of images which are accessible from the root of the web module in the resources/images directory. When a JSF template is subsequently processed, any references to content in a library will be translated into the correct URL. The example below is a snippet from a HTML template which uses an image from the image library.

<h:graphicImage library="images" name="theme_summer_text.gif" style="width: 209px; height: 198px; border: 0px;" alt="Gardens of Summer: They all start with the right colors, and we've got them all." />
  

The filter therefore needs to also intercept calls to the image library and redirect clients accordingly.

Building and running the code

All the code shown in this article, and indeed for all future articles, is available in Github. The convention is that the master branch for any repository represents the latest state of the code. Any branches will be named to reflect the technology or architecture that they are demonstrating, with the same name being used across repositories where applicable. So, for this article you will need to do the following:

  1. Build an image service:
  2. Build the Plants by WebSphere monolithic application:
    1. Configure your WebSphere classic installation by following the instructions in the Plants by WebSphere repository
    2. Clone/fork the ImageService branch from the Plants by WebSphere repository
    3. Build using maven ($ mvn install) and copy the built EAR file from the target directory to your deployment directory as specified in the configuration instructions.
    4. Start your WebSphere classic server.

At this point you should now be able to go to the Plants by WebSphere application and have it work as normal. In order to see if the redirection is working, turn on your browser development tools and watch the requests and responses flow between the browser and the server:


PbWMonolithToMS-RedirectLog

In this article we looked at the first evolutionary stage of the Image service for Plants by WebSphere. It covered the technology choices involved and how to use the new Liberty Starter to build the service from scratch in a really quick and easy way. Evolution using interception and redirection was introduced as a way to make architectural changes without modifying the existing source code and how it provides a mechanism to try something out. Finally, we examined how testing is used to baseline your existing monolithic application to help ensure a smooth evolution to microservices. The next articles will continue the evolutionary journey as outlined in An evolved image service for Plants by WebSphere and further decouples the Image service using service registries.

Join The Discussion

Your email address will not be published. Required fields are marked *