2

Suppose I have the following classes defined:

public interface A {}

public class A1 implements A {}

public class A2 implements A {}

public class XServlet<T extends A> extends HttpServlet {
    public XServlet(T delegate){}
}

Additionally in one of my Guice modules I have the foollowing bindings:

bind(A.class).annotatedWith(Names.named("a1")).to(A1.class);
bind(A.class).annotatedWith(Names.named("a2")).to(A2.class);

Now I need to create a ServletModule that defines two instances of "XServlet" with different arguments. For "/x" pattern I want it to use whatever is bound to A.class annotated with "a1", and for "/y" pattern whatever is bound to A.class and annotated with "a2". Something like:

serve("/x").with(???);
serve("/y").with(???);

What should be there instead of '???'? Is it possible at all?

andrew.z
  • 1,039
  • 3
  • 15
  • 24

1 Answers1

1

Two problems here: One is varying the binding annotation of XServlet with the serve method, and the other is varying the binding of A based on the annotation of XServlet.

The serve method half is pretty straightforward to solve: Manually create a Key. This binds "/x" to @Named("a1") XServlet.

serve("/x").with(Key.get(XServlet.class, Names.named("a1")));

The second half is known as the "robot legs problem" and is fixable using private modules:

install(new PrivateModule() {
  @Override void configure() {
    // These are only bound within this module, and are not visible outside.
    bind(A.class).to(A1.class);
    bind(XServlet.class).annotatedWith(Names.named("a1"));
    // But you can expose bindings as you'd like.
    expose(XServlet.class).annotatedWith(Names.named("a1"));
  }
});

UPDATE: If the named bindings you mentioned before cannot be moved to the private module, you can always bind the non-annotated binding in the private module to the annotated binding in another module. The binding in the private module should look like this:

// Fulfill requests for an unannotated A by looking up @Named("a1") A,
// though @Named("a1") A is bound elsewhere.
bind(A.class).to(Key.get(A.class, Names.named("a1")));

If you're trying to bind a dozen of these, you may find it easier to create a private static function that looks like:

private static Module moduleForServlet(
    final Class<? extends A> aClass, final String namedAnnotationString) {
  return new PrivateModule() { /* see above */ };
}

Docs:

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • Thanks Jeff. But your solution doesn't solve my problem. It still requires me to have "bind(A.class).to(A1.class);" in my private module - and that is exactly what I would like to avoid. As I mentioned, I already have that binding in another module and I cannot move to the module where I bind the servlet. – andrew.z Dec 24 '12 at 06:39
  • @andrew.z You can use the same technique to `bind(A.class).to(Key.get(A.class, Names.named("a1")))`. I updated my answer to demonstrate. I do not think there is a Guice feature that would automatically, for all annotations N, bind XServlet-with-N to use dependency YourDependency-with-N all in a single statement. – Jeff Bowman Dec 24 '12 at 07:59