27

I'm working on a project that consists of four parts:

  • The Main project that brings everything together. This contains the public static void main(String... args) entry point.
  • Component A
  • Component B
  • A 3rd party Common component that both A and B refer to.

I'm using Guice for the plumbing between all four parts, and this is my problem:
In both As and Bs main Guice modules I install a module that extends one that is defined in Common. At runtime this setup fails with the following error:

A binding to common.SomeClass was already configured at common.AbstractCommonModule.configure(). [source]

The reason for this is that I'm invoking common.AbstractCommonModule.configure() twice; once by installing a subclass instance of common.AbstractCommonPrivateModule from Component A's com.a.MainModule.configure(), and a second time from Component B's com.b.MainModule.configure().

Installing just one instance of common.AbstractCommonPrivateModule in Main is not an option, because AbstractCommonPrivateModule implements a specific binder method bindComplicatedStuff(ComplicatedStuff), for which I only know the argument inside A and B, respectively.

I tried working around this whole thing by wrapping A's and B's respective main Guice modules in PrivateModules. However, this failed with the next error:

Unable to create binding for %s. It was already configured on one or more child injectors or private modules %s%n If it was in a PrivateModule, did you forget to expose the binding? [source]

In my case, A's and B's respective main Guice modules are in fact ServletModules - which apparently I can install twice from Main.

How can I get around these errors and install the AbstractCommonPrivateModule module twice?

Edit: I uploaded some example code (with explanation about some details) to GitHub

derabbink
  • 2,419
  • 1
  • 22
  • 47
  • 4
    This sounds like an intersting problem, but I am having trouble fully understanding it without code examples. Could you post (or link a gist) what you tried so far? – Jan Galinski Nov 23 '15 at 17:46
  • 2
    What does the `common.SomeClass` binding look like? You may be able to write it in a way the Guice can automatically de-duplicate it for you. – Tavian Barnes Nov 23 '15 at 21:10
  • I've added some [code on GitHub](https://github.com/derabbink/simple_web_stack). Sorry about the delay – derabbink Jan 23 '16 at 16:19

2 Answers2

18

Rather than having A and B install Common, have them requireBinding()'s for the classes they need from Common. Then modules that rely on A or B will need to also install Common. This may feel a little odd, but it's actually desirable, since A and B are now less tightly-coupled to Common.


Update

The reason I am installing two ShiroWebModules is because I want the Jersey resources in the ui module to only be secured using one Shiro configuration (one that unserstands password-protecting resources), while all Jersey resources in the api module should be be secured using an entirely different Shiro configuration (one that understands only bearer tokens as an authentication mechanism).

Broadly speaking, this is intractable. A Guice Injector provides one way of doing something (generally one implementation of an interface) to the whole application; not different mechanisms per package. Your two Modules, SwsApiServletModule and SwsUiServletModule provide a number of identical bindings, and SwsModule installs them both together. In essence you're saying "Guice, please provide a bearer-token-based authentication mechanism" then immediately after saying "Guice, please provide a password-based authentication mechanism". It can only do one or the other, so rather than picking one arbitrarily, it fails-fast.

Of course, there are a number of solutions, depending on what exactly your needs are. The most common is to use binding annotations and to have the UI and API code request different annotation. That way you can install two different implementations (with different annotations) of the same interface or class.

Here's an example:

package api;

public class ApiResources {
  @Inject
  public ApiResources(@ApiAuthMechanism AuthMechanism auth) {
    this.auth = auth;
  }
}

---

package api;

public class ApiModule implements Module {
  public void configure() {
    bind(AuthMechanism.class).annotatedWith(ApiAuthMechanism.class)
        .to(BearerTokenAuthMechanism.class);
  }
}

---

package ui;

public class UiResources {
  @Inject
  public UiResources(@UiAuthMechanism AuthMechanism auth) {
    this.auth = auth;
  }
}

---

package ui;

public class UiModule implements Module {
  public void configure() {
    bind(AuthMechanism.class).annotatedWith(UiAuthMechanism.class)
        .to(PasswordAuthMechanism.class);
  }
}

---

package webap;

public class WebappModule implements Module {
  public void configure() {
    // These modules can be installed together,
    // because they don't install overlapping bindings 
    install(new ApiModule());
    install(new UiModule());
  }
}

You mention in a comment that you don't have control of the overlapping bindings being installed because they're coming from a third-party module. If that is the case (I didn't see where that was happening in your code) it's possible the third party doesn't want you doing what you're trying to do, for security reasons. For example, simply binding the password-based mechanism might introduce vulnerabilities in the whole app. It might be worth trying to better understand how the third party intends for their modules to be used.

Another option, which isn't ideal but can work for some use cases, is to use two wholly separate Injector instances, one with each binding. Then you manually pass the instances you need to the UI and API code directly. This somewhat defeats the purpose of Guice, but it isn't always the wrong decision. Using child Injectors can make this less painful.


As an aside, your "sample code" is enormous, and probably more than 90% is unrelated to the problem. In the future please take the time to create an SSCCE that contains only the code relevant to the problem at hand. There's simply no way anyone's going to sift through 100+ Java files and 7,300+ lines of code to understand your problem. Not only will this make it easier for people who are trying to help you, but simply trying to create an SSCCE that demonstrates the problem will often be enough to help you understand and resolve it yourself.

dimo414
  • 47,227
  • 18
  • 148
  • 244
  • I like this suggestions, and generally this is what I'd be doing. However, in my case the `common.SomeClass` binding is constructed by a 3rd party library's `Module`. And I cannot simply install that module just once from my `Main` module, because it depends on other bindings, which are different within the scope of `A` and `B`. I added [sample code to GitHub](https://github.com/derabbink/simple_web_stack) – derabbink Jan 23 '16 at 17:37
  • Thanks for the SSCCE. I'll try to play with it this weekend. – dimo414 Jan 23 '16 at 19:47
  • Thanks for extending your answer. On you side note: I realize there's a lot of auxiliary code in the "SSCCE", but I wanted to provide a non-contrived example that demonstrates the issue using the actual library I'm having the issue with (Shiro), and that requires some auxiliary code (probably less than mine). However, that's also why I included a readme file – derabbink Feb 07 '16 at 20:44
  • I appreciate the intent, but the problem with the non-contrived example is that there remain several moving parts, one or more of which *might* be involved. A contrived yet representative example allows us to understand the whole problem, whereas I simply cannot spent the time to understand what's going on throughout the code sample you posted. – dimo414 Feb 08 '16 at 23:47
1

To install the same module twice, override the .equals method in your module to refer to class rather than object equality. Guice won't install a module that is equal to one that has already been installed. This doesn't help much most of the time as you type:

install new AbstractCommonPrivateModule();

and so each object is a different instance which won't be equal to the last. Overriding the equals method gets around that:

@Override
public boolean equals(Object obj) {
    return obj != null && this.getClass().equals(obj.getClass());
}

// Override hashCode as well.
@Override
public int hashCode() {
    return this.getClass().hashCode();
}

However, note that this method is often incorrect.

Why not to do the above?

At this point, you're not really making use of Guice or dependency injection. Instead, you've tightly coupled the implementation of AbstractCommonPrivateModule with the implementation of B and C which install it. As mentioned by @dimo414, it seems like here the OP really wants to use two different ShiroWebModules, which is exactly what Guice is good at doing by installing those two different modules at a higher level. Likewise, higher level installs let you swap out while testing. If you actually want to swap one of the modules out at some point, Guice will again break.

This can also break if you override a module (which is another useful tool for testing).

The OP also wants to install a generic module twice. Wrapping another library's generic module adds additional risk; the original authors may have very good reasons for not implementing the above trick themselves, such as security.

hubatish
  • 5,070
  • 6
  • 35
  • 47