7

I'm having some problems with Spring Boot and JSF. The servlet appears to start up correctly, but when I attempt to access a resource I get the following exception

java.lang.IllegalStateException: Could not find backup for factory javax.faces.context.FacesContextFactory. 
    at javax.faces.FactoryFinder$FactoryManager.getFactory(FactoryFinder.java:1011)
    at javax.faces.FactoryFinder.getFactory(FactoryFinder.java:343)
    at javax.faces.webapp.FacesServlet.init(FacesServlet.java:302)
    at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1284)
    at org.apache.catalina.core.StandardWrapper.allocate(StandardWrapper.java:884)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:134)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
    at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:683)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1720)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1679)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:724)

My Application class is as follows

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }

    @Bean
    public FacesServlet facesServlet() {
        return new FacesServlet();
    }

    @Bean
    public ServletRegistrationBean facesServletRegistration() {
        ServletRegistrationBean registration = new ServletRegistrationBean(
            facesServlet(), "*.xhtml");
        registration.setName("Christmas");
        return registration;
    }

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

I have no web.xml or faces-config.xml, and my pom.xml is as follows

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.x.y.z</groupId>
    <artifactId>project</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.5.RELEASE</version>
    </parent>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.primefaces</groupId>
            <artifactId>primefaces</artifactId>
            <version>5.0</version>
        </dependency>

        <!-- JSF 2 -->
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-api</artifactId>
            <version>2.1.11</version>
        </dependency>

        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-impl</artifactId>
            <version>2.1.11</version>
        </dependency>
    </dependencies>

</project>

I have a suspicion that there are some conflicts in the dependencies relating to the jsf api, but I can't seem to figure out where. Any help on fixing this issue would be greatly appreciated.

Jonathan Viccary
  • 722
  • 1
  • 9
  • 27
  • I have removed the servlet dependencies and i am still having the same issue. In what way is my runtime classpath a mess? How can I clean it? – Jonathan Viccary Aug 25 '14 at 06:41
  • Thanks, I believe I have my pom configured in the manner that the link suggests. I have the 2 dependencies on jsf-api and jsf-impl. I have even tried making the jsf-api dependency have "provided" scope, but the issue persists. – Jonathan Viccary Aug 25 '14 at 06:50
  • I suggest taking a look at [this project](https://github.com/stephanrauh/JSF-on-Spring-Boot). Also see http://stackoverflow.com/questions/24671624/jsf-annotations-dont-work-with-spring-boot. – M. Deinum Aug 25 '14 at 08:37

2 Answers2

18

To get JSF working on Spring Boot without a web.xml or faces-config.xml you need to force it to load its configuration files via an init parameter on the ServletContext. An easy way to do that is to implement ServletContextAware:

public class Application implements ServletContextAware {

    // ...

    @Override
    public void setServletContext(ServletContext servletContext) {
        servletContext.setInitParameter("com.sun.faces.forceLoadConfiguration", Boolean.TRUE.toString());           
    }
}

JSF's ConfigureListener also has a dependency on JSP, so you'll need to add a dependency on Jasper to your pom:

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
</dependency>

It's not directly related to your problem, but you don't need to declare FacesServlet as a bean. The ServletRegistrationBean is sufficient.

This leaves Application.java looking as follows:

import javax.faces.webapp.FacesServlet;
import javax.servlet.ServletContext;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.embedded.ServletListenerRegistrationBean;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.ServletContextAware;

import com.sun.faces.config.ConfigureListener;

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class Application implements ServletContextAware {

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }

    @Bean
    public ServletRegistrationBean facesServletRegistration() {
        ServletRegistrationBean registration = new ServletRegistrationBean(
            new FacesServlet(), "*.xhtml");
        registration.setLoadOnStartup(1);
        return registration;
    }

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

    @Override
    public void setServletContext(ServletContext servletContext) {
        servletContext.setInitParameter("com.sun.faces.forceLoadConfiguration", Boolean.TRUE.toString());       
    }
}
Andy Wilkinson
  • 108,729
  • 24
  • 257
  • 242
  • Yes, I did. The OP is using Spring Boot and an embedded servlet container, i.e. it's Spring Boot that is initialising Tomcat. Crucially, setServletContext is called _before_ ConfigureListener is driven which allows the init parameter to take effect. – Andy Wilkinson Aug 26 '14 at 15:50
  • How should be [Application] without [@EnableAutoConfiguration] annotation ? I used your solution without this annotation and it still needs the two files [web.xml] and [faces-config.xml]. See this post http://stackoverflow.com/questions/22544214/spring-boot-and-jsf-primefaces-richfaces/31026268#31026268. – Serge Tahé Jun 24 '15 at 12:18
  • The forceLoadConfiguration parameter did solve the problem of the "backup for factory" Exception in my project (packaged jar), but now it doesn't find the .xhtml files and returns 404 error. Previously, in my project without the forceLoadConfiguration I everything worked running "mvn spring-boot:run" but failed when packaging it and runnning it in an independent jar with the dependencies. – Cenobyte321 Aug 17 '15 at 18:40
  • Disregard what I posted, I just had to change packaging from jar to war, it now works perfectly. – Cenobyte321 Aug 18 '15 at 04:14
  • @Cenobyte321 : what did you do exactly ? I ran in the same problem : packaging the application with its dependencies in an unique jar, executing this jar and bumping into the error [java.lang.IllegalStateException: Could not find backup for factory javax.faces.context.FacesContextFactory.] When run into the Netbeans IDE (without packaging), the application works. – Serge Tahé Sep 13 '16 at 10:11
  • Change jar to war in the pom.xml. I have a Github project with everything set up: https://github.com/cenobyte321/Spring-Boot-JSF-Example – Cenobyte321 Sep 13 '16 at 14:45
1

probably you forget add listener com.sun.faces.config.ConfigureListener.class :

  @Bean
    public ServletContextInitializer servletContextInitializer() {

        return sc -> {
            ***sc.addListener(com.sun.faces.config.ConfigureListener.class);***
            sc.setInitParameter("com.sun.faces.expressionFactory", "com.sun.el.ExpressionFactoryImpl");
            sc.setInitParameter("com.sun.faces.forceLoadConfiguration", Boolean.TRUE.toString());
            sc.setInitParameter("facelets.DEVELOPMENT", Boolean.TRUE.toString());
            sc.setInitParameter("javax.faces.DEFAULT_SUFFIX", ".xhtml");
            sc.setInitParameter("javax.faces.FACELETS_LIBRARIES", "springsecurity.taglib.xml");
            sc.setInitParameter("javax.faces.FACELETS_REFRESH_PERIOD", "1");
            sc.setInitParameter("javax.faces.FACELETS_SKIP_COMMENTS", Boolean.TRUE.toString());
            sc.setInitParameter("javax.faces.PARTIAL_STATE_SAVING_METHOD", Boolean.TRUE.toString());
            sc.setInitParameter("javax.faces.PROJECT_STAGE", "Development");
            sc.setInitParameter("javax.faces.STATE_SAVING_METHOD", "server");
            sc.setInitParameter("primefaces.CLIENT_SIDE_VALIDATION", Boolean.TRUE.toString());
            sc.setInitParameter("primefaces.FONT_AWESOME", Boolean.TRUE.toString());
            sc.setInitParameter("primefaces.THEME", "Omega");
        };
    }
Iakov Senatov
  • 151
  • 1
  • 5