JSF cant resolve bean in xhtml file using spring boot - spring

i have a simple JSF xhtml page contains xml:
<h:body>
<h:outputText value='#{someBean == null ? "null" : "not null"}'/>
</h:body>
my faces-config contains:
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
and i have a java configuration bean like
#Bean
public ServletRegistrationBean jsfServletRegistration (ServletContext servletContext) {
servletContext.setInitParameter("com.sun.faces.forceLoadConfiguration", Boolean.TRUE.toString());
ServletRegistrationBean srb = new ServletRegistrationBean();
srb.setServlet(new FacesServlet());
srb.setUrlMappings(Collections.singletonList("*.xhtml"));
srb.setLoadOnStartup(1);
return srb;
}
and a bean like:
#Component
#ViewScoped
#ManagedBean
public class SomeBean {
public String message() {
return "some";
}
}
here, when i go to the page i get null string as response...
note: i can access my pages using browser.
why cant i get someBean inside xhtml file ?

Related

#ViewScoped bean behaves like #SessionScoped or #ApplicationScoped

I have a Spring Boot application using JSF/CDI. Backing beans are annotated with #Named #ViewScoped, but while browsing between other pages values still stay on textboxes. Values should've been cleared. In other words, the backing beans unexpectedly behave like #SessionScoped or #ApplicationScoped.
My input
<p:inputText value="#{parameterBean.spreadValue}" label=" "
styleClass="inputText" id="spreadValue">
<p:ajax event="change" />
<p:ajax event="valueChange" />
</p:inputText>
My bean
#Named("parameterBean")
#ViewScoped
public class parameterBean implements Serializable {
// ...
}
This is JSF configuration
#Configuration
public class JSFConfig {
#Bean
ServletRegistrationBean<FacesServlet> jsfServletRegistration(ServletContext servletContext) {
servletContext.setInitParameter("com.sun.faces.forceLoadConfiguration", Boolean.TRUE.toString());
// FacesServlet registration
ServletRegistrationBean<FacesServlet> srb = new ServletRegistrationBean<>();
srb.setServlet(new FacesServlet());
srb.setUrlMappings(Arrays.asList("*.xhtml"));
srb.setLoadOnStartup(1);
return srb;
}
}

How to pass Injected/Autowired object from Spring to ManagedBean?

I am working on a project with Spring and EJB/Primefaces and I want to pass values from the spring context to a managed bean. I will demonstrate with a sample code to clarify further.
Let's say we have the following domain class (I keep it simple for better readability):
public class Store {
#JsonProperty("store_name")
private String storeName;
//constructors, getters and setters...
}
The reason of #JsonProperty is because I am getting this value from an other application that POSTs a Json to the following Controller:
#Controller
#RequestMapping("/store")
public class StoreController {
#Autowired
private Store store;
#RequestMapping(method = RequestMethod.POST)
public String getStoreResponse(#RequestBody String store) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
LOGGER.info("Store Before Post: " + store.getName());
store = mapper.readValue(request, Store.class);
LOGGER.info("Store After Post: " + store.getName());
return "store";
}
}
I have configured the store bean at a BeanConfig class:
#Configuration
public class BeanConfig {
#Bean(name = "store")
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Store store() {
Store store = new Store();
store.setName("Test Store Name");
return store;
}
}
This is my managed bean:
#ManagedBean
#SessionScoped
public class StoreView extends SpringBeanAutowiringSupport {
private static final Logger LOGGER = LoggerFactory.getLogger(Store.class);
//#ManagedProperty("#{store}")
#Autowired
private Store store;
public void test() {
LOGGER.info("TEST " + store.getName());
}
//getters and setters
}
finally my xhtml:
<h:panelGrid columns="3">
<p:outputLabel for="j_store" value="#{messages['storeview.name']}" />
<p:inputText id="j_store" value="#{storeView.store.name}" />
<p:message for="j_store" />
<h:panelGroup />
<p:commandButton value="#{messages['storeview.test']}" action="#{storeView.test}" update="#form" ajax="false" />
</h:panelGrid>
When I am posting sample data using postman, the first time, the logger outputs:
10:35:57,433 INFO [com.store.test.controllers.StoreController] (default task-2) Store Before Post: Test Store Name
10:35:57,488 INFO [com.store.test.controllers.StoreController] (default task-2) Store After Post: posted store name
and if I continue calling the controller I keep getting the "posted store name", so it has kept the value.
But when I am going to the store.xhtml and hit the test button to submit the form, it still has the value set in the bean configuration file ("Test Store Name") and from that point on it keeps the value that I submit in the inputText.
I suspect it has to do with Spring and Faces context, I do not know if what I want to do is possible. If it is, please point out what should I change to make it work, otherwise, please provide me with an alternative solution.
Thanks in advance.
You are mixing #Autowired and #ManagedBean annotations.
#Autowired is managed by Spring while #ManagedBean is managed by JSF.
That means that probably you will have 2 instances of Store, the one modified by controller is not the same instance used by the managed bean.
You should annotate as #ManagedProperty("#{store}") your store attribute in managed bean and define getter and setter.
To get it to work you also must define spring Expression Language resolver in faces-config.xml
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
Since jsf session is different from mvc session, you also have to use singleton scope in the definition of Store object.
#Scope(value = "singleton"........

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

Integrating JSF managed bean annotations with Spring Boot

I use Spring boot with JSF 2.2. My problem is that I can create #ManagedBean from javax.annotation.ManagedBean and it is working in my index.xhtml when I run the app, but when I want to use javax.faces.bean.ManagedBean is not displaying the value. What's the difference between those two? Why I can't use the javax.faces.bean.ManagedBean? ( I don't have web.xml file, all is configured in classes)
The javax.annotation.* annotations are meant to be a move from the classic JSF annotations to a CDI approach. The Spring Framework has the ability to read some CDI annotations, so that could be the reason why this annotation "works". However, the trend in CDI is to use #Named, overall.
In a Spring Boot application, it's Spring the one scanning your annotations, not JSF. So, even you could think that the application works with #ManagedBean, you'll see that the #*Scoped annotations are useless, because all the created beans happen to be singletons, which is Spring's default scope.
In the end the choice I made was to use vanilla Spring annotations and scopes. As Spring lacks the JSF view scope, also a custom scope to emulate it.
MyBean.java:
#Component
#Scope("view")
public class MyBean {
//Here it goes your logic
}
ViewScope.java:
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 arg0, Runnable arg1) {
}
#Override
public Object remove(String name) {
return FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(name);
}
#Override
public Object resolveContextualObject(String arg0) {
return null;
}
}
Register the view scope with a CustomScopeConfigurer:
#Bean
public static CustomScopeConfigurer viewScope() {
CustomScopeConfigurer configurer = new CustomScopeConfigurer();
configurer.setScopes(
new ImmutableMap.Builder<String, Object>().put("view", new ViewScope()).build());
return configurer;
}
Finally, do not forget to add the Spring EL resolver in your faces-config.xml to make the Spring beans available through EL expressions:
<application>
<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
See also:
Why are there different bean management annotations
Backing beans (#ManagedBean) or CDI Beans (#Named)?
Configuring Spring Boot with JSF

Using JSF2 #ManagedProperty in Spring managed backing bean

I have a JSF backing bean that is Spring managed but I would like to be able to make use of the #ManagedProperty from JSF. The following does not work:
#Component
#Scope(Scopes.REQUEST)
public class MyRequestBean {
#ManagedProperty(value="#{param.bcIndex}")
private int bcIndex;
public int getBcIndex() {
return bcIndex;
}
public void setBcIndex(int bcIndex) {
this.bcIndex = bcIndex;
}
}
Suggestions?
Actually it is quite simple. I know of three ways to do your injection:
Use Spring's #Value annotation together with implicit El #{param} object:
#Value("#{param.bcIndex}")
private int bcIndex;
Make use of ExternalContext#getRequestParameterMap in a #PostConstruct / preRenderView listener:
//#PostConstruct
public void init() {
bcIndex = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("bcIndex");
}
Make a binding in your view utilizing <f:viewParam>:
<f:metadata>
<f:viewParam name="index" value="#{myRequestBean.bcIndex}" />
</f:metadata>

Resources