Java: Can't get a working ServiceLoader
Asked Answered
C

0

0

I am very new to Java (coming from C# .NET background). Trying to create an "expansion/IoC" style architecture via a combination of Google Guice and ServiceLoader, but can't seem to get the ServiceLoader part working running Java 1.7.0_75 and IntelliJ IDEA 14.

I have a module within my project which is effectively a base class library called ViProJ.Bcl that contains an interface called com.vipro.bcl.SimplePluginLoader.

I have another module called ViProJ.TestModules which contains a single implementation of SimpleModuleLoader called com.vipro.test.TestModule1.

Within my src folder is a resources folder (marked as a resource within the module settings screen) that contains a folder called META-INF.services that contains a single file called com.vipro.bcl.SimpleModuleLoader. The value within this file is com.vipro.test.TestModule1.

I have two artefacts being created (into same directory) by IntelliJ, the first is a module with a Main function that runs from the command line. It loads service loader like so:

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

The other is the aforementioned test library. When I debug this, the service loader contains no classes, so the exporting of the class has not worked.

The two JARs in a single artefact folder

When I open the jar file in 7zip, I can see META-INF\services\com.vipro.bcl.SimpleModuleLoader so IntelliJ has correctly packaged these files up.

"services" resource folder got copied in successfully

The MANIFEST.MF file looks like this:

Manifest-Version: 1.0
Export-Package: com.google.inject.name;version="1.3",com.google.inject
 .binder;version="1.3",com.google.inject.spi;version="1.3",com.google.
 inject.matcher;version="1.3",com.google.inject.util;version="1.3",com
 .google.inject;version="1.3"
Bundle-Name: guice
Created-By: 1.6.0_23 (Sun Microsystems Inc.)
Bundle-RequiredExecutionEnvironment: J2SE-1.5,JavaSE-1.6
Bundle-Copyright: Copyright (C) 2006 Google Inc.
Ant-Version: Apache Ant 1.7.1
Bundle-Vendor: Google, Inc.
Bundle-Version: 3.0
Bundle-ManifestVersion: 2
Bundle-Description: Guice is a lightweight dependency injection framew
 ork for Java 5 and above
Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
Bundle-SymbolicName: com.google.inject
Import-Package: javax.inject,org.aopalliance.intercept
Bundle-DocURL: http://code.google.com/p/google-guice/

Not really sure why the manifest is talking about Guice and not my module? Probably not causing this to fail though? Can anyone tell me why this isn't working? I'm sure I haven't given you everything you need, but not really sure what else I should include here?

The modules Module is using an .impl file for building instead of a pom.xml (Maven), this is what that looks like:

<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
  <component name="NewModuleRootManager" inherit-compiler-output="true">
    <exclude-output />
    <content url="file://$MODULE_DIR$">
      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/src/resources" type="java-resource" />
    </content>
    <orderEntry type="inheritedJdk" />
    <orderEntry type="sourceFolder" forTests="false" />
    <orderEntry type="module" module-name="ViProJ.Bcl" />
    <orderEntry type="library" name="guice-3.0" level="project" />
    <orderEntry type="library" name="guice-multibindings-3.0" level="project" />
  </component>
</module>

My injection code uses a MultiBinder, like this:

// dynamic modules via service loader
Multibinder<IModule> modBinder = Multibinder.newSetBinder(binder(), IModule.class);
ServiceLoader<SimpleModuleLoader> loader = ServiceLoader.load(SimpleModuleLoader.class);
for (SimpleModuleLoader moduleLoader : loader) {
     for (Class<? extends IModule> moduleClass : moduleLoader.getModules()) {
        System.out.print("Found expansion module");
        modBinder.addBinding().to(moduleClass);
     }
}
Caning answered 11/3, 2015 at 10:23 Comment(5)
Can you share your Maven pom.xml file? Is your intention that you will use ServiceLoader to find modules and use those modules to build an Injector? Have you seen the example of Using Multibindings to host Plugins in the Guice documentation?Different
Hmmm don't seem to have a pom.xml file here? Have included the impl file instead as I think this is an IntelliJ equivalent?Caning
I think part of the reason why it's crickets here is because you're asking about the intersection of several different technologies. Perhaps you should split this into two questions: (1) "How do I build a plugin system using Guice and a ServiceLoader?"; and (2) "How to I get my build system set up so that ServiceLoader works?". I think #2 is the one you're more interested in.Different
Expanding on that: it would be helpful if you could set up your project to build from the command line (for example, using a tool like Maven). I use IntelliJ myself, but I don't use its project files directly, I use Maven (and proprietary tools at my current job). The weird contents of the MANIFEST.MF file and the difficulty making ServiceLoader work indicate that the problem likely lies with your build system, not with your code. Basically, can you create an MVCE that doesn't use Guice at all?Different
Thanks Daniel, I think I'll modify the question somewhat - a MVCE as you call it! ThanksCaning

© 2022 - 2024 — McMap. All rights reserved.