0

I have a piece of existing code that works using reflection, but I'd like to start creating the objects using dependency injection and Guice, if possible.

Here's how it currently works:

  1. Configuration (.properties) file is loaded, with a string like
    • objects=Foo,^ab..$;Bar,^.bc.$;Baz,i*
    • Note: Foo, Bar, and Baz are classes that implement MyInterface
    • Each pair has a regular expression paired with it.
  2. Input data is fed in from another source. Imagine for this example, the data is:
    • String[]{ "abab", "abcd", "dbca", "fghi", "jklm" }
  3. I then want to create new instances of Foo, Bar and Baz that are created by Guice.
    • The instances created, in this case, would be:
      • new Foo("abab");
      • new Foo("abcd");
      • new Bar("abcd");
      • new Bar("dbca");
      • new Baz("fghi");
      • "jklm" would not create any new instances, as it has no matching pattern.

Here's how it works currently (this is the best I could do sscce-wise), using reflection:

public class MyInterfaceBuilder {
    private Classloader tcl = Thread.currentThread().getContextClassLoader();

    private Pattern p;
    private Class<? extends MyInterface> klass;

    public InterfaceBuilder(String className, String pattern) {
        this.pattern = Pattern.compile(pattern);
        this.klass = makeClass(className);
    }

    private static Class<? extends Interface> makeClass(String className) {
        String fullClassName = classPrefix + className;
        Class<?> myClass;
        try {
            myClass = tcl.loadClass(fullClassName);
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("Class not found: " + fullClassName, e);
        } 

        if(MyInterface.class.isAssignableFrom(myClass)) {
            return (Class<? extends MyInterface>) myClass; 
        } else {
            throw new IllegalArgumentException(fullClassName + " is not a MyInterface!");
        }
    }

    public MyInterface makeInstance(String type) {
        if (pattern == null || pattern.matcher(type).find()) {
            MyInterface newInstance = null;
            try {
                newInstance = klass.getConstructor(String.class).newInstance(type);
            } catch (Exception e) {
                // Handle exceptions
            }
            return newInstance;
        } else {
            return null;
        }
    }
}

How can I duplicate this functionality (loading the classes dynamically at run-time, and creating exactly the matching instances) having using Guice?

durron597
  • 31,968
  • 17
  • 99
  • 158

3 Answers3

1

I'm pretty sure you cannot do this without any reflection and using only Guice. This is because Guice isn't made for such things. Guice's task is to help with dependency management, not with different strategies of creating objects (well, to some extent it is, but not as much as you need).

However, if you need to use your objects created using the information from file as a dependencies for other objects, you can do that. Just preload your objects into some kind of map, I guess something like this would do:

Map<String, MyInterface> myInterfaceMap;
// You fill it with pairs "abcd" -> new Foo("abcd"), "abab" -> new Foo("abab") etc

Then there are two possibilities present. If your set of string keys is statically known and you want to take advantage of it (e.g. inject objects with some keys into some classes, and objects with other keys into different classes), then you can pass the map to the module and create a set of bindings dynamically, using @Named binding annotation:

for (Map.Entry<String, MyInterface> entry : myInterfaceMap) {
    bind(MyInterface.class)
        .annotatedWith(Names.named(entry.getKey()))
        .toInstance(entry.getValue());
}

After this you can inject these objects as follows:

class SomeOtherClass {
    // previous 'new Foo("abcd")' object will be injected here
    @Inject
    SomeOtherClass(@Named("abcd") MyInterface interface) {
        // whatever
    }
}

If your set of string keys is dynamic, then you likely want to inspect these objects at runtime as a collection. In this case you can bind it as usual:

bind(new TypeLiteral<Map<String, MyInterface>>() {}).toInstance(myInterfaceMap);

Then you can inject it:

@Inject
SomeOtherClass(Map<String, MyInterface> interface) {
    // whatever
}

Note that, obviously, you can bind a map even if your set of the keys is static, and vice versa, i.e. you can create multiple @Named bindings even if the set of the keys is dynamic. But I think these use cases are unlikely, though.

Note that above holds only if you want to inject your objects into other objects. The example above rather easily can be modified to support injection of your objects' own dependencies. However, if neither is your case, that is, you do not want to inject your objects as dependencies and they do not have dependencies themselves, then it is likely that you do not need Guice at all for this task.

UPDATE (taking the comment into account)

Ok, you want to inject dependencies of your objects.

If your key string must be supplied to the objects via constructor, then the most simple way, I guess, will be usage of method/field injections. This way the whole process will look like this. First you create your objects as usual and then you use Injector.injectMembers() method in loop, like this:

Map<String, MyInterface> myInterfaceMap = ...;  
Injector injector = ...;  // create the injector
for (MyInterface myInterface : myInterfaceMap.values()) {
    injector.injectMembers(myInterface);
}

This is the simplest solution possible, but it requires that all dependencies of your objects are supplied through methods, not through constructors.

If your dependencies must be supplied through constructors, then things get more complicated. You will have to write a factory for your classes manually and integrate it with Guice. The factory can look like this:

public interface MyInterfaceFactory {
    MyInterface create(String name);
}

public class ReflectiveFromFileMyInterfaceFactory implements MyInterfaceFactory {
    // You have to inject providers for all dependencies you classes need
    private final Provider<Dependency1> provider1;
    private final Provider<Dependency2> provider2;
    private final Provider<Dependency3> provider3;

    @Inject
    ReflectiveFromFileMyInterfaceFactory(Provider<Dependency1> provider1,
                                         Provider<Dependency2> provider2,
                                         Provider<Dependency3> provider3) {
        this.provider1 = provider1;
        this.provider2 = provider2;
        this.provider3 = provider3;
    }

    @Override
    public MyInterface create(String name) {
        // Here you query the file and create an instance of your classes
        // reflectively using the information from file and using providers
        // to get required dependencies
        // You can inject the information from file in this factory too, 
        // I have omitted it for simplicity
    }
}

You then bind your factory in a module:

bind(MyInterfaceFactory.class).to(ReflectiveFromFileMyInterfaceFactory.class);

and then inject it as usual.

This approach, however, requires you to know in advance which dependencies your classes have.

If you do not know in advance which dependencies your classes have, then I think you can achieve what you want using private modules and something of the above, but in your case this quickly can get unwieldy. But it may be possible that you won't need to use reflection if you will use private modules.

durron597
  • 31,968
  • 17
  • 99
  • 158
Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
  • Ironically, in converting my scenario to an sscce, I removed the main problem I'm having, which is that I want to be able to inject **other, unrelated** dependencies into the constructors of `Foo`, `Bar`, etc., and in theory if Guice were doing the object creation I wouldn't even need to know what they were at the data feed step. In other words, I **don't** need to inject `Foo`, `Bar` into stuff, but I **do** need to be able to inject stuff into them. – durron597 Jun 18 '13 at 16:09
  • @durron597 Ok, that is rather simple thing to do. I will expand my answer in several minutes. – Vladimir Matveev Jun 18 '13 at 16:11
  • Well, simple to some extent. Now I'm not sure whether I can come up with the most generic possible solution, but still. – Vladimir Matveev Jun 18 '13 at 16:14
  • I upvoted you for effort, but this is still not ideal (requiring dependency knowledge), and creating the classes directly with Reflection. However, your post and some research on my part have inspired me to create my own answer, let me know what you think. – durron597 Jun 18 '13 at 17:21
0

Upon further reflection, I'm starting to wonder if I should care less about passing runtime arguments into constructors and more about using the create and configure concept mentioned in this answer. The example below has no error checking, but the actual implementation version would throw lots of NullPointerExceptions and IllegalArgumentExceptions for bad data. But here's the idea:

Basically, it would be something like:

// This could be done a number of different ways
public static void main() {
  Injector inj = Guice.createInjector(new MyOuterModule());
  Injector child = inj.createChildInjector(new MyPluginModule(/* interfaceFileName? */));
  MyApp app = child.getInstance(MyApp.class);
  app.run();
}


public class MyPluginModule extends AbstractModule {
  @Override
  protected void configure() {
    MapBinder<String, MyInterface> mapBinder
          = newMapBinder(binder(), String.class, MyInterface.class);
    // These could probably be read from a file with reflection
    mapBinder.addBinding("Foo").to(Foo.class);
    mapBinder.addBinding("Bar").to(Bar.class);
  }
}

public class InterfaceFactory {
  private Pattern p;
  @Inject private Map<Provider<MyInterface>> providerMap;
  private Provider<MyInterface> selectedProvider;

  public void configure(String type, String pattern) {
    p = Pattern.compile(pattern);
    selectedProvider = providerMap.get(type);
  }

  public MyInterface create(String data) {
    if(pattern.matcher(data).find()) {
      MyInterface intf = selectedProvider.get();
      intf.configure(data);
    }
  }
}

This seems a lot cleaner than what I have now.

Pros:

  1. Using Guice to create objects
  2. Reflection is minimized and compartmentalized
  3. I don't need any knowledge of dependencies

Cons:

  1. I have to write my classes to be able to know what to do if they are created without being configured
  2. Either I need to be able to read my configuration file before adding the plugin bindings, or define them in code.
Community
  • 1
  • 1
durron597
  • 31,968
  • 17
  • 99
  • 158
  • How do you use `InterfaceFactory` in this case? From the original question I thought that you wanted to abstract from the real name of the class somehow, and in this code it seems that you will have to name them explicitly via `configure()` method. – Vladimir Matveev Jun 18 '13 at 18:47
  • BTW, I cannot see how you provide `String` argument to `Foo`/`Bar` constructor. If they do accept `String` in the constructor, your code will fail since there are no bindings of `String` class in your modules. I still think that private modules will be appropriate solution; I'm working on the example right now. – Vladimir Matveev Jun 18 '13 at 19:12
  • @VladimirMatveev That's my point, I would change `Foo` and `Bar` to remove the `String` from the constructor and use a `configure` method instead. It's not ideal, that's what Con #1 is talking about. I would have multiple instances of `InterfaceFactory` for every entry in `objects=name,pattern;name,pattern;...` etc. – durron597 Jun 18 '13 at 19:49
0

I'm adding another answer since the first one is too big already.

I was able to achieve seemingly what you need using multibinder and private modules.

First of all, these are the links which helped me:
https://groups.google.com/forum/#!topic/google-guice/h70a9pwD6_g
https://groups.google.com/forum/#!topic/google-guice/yhEBKIHpNqY
Generalize guice's robot-legs example with Multibinding

Basic idea is as follows. First we create a mapping from names to classes. This should be done via reflection manually anyway, because your class names are defined by strings in the configuration file, but Guice requires Class objects (at least) to establish bindings.

Next we iterate through this mapping and for each correspondence name -> class install a private module which binds a String annotated with some binding annotation to name. It also binds MyInterface with some unique annotation to the class class. Then it exposes this binding of class, which is added to the set via Multibinder in the outer module.

This method allows automatic resolution of your classes dependencies and also provides generic means of setting each object's name.

Update: here is the code: https://github.com/dpx-infinity/guice-multibindings-private-modules

Community
  • 1
  • 1
Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
  • I'm not following you entirely, but if I understand correctly, you should be aware that the input data is not available at application startup; it only becomes available once the application has finished establishing it's network connection. – durron597 Jun 18 '13 at 19:52
  • @durron597, that is ok if this happens before creation of the injector. – Vladimir Matveev Jun 18 '13 at 19:55
  • And if it does not, you can always create child injector :) – Vladimir Matveev Jun 18 '13 at 19:59