Vaadin Window component not fully injected with Guice

787 Views Asked by At

I have a very simple Vaadin application. I want to inject the main window of my Vaadin app using Guice.

My problem is that my main window is injected, but none of my @Inject directives inside this main window component are processed.

The full code source of my use cas is available on a bitbucket repo

The Vaadin application class:

public class MyVaadinApplication extends Application {

    protected final Provider<MainWindow> mainWindowProvider;

    @Inject
    @Named("title")
    protected String title;

    @Inject
    public MyVaadinApplication(Provider<MainWindow> mainWindowProvider) {
        this.mainWindowProvider = mainWindowProvider;
    }

    @Override
    public void init() {
        System.out.println("MyVaadinApplication:: found value <"+title+"> for injected title");
        setMainWindow(this.mainWindowProvider.get());
    }
}

The MainWindow class:

public class MainWindow extends Window {

    @Inject
    @Named("title")
    protected String title;
    @Inject
    @Named("label")
    protected String label;
    private static int globalCounter = 0;
    private int localCounter;

    public MainWindow(String caption, ComponentContainer content) {
        super(caption, content);
        initUI();
    }

    public MainWindow(String caption) {
        super(caption);
        initUI();
    }

    public MainWindow() {
        super();
        initUI();
    }

    private void initUI() {
        localCounter = globalCounter;
        globalCounter++;
        this.setCaption(title);
        this.addComponent(new Button(label+" ("+localCounter+")"));
    }
}

The GuiceServletContextListener where the guice binding are defined:

public class GuicyServletConfig extends GuiceServletContextListener {

    @Override
    protected Injector getInjector() {

        ServletModule module = new ServletModule() {

            @Override
            protected void configureServlets() {
                serve("/*").with(GuiceApplicationServlet.class);

                bind(Application.class).to(MyVaadinApplication.class).in(ServletScopes.SESSION);
                bind(MainWindow.class).in(ServletScopes.SESSION);
                bindConstant().annotatedWith(Names.named("title")).to("This is a guicy Vaadin Application");
                bindConstant().annotatedWith(Names.named("label")).to("Guice me!");
            }
        };

        Injector injector = Guice.createInjector(module);

        return injector;
    }
}

What happens is that the MainWindow is correctly injected, but the label and title strings are always null. Any idea?

6

There are 6 best solutions below

0
On BEST ANSWER

the solution is to inject anything that need to be injected in the MainWindow class using constructor instead of field injection. See the tag stackoverflow-working-with-constructor-injection on the hg repo.

Samples of the source code

public class MainWindow extends Window {

    protected String title;
    protected String label;
    protected LabelCaption labelCaption;
    private static int globalCounter = 0;
    private int localCounter;

    @Inject
    public MainWindow(@Named("title") String title, @Named("label") String label, LabelCaption labelCaption) {
        super();
        this.title = title;
        this.label = label;
        this.labelCaption = labelCaption;
        initUI();
    }

    private void initUI() {
        localCounter = globalCounter;
        globalCounter++;
        this.setCaption(title);
        String labelFromLabelCaption = "labelCaption is null";
        if (labelCaption != null) {
            labelFromLabelCaption = labelCaption.getCaption();
        }
        this.addComponent(new Button(label + "/" + labelFromLabelCaption + "/(" + localCounter + ")"));
    }
}

Does not know yet why field injection does not work in this case.

The solution was injected to me thanks to this post.

0
On

I'll suggest you to create a setter for label and title. Then define the @Inject on them. Both are protected I don't know guice behaviour in this case but all my working examples work with setter annotated or public fields annotated.

1
On

why not just something like this?

bind(String.class)
.annotatedWith(Names.named("title"))
.toInstance("This is a guicy Vaadin Application");
1
On

Is the title injected in the application classe?

Could your issue deal with named annotation only? Try to inject a bean in the window to double check...

1
On

looks like your using a provider to get at the actual instance. did you try with just injecting MainWindow directly? Also test if injection actually happens by adding a

@Inject void foo(Injector i){
 System.out.println("injected");
}
0
On

You should also consider something like the following:

public class MainWindow {
  @Inject
  public MainWindow(Injector injector) { 
    this.injector = injector;
  }

  @Override 
  protected void attach() {
     super.attach(); // must call!!!

     this.title = injector.getInstance(something);
     ...

     // set up listeners, etc, here
  }

  @Override
  protected void detach() {
    // disconnect listeners, remove child components
    super.detach();
  }

}

By doing this you can keep the size of your Application instance minimized; in some environments it's serialized when stored. Deferring construction of subcomponents until they're really needed (and doing the same with attached listeners) can help a lot. There are more messages on the Vaadin forums on this topic.