javaurlclassloaderserviceloaderpluggable

ServiceLoader using ClassLoader pointing to different path


Have been trying this since few days and can't get it working!

I am trying to build a pluggable java application where I can run it from command line and provide plugins (jars) in a separated folder. It seems the ServiceLoader would fit my requirement but I think I need a special case where the jars are not part of the classpath whereas they are stored in a different location, and for this reason I would need to use a ClassLoder pointing its url to this file system path.

One of the plugin i want to provide to the main application is a log jar with some custom features.

Here below the code I am using , but can't get to go into the for/loop .. it means that the ServiceLoader is not able to identify/match any class implementation :

final URL u = new File("C:\\data\\myLogJar-1.0-SNAPSHOT.jar").toURI().toURL();
ClassLoader ucl = new URLClassLoader(new URL[] {u});

ServiceLoader<Log> loader = ServiceLoader.load(Log.class, ucl);
for (Iterator<Log> iterator = loader.iterator(); iterator.hasNext(); ) {
    System.out.println(iterator.next());
}
loader = ServiceLoader.load(Log.class,ucl);
for (final Log log : loader) {
    log.info("Test log");                    
}

I wish you could help! Thanks a lot

==== adding project files :

Main pluggable application :

enter image description here

    package com.company.dep.automation;

import com.company.dep.automation.pluggable.Log;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

import java.util.*;

public class Main {

    private static ServiceLoader<Log> serviceLoader;

    public static void main(String[] args) {

        final URL u;
        ClassLoader ucl = null;

        try {
            u = new File("C:\\data\\myLogJar-1.0-SNAPSHOT.jar").toURI().toURL();
             ucl = new URLClassLoader(new URL[]{u});
        } catch (MalformedURLException e1) {
            e1.printStackTrace();
        }

        ServiceLoader<Log> loader = ServiceLoader.load(Log.class, ucl);
        for (Iterator<Log> iterator = loader.iterator(); iterator.hasNext(); ) {
            System.out.println(iterator.next());
        }

        loader = ServiceLoader.load(Log.class, ucl);
        for (final Log log : loader) {
            log.info("Test log");
        }

    }

}

the "Log" plugin

The interface Log

package com.company.automation.service;

public interface Log {

    void trace(String message);
    void debug(String message);
    void info(String message);
    void warn(String message);
    void severe(String message);
    void error(String message);
    void fatal(String message);

}

Its implementation

package com.company.automation.service.impl;

import com.company.automation.service.Log;

public class LogImpl implements Log {

    @Override
    public void trace(String message) {
        log("TRACE --> " + message);
    }

    @Override
    public void debug(String message) {
        log("DEBUG --> " + message);
    }

    @Override
    public void info(String message) {
        log("INFO --> " + message);
    }

    @Override
    public void warn(String message) {
        log("WARN --> " + message);
    }

    @Override
    public void severe(String message) {
        log("SEVERE --> " + message);
    }

    @Override
    public void error(String message) {
        log("ERROR --> " + message);
    }

    @Override
    public void fatal(String message) {
        log("FATAL --> " + message);
    }

    private void log(String message) {
        System.out.println(message);
    }

}

enter image description here

=================

Adjusted the project structure as following but still doesnt work :

Main App : enter image description here

Extension app : enter image description here


Solution

  • It doesn't work because it is not the same class Log, your main method try to find implementations of com.company.dep.automation.pluggable.Log while your jar defines an implementation of com.company.automation.service.Log such that ServiceLoader.load simply cannot find anything.

    You should move the interface com.company.automation.service.Log from the extension jar to the project with your Main class and import com.company.automation.service.Log instead of com.company.dep.automation.pluggable.Log in your Main class then everything should work.