Skip to main content

Java extension mechanism — Self-discovery & bean instantiation

In the Java world, libraries are created to separate the code for cross-cutting-concerns and packaged inside jars. If a project declares these libraries as  or  dependencies, it can use the functionality provided by the library as-is.

What if, the project does want to use the library functionality, but with a small change in some behavior out of full functionality?

While designing the library, programmers carefully decide what behaviors can be modified. These small behavior changes are called extensions. It can not be achieved if the library is not coded to accommodate extensions.

In this post, we will see, .

(SPI) Service Provider Interface Pattern.

A Service is a set of Interfaces and a ServiceProvider is the concrete implementation of a Service. The service provider can be loaded at runtime by adding them to the application’s classpath. 

ServiceLoader

Java-1.6 exposed a final & Iterable class This class has a mechanism to search & load instances  that implement a given interface Extensible libraries are not the only but a highly common use case for 

In the below example, we use ServiceLoader to load the bean into a collection using plain java. Similar to what spring does in the spring context.

Code

1. Create a maven project

Open your favorite command line tool and run the following command.

mvn archetype:generate \
-DgroupId=com.mps.app \
-DartifactId=greeting \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DarchetypeVersion=1.4 \
-DinteractiveMode=false

2. Prepare for coding

Open this project in your favorite IDE & fix POM

NOTE: maven quickstart archetype is not up to date, it defaults to older versions. So you should update the pom file to have a newer java version under  & 
Also, if you want, for easy understanding you can delete the build/plugin sections from this autogenerated pom. in that case  will pick defaults.

2. Create an interface (Service)

package com.mps.app;

public interface Greeting {
String helloGreeting();
}

3. Add an extension mechanism

Create a class  with the below code

package com.mps.app;

import java.util.Iterator;
import java.util.ServiceLoader;

public class Greeter {

public static void greet() {
Greeting greetingProvider = getGreetingProvider();

String greeting = (greetingProvider != null)
? greetingProvider.helloGreeting()
: getDefaultHello();

System.out.println("Greeting = " + greeting);
}

private static Greeting getGreetingProvider() {
ServiceLoader<Greeting> loader
= ServiceLoader.load(Greeting.class);
Iterator<Greeting> iterator = loader.iterator();

if (iterator.hasNext()) {
return iterator.next();
}
return null;
}

private String getDefaultHello() {
return "Hello World!";
}

}

Take a closer look at the method .
This method searches for all Greeting interface implementations and returns the first one. if any.

4. Main class

For clarity's sake, rename the App.java file created by maven to GreetApp.java
Use the below code to call 

package com.mps.app;

public class GreetApp {
public static void main(String[] args) {
new GreetingInvoker().invokeGreeting();
}
}

When you run , it prints 

5. Install the library

Let’s package this code into a library. And to make this library available for other projects on our local machines, let’s install it in our local m2. 
Run below one command for doing both.

Close your IDE, we are done with this project.

Now we’ve completed creating our extensible library. let's see how to use it.

Create another maven project.

mvn archetype:generate \
-DgroupId=com.mps.app \
-DartifactId=special-greetings \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DarchetypeVersion=1.4 \
-DinteractiveMode=false

2. Open this in your favorite IDE and fix the POM file as previously stated.

3. Add the library as a dependency

Add the below line under the section in pom

<dependency>
<groupId>com.mps.app</groupId>
<artifactId>greeting</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>

4. Use library functionality

Add this piece of code in  created by Maven

package com.mps.app;

public class App {
public static void main(String[] args) {
new Greeter().greet();
}
}

When you run this program, it prints 

This is a default functionality provided by this library. 
However. we envisioned, that different applications might want to greet in different manners. So we provided an extension point in our library.

5. Create out ServiceProvider

To use the extensibility of our library, Create a class implementing the  interface as below.

package com.mps.app;

public class SpecialGreeting implements Greeting{

@Override
public String helloGreeting() {
return “Special Hello !";
}
}

Now when you run App.java what happens?
It prints  not 

6. Special Handling for ServiceLoader

Unlike Spring ContextLoader, java ServiceLoader doesn’t scan the whole classpath without instructions. And the instructions are, 
- The file that is placed inside the folder META-INF/services/ 
The filename should be the fully qualified name of the Service (interface)
- The file content should be the fully qualified name of ServiceProvider/s.

As we are working on a maven project, everything inside the  folder is packaged under the classpath. So let's create folder  and add a file  in META-INF/services/. 
Refer below picture

Now when you run App.java It prints, 

This is how we can use the ServiceLoader class to create Extensible libraries.

With spring this can be achieved in a lot easier fashion, but in that case, you will be tied to a framework. In my SpringBoot Magic post, I’ve explained the custom starter projects which achieve similar functionality. but code is highly coupled to spring.

If you learned something new, feel free to show your response as a comment or clap on this post.

Comments

Popular posts from this blog

An Introduction to Quartz Scheduler

It's a common use case to have an enterprise application, perform specific work, at a specific time or in response to a specific action. In other words, “There is an ask to execute a  Job  upon a predefined  Trigger ”. This brings us to the need for a  Scheduling System.  A system, where  Jobs  &  Trigger  can be registered and the system will manage the remaining complexity. Thankfully for the Java systems,  Quartz  is for rescue. It‘s an open-source library that has been extensively used in enterprise applications for more than a decade. Components in Quartz Sub System: Following are the all major component in the Quartz subsystem: Scheduler : It’s the control room of Quartz. It maintains everything required for scheduling,  such as managing listeners ,  scheduling jobs , clustering, transactions & job persistence. It maintains a registry of  JobDetails ,  Listeners  &  Triggers , and executes Job & Listeners when their associated Trigger is fired. SchedulerFactor

Unable to Redo in VS-Code & Intellij

Since the beginning of personal computers, few keyboard shortcuts are common among all operating systems and software. The ubiquitous cmd+c (copy), cmd+v(paste) , cmd+z (undo) and cmd+y (redo) I am not sure why, both of my favorite IDEs,  Visual Studio Code  &  Intellij  decided to not use  cmd+Y for redo.Below are the quick steps to configure  cmd+Y for a redo in VS-Code & Intellij Visual Studio Code Open VS Code & Go to keyboard shortcuts There will be a search bar at the top Type “  redo  “ in the search bar. You can see on my system its still mapped to  shift+cmd+z Double click on  ⇧ ⌘ z  and the below box will appear. Do not click anywhere or type anything on the keyboard except the key you want to assign, in our case it was  cmd+y,  so type  cmd+y Press Enter and you are done. Now you can use  cmd+z  for undo and  cmd+y  to redo like always Intellij It is also as simple as VS-Code Open IntelliJ & go to  Preferences In the search bar search for the  redo. In the ri

My Custom Built Desktop. The Questions & The Answers!

If  you want to avoid overpriced pre-builts like the M1 Mac Mini, Mac Pro, or Dell XPS Desktop without compromising on performance, a self-built desktop is a preferred option. It's also a great choice if you enjoy building things. custom built with ASUS-PRIME-P If you choose to build a custom PC, be prepared to invest time in researching and assembling compatible components.  In this post, I'll share my experience building this colorful powerhouse. I'll cover: Why did I do it.  Key questions to ask when selecting components Thought process behind component choices Components used in my build Benchmark comparisons . ** My second custom-build **.  ***  Disclaimer: Not an Apple product. Just a free apple sticker is used *** Why did I do it I decided to get a desktop during the pre-MacM1 era (yes, that’s a thing). After browsing many websites, I found that well-configured prebuilt PCs were overpriced, while cheaper ones had subpar components. Unable to choose between the option

Time Zones, Meridian, Longitude, IDL… It's more politics than science.

Once, I was working on a few geospatial APIs handling many time zones. While writing tests, I realized I did not know much about timezones. A lame excuse might be, my subpar schooling as a village kid. Nevertheless, I decided to turn the pages on timezones, what I found was more politics than science. Photo by  Arpit Rastogi  on  Unsplash Before diving into anomalies, let’s talk about history then we will go to science followed by politics. History The world without time zones By 300 BCE, the western world agreed that the earth is round. Each developed civilization devised its unique distinct system to measure distances, times & absolute locations, but relative to prime locations within their civilizations. It all worked in ancient times because long-distance travel was not prevalent among common people. Only merchants or armies traveled long distances. And they already developed systems that worked on their predetermined routes, irrespective of the time differences between locatio

BDD (1) — Behavior Driven Development

A wise man ( narcissist me ) once said, “Life is all about the question and answers. The trick to a meaningful life is,  To ask the right questions to yourself, so you can get on the right path to search for the answer .” The very first question one should always ask oneself is WHY.  Let's discuss our WHY in the current case. Why BDD Let's take a step back and start with the well-known software development practice TDD ( Test-Driven Development).  In TDD, the very first thing developers do is, set up the technical expectations from the code by writing failing test cases. After the expectation is set, the code is written/modified to finally pass all of the failing tests. It's an  Acceptance driven development strategy . TDD works fine to create a robust technically working product. But the whole TDD approach revolves only around technical teams. It barely involves the business analysis or product owners to validate the business aspect of a feature, they get involved only aft