Vaadin 22 with web.xml and Spring

1.1k views Asked by At

I have a working Vaadin 18 prototype that uses web.xml and Spring.

The prototype is for a port of a large webapp that uses web.xml and vanilla Spring. I want to update the front-end without dropping web.xml or using Spring Boot.

It works fine in Vaadin 18.0.6, but fails on startup in Vaadin 22.0.0 with:

java.lang.IllegalStateException: The application Lookup instance is not found in the interface com.vaadin.flow.server.VaadinContext instance. It means that the container has not executed Lookup initialization code: so either the container is not Servlet 3.0 compatible or project configuration is broken.
    at com.vaadin.flow.server.startup.ApplicationConfiguration.lambda$get$0 (ApplicationConfiguration.java:58)
    at com.vaadin.flow.server.VaadinServletContext.getAttribute (VaadinServletContext.java:73)
    at com.vaadin.flow.server.startup.ApplicationConfiguration.get (ApplicationConfiguration.java:50)

Initial digging suggests that the disable.automatic.servlet.registration context parameter is no longer supported.

What is required to get the prototype working in Vaadin 22?

Here is the web.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
     version="3.1"
     metadata-complete="false">

<!-- NOTE: metadata-complete="false" is required, in order to invoke
           ClassLoaderAwareServletContainerInitializer subclasses (DevModeInitializer etc) -->

<context-param>
    <!-- disables registration of the VaadinServlet. This is required as it is registered via AppServlet -->
    <param-name>disable.automatic.servlet.registration</param-name>
    <param-value>true</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>AppServlet</servlet-name>
    <servlet-class>org.example.AppServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>AppServlet</servlet-name>
    <url-pattern>/app</url-pattern>
    <url-pattern>/app/*</url-pattern>
</servlet-mapping>

</web-app>

And the corresponding servlet:

/**
 * Servlet to wire Vaadin into web.xml.
 */
public class AppServlet extends HttpServlet {

    private SpringServlet delegate;

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        ServletContext servletContext = getServletContext();
        WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
        delegate = new SpringServlet(context, false);
        delegate.init(config);
    }

    @Override
    public void destroy() {
        if (delegate != null) {
            delegate.destroy();
        }
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        delegate.service(request, response);
    }
}
3

There are 3 answers

0
tanderson On BEST ANSWER

As per this comment, the solution is to add a com.vaadin.flow.spring.VaadinApplicationConfiguration bean to applicationContext.xml.

This requires <context:annotation-config/> in order to work e.g.:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
                           http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="com.vaadin.flow.spring.VaadinApplicationConfiguration"/>
</beans>

If you don't want to use <context:annotation-config/>, the beans that VaadinApplicationConfiguration creates can be added directly:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="com.vaadin.flow.spring.SpringApplicationConfigurationFactory"/>
    <bean class="com.vaadin.flow.spring.SpringLookupInitializer.SpringApplicationContextInit"/>
</beans>

This isn't recommended as SpringApplicationContextInit is internal to Vaadin.

0
Archie On

Here's a variant that I came up with. I want to use web.xml and Vaadin Spring, but not Spring Boot or Spring Security.

In my applicationContext.xml file, I'm using <context:component-scan> to find all of the various Vaadin Spring config beans, but then excluding those config beans specific to Spring Boot and Spring Security:

<!-- Vaadin annotation-based config, but excluding Spring Boot and Spring Security -->
<context:component-scan base-package="com.vaadin.flow.spring">
    <context:exclude-filter type="regex" expression="com\.vaadin\.flow\.spring\.security\..*"/>
    <context:exclude-filter type="regex" expression="com\.vaadin\.flow\.spring\.SpringBootAutoConfiguration"/>
    <context:exclude-filter type="regex" expression="com\.vaadin\.flow\.spring\.SpringSecurityAutoConfiguration"/>
</context:component-scan>

I do not have metadata-complete="false" in my web.xml because I do all other Spring configuration via XML (just a legacy thing).

0
Heri On

My setup:

  • Eclipse 2022-06
  • embedded tomcat-server 8.5
  • log4j2
  • vaadin 23.2.3 (plain Spring MVC, without springboot)

The solution for me was to provide a concrete derivation of VaadinMVCWebAppInitializer and AppShellConfigurator.

    @Theme(value = "xxxx")
    @PWA(name = "My Application", shortName = "MyApp", iconPath = "icons/MyIcon.png",
 offlineResources = {})
    public class AppShellConfiguratiorImpl implements AppShellConfigurator {}

and

    public class VaadinMVCWebAppInitializerImpl extends VaadinMVCWebAppInitializer {
        @Override
        protected Collection<Class<?>> getConfigurationClasses()  {
            return Collections.singletonList( MySpringConfiguration.class );
        }
    }

and

    @Configuration
    @ImportResource({ "classpath*:spring/dataSource-context.xml",
                      "/WEB-INF/security-context.xml" })
    @ComponentScan("my.base.package")
    @NpmPackage(value = "some npm package", version = "some version")
    public class MySpringConfiguration {}

See also this vaadin doc