Guice and Reflections inside Jar File

1.4k views Asked by At

I am experimenting with Google Guice (3.0) and Google Reflections (0.9.6).

I have the following files:

Operation.java:

package com.company1.calculator;

public interface Operation {
    public int apply(int a, int b);
}

Addition.java:

package com.company1.calculator;

public class Addition implements Operation {
    @Override
    public int apply(int a, int b) {
        return a + b;
    }
}

Other various "operation" classes spread across multiple packages.

CalculatorModule.java:

package com.company1.calculator;

import com.google.inject.AbstractModule;
import com.google.inject.multibindings.MapBinder;
import org.reflections.Reflections;

public class CalculatorModule extends AbstractModule {
    @Override
    protected void configure() {
        Reflections reflections = new Reflections("");
        MapBinder<String, Operation> map = MapBinder.newMapBinder(binder(), String.class, Operation.class);
        for (Class<? extends Operation> o : reflections.getSubTypesOf(Operation.class)) {
            map.addBinding(o.getSimpleName()).to(o);
        }
    }
}

Calculator.java:

package com.company1.calculator;

import com.google.inject.Inject;

import java.util.Map;

public class Calculator {
    private Map<String, Operation> operations;

    @Inject
    public Calculator(Map<String, Operation> operations) {
        this.operations = operations;
    }

    public void printCalculations(int a, int b) {
        System.out.println("Calculator: " + a + " " + b);

        for (String s : operations.keySet()) {
            System.out.print(s + ": ");
            System.out.println(operations.get(s).apply(a, b));
        }
    }
}

And finally, App.java:

package com.company1.calculator;

import com.google.inject.Guice;
import com.google.inject.Injector;

public class App {
    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new CalculatorModule());
        Calculator c = injector.getInstance(Calculator.class);
        c.printCalculations(3, 3);
    }
}

After executing App.java inside of IntelliJ I get the following output, as expected:

Calculator: 3 3
Modulus: 0
Division: 1
Multiplication: 9
Addition: 6

However, when I package this application as a jar I only get the following output:

Calculator: 3 3

Why is this and how can I fix it? The closest thing I found was Issue 48, but that says fixed as of May 2011. Surely that would have made it into Maven by now...

1

There are 1 answers

0
idle On

I was able to reproduce this exactly as described. The issue appears to be that the default Reflections constructor attempts to find the location on the disk where each given parameter can be found. When passed "" in an IDE it finds the /bin directory, and when run from /tmp/so8780228.jar it finds /tmp/ (breakpoint at ConfigurationBuilder.java:87)

The simple workaround is to give something other than "", which gives the configuration build no useful information, and instead give something like "com.company1" which can be found in exactly one location.

Reflections reflections = new Reflections("com.company1");

In this case the ConfigurationBuilder correctly finds the path /tmp/so8780228.jar to scan and everything works as expected.

Alternatively, you could interact more with the ConfigurationBuilder and tell it to search every jar on the classpath (e.g. iterate System.getProperty(java.class.path) and add each), but performance will suffer.