Vaadin Lookup VS Vaadin Instantiator

Keywords: Vaadin Initialization. Quoting Javadoc, “Lookup Provides a way to discover services used by Flow (SPI). Dependency injection frameworks can provide an implementation that manages instances according to the conventions of that framework. This is similar to the Instantiator class but a Lookup instance is available even before a VaadinService instance is created (and as a consequence there is no yet an Instantiator instance).”

In short, Vaadin Lookup is pretty low-level stuff used to instantiate Vaadin internals (including the Instantiator itself); while Instantiator is used to create instances of Routes, i18n and components in Polymer/LitTemplates. Apps may encounter a need to override Instantiator; however there’s really no use-case to override Lookup yourself. Correct implementation of Lookup is provided automatically (for example for the OSGi environment there’s OsgiLookupImpl).

Initializing Lookup

What if you are tinkering with the servlet environment (perhaps trying to run Vaadin in embedded jetty), and suddenly you’re getting NullPointerExceptions such as Cannot invoke "com.vaadin.flow.server.StaticFileHandler.serveStaticResource(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)" because "this.staticFileHandler" is null because getContext().getAttribute(Lookup.class) returned null? Who creates Lookup?

Everything starts in LookupServletContainerInitializer. It implements ServletContainerInitializer and has @HandlesTypes annotation, which tells the servlet container to perform classpath discovery of given classes and hand them over to LookupServletContainerInitializer.

LookupServletContainerInitializer.getLookupInitializer() then discovers proper AbstractLookupInitializer: if your app defines a class which extends AbstractLookupInitializer then that class is automatically used; otherwise the default LookupInitializer is used. Then AbstractLookupInitializer.initialize() is called, which creates the Lookup instance and calls VaadinApplicationInitializationBootstrap closure which finally sets the Lookup instance to the VaadinServletContext. Holy shit this could not be more complicated.

In short: if you’re getting those NPEs, make sure your servlet container finds and runs LookupServletContainerInitializer.

Karibu-Testing runs no servlet container and therefore calls LookupServletContainerInitializer manually, then ensures that Lookup is properly set in the VaadinServletContext

Yet Another Way For a custom Instantiator

You could in theory introduce your own AbstractLookupInitializer which provides a custom InstantiatorFactory which loads your Instantiator, but that’s a really complicated way. For simpler ways, see Vaadin custom instantiator.

Spring

Vaadin 23+ Spring plugin initializes Lookup differently. The old Lookup Provider SPI is disabled. Instead, SpringLookupInitializer is called from VaadinServletContextInitializer. The VaadinServletContextInitializer is created as a bean in SpringBootAutoConfiguration.contextInitializer(); since it’s ServletContextInitializer it’s probably loaded by Spring automatically as long as SpringBootAutoConfiguration Spring configuration is activated.

Non-Boot project

Some projects may use Spring but not Spring Boot, they would package themselves to WAR and would be deployed to a servlet container such as Tomcat. Is it possible to use Vaadin-Spring plugin in such Boot-less environment?

Since ServletContextInitializer interface (implemented by VaadinServletContextInitializer) comes from spring-boot.jar, it would suggest that either the plugin would fail with a NoClassDefFoundError (if Spring Boot is missing from classpath), or the VaadinServletContextInitializer (and by extension, SpringLookupInitializer) is not initialized (if Spring Boot is pulled in to classpath transitively, but not activated).

Either way, the SpringLookupInitializer is not called, leading to this error:

Sep 09, 2024 4:58:09 PM org.apache.catalina.core.StandardContext listenerStart
SEVERE: Exception sending context initialized event to listener instance of class [com.vaadin.flow.server.startup.ServletContextListeners]
java.lang.IllegalStateException: The application Lookup instance is not found in VaadinContext. The instance is suppoed to be created by a ServletContainerInitializer. Issues known to cause this problem are:
- A Spring Boot application deployed as a war-file but the main application class does not extend SpringBootServletInitializer
- An embedded server that is not set up to execute ServletContainerInitializers
- Unit tests which do not properly set up the context for the test

	at com.vaadin.flow.server.startup.ApplicationConfiguration.lambda$get$0(ApplicationConfiguration.java:47)
	at com.vaadin.flow.server.VaadinServletContext.getAttribute(VaadinServletContext.java:66)
	at com.vaadin.flow.server.startup.ApplicationConfiguration.get(ApplicationConfiguration.java:41)
	at com.vaadin.flow.server.DeploymentConfigurationFactory.createPropertyDeploymentConfiguration(DeploymentConfigurationFactory.java:74)
	at com.vaadin.flow.server.startup.ServletDeployer$StubServletConfig.createDeploymentConfiguration(ServletDeployer.java:178)

Seems like the fix is to use @EnableWebMvc instead of @SpringBootApplication, but you might still need to have Spring Boot on the classpath. See Using Vaadin With Spring MVC for more details.

Also make sure the Spring MVC DispatcherServlet is running: Vaadin Servlet Auto-loading otherwise none of the servlets will be served.

Written on October 14, 2022