'Springboot with JSF and SOAP interceptors not working properly

I am currently migrating lot of old code to springboot applications. We have JSF and SOAP web service which migrated successfully. I am facing issue related to WsConfigurerAdapter. If we enable EndpointInterceptor then FacesServlet does not initialize properly and throws below error.

15:28:41.045 [http-nio-8080-exec-1] ERROR j.faces - Unable to obtain InjectionProvider from init time FacesContext. Does this container implement the Mojarra Injection SPI?
15:28:41.046 [http-nio-8080-exec-1] ERROR j.faces - Application was not properly initialized at startup, could not find Factory: javax.faces.context.FacesContextFactory. Attempting to find backup.
15:28:41.047 [http-nio-8080-exec-1] ERROR o.a.c.c.C.[.[.[/KSF] - Servlet.init() for servlet [FacesServlet] threw exception
java.lang.IllegalStateException: Could not find backup for factory javax.faces.context.FacesContextFactory.
        at javax.faces.FactoryFinderInstance.getFactory(FactoryFinderInstance.java:541) ~[javax.faces-2.3.9.jar!/:2.3.9]
        at javax.faces.FactoryFinder.getFactory(FactoryFinder.java:292) ~[javax.faces-2.3.9.jar!/:2.3.9]
        at javax.faces.webapp.FacesServlet.init(FacesServlet.java:374) ~[javax.faces-2.3.9.jar!/:2.3.9]
        at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1164) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.catalina.core.StandardWrapper.allocate(StandardWrapper.java:804) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:128) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1732) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.56.jar!/:?]
        at java.lang.Thread.run(Thread.java:748) [?:1.8.0_222-4-redhat]
15:28:41.055 [http-nio-8080-exec-1] ERROR o.a.c.c.C.[.[.[.[FacesServlet] - Allocate exception for servlet [FacesServlet]
java.lang.IllegalStateException: Could not find backup for factory javax.faces.context.FacesContextFactory.
        at javax.faces.FactoryFinderInstance.getFactory(FactoryFinderInstance.java:541) ~[javax.faces-2.3.9.jar!/:2.3.9]
        at javax.faces.FactoryFinder.getFactory(FactoryFinder.java:292) ~[javax.faces-2.3.9.jar!/:2.3.9]
        at javax.faces.webapp.FacesServlet.init(FacesServlet.java:374) ~[javax.faces-2.3.9.jar!/:2.3.9]
        at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1164) ~[tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.catalina.core.StandardWrapper.allocate(StandardWrapper.java:804) ~[tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:128) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1732) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-embed-core-9.0.56.jar!/:?]
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.56.jar!/:?]
        at java.lang.Thread.run(Thread.java:748) [?:1.8.0_222-4-redhat]

Web service configuration code

@EnableWs
@Configuration
public class MyWebServiceConfig {

    @Bean
    public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet(ApplicationContext applicationContext) {
        MessageDispatcherServlet servlet = new MessageDispatcherServlet();
        servlet.setApplicationContext(applicationContext);
        servlet.setTransformWsdlLocations(true);
        ServletRegistrationBean<MessageDispatcherServlet> servletRegistrationBean = new ServletRegistrationBean<>(servlet, "/Services/*");
        servletRegistrationBean.setName("MessageDispatcherServlet");
        return servletRegistrationBean;
    }

    @Bean // this is causing JSF deployment issue
    public WsConfigurerAdapter csfWsConfigurerAdapter() {
        return new WsConfigurerAdapter() {
            @Override
            public void addInterceptors(List<EndpointInterceptor> interceptors) {
                if (interceptors == null) {
                    interceptors = new ArrayList<>();
                }
                MyPayloadValidatingInterceptor validatingInterceptor = new MyPayloadValidatingInterceptor();
                validatingInterceptor.setValidateRequest(true);
                validatingInterceptor.setValidateResponse(false);
                validatingInterceptor.setSchemas(getSchemas());
                try {
                    validatingInterceptor.afterPropertiesSet();
                } catch (Exception e) {}
                interceptors.add(validatingInterceptor);
            }
        };
    }
}

I am using prime faces with JSF so important dependency are listed below

<properties>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <main.basedir>${project.basedir}/..</main.basedir>
        <spring.boot.version>2.6.3</spring.boot.version>
        <primefaces.version>5.3</primefaces.version>
        <faces.version>2.3.9</faces.version>        
    </properties>
...
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.primefaces</groupId>
            <artifactId>primefaces</artifactId>
            <version>${primefaces.version}</version>
        </dependency>        
        <dependency>
            <groupId>org.glassfish</groupId>
            <artifactId>javax.faces</artifactId>
            <version>${faces.version}</version>
        </dependency>
        <dependency>
           <groupId>org.apache.tomcat.embed</groupId>
           <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>

faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config 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/web-facesconfig_2_2.xsd"
    version="2.2">

    <application>
        <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
    </application>

</faces-config>

Faces/JSF Configuration

@Configuration
public class MyJSFConfig implements ServletContextAware, WebMvcConfigurer {

    @Bean
    public ServletRegistrationBean<FacesServlet> servletRegistrationBean() {
        ServletRegistrationBean<FacesServlet> servletRegistrationBean = new ServletRegistrationBean<>(
                new FacesServlet(), "*.jsf", "*.xhtml");
        servletRegistrationBean.setName("FacesServlet");
        servletRegistrationBean.setLoadOnStartup(1);
        return servletRegistrationBean;
    }

    @Bean
    public ServletListenerRegistrationBean<ConfigureListener> jsfConfigureListener() {
        return new ServletListenerRegistrationBean<ConfigureListener>(new ConfigureListener());
    }

    @Override
    public void setServletContext(ServletContext servletContext) {
        // spring boot only works if this is set
        // Iniciar el contexto de JSF
        // http://stackoverflow.com/a/25509937/1199132
        servletContext.setInitParameter("com.sun.faces.forceLoadConfiguration", Boolean.TRUE.toString());
        servletContext.setInitParameter("javax.faces.FACELETS_SKIP_COMMENTS", Boolean.TRUE.toString());       
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/my").setViewName("forward:/my/index.jsf");
    }

I am thinking this is classloading issue for springboot. WsConfigurerAdapter is used DelegatingWsConfiguration and configured conditional on missing bean in WebServicesAutoConfiguration. We are on latest springboot release



Solution 1:[1]

It is more related to how spring post bean processing works. After careful debugging found out ServletContextAware method was not called due to that servletcontext was not initialized and Faces Context was not initialized properly.

@Override
    public void setServletContext(ServletContext servletContext) {
        // spring boot only works if this is set
        // Iniciar el contexto de JSF
        // http://stackoverflow.com/a/25509937/1199132
        servletContext.setInitParameter("com.sun.faces.forceLoadConfiguration", Boolean.TRUE.toString());
        servletContext.setInitParameter("javax.faces.FACELETS_SKIP_COMMENTS", Boolean.TRUE.toString());       
    }

I changed to ServletContextInitializer which gets called on startup and initialize required values properly.

Below question answer was helpful in identifying. setServletContext not activating inside @Configuration file

Full working sample project as below location https://github.com/gaurangparmar/my-springboot-jsf

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Gaurang Parmar