The Service Locator Module Pattern in a Nutshell

Phil Larkin / Software Engineer | 15/08/2022

Java, The Service Locator and Modules

One of the frustrations I faced whilst becoming Java certified, was the questions on the service locator design pattern and its use with Java’s Module system.

I found precious little resources on this combination, and none outside the official study guide that used the same terminology as the official practice exams.

Even guides to the module system that I found, were wrapped in their own example code, thus difficult to extrapolate to Oracle’s example of the Service Locator.

What I’d like to cover in this insight, is a simple example of the Service Locator design pattern implemented using modules, to give you a helping hand for the Java 11 certification exam.

Some Quick Definitions

  • Service Locator - Service locator is a design pattern that allows a service to be decoupled from its consumer by using a “service locator” to retrieve it. In Java an interface or abstract class is used to define the structure of the service. So the locator code will return an interface, an abstract class or a collection of interfaces/abstract classes. For simplicity I’ll just be using interfaces in the text below.

  • Java Modules - The Java Platform Module System was introduced in Java 9 as a way of increasing encapsulation between Jars. A module is simply a jar with a special descriptor file ‘module-info.java’ in its root. In order for a module to be treated as such, it needs to be on the module path, not the class path. I’ll cover this a little later, when I get to compiling and running my code.

  • Directives - The individual lines of the ‘module-info.java’ are referred to as directives. Here’s a quick description of each of them.
    • exports - Describes which packages can be read externally.

    • exports… to… - Reduces the scope of the exports directive to a list of modules.

    • opens - Allows access to the declared package via reflection.

    • requires - Used to declare a module that is needed by the current module.

    • requires… transitive - Specified that any module that needs the current module, also needs the declared module.

    • provides… with… - This statement declared that the produced class is implemented by the concrete class after the ‘with’ clause.

    • uses - This module uses a given interface and can locate concrete implementations.

The Way The Book Has It

I’ve put together an example on Github where you can see a full implementation of the design pattern using the module system, with all the modules named as they are referred to in the book, but let me talk through them here.

The Service Provider Interface

This is the module that contains the interface that shall be provided. Which means that it ‘exports’ said interface and all the other modules need a statement for ‘requires’ this module.

module serviceProviderInterface{exports thepackage.interfacepackage;
}

The Service Locator

This is the module that includes the code to locate implementations of the interface. It’s the only module that has the ‘uses’ directive, but don’t let that fool you in the exam. it also needs access to the service provider interface and it ‘exports’ the package containing the service locator code.

module serviceLocator{
requires serviceProviderInterface;
uses thepackage.interfacepackage.ServiceProviderInterface; exports thepackage.locatorpackage;}



It is assumed here that the ‘thepackage.locator package’ contains a method somewhere which uses the ServiceLoader class and returns the interface using the following code somewhere:

ServiceLoader<ServiceProviderInterface> loader = ServiceLoader.load(ServiceProviderInterface.class);


I did this using some simple static methods, which are not much removed from the example in the book.

The Consumer

The consumer module ‘requires’ both the Service Provider Interface and the Service Locator modules.

module consumer {
requires serviceLocator;
requires serviceProviderInterface;
}


Of course without an implementation the ServiceLoader will never return anything, but nothing that has come so far has needed to reference the implementation.

The Service Provider

The service provider module ‘provides’ the interface ‘with’ a concrete class. So if you see the ‘provides’ directive think Service Provider, but once again don’t let the exam fool you. This module also needs access to the interface module.


module serviceProvider{
requires serviceProviderInterface; provides thepackage.interfacepackage.ServiceProviderInterface with thepackage.providerpackage.ServiceProvider;
}

Running the Code

The modules are compiled using ‘javac’, packaged using the ‘jar’ command and placed in a common folder, to be used as the module path. Then the java command needs to specify the module path and the location of the main method:

java -p mods -m consumer/thepackage.consumerpackage.Consumer

I wrote a bash script to do all this for me, if you are unsure of the correct arguments. Saying that, the exam expects you to know how to use the command line, when it comes to the module system. I just don’t feel I have the word count to cover that topic here. Sorry!

Combining Modules

I can cover another point or two I can for the exam though. Firstly, the Service Provider Interface and the Service Locator are considered to be parts of the Service. The Service Provider itself is not. Secondly, you are expected to know what the above pattern looks like if you were to combine any two of the modules into one. What needs to be remembered is that you don’t need to export anything that is only used internally to the new module.



For my example I combined the Service Provider Interface and the Service Locator into one module that I called the Service.

module service{

exports thepackage.interfacepackage; uses thepackage.interfacepackage.ServiceProviderInterface; exports thepackage.locatorpackage;
}

I needed to tweak my lovely bash script, but it all worked.

A Star Wars Example

All the other blogs I read didn’t rely on examples out of pure foolishness. Examples add context. Considering how dry everything I wrote so far is, I feel the need to add some space fantasy to the mix. So, back on github, I took my original 4 module example and replaced all the names with more thematic ones:

Old Module Name

New Module Name

serviceProviderInterface

rebelHero

serviceLocator

rebelHeroLocator

consumer

rebelRecruiter

serviceProvider

heroProvider


I also replaced all the package and class names, but I don’t feel that illustrates anything beyond my own descent into madness. The bash script to package and run it simply runs it three times to illustrate the recruitment of three Jedi to the rebel cause!


##### Running the Consumer #####

We have recruited: Luke Skywalker

We have recruited: Corran Horn

We have recruited: Yoda


You can see how we might add a new implementation of the Hero interface, say AcePilots, in the heroProvider, and modify its module-info file to allow them to be found by the rebelHeroLocator.

provides thepackage.heroInterfaces.Hero with thepackage.heros.Jedi, thepackage.heros.AcePilot;

One could even add a new module of mercenaries that mirror the structure of the given Service Provider to add a new type of anti-hero to the mix.

Passing The Exam

The Java 11 exam is full of trick questions, but when it comes down to specialised knowledge, there are fewer tricks. It’s likely that all questions you get on the module system will be straight tests of your knowledge. It’s my hope that I have given you at least four percent by writing this. Saying that, modules and the service locator pattern are not the only place where the exam uses bespoke language, so I would still recommend getting hold of the official books.

Until the next time I feel like going on a code related rant, may the force be with you.




Message icon

Explore our latest insights

INSIGHT LIBRARY

Amy Hanson / IT Consultant

26/09/2022

Carl Roberts / Managing Director

06/09/2022

Message icon

Come and help us on our mission...

Join our team