Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot JSF Integration

Environment :

Tomcat 8

Spring Boot 1.5

JSF 2.2

Apache MyFaces

Spring MVC

Code :

I am integrating Spring Boot and JSF 2.2 in Servlet 3.0 environment.

Config Classes :

JSFConfig.java - Config for JSF.

@Configuration
@ComponentScan({"com.atul.jsf"})
public class JSFConfig {

        @Bean
        public ServletRegistrationBean servletRegistrationBean() {
            FacesServlet servlet = new FacesServlet();
            return new ServletRegistrationBean(servlet, "*.jsf");
        }

}

Spring Boot Main Class :

@SpringBootApplication
@Import({ // @formatter:off 
    JPAConfig.class,
    ServiceConfig.class, // this contains UserServiceImpl.java class.
    WebConfig.class,
    JSFConfig.class,
})
public class SpringbootJpaApplication extends SpringBootServletInitializer{

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

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

Managed Bean :

UserBean.java - Managed Bean for JSF

@ManagedBean
@SessionScoped
public class UserBean implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String name;
    @ManagedProperty(value="#{userServiceImpl}")
    private UserServiceImpl userServiceImpl;

    public void addUser(){      
        System.out.println("User Gets added "+this.name);       
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public UserServiceImpl getUserServiceImpl() {
        return userServiceImpl;
    }

    public void setUserServiceImpl(UserServiceImpl userServiceImpl) {
        this.userServiceImpl = userServiceImpl;
    }
}

Facelets :

home.xhtml - home page

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:h="http://java.sun.com/jsf/html">

    <h:head>
        <title>JSF 2.0 Hello World</title>
    </h:head>
    <h:body>
        <h2>JSF 2.0 Hello World Example - hello.xhtml</h2>
        <h:form>
           <h:inputText value="#{userBean.name}"></h:inputText>
           <h:commandButton value="Submit" action="#{userBean.addUser}"></h:commandButton>
        </h:form>
    </h:body>
</html>

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>
    <lifecycle>
        <phase-listener>org.springframework.web.jsf.DelegatingPhaseListenerMulticaster</phase-listener>
    </lifecycle>

</faces-config>

Issue :

1)when I submit form in home.xhtml , userBean.addUser gets called. 2)userBean.name gets set with values entered by user. 3)But userServiceImpl is NULL. 4)Does that mean that Spring and JSF is not getting integrated ? I have also registered SpringBeanFacesELResolver as mentioned in

faces-config.xml 

I also tried removing all JSF specific annotations from UserBean.java and used only Spring specific annotations like below -

 @Component
    @SessionScoped 
    public class UserBean implements Serializable{

        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        private String name;
        @Autowired
        private UserServiceImpl userServiceImpl;


    }

But when I submit form , I am getting target Unreachable error for #{userBean) . That means userBean is not discoverable for Spring

5)Am I missing anything here ? 6)I am not using embedded tomcat provided with Spring Boot

like image 876
Atul Avatar asked Sep 13 '17 02:09

Atul


People also ask

Can Spring Boot be used for JSF applications?

Spring Boot can leverage any type of app, not only microservices. Let's build a JSF application with Spring Boot, covering integration and configuration along the way. Join the DZone community and get the full member experience.

What is the JSF integration for Spring Web Flow?

13. JSF Integration 13.1. Introduction Spring Web Flow provides a JSF integration that lets you use the JSF UI Component Model with Spring Web Flow controllers. Web Flow also provides a Spring Security tag library for use in JSF environments, see Section 13.9, “Using the Spring Security Facelets Tag Library” for more details.

How to use JSF managed beans as spring beans?

In Spring Boot application, JSF management Bean can also be declared to be Spring bean. SpringBeanFacesELResolver allows us to use JSF managed beans as Spring beans and also to inject other spring beans there.

How do I reference a JSF XHTML file in Spring Boot?

We will create faces-config.xml in in the src/main/resources/META-INF/faces-config.xml and add the el-resolver and phase-listener. To demonstrate that a JSF xhtml file can reference both a spring bean and a JSF managed bean we will create index.xhtml file in spring boot default location src/main/webapp.


1 Answers

This is the way I have JSF working with Spring Boot (full sample project at Github, updated with JSF 2.3 and Spring Boot 2):

1. Dependencies

In addition to the standard web starter dependency, you'll need to include the tomcat embedded jasper marked as provided (thanks @Fencer for commenting in here). Otherwise you'll get an exception at application startup, because of JSF depending on JSP processor (see also the first link at the end of my answer).

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

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

2. Servlet registration

Register the JSF servlet and configure it to load on startup (no need of web.xml). If working with JSF 2.2, your best is to use *.xhtml mapping, at least when using facelets:

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

Make your configuration class implement ServletContextAware so that you can set your init parameters. Here you must force JSF to load configuration:

@Override
public void setServletContext(ServletContext servletContext) {
    servletContext.setInitParameter("com.sun.faces.forceLoadConfiguration",
            Boolean.TRUE.toString());
    servletContext.setInitParameter("javax.faces.FACELETS_SKIP_COMMENTS", "true");
    //More parameters...
}

3. EL integration

Declare the EL resolver in the faces-config.xml. This is going to be the glue between your view files and your managed bean properties and methods:

<?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>

4. The View Scope

Write a custom Spring scope to emulate the JSF view scope (keep in mind your beans are going to be managed by Spring, not JSF). It should look some way like this:

public class ViewScope implements Scope {

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        Map<String, Object> viewMap = FacesContext.getCurrentInstance()
            .getViewRoot().getViewMap();
        if (viewMap.containsKey(name)) {
            return viewMap.get(name);
        } else {
            Object object = objectFactory.getObject();
            viewMap.put(name, object);
            return object;
        }
    }

    @Override
    public String getConversationId() {
        return null;
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {

    }

    @Override
    public Object remove(String name) {
        return FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(name);
    }

    @Override
    public Object resolveContextualObject(String arg0) {
        return null;
    }

}

And register it in your configuration class:

@Bean
public static CustomScopeConfigurer viewScope() {
    CustomScopeConfigurer configurer = new CustomScopeConfigurer();
    configurer.setScopes(
            new ImmutableMap.Builder<String, Object>().put("view", new ViewScope()).build());
    return configurer;
}

5. Ready to go!

Now you can declare your managed beans the way below. Remember to use @Autowired (preferably in constructors) instead of @ManagedProperty, because you're dealing with Spring Beans.

@Component
@Scope("view")
public class MyBean {

    //Ready to go!

}

Still pending to achieve

I couldn't get JSF specific annotations work in the Spring Boot context. So @FacesValidator, @FacesConverter, @FacesComponent, and so on can't be used. Still, there's the chance to declare them in faces-config.xml (see the xsd), the old-fashioned way.


See also:

  • Spring Boot with JSF; Could not find backup for factory javax.faces.context.FacesContextFactory
  • Porting JSF 2.0’s ViewScope to Spring 3.0
  • JSP file not rendering in Spring Boot web application
like image 145
Xtreme Biker Avatar answered Oct 01 '22 07:10

Xtreme Biker