springvaadinvaadin4spring

How to use the new Vaadin Spring Boot addon together with Vaadin4Spring EventBus


The Vaadin team has currently released an addon which is as I understood based on the unofficial Vaadin4Spring addon:

Vaadin Spring Boot: https://vaadin.com/directory#addon/vaadin-spring-boot

Vaadin4Spring: https://github.com/peholmst/vaadin4spring

Please note! As of February 2015, Vaadin is working on an official Spring add-on which will be a small subset of Vaadin4Spring. Once the official add-on is released, Vaadin4Spring will be converted into a set of add-ons that provide features that the official add-on does not have. You can follow the progress of the official add-on here: https://github.com/vaadin/spring

However, the Vaadin Spring Boot is missing some cool features which Vaadin4Spring has (like said above by Petter Holmström, the author of Vaadin4Spring), like the EventBus framework, which is really useful.

Now I have set up a Maven project both with the Vaadin Spring Boot addon and the Vaadin4Spring addon:

    <!-- Vaadin Spring Boot -->
    <dependency>
        <groupId>com.vaadin</groupId>
        <artifactId>vaadin-spring-boot</artifactId>
        <version>${vaadin.spring.boot.version}</version>
    </dependency>

Basically, I would like to use the features of both the frameworks (or better, I would like to use Vaadin Spring Boot as the main framework and use the Vaadin4Spring EventBus features together with it). The problem I have noticed, however, is that it seems that both the frameworks cannot coexist with each other yet.

If I add both the addons as above, when I run the application, no views and no UIs are found (below the log showing what I mean):

2015-03-01 22:07:22.001... SpringViewProvider: Looking up VaadinViews 
2015-03-01 22:07:22.006  WARN SpringViewProvider: No VaadinViews found ... 
...
2015-03-01 22:12:12.584  INFO: Checking the application context for Vaadin UIs 
2015-03-01 22:12:12.594  WARN: Found no Vaadin UIs in the application context

But the Views and the UI do exist! They are managed by the Vaadin Spring Boot addon (here is some sample code):

UI:

@SpringUI("")
@Theme("valo")
public class DemoUI extends UI {

    private static final long serialVersionUID = 193481619798227053L;

    @Autowired
    private Greeter greeter;

    @Autowired
    private ApplicationContext applicationContext;

    private final SpringViewProvider viewProvider;

    private VerticalLayout layout;

    @Autowired
    public DemoUI(SpringViewProvider viewProvider) {
        this.viewProvider = viewProvider;
    }

    @Override
    protected void init(VaadinRequest request) {        
        layout = new VerticalLayout();
        layout.setMargin(true);
        layout.setSpacing(true);
        setContent(layout);

        Label greetings = new Label(greeter.getGreeting());
        layout.addComponent(greetings);

        final CssLayout navigationBar = new CssLayout();
        navigationBar.addStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP);
        navigationBar.addComponent(createNavigationButton("View Scoped View", ViewScopedView.VIEW_NAME));
        navigationBar.addComponent(createNavigationButton("UI Scoped View", UIScopedView.VIEW_NAME));
        navigationBar.addComponent(createNavigationButton("Another UI Scoped View", AnotherUIScopedView.VIEW_NAME));
        layout.addComponent(navigationBar);     

        final Panel viewContainer = new Panel();
        viewContainer.setSizeFull();
        layout.addComponent(viewContainer);
        layout.setExpandRatio(viewContainer, 1.0f);

        Navigator navigator = new Navigator(this, viewContainer);
        navigator.addProvider(viewProvider);



    }

    private Button createNavigationButton(String caption, String viewName) {
        Button button = new Button(caption);
        button.addStyleName(ValoTheme.BUTTON_SMALL);
        button.addClickListener(event -> { 
            try {
                getUI().getNavigator().navigateTo(viewName);
            }
            catch (IllegalArgumentException e) {
                // view with the given name is not mapped
                System.out.println("Not mapped view with name: " + viewName);
            }
        });
        return button;
    }

}

A view annotated with @SpringView:

@SpringView(DefaultView.VIEW_NAME)
public class DefaultView extends VerticalLayout implements View {

    private static final long serialVersionUID = -2052937117362922764L;

    public static final String VIEW_NAME = "";

    @Override
    public void enter(ViewChangeEvent event) {
         // the view is constructed in the init() method()      
    }

    @PostConstruct
    void init() {
        addComponent(new Label("This is the default view"));
    }

}

The same goes for other views. And everything works, when these two dependencies are removed from the pom.xml:

    <!-- Vaadin4Spring addon, the EventBus framework needs this addon -->
    <dependency>
        <groupId>org.vaadin.spring</groupId>
        <artifactId>spring-boot-vaadin</artifactId>
        <version>0.0.5-SNAPSHOT</version>
    </dependency>
    <!-- Vaadin4Spring EventBus feature I would like to use with Vaadin Spring Boot -->
    <dependency>
        <groupId>org.vaadin.spring</groupId>
        <artifactId>spring-vaadin-eventbus</artifactId>
        <version>0.0.5-SNAPSHOT</version>
    </dependency>

Not allowing me to use the EventBus framework. Did anyone already explore the new addon too and had the same need? Did you find out how to integrate the two worlds? Or is it still early to use Vaadin4Spring tools into Vaadin Spring Boot and we should wait that the Vaadin4Spring features get converted into a set of addons which will complete Vaadin Spring Boot as is said in the first block I posted?

EDIT: I also tried to add a @ComponentScan annotation to the Spring Boot application class while having the three dependencies together, didn't help...

If I add spring-vaadin instead of spring-boot-vaadin:

        <!-- Vaadin4Spring -->
        <dependency>
            <groupId>org.vaadin.spring</groupId>
            <artifactId>spring-vaadin</artifactId>
            <version>${vaadin4spring.version}</version>
        </dependency>
        <!-- Vaadin4Spring EventBus framework -->
        <dependency>
            <groupId>org.vaadin.spring</groupId>
            <artifactId>spring-vaadin-eventbus</artifactId>
            <version>${vaadin4spring.version}</version>
        </dependency>

Views and UIs are loaded and I can see them in the browser, but as soon as I want to start using an event bus, I get the following exception:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'vaadinSpringBootDemoApplication': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.vaadin.spring.events.EventBus$ApplicationEventBus demo.VaadinSpringBootDemoApplication.applicationEventBus; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.vaadin.spring.events.EventBus$ApplicationEventBus] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1202)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:762)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:691)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:321)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:961)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:950)
    at demo.VaadinSpringBootDemoApplication.main(VaadinSpringBootDemoApplication.java:21)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.vaadin.spring.events.EventBus$ApplicationEventBus demo.VaadinSpringBootDemoApplication.applicationEventBus; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.vaadin.spring.events.EventBus$ApplicationEventBus] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:561)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
    ... 16 common frames omitted
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.vaadin.spring.events.EventBus$ApplicationEventBus] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1308)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1054)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:949)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533)
    ... 18 common frames omitted

It seems that the EventBus.* beans don't get registered inside the Spring IoC container. What to do?


Solution

  • The problem is that VaadinSessionScope is not implemented yet in official spring4vaadin. You need to create that bean thought the unofficial spring4vaadin.

    Your pom file:

    <!-- Official Vaadin4Spring -->
    <dependency>
        <groupId>com.vaadin</groupId>
        <artifactId>vaadin-spring</artifactId>
        <version>${vaadin.spring.version}</version>
    </dependency>
        <dependency>
        <groupId>com.vaadin</groupId>
        <artifactId>vaadin-spring-boot</artifactId>
        <version>${vaadin.spring.boot.version}</version>
    </dependency>
    <!-- Unofficial Vaadin4Spring -->
    <dependency>
        <groupId>org.vaadin.spring</groupId>
        <artifactId>spring-vaadin</artifactId>
        <version>${vaadin4spring.version}</version>
    </dependency>
    <!-- Vaadin4Spring EventBus Addon -->
    <dependency>
        <groupId>org.vaadin.spring</groupId>
        <artifactId>spring-vaadin-eventbus</artifactId>
        <version>${vaadin4spring.version}</version>
    </dependency>
    

    UI example:

    @SpringUI("/ui")
    @Title("Test")
    @Theme("valo")
    public class MainUI extends UI {
    
        private final Logger log = LoggerFactory.getLogger(MainUI.class);
    
        @Inject
        EventBus.UIEventBus eventBus;
    
        @Override
        protected void init(VaadinRequest request) {
            eventBus.subscribe(this);
    
            setContent(new Button("Create event", (e) -> {
                eventBus.publish(EventScope.UI, "Payload string.");
            }));
        }
    
        @EventBusListenerMethod
        public void onEvent(String str) {
            log.debug("Event received: {}", str);
        }
    }
    

    Application main class

    @Configuration
    @ComponentScan
    @EnableAutoConfiguration
    @EnableVaadinEventBus
    public class Application {
    
        private static final Logger log = LoggerFactory.getLogger(Application.class);
    
        @Bean
        static VaadinSessionScope vaadinSessionScope() {
            return new VaadinSessionScope();
        }
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }