Flow: Application run with Spring Boot Maven plugin does not discover all routes

Created on 1 Mar 2018  路  13Comments  路  Source: vaadin/flow

Running a Flow application with Spring Boot Maven plugin does not discover @Route components in dependencies.

In this app: https://github.com/heruan/flow-spring-multimodule there are two modules:

  • flow-spring-app: with a @Route("") component
  • flow-spring-module: with a @Route("module") component

Running flow-spring-app with mvn spring-boot:run opening http://localhost:8080/module returns:

Could not navigate to 'module'. Reason: Couldn't find route for 'module'

But other Spring beans from the module (e.g. ModuleService) are discovered correctly.

Note that if the app is deployed in WildFly, all works as expected. I'm facing this with integration tests, where a component I'm testing has a new RouterLink("Foo", ComponentInOtherModule.class) which fails since the target component isn't discovered during the test (run with the spring-boot-maven-plugin).

bug routing spring

Most helpful comment

In reality we have logic which implements almost the same workaround already for the Spring Boot application.

It doesn't work in your case by the reason of different package names.
Your application package name is to.lova.flow.spring.app. And this package (the application package) is used to discover routes in our "workaround" implementation.
But your maven module has routes in the package to.lova.flow.spring.module. So the routes are not discovered there.

The workaround in your case (without writing any additional code) would be to move your application class into to.lova.flow.spring app or any its parent package.

But we should provide a way to specify packages where routes should be discovered (the same way as @EnableJpaRepositories annotation does).

There is even "TODO" methods in the code which I made explicitly keeping in mind that we will need to adjust package name retrieval for those cases. But I had no time to fix this.

All 13 comments

The reason for this is that @Route types are generally picked up using the servlet container's own classpath scanning mechanism (javax.servlet.ServletContainerInitializer).

The embedded servlet container used by Spring Boot has this functionality explicitly disabled for performance reasons, so in those cases we have to fall back to using Spring's own classpath scanning mechanism that has different logic for determining where to look.

One potential way around this might be to consistently use the Spring mechanism even in cases when the servlet container's own scanning has already been triggered.

The embedded servlet container used by Spring Boot has this functionality explicitly disabled for performance reasons

Is it possible to re-enable it somehow, as a workaround until a proper solution?

This is working for me as a workaround:

@Component
public class SpringRouteRegistryInitializer extends RouteRegistryInitializer
        implements ServletContextInitializer {

    private static final String PACKAGE = "it.axians";

    @Override
    public void onStartup(ServletContext servletContext)
            throws ServletException {
        ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(
                false);
        provider.addIncludeFilter(new AnnotationTypeFilter(Route.class));
        provider.addIncludeFilter(new AnnotationTypeFilter(RouteAlias.class));
        Set<Class<?>> classSet = provider.findCandidateComponents(PACKAGE)
                .stream().map(BeanDefinition::getBeanClassName)
                .map(className -> {
                    try {
                        return Class.forName(className);
                    } catch (ClassNotFoundException e) {
                        return null;
                    }
                }).filter(Objects::nonNull).collect(Collectors.toSet());
        super.onStartup(classSet, servletContext);
    }

}

Great that you found a workaround!

Is it possible to re-enable it somehow, as a workaround until a proper solution?

I don't think so. See https://github.com/spring-projects/spring-boot/issues/321 for more details.

Since there is a workaround for this bug - I'm deprioritizing this issue for now in favor of fixing other bugs.

Let us know if the workaround is not an option anymore.

Totally agree, no problem with that! BTW it would be cool to have visibility of issue priorities, e.g. with labels or milestones.

In reality we have logic which implements almost the same workaround already for the Spring Boot application.

It doesn't work in your case by the reason of different package names.
Your application package name is to.lova.flow.spring.app. And this package (the application package) is used to discover routes in our "workaround" implementation.
But your maven module has routes in the package to.lova.flow.spring.module. So the routes are not discovered there.

The workaround in your case (without writing any additional code) would be to move your application class into to.lova.flow.spring app or any its parent package.

But we should provide a way to specify packages where routes should be discovered (the same way as @EnableJpaRepositories annotation does).

There is even "TODO" methods in the code which I made explicitly keeping in mind that we will need to adjust package name retrieval for those cases. But I had no time to fix this.

Great, thank you @denis-anisimov! I would suggest @RouteScan in the line of Spring鈥檚 @EntityScan or @ComponentScan.

@RouteScan would have been good if it had been only routes issue.
We have a number of types that are scanned at startup and for each and every of them we have this workaround implemented. Technically each such scanning should have its own annotation but I think it's overhead to introduce an additional annotation for every such case.
So I've added @VaadinScan annotation which covers all such cases ( routes, custom elements, error pages, some validations).
The annotation name may be changed if nobody likes it. But at least the functionality is implemented in the provided PR.

I actually noticed that you may use @SpringBootApplicaiton annotation with packages to scan as a value (so it's the default base packages). So it may already work without any additional annotation.

Still need to update tutorials.

We have a number of types that are scanned at startup [鈥

Then I'd see more fit to specify the packages to scan via a String[] attribute in @EnableVaadin instead of a @VaadinScan annotation.

I actually noticed that you may use @SpringBootApplicaiton annotation with packages to scan as a value (so it's the default base packages). So it may already work without any additional annotation.

It doesn't, unfortunately 馃槙

Then I'd see more fit to specify the packages to scan via a String[] attribute in @EnableVaadin instead of a @VaadinScan annotation.

Actually, it's a good idea.

It doesn't, unfortunately 馃槙

True, @SpringBootApplicaiton annotation doesn't set default autoconfiguration package.
I will add the logic which handles packages from @SpringBootApplicaiton annotation also.

Issue moved to vaadin/spring #266 via ZenHub

Was this page helpful?
0 / 5 - 0 ratings