JSF injecting and accessing Session Scoped ManagedBean in a Request Scoped Bean - session

I have a #SessionScoped ManagedBean that I've injected into a #RequestScoped to access the user stored in the session. My code is working I just want to know if I'm using a good practice, if not can you tell me what's wrong please? Because I'm new to JSF and I don't want to learn some bad coding from the beginning, thank you very much in advance.
My Entity Utilisateur :
#Entity
public class Utilisateur {
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY )
private Long id;
#NotNull( message = "Veuillez saisir une adresse email" )
#Pattern( regexp = "([^.#]+)(\\.[^.#]+)*#([^.#]+\\.)+([^.#]+)", message = "Merci de saisir une adresse mail valide" )
private String email;
#Column( name = "mot_de_passe" )
#NotNull( message = "Veuillez saisir un mot de passe" )
#Pattern(regexp = ".*(?=.{8,})(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).*", message = "Le mot de passe saisi n'est pas assez sécurisé")
private String motDePasse;
#NotNull( message = "Veuillez saisir un nom d'utilisateur" )
#Size( min = 3, message = "Le nom d'utilisateur doit contenir au moins 3 caractères" )
private String nom;
#Column( name = "date_inscription" )
private Timestamp dateInscription;
//getters .. setters..
}
My Entity Ferme :
#Entity
public class Ferme {
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY )
#Column( name = "id_ferme" )
private Long id_ferme;
#Column( name = "nom_ferme" )
private String nom_ferme;
#ManyToOne
#JoinColumn( name = "utilisateur_id" )
private Utilisateur utilisateur;
//getters .. setters..
}
My #Statless DAO :
#Stateless
public class UtilisateurDao {
#PersistenceContext( unitName = "myBD_PU" )
private EntityManager em;
public List<Ferme> lister( Utilisateur user) throws DAOException {
try {
TypedQuery<Ferme> query = em.createQuery( "SELECT u FROM Ferme u WHERE u.utilisateur = :userid", Ferme.class );
query.setParameter("userid", user);
return query.getResultList();
} catch ( Exception e ) {
throw new DAOException( e );
}
}
}
My LoginBean :
#ManagedBean
#SessionScoped
public class LoginBean implements Serializable {
private static final long serialVersionUID = 1L;
private String email,mdp;
private Utilisateur user;
private boolean LoggedIn;
#EJB
UtilisateurDao utilisateurDao;
// getters .. setters
public String authentification() {
if (utilisateurDao.login(email, mdp) != null) {
user = utilisateurDao.login(email, mdp);
LoggedIn = true;
return "listeFermes.xhtml?faces-redirect=true";
}
LoggedIn = false;
FacesMessage message = new FacesMessage( "E-mail ou Mot de passe incorrecte!" );
FacesContext.getCurrentInstance().addMessage( null, message );
return "";
}
public String logout() {
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
return "/login.xhtml?faces-redirect=true";
}
}
My ListeFermesBean :
#ManagedBean
#RequestScoped
public class ListeFermesBean implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#ManagedProperty(value="#{loginBean}")
private LoginBean loginBean;
#EJB
UtilisateurDao utilisateurDao;
private Utilisateur user;
private List<Ferme> liste;
public List<Ferme> getListe() {
liste = new ArrayList<Ferme>();
user = loginBean.getUser();
return liste = utilisateurDao.lister(user);
}
}
Login.xhtml :
<!DOCTYPE html>
<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:form id="Login">
<fieldset>
<legend>Login</legend>
<h:outputLabel for="email">Adresse email <span class="requis">*</span></h:outputLabel>
<h:inputText id="email" value="#{loginBean.email}" size="20" maxlength="60">
</h:inputText>
<h:message id="emailMessage" for="email" errorClass="erreur" />
<br />
<h:outputLabel for="motdepasse">Mot de passe <span class="requis">*</span></h:outputLabel>
<h:inputSecret id="motdepasse" value="#{loginBean.mdp}" size="20" maxlength="20">
</h:inputSecret>
<h:message id="motDePasseMessage" for="motdepasse" errorClass="erreur" />
<br />
<h:messages globalOnly="true" infoClass="erreur" />
<h:commandButton value="Login" action="#{loginBean.authentification}" styleClass="sansLabel">
</h:commandButton>
<br />
<h:commandButton value="Logout" action="#{loginBean.logout}" styleClass="sansLabel" />
<br />
<h:link value="Inscrivez-vous" outcome="inscription" />
</fieldset>
</h:form>
</h:body>
</html>
And finally the listeFermes.xhtml page which displays the List from listeFermesBean by User id stored in the object User in the session.
<!DOCTYPE html>
<html lang="fr"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:c="http://java.sun.com/jstl/core">
<h:head>
<title>SUCCES</title>
</h:head>
<h:body>
<ui:fragment rendered= "#{!loginBean.loggedIn}">
Not logged !
</ui:fragment>
<ui:fragment rendered= "#{loginBean.loggedIn}">
Welcome : #{loginBean.user.nom} <br />
E-mail : #{loginBean.user.email} <br />
<table border="1">
<tr>
<td>Nom Ferme</td>
<td>Nom User</td>
<td>ID User</td>
</tr>
<c:forEach items="#{ listeFermesBean.liste }" var="x">
<tr>
<td>#{x.nom_ferme}</td>
<td>#{x.utilisateur.nom}</td>
<td>#{x.utilisateur.id}</td>
</tr>
</c:forEach>
</table>
</ui:fragment>
</h:body>
</html>

As said in the comment you should use cdi injection. I believe this is a big no no as well:
public List<Ferme> getListe() {
liste = new ArrayList<Ferme>();
user = loginBean.getUser();
return liste = utilisateurDao.lister(user);
}
You should not do any business intensive things in your getters/setters. The reason is those can be called multiple times in the background.
Instead you should call your services in a method that is called AFTER the service has been injected.
#PostConstruct
public void init(){
listeFerm = utilisateurDao.lister(user);
}
public List<Ferm> getListFerm(){
return listFerm;
}
You didn't post your auth method(probably on purpose though).
Reguarding your Auth system you said you will deal with this after but still you don't need to go through the DAO with that. You should read about JAAS in the doc which is how to deal with this automatically then you don't need to go through a service and you can authenticate users in the bean. ie: Request.login(username, password) if my memory serves me right. You have to read about the subject though, you should use hash + salt when authenticating users.

#Named
#SessionScoped
public class LoginBean implements Serializable {
private static final long serialVersionUID = 1L;
private String email,mdp;
private Utilisateur user;
private boolean LoggedIn;
private List<Ferme> liste;
private Ferme ferme = new Ferme();
#Inject
UtilisateurDao utilisateurDao;
public Ferme getFerme() {
return ferme;
}
public void setFerme(Ferme ferme) {
this.ferme = ferme;
}
public void setListe(List<Ferme> liste) {
this.liste = liste;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getMdp() {
return mdp;
}
public void setMdp(String mdp) {
this.mdp = mdp;
}
public Utilisateur getUser() {
return user;
}
public void setUser(Utilisateur user) {
this.user = user;
}
public boolean isLoggedIn() {
return LoggedIn;
}
public void setLoggedIn(boolean loggedIn) {
LoggedIn = loggedIn;
}
public String authentification() {
if (utilisateurDao.login(email, mdp) != null) {
user = utilisateurDao.login(email, mdp);
LoggedIn = true;
return "listeFermes.xhtml?faces-redirect=true";
}
LoggedIn = false;
FacesMessage message = new FacesMessage( "Wrong E-mail or Password!" );
FacesContext.getCurrentInstance().addMessage( null, message );
return "";
}
public String logout() {
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, new FacesMessage("You are disconnected!"));
ExternalContext externalContext = context.getExternalContext();
externalContext.getFlash().setKeepMessages(true);
externalContext.invalidateSession();
return "/login.xhtml?faces-redirect=true";
}
public void deleteFerme(int id) {
utilisateurDao.supprimerFerme(user, id);
}
public void addFerme() {
utilisateurDao.creerFerme(ferme, user);
}
public List<Ferme> getListe() {
return liste;
}
#PostConstruct
public void init(){
liste = utilisateurDao.lister(user);
}
// Should I declare my Ferme and then initilize it new Ferme(); in the #PostConstruct too ? or leave it like it is ?
// & should I use the CRUD methods that use my "User" object stored in the session, in the #SessionScope bean? and then call the methods in my #RequestScoped bean? like this :
#SessionScope
public class LoginBean {
..
..
public void addFerme() {
utilisateurDao.creerFerme(ferme, user);
}
}
//////..... and then in the RequestScoped bean :
#RequestScoped
..
..
#Inject
private LoginBean loginBean;
public void addFerme() {
loginBean.addFerme();
}
// login Method
#Stateless
public class UtilisateurDao {
#PersistenceContext( unitName = "MyBD_PU" )
private EntityManager em;
public Utilisateur login(String email, String mdp) throws DAOException {
Utilisateur user = null;
Query requete = em.createQuery( JPQL_LOGIN );
requete.setParameter( PARAM_EMAIL, email ).setParameter(PARAM_PWD, mdp);
try {
user = (Utilisateur) requete.getSingleResult();
if (user!=null) {
return user;
}
return null;
} catch ( NoResultException e ) {
return null;
} catch ( Exception e ) {
FacesMessage message = new FacesMessage( FacesMessage.SEVERITY_ERROR, e.getMessage(), null );
FacesContext facesContext = FacesContext.getCurrentInstance();
facesContext.addMessage( null, message );
}
return user;
}
}

Related

Class javax.el.BeanELResolver can not access a member of class java.util.Collections$UnmodifiableCollection in Spring Boot, JSP

I have this problem in my code, where I cannot submit a signup form in jsp. Here are the code intervening in the signup operation.
User.java
#Entity
#Table(name = "user", catalog = "spring_abc", uniqueConstraints = {#UniqueConstraint(columnNames = "email"),
#UniqueConstraint(columnNames = "username")})
public class User implements java.io.Serializable {
public static final String ROLE_ADMIN = "admin";
public static final String ROLE_MEMBER = "member";
private static final long serialVersionUID = 7376961072110620611L;
private Integer id;
private Long commentCount;
private Date createAt;
private String description;
#Email(message = "Entrez le format de l'email correct")
private String email;
private long number;
private String password;
public void setDepartement(String departement) {
this.departement = departement;
}
private long points;
private String role;
#Size(max = 60, message = "Pas plus de soixante caractères")
private String signature;
private Long topicCount;
#Size(max = 60, message = "Pas plus de soixante caractères")
private String twitter;
private Date updateAt;
#Pattern(regexp = "^(?!_)(?!.*?_$)[A-Za-z0-9|_]{3,9}", message = "Nom d'utilisateur, ne peut pas commencer ou se terminer par un trait de soulignement")
private String username;
private Set<Follow> followsForFollowerId = new HashSet<Follow>(0);
private Set<Notification> notifications = new HashSet<Notification>(0);
private Set<Topic> topics = new HashSet<Topic>(0);
private Set<Comment> comments = new HashSet<Comment>(0);
private Set<Focus> focuses = new HashSet<Focus>(0);
private Set<Collection> collections = new HashSet<Collection>(0);
private Set<Follow> followsForFollowingId = new HashSet<Follow>(0);
private Set<Comment> likeComments = new HashSet<Comment>();
The methode signup the service Part.
#Transactional
public User signup(User user, String password1, Errors errors) {
if (errors.hasErrors()) {
return null;
}
String username = user.getUsername();
String password = user.getPassword();
String email = user.getEmail();
if (userRepo.findOneByUsername(username) != null) {
errors.rejectValue("username", "username", "Nom d'utilisateur déjà enregistré");
} else if (userRepo.findOneByEmail(email) != null) {
errors.rejectValue("email", "email", "E-mail est déjà enregistré");
} else if (!password.equals(password1)) {
errors.rejectValue("password", "password", "Les mots de passe ne se correspondent pas");
}
if (errors.hasErrors()) {
return null;
}
user.setPassword(EncryptUtil.encryptUsernameAndPassword(username, password));
user.setCreateAt(new Date());
return userRepo.save(user);
}
Controller Account.java
#RequestMapping(value = "/signup", method = RequestMethod.POST)
public String singupAction(#Validated User user,
Errors erros,
String password1,
RedirectAttributes attributese,
HttpServletRequest request) {
User signinUser = userServ.signup(user, password1, erros);
if (erros.hasErrors()) {
attributese.addFlashAttribute("user", user);
attributese.addFlashAttribute("error", erros.getAllErrors());
return "redirect:/account/signup";
}
request.getSession().setAttribute("user", signinUser);
attributese.addFlashAttribute("msg", "Vous êtes inscrit avec succès");
return "redirect:/";
}
Signup.jsp, The problem appears when i try to validate information.
<form action="${x}/account/signup" data-toggle="validator" role="form" method="POST">
<input class="form-control" data-error="S'il vous plaît entrer une adresse email valide" placeholder="E-mail" type="email" id="email" name="email" value="${user.email}" />
<input class="form-control" id="username" data-error="Entre 6 et 18 caractères" pattern="^[_A-z0-9]{6,18}$" placeholder="Votre username" type="text" name="username" value="${user.username}" />
<input class="form-control" data-minlength="6" type="password" name="password" id="password" placeholder="Mot de passe" data-error="Entre 6 et 18 caractères" />
<input type="submit" class="btn btn-info btn-block" value="S'inscrire">
The stack I'm getting doesn't make any sense to me. It doesn't point out the problem
org.apache.jasper.JasperException: javax.el.ELException: java.lang.IllegalAccessException: Class javax.el.BeanELResolver can not access a member of class java.util.Collections$UnmodifiableCollection with modifiers "public"
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:492)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:378)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:684)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:501)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137)
Caused by: javax.el.ELException: java.lang.IllegalAccessException: Class javax.el.BeanELResolver can not access a member of class java.util.Collections$UnmodifiableCollection with modifiers "public"
at javax.el.BeanELResolver.invokeMethod(BeanELResolver.java:742)
at javax.el.BeanELResolver.invoke(BeanELResolver.java:470)
at javax.el.CompositeELResolver.invoke(CompositeELResolver.java:257)
You need to add getters and setters in your entity for each field.
e.g. private long number;
Should have defined following in your entity.
public long getNumber(){ return this.number;}
public void setNumber(long number){ this.number = number; }
from your stacktrace it is clear that it is trying to access fields with public getter method but it couldn't find it so thrown exception.

Error with NullPointerExcetion Native Method Accessor, Can give me a solution

i browse "http://localhost:8080/subject/form" for fill information in Subjectform.jsp, after will direct "http://localhost:8080/subject/add" for insert subject object in table subject. But it not insert subject object and encouter following error:
null<br/>
edu.java.spring.controller.SubjectController.addSubject(SubjectController.java:5
0)<br/>
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)<br/>
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)<br
/>
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.jav
a:43)<br/>
java.lang.reflect.Method.invoke(Method.java:497)<br/>
org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHa
ndlerMethod.java:215)<br/>
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(I
nvocableHandlerMethod.java:132)<br/>
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMet
hod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)<br/>
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapt
er.invokeHandleMethod(RequestMappingHandlerAdapter.java:749)<br/>
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapt
er.handleInternal(RequestMappingHandlerAdapter.java:689)<br/>
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(A
bstractHandlerMethodAdapter.java:83)<br/>
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.j
ava:938)<br/>
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.ja
va:870)<br/>
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet
.java:961)<br/>
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:86
3)<br/>
javax.servlet.http.HttpServlet.service(HttpServlet.java:707)<br/>
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:8
37)<br/>
javax.servlet.http.HttpServlet.service(HttpServlet.java:790)<br/>
org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:801)<br/>
org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:587)<br/>
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)<br
/>
org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:577)<br/>
org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223
)<br/>
org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:112
6)<br/>
org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515)<br/>
org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
<br/>
org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1060
)<br/>
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)<br
/>
org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerC
ollection.java:215)<br/>
org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java
:110)<br/>
org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:113)<
br/>
org.eclipse.jetty.server.Server.handle(Server.java:509)<br/>
org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:288)<br/>
org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:240)<br/>
org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:539)<br/>
org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:620)
<br/>
org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:540)<
br/>
java.lang.Thread.run(Thread.java:745)<br/>
Here file SubjectController.class
#Controller
public class SubjectController {
#Autowired
public StudentDAO studentDao;
public SubjectDao subjectDao;
#RequestMapping(value = "subject/form",method = RequestMethod.GET)
public ModelAndView subjectForm (){
ModelAndView model = new ModelAndView("SubjectForm", "subject", new Subject());
List<Student> students = studentDao.listStudents();
Map<Integer,String> map = new HashMap<Integer,String>();
for(int i=0;i<students.size();i++){
map.put(students.get(i).getId(), students.get(i).getName());
}
model.getModelMap().put("studentList",map);
return model;
}
#RequestMapping(value="subject/add",method = RequestMethod.POST)
public void addSubject(#Valid #ModelAttribute("subject")Subject subject){
// ModelAndView model = new ModelAndView("redirect:/subject/list");
subjectDao.insert(subject);
// return model;
}
Here file SubjectHibernateDaoImpl.class
public class SubjectHibernateDaoImpl implements SubjectDao {
#Autowired
public LocalSessionFactoryBean sessionFactory;
#Override
public void insert(Subject subject){
Session session = sessionFactory.getObject().openSession();
try {
session.save(subject);
session.flush();
} finally {
// TODO: handle finally clause
session.close();
}
}
public List<Subject> listSubject() {
// TODO Auto-generated method stub
Session session = sessionFactory.getObject().openSession();
Query query = session.createQuery("from Subject");
try {
return query.list();
} finally {
// TODO: handle finally clause
session.close();
}
}
Here file Subject.class
#Entity
#Table(name = "subject",uniqueConstraints={#UniqueConstraint(columnNames="id")})
public class Subject {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Column(name = "title", nullable = false, length = 200)
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
#Column(name = "student", nullable = false)
public int getStudent() {
return student;
}
public void setStudent(int student) {
this.student = student;
}
#Column(name = "score", nullable = false)
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public int id;
public String title;
public int student;
public int score;
}
Here file Subject.jsp
<html>
<head>
<title>Add New Subject Information</title>
</head>
<body>
<h2>Please Input Subject Information</h2>
<form:form method="POST" modelAttribute="subject" action="add">
<table>
<tr>
<td><form:label path="student">Student</form:label></td>
<td><form:input path="student" type = "number"/></td>
</tr>
<tr>
<td><form:label path="title">Title</form:label></td>
<td><form:input path="title"/></td>
</tr>
<tr>
<td><form:label path="score">Score</form:label></td>
<td><form:input path="score" type = "number"/></td>
</tr>
<tr>
<td colspan="3"><input type="submit" value="Submit"/></td>
</table>
</form:form>
</body>
</html>
Looks like you forgot to add the #Autowired annotation. It's present for the studentDao but not for the subjectDao, resulting in that object not being initialized.

Spring MVC test failure

I'm writing simple integration tests for my app using the Spring MVC Test framework. I have two basic test cases:
The Add link form is filled in correctly (the URL and optional description input fields are entered) and a link is added to a database via POST and then the client is redirected to the /link URL.
The Add link form is empty, so the /links/create view is rendered and form errors from BindingResult are presented.
The testAddLink() test passes successfully, but the problem occurs with the testAddEmptyLink() test method.
In this test, the 'links/create' view should be rendered and I should get a 200 status code once the expression
result.hasErrors()
is true.
However, I'm getting a 302 response, which should be sent when there are no errors in the form (the URL has been set correctly)
It seems the test method testAddEmptyLink() cannot deal with form errors with BindingResult.
Do you have any ideas what could be the cause it cannot deal with form errors?
Thanks in advance.
Link Entity
#Entity
#Table(name = "links")
public class Link {
#Id #GeneratedValue
private Integer ID;
#Column(name = "url") #NotNull #NotEmpty #URL
private String URL;
private String description;
#Column(name="created_at", nullable = false)
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime")
private LocalDateTime createdAt;
#Column(name="updated_at", nullable = true)
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime")
private LocalDateTime updatedAt;
#ManyToOne
#JoinColumn(name = "category_id")
private Category category;
public Integer getID() {
return ID;
}
public void setID(Integer ID) {
this.ID = ID;
}
public String getURL() {
return URL;
}
public void setURL(String URL) {
this.URL = URL;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdateddAt() {
return updatedAt;
}
public void setUpdateddAt(LocalDateTime updateddAt) {
this.updatedAt = updateddAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
public Category getCategory() {
return category;
}
public void setCategory(Category category) {
this.category = category;
}
}
LinkController
#Controller
public class LinkController {
#Autowired(required = true) #Qualifier(value = "linkService")
private LinkService linkService;
#Autowired
private CategoryService categoryService;
#RequestMapping(value = "/links")
public String getLinks(Model model) {
model.addAttribute("results", linkService.getLinks());
return "links/index";
}
#RequestMapping(value = "/links/create", method = RequestMethod.GET)
public ModelAndView showLinkForm() {
ModelAndView model = new ModelAndView("links/create");
model.addObject("link", new Link());
model.addObject("categories", categoryService.getCategories());
return model;
}
#RequestMapping(value = "/links/create", method = RequestMethod.POST)
public String addLink(#Valid #ModelAttribute("link") Link link, BindingResult result, Model model) {
if (result.hasErrors()) {
model.addAttribute("categories", categoryService.getCategories());
return "links/create";
}
linkService.addLink(link);
return "redirect:/links";
}
}
LinkControllerTest
#ContextConfiguration(classes = {AppInitializer.class})
#RunWith(SpringJUnit4ClassRunner.class)
public class LinkControllerTest {
#Mock
private LinkService linkService;
#InjectMocks
private LinkController linkController;
private MockMvc mockMvc;
#Before
public void setUp() {
// Process mock annotations
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(linkController).build();
}
#Test
public void testAddLink() throws Exception {
mockMvc.perform(post("/links/create")
.param("URL", "http://test.com")
.param("description", "Lorem Ipsum")
.param("category.ID", "1"))
.andExpect(status().is3xxRedirection())
.andExpect(view().name("redirect:/links"));
}
#Test
public void testAddEmptyLink() throws Exception {
mockMvc.perform(post("/links/create")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.sessionAttr("link", new Link())
)
.andExpect(status().isOk())
.andExpect(view().name("links/create"))
.andExpect(forwardedUrl("/WEB-INF/views/links/create.jsp"))
.andExpect(model().attributeHasFieldErrors("link", "URL", "description"))
.andExpect(model().attribute("link", hasProperty("URL", isEmptyOrNullString())))
.andExpect(model().attribute("link", hasProperty("description", isEmptyOrNullString())));
}
}
create.jsp (View)
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%#taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Create Category</title>
</head>
<body>
<form:form action="" modelAttribute="category">
<div>
<form:label path="name">Name</form:label>
<form:input path="name" />
<form:errors path="name" cssClass="error"></form:errors>
</div>
<div>
<form:label path="description">Description</form:label>
<form:input path="description" />
<form:errors path="description" cssClass="error"></form:errors>
</div>
<div>
<input type="submit" value="Create Category">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</div>
</form:form>
</body>
</html>

Error message is not displayed in freemarker template for custom FieldMatchValidator in Spring application

As stated in the title I can't make the error message for the FieldMatchValidator to show in my freemarker template. All the other "regular" error messages, such as #NotNull are displayed with the accurate message from message.properties. The validation with FieldMatchValidator works since the result.hasErrors() returns true and the result objects holds the FieldMatch error. What have I missed?
applicationContext.xml:
<!-- Invokes Spring MVC #Controller methods -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="webBindingInitializer">
<!-- Configures Spring MVC DataBinder instances -->
<bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="validator" ref="validator"/>
</bean>
</property>
</bean>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="messageInterpolator">
<bean class="com.reco.web.mvc.validator.CustomMessageInterpolator">
<property name="messageSource" ref="messageSource"/>
</bean>
</property>
</bean>
Controller:
#Controller
public class RegisterController extends MyWebController{
private static final String REGISTER_FORM_VIEW = "/action/register/register";
private static final String REGISTER_SUCCESS_VIEW = "/action/register/success";
private org.springframework.validation.Validator validator;
#Autowired
public RegisterController(MessageSource messageSource, Validator validator) {
super(messageSource);
this.validator = validator;
}
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator((org.springframework.validation.Validator)validator);
}
#RequestMapping(value = "/action/register", method = RequestMethod.GET)
public ModelAndView getRegisterForm() {
ModelAndView mav = new ModelAndView(REGISTER_FORM_VIEW);
mav.addObject("registerForm", new RegisterForm());
return mav;
}
#RequestMapping(value = "/action/register", method = RequestMethod.POST)
public final String register(#Valid RegisterForm registerForm, BindingResult result, ModelMap model, HttpServletRequest request) {
if (result.hasErrors()) {
return REGISTER_FORM_VIEW;
}
return "redirect:" + REGISTER_SUCCESS_VIEW;
}
}
Annotation:
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = FieldMatchValidator.class)
#Documented
public #interface FieldMatch {
String message() default "{constraints.fieldmatch}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* #return The first field
*/
String first();
/**
* #return The second field
*/
String second();
/**
* Defines several <code>#FieldMatch</code> annotations on the same element
*
* #see FieldMatch
*/
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Documented
#interface List {
FieldMatch[] value();
}
}
Validator:
public class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object>
{
private String firstFieldName;
private String secondFieldName;
#Override
public void initialize(final FieldMatch constraintAnnotation)
{
firstFieldName = constraintAnnotation.first();
secondFieldName = constraintAnnotation.second();
}
#Override
public boolean isValid(final Object value, final ConstraintValidatorContext context)
{
try
{
final Object firstObj = BeanUtils.getProperty(value, firstFieldName);
final Object secondObj = BeanUtils.getProperty(value, secondFieldName);
return firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj);
}
catch (final Exception ignore)
{
// ignore
}
return true;
}
}
freemarker template:
<!doctype html>
<html lang="sv">
<head>
<meta charset="utf-8"/>
<title></title>
</head>
<body id="register">
<div id="content">
<form id="registerForm" action="${rc.contextPath}/action/register" method="POST">
<p>
<label for="firstname">firstname</label>
<#spring.formInput "registerForm.firstName" />
<#spring.showErrors "", "error"/>
</p>
<p>
<label for="lastname">lastname</label>
<#spring.formInput "registerForm.lastName" />
<#spring.showErrors "", "error"/>
</p>
<p>
<label for="email">email</label>
<#spring.formInput "registerForm.email" />
<#spring.showErrors "", "error"/>
</p>
<p>
<label for="email_again">email_again</label>
<#spring.formInput "registerForm.confirmEmail" />
<#spring.showErrors "", "error"/>
</p>
<p>
<label for="password">password</label>
<#spring.formPasswordInput "registerForm.password" />
<#spring.showErrors "", "error"/>
</p>
<p>
<label for="password_again">password_again</label>
<#spring.formPasswordInput "registerForm.confirmPassword" />
<#spring.showErrors "", "error"/>
<#spring.showErrors "", "confirmPassword"/>
</p>
<input type="submit"/>
</form>
</div>
</body>
</html>
FormObject:
#FieldMatch.List({
#FieldMatch(first = "password", second = "confirmPassword", message = "validation.message.confirm.password"),
#FieldMatch(first = "email", second = "confirmEmail", message = "validation.message.confirm.email")
})
public class RegisterForm implements Serializable {
private static final int MAX_TEXT_FIELD_SIZE = 32;
/**
* Password min size.
*/
private static final int PASSWORD_MIN_SIZE = 8;
/**
* Password max size.
*/
private static final int PASSWORD_MAX_SIZE = 36;
#NotNull(message = "validation.message.firstname.empty")
#Size(min = 1, max = MAX_TEXT_FIELD_SIZE, message = "validation.message.firstname.length")
#Pattern(regexp = "^[^<>]*$", message = "validation.message.invalid.characters")
private String firstName;
#NotNull(message = "validation.message.lastname.empty")
#Size(min = 1, max = MAX_TEXT_FIELD_SIZE, message = "validation.message.lastname.length")
#Pattern(regexp = "^[^<>]*$", message = "validation.message.invalid.characters")
private String lastName;
#NotNull(message = "validation.message.email.empty")
#Pattern(regexp = ".+#.+\\.[a-z]+", message = "validation.message.email", flags = { Pattern.Flag.CASE_INSENSITIVE })
private String email;
#NotNull(message = "validation.message.email.empty")
#Pattern(regexp = ".+#.+\\.[a-z]+", message = "validation.message.email", flags = { Pattern.Flag.CASE_INSENSITIVE })
private String confirmEmail;
#NotNull
#Size(min = PASSWORD_MIN_SIZE, max = PASSWORD_MAX_SIZE)
private String password;
#NotNull
#Size(min = PASSWORD_MIN_SIZE, max = PASSWORD_MAX_SIZE)
private String confirmPassword;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getConfirmEmail() {
return confirmEmail;
}
public void setConfirmEmail(String confirmEmail) {
this.confirmEmail = confirmEmail;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getConfirmPassword() {
return confirmPassword;
}
public void setConfirmPassword(String confirmPassword) {
this.confirmPassword = confirmPassword;
}
}
I solved the problem by adding the proper constraint validation message:
errorMessage = constraintAnnotation.message();
to the proper field inside the custom FieldMatchValidator. In my case I added the error to the second field:
context.buildConstraintViolationWithTemplate(errorMessage).addNode(secondFieldName).addConstraintViolation();
FieldMatchValidator
public class FieldMatchValidator implements ConstraintValidator<FieldMatch, Object>
{
private String firstFieldName;
private String secondFieldName;
private String errorMessage;
#Override
public void initialize(final FieldMatch constraintAnnotation)
{
firstFieldName = constraintAnnotation.first();
secondFieldName = constraintAnnotation.second();
errorMessage = constraintAnnotation.message();
}
#Override
public boolean isValid(final Object value, final ConstraintValidatorContext context)
{
try
{
final Object firstObj = BeanUtils.getProperty(value, firstFieldName);
final Object secondObj = BeanUtils.getProperty(value, secondFieldName);
boolean valid = firstObj == null && secondObj == null || firstObj != null && firstObj.equals(secondObj);
if(!valid){
context.buildConstraintViolationWithTemplate(errorMessage).addNode(secondFieldName).addConstraintViolation();
}
return valid;
}
catch (final Exception ignore)
{
// ignore
}
return true;
}
}

Spring MVC Pre Populate Checkboxes

First little background info. Got a fairly standard User Role relationship where the User can have many roles. I have roles defined as a set within the user class. Now I know that html forms have all the values as strings and trying to get values as my custom Role object does not work. I implemented an initbinder to convert the id's back into object so that I can retrieve the selected values off of my checkboxes, that part works.
But I can't seem to go back the other way. I retrieve a User from the database that already has roles and want to pre populate role checkboxes with all the roles that a user has. Based on this example :
Checkboxes example
They say that:
form:checkboxes items="${dynamic-list}" path="property-to-store"
For multiple checkboxes, as long as the “path” or “property” value is
equal to any of the “checkbox values – ${dynamic-list}“, the matched
checkbox will be checked automatically.
My interpretation of that is I should be able to feed it a Set of all the roles and define the path to be the roles from the User object and it should match them thus causing the check box to pre populate.
Every example out there seems to have the value of dynamic-list as a String[]. Well thats great and dandy but how does this work for custom objects that our defined as a Set? Can I still use this one line definition for checkboxes or do I need to do some kind of data binding heading into the view also?
Here is my user dto, user controller, custom form binder, and user edit page.
User DTO
#Entity
#Table
public class User extends BaseDto
{
#Column(updatable = false) #NotBlank
private String username;
#Column(name = "encrypted_password") #Size(min = 6, message = "password must be at least 6 characters") #Pattern(regexp = "^\\S*$", message = "invalid character detected")
private String password;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column #NotNull
private boolean enabled;
#Column #Email #NotBlank
private String email;
#Transient
private String confirmPassword;
#ManyToMany(targetEntity = Role.class, fetch = FetchType.EAGER, cascade = CascadeType.REFRESH) #JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles;
public User()
{
}
public User(final String usernameIn, final String passwordIn, final String firstNameIn, final String lastNameIn, final String emailIn, final boolean enabledIn)
{
username = usernameIn;
password = passwordIn;
firstName = firstNameIn;
lastName = lastNameIn;
email = emailIn;
enabled = enabledIn;
}
public String getUsername()
{
return username;
}
public void setUsername(final String usernameIn)
{
username = usernameIn;
}
public String getPassword()
{
return password;
}
public void setPassword(final String passwordIn)
{
password = passwordIn;
}
public String getFirstName()
{
return firstName;
}
public void setFirstName(final String firstNameIn)
{
firstName = firstNameIn;
}
public String getLastName()
{
return lastName;
}
public void setLastName(final String lastNameIn)
{
lastName = lastNameIn;
}
public String getEmail()
{
return email;
}
public void setEmail(final String emailIn)
{
email = emailIn;
}
public String getConfirmPassword()
{
return confirmPassword;
}
public void setConfirmPassword(final String confirmPasswordIn)
{
confirmPassword = confirmPasswordIn;
}
public boolean isEnabled()
{
return enabled;
}
public void setEnabled(final boolean enabledIn)
{
enabled = enabledIn;
}
public Set<Role> getRoles()
{
return roles;
}
public void setRoles(final Set<Role> rolesIn)
{
roles = rolesIn;
}
}
User Controller
#Controller #RequestMapping("/user")
public class UserController
{
#Autowired private UserService userService;
#Autowired private UserDao userDao;
#Autowired private RoleDao roleDao;
#InitBinder
public void bindForm(final WebDataBinder binder)
{
binder.registerCustomEditor(Set.class, "roles", new CustomFormBinder<RoleDao>(roleDao, Set.class));
}
#RequestMapping(method = RequestMethod.GET)
public String index(final ModelMap modelMap)
{
return "/user/index";
}
#RequestMapping(value = "/create", method = RequestMethod.GET)
public String create(final ModelMap modelMap)
{
modelMap.addAttribute("userInstance", new User());
modelMap.addAttribute("validRoles", new HashSet<Role>(roleDao.findAll()));
return "/user/create";
}
#RequestMapping(value = "/save", method = RequestMethod.POST)
public String save(final ModelMap modelMap, #Valid #ModelAttribute("userInstance") final User user, final BindingResult bindingResult)
{
// TODO move to service validation
if (user.getPassword() == null || !user.getPassword().equals(user.getConfirmPassword()) )
{
bindingResult.addError(new FieldError("userInstance", "password", "password fields must match"));
bindingResult.addError(new FieldError("userInstance", "confirmPassword", "password fields must match"));
}
if (user.getRoles() == null || user.getRoles().isEmpty())
{
bindingResult.addError(new FieldError("userInstance", "roles", "Must select at least one role for a User"));
}
if (bindingResult.hasErrors())
{
modelMap.addAttribute("validRoles", new HashSet<Role>(roleDao.findAll()));
return "/user/create";
}
userService.save(user);
return "redirect:/user/list";
}
#RequestMapping(value = "/edit/{id}", method = RequestMethod.GET)
public String edit(#PathVariable final Integer id, final ModelMap modelMap)
{
final User user = userDao.find(id);
if (user != null)
{
modelMap.addAttribute("userInstance", user);
modelMap.addAttribute("validRoles", new HashSet<Role>(roleDao.findAll()));
return "/user/edit";
}
return "redirect:/user/list";
}
#RequestMapping(value = "/edit", method = RequestMethod.GET)
public String editCurrent(final ModelMap modelMap)
{
return edit(userService.getLoggedInUser().getId(), modelMap);
}
#RequestMapping(value = "/update", method = RequestMethod.POST)
public String update(#Valid #ModelAttribute("userInstance") final User user, final BindingResult bindingResult)
{
if (bindingResult.hasErrors())
{
return "/user/edit";
}
userService.save(user);
return "redirect:/user/list";
}
#ModelAttribute("userInstances")
#RequestMapping(value = "/list", method = RequestMethod.GET)
public List<User> list()
{
return userDao.findAll();
}
}
Custom Form Binder
public class CustomFormBinder<T extends GenericDao> extends CustomCollectionEditor
{
private final T dao;
private static final Logger LOG = LoggerFactory.getLogger(CustomFormBinder.class);
public CustomFormBinder(final T daoIn, final Class collectionType)
{
super(collectionType, true);
dao = daoIn;
}
#Override
protected Object convertElement(final Object element)
{
try
{
// forms should return the id as the itemValue
return dao.find(Integer.valueOf(element.toString()));
}
catch (NumberFormatException e)
{
LOG.warn("Unable to convert " + element + " to an integer");
return null;
}
}
}
User Edit View
<html>
<head>
<title>Create User</title>
</head>
<body>
<c:url value="/user/update" var="actionUrl"/>
<form:form method="post" commandName="userInstance" action="${actionUrl}">
<h1>Edit User ${userInstance.username}</h1>
<div>
<form:label path="username">Username:</form:label>
<form:input path="username" id="username" readonly="true"/>
</div>
<div>
<form:label path="password">Password:</form:label>
<form:input path="password" id="password" type="password" readonly="true"/>
<tag:errorlist path="userInstance.password" cssClass="formError"/>
</div>
<div>
<form:label path="firstName">First Name:</form:label>
<form:input path="firstName" id="firstName"/>
<tag:errorlist path="userInstance.firstName" cssClass="formError"/>
</div>
<div>
<form:label path="lastName">Last Name:</form:label>
<form:input path="lastName" id="lastName"/>
<tag:errorlist path="userInstance.lastName" cssClass="formError"/>
</div>
<div>
<form:label path="email">Email:</form:label>
<form:input path="email" id="email" size="30"/>
<tag:errorlist path="userInstance.email" cssClass="formError"/>
</div>
<div>
**<%--Want to Pre Populate these checkboxed--%>
<form:checkboxes title="Assigned Roles:" path="roles" id="roles" items="${validRoles}" itemLabel="displayName" itemValue="id" element="div"/>**
<tag:errorlist path="userInstance.roles" cssClass="formError"/>
</div>
<form:hidden path="enabled"/>
<form:hidden path="id"/>
<form:hidden path="version"/>
<div class="submit">
<input type="submit" value="Update"/>
Cancel
</div>
</form:form>
</body>
</html>
You need a correct implemented equals method for Role!
If this is not enough have a look at class oorg.springframework.web.servlet.tags.form.AbstractCheckedElementTag. The method void renderFromValue(Object item, Object value, TagWriter tagWriter) is where the the checked flag is set.

Resources