Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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.

why not just something like this?

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

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...

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");
}

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top