Wednesday, 10 April 2024

Discover and load the implementations of a service using ServiceLoader in Java

The java.util.ServiceLoader class in Java provides a simple mechanism for discovering and loading implementations of a service interface from the classpath. It is part of the Java Standard Library and is often used in modular or plugin-based applications where different implementations of a service interface may be available.

 


Here's how ServiceLoader works:

 

1. Service Provider Interface

First, you define a service interface that represents a contract or API. This interface defines the methods or behaviour that implementations must adhere to.

                                    

To demonstrate the example, I defined a log-service project, and it has LogService interface.

 

LogService.java

package com.sample.app.log.service;

public interface LogService {
	void log(String msg);
}

 

2. Service Provider Implementations

Next, you create one or more implementations of the service interface. These implementations are typically packaged as JAR files and made available in the classpath.

To demonstrate the example, I defined two libraries sdk1, and sdk2 which implement LogService interface.

 

Example

 

public class Sdk1Logger implements LogService {

	@Override
	public void log(String msg) {
		System.out.println("From Sdk1Logger " + new Date() + " : " + msg);
	}

}

 

3. Service Configuration File

Include a special file named META-INF/services/{fully-qualified-service-interface-name} in the JAR files containing the implementations. This file lists the fully qualified class names of the service provider implementations, one per line.

 

com.sample.app.log.service.LogService

com.sample.app.sdk1.log.service.Sdk1Logger

 


4. Service Loading

At runtime, you use the ServiceLoader class to dynamically load implementations of the service interface. The ServiceLoader class reads the service configuration files from the classpath and instantiates the service provider implementations.

ServiceLoader<LogService> logServicesLoader = ServiceLoader.load(LogService.class);

5. Iterating Over Implementations

You can then iterate over the loaded service provider implementations using the iterator() method of ServiceLoader. This returns an iterator that provides access to the instantiated service provider objects.

Iterator<LogService> logServiceIterator = logServicesLoader.iterator();

while (logServiceIterator.hasNext()) {
	LogService logService = logServiceIterator.next();
	logService.log("Hello World");
}

Find the below step-by-step procedure to build the working application.

 

log-service project setup

Create new maven project ‘log-service’ and update pom.xml like below.

 

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.sample.app</groupId>
	<artifactId>log-service</artifactId>
	<version>1.0.0</version>

	<build>
		<plugins>


			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<version>3.0.0-M6</version>
			</plugin>


			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-release-plugin</artifactId>
				<version>2.5.3</version>
				<configuration>
					<localCheckout>true</localCheckout>
					<autoVersionSubmodules>true</autoVersionSubmodules>
				</configuration>
			</plugin>

		</plugins>
	</build>
</project>

Define LogService interface.

 

LogService.java

package com.sample.app.log.service;

public interface LogService {
	void log(String msg);
}

Setup sdk1

Create sdk1 maven project and update pom.xml like below.

 

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.sample.app</groupId>
	<artifactId>sdk1</artifactId>
	<version>1.0.0</version>

	<dependencies>

		<dependency>
			<groupId>com.sample.app</groupId>
			<artifactId>log-service</artifactId>
			<version>1.0.0</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>


			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<version>3.0.0-M6</version>
			</plugin>


			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-release-plugin</artifactId>
				<version>2.5.3</version>
				<configuration>
					<localCheckout>true</localCheckout>
					<autoVersionSubmodules>true</autoVersionSubmodules>
				</configuration>
			</plugin>

		</plugins>
	</build>
</project>

Define Sdk1Logger class.

 

Sdk1Logger.java

package com.sample.app.sdk1.log.service;

import java.util.Date;

import com.sample.app.log.service.LogService;

public class Sdk1Logger implements LogService {

	@Override
	public void log(String msg) {
		System.out.println("From Sdk1Logger " + new Date() + " : " + msg);
	}

}

Create a file 'com.sample.app.log.service.LogService' in src/main/resources/META-INF/services folder of sdk1 project.

 

com.sample.app.log.service.LogService

com.sample.app.sdk1.log.service.Sdk1Logger

sdk2 project setup

Create a new maven project sdk2 and update pom.xml with maven dependencies.

 

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.sample.app</groupId>
	<artifactId>sdk2</artifactId>
	<version>1.0.0</version>

	<dependencies>

		<dependency>
			<groupId>com.sample.app</groupId>
			<artifactId>log-service</artifactId>
			<version>1.0.0</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>


			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-surefire-plugin</artifactId>
				<version>3.0.0-M6</version>
			</plugin>


			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-release-plugin</artifactId>
				<version>2.5.3</version>
				<configuration>
					<localCheckout>true</localCheckout>
					<autoVersionSubmodules>true</autoVersionSubmodules>
				</configuration>
			</plugin>

		</plugins>
	</build>
</project>

Define Sdk2Logger class.

 

Sdk2Logger.java

package com.sample.app.sdk2.log.service;

import com.sample.app.log.service.LogService;

public class Sdk2Logger implements LogService {

	@Override
	public void log(String msg) {
		System.out.println("From Sdk2Logger : " + msg);
	}

}

Create a file 'com.sample.app.log.service.LogService' in src/main/resources/META-INF/services folder of sdk2 project.

 

com.sample.app.log.service.LogService

com.sample.app.sdk2.log.service.Sdk2Logger

Main project setup

Create new maven project ‘service-loader-demo’. Update pom.xml with maven dependencies.

 

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.sample.app</groupId>
	<artifactId>service-loader-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<dependencies>

		<dependency>
			<groupId>com.sample.app</groupId>
			<artifactId>sdk1</artifactId>
			<version>1.0.0</version>
		</dependency>

		<dependency>
			<groupId>com.sample.app</groupId>
			<artifactId>sdk2</artifactId>
			<version>1.0.0</version>
		</dependency>


	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-jar-plugin</artifactId>
				<version>3.2.0</version> <!-- Use the latest version -->
				<configuration>
					<archive>
						<manifest>
							<addClasspath>true</addClasspath>
							<mainClass>com.sample.app.App</mainClass>
						</manifest>
					</archive>
				</configuration>
			</plugin>
		</plugins>
	</build>


</project>

Define main application class.

 

App.java

package com.sample.app;

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

import com.sample.app.log.service.LogService;

public class App {
	public static void main(String[] args) {

		ServiceLoader<LogService> logServicesLoader = ServiceLoader.load(LogService.class);

		Iterator<LogService> logServiceIterator = logServicesLoader.iterator();

		while (logServiceIterator.hasNext()) {
			LogService logService = logServiceIterator.next();
			logService.log("Hello World");
		}
	}

}

Build log-service artifact

Navigate to pom.xml file of log-service and execute below command.

mvn package

 

Build sdk1 artifact

Navigate to pom.xml file of sdk1 and execute below command.

mvn package

 

Build sdk2 artifact

Navigate to pom.xml file of sdk2 and execute below command.

mvn package

 

Build service-loader-demo project

Navigate to pom.xml file of service-loader-demo and execute below command.

mvn package

 

Copy all the jar files to a folder and run service-loader-demo-0.0.1-SNAPSHOT.jar file to test the application.

$ java -jar service-loader-demo-0.0.1-SNAPSHOT.jar 
From Sdk1Logger Wed Apr 10 20:09:20 IST 2024 : Hello World
From Sdk2Logger : Hello World

You can download this application from this link.



 

You may like

Miscellaneous

How to Get Client Certificate from HttpServletRequest

Programmatically import certificate to cacerts file in Java

Command to check CPU temperature in Mac

Quick guide to Java DecimalFormat class

Compress and decompress a string in Java

No comments:

Post a Comment