I'm using spring CDI and a customized "View" scope. (See this about how it works.)
The view bean is annotated with JSR-303 validation rules as following:
#Scope("view")
public class MyBean implements Serializable {
String message;
#NotNull
#Size(min = 10)
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public void action1() {
...
}
}
And the user form:
...
<h:form id="form1">
<h:inputText name="message" value="${myBean.message}" />
<p:commandButton value="Update" actionListener="${myBean.action1}" />
</h:form>
However, the validation doesn't work. Am I missing something in faces-config.xml? I guess there should be some proxy classes involved in, which maybe generated by AspectJ weaver or so. Right?
JSR 303 validation for JSF is auto enabled if you add implementation jar to classpath.
Related
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;
}
}
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"........
I have a Spring MVC #Controller (with a Spring Validator) and a JSF page.
What I want is to show validation errors in the JSF page using the JSF <h:messages /> tag.
As far as I know:
MessageContext + MessageBuilder works for webflows but it is not available for #Controller
I've read the Spring documentation about Validation and it seems (last paragraph) the only way is using Spring MVC tags.
¿Is there a way to show errors with <h:messages /> from my #Controller Validator without using Spring MVC tags?
Not by default there isn't.
What you could do is create a HandlerInterceptor which implements the postHandle method (I assume you use normal Spring MVC things for everything). In this method you could lookup all model attributes of the type BindingResult, retrieve all the errors from it and transfer them to JSF.
Something like this should work (haven't tested it and depends on what you use furthermore)
public class ErrorTransferingInterceptor extends HandlerInterceptorAdapter implements MessageSourceAware {
private MessageSource messageSource;
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
FacesContext ctx = FacesContext.getCurrentInstance();
for (Object o : modelAndView.getModel().values() ) {
if (o instanceof Errors) {
Errors result = (Errors) o;
for (ObjectError err : result.getAllErrors() ) {
String msg = messageSource.getMessage(err, LocaleContextHolder.getLocale());
if (err instanceof FieldError) {
ctx.addMessage(((FieldError) err).getField(), new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, null));
} else {
ctx.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, null));
}
}
}
}
}
#Override
public void setMessageSource(MessageSource messageSource) {
this.messageSource=messageSource;
}
}
After fooling around with Spring and JSF messages integration, finally i found the easiest way was:
Add faces messages directly in the application controller (though #M. Deinum shows an architectural solution)
Use jsf validators instead of spring validators, still being possible to access the spring context
That works and i think it's a clean integration.
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>
How can I validate the entered username in an input text field if it is available according to the database? I am using JSF2.
Just implement a Validator yourself.
#ManagedBean
#RequestScoped
public class UserNameAvailableValidator implements Validator {
#EJB
private UserService userService;
#Override
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
String userName = (String) value;
if (!userService.isUsernameAvailable(userName)) {
throw new ValidatorException(new FacesMessage("Username not avaliable"));
}
}
}
(please note that it's a #ManagedBean instead of #FacesValidator because of the need to inject an #EJB; if you're not using EJBs, you can make it a #FacesValidator instead)
Use it as follows:
<h:inputText id="username" value="#{register.user.name}" required="true">
<f:validator binding="#{userNameAvailableValidator}" />
<f:ajax event="blur" render="username_message" />
</h:inputText>
<h:message id="username_message" for="username" />