In Struts2 I can't use modeldriven with validate - validation

In the struts.xml:
<action name="User_UserFormSubmit" class="actions.UserManager">
<result name="input" >/jsp/user_form.jsp</result>
<result name="success" type="redirectAction"> success_register</result>
</action>
My class:
public class UserManager extends ActionSupport implements ModelDriven<User>{
private User user = new User();
#Override
public User getModel() {
return user;
}
public String validate() {
addActionError("blabla");
}
public String execute() {
return SUCCESS;
} ...
then in the jsp:
<s:property value="getActionErrors()"/>
I expect in the input result :
<li> blabla </li>
I succefully arrived to user_form.jsp, but the actionError does not appear
I tried without the "implements ModelDriven" and it work
The model driven erase the actionErrors (I supposed)
I want to use validate and modeldriven ¿any idea?

Not a big fan of model driven... but here is an example.
Before the example please note that using validate() does not make much sense in terms of ModelDriven. The reason is that the Model should be used over several actions and so the validation should probably be consistent. You don't use model driven just to make property names a bit shorter (to do that you use the struts2 push tag). As such validation should be done with xml as the model is bigger than any one action. Each action which uses that model uses the Visitor validator. This validator merely looks up the xml validation file for the model. The following example however will use the validate() method in the action to save time.
The following example will use the struts2-conventions-plugin to reduce the example size (adding it to your project is simply a matter of adding one jar).
create: com.quaternion.action.AddUser
package com.quaternion.action;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
public class AddUser extends ActionSupport implements ModelDriven<User>{
User user = new User();
#Override
public User getModel() {
return user;
}
#Override
public void validate(){
if (user.age != 12) {
super.addActionError("bla bla bla");
}
}
}
create: com.quaternion.action.User
package com.quaternion.action;
public class User {
public String name;
public int age;
}
create: /WEB-INF/content/add-user-input.jsp
<%#taglib prefix="s" uri="/struts-tags"%>
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>Form</title>
</head>
<body>
<h1>Form</h1>
<s:actionerror/>
<s:form action="add-user">
<s:textfield name="name"/>
<s:textfield name="age"/>
<s:submit/>
</s:form>
</body>
</html>
create: /WEB-INF/content/add-user-success.jsp
<%#taglib prefix="s" uri="/struts-tags"%>
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>Success</title>
</head>
<body>
<h1>Success</h1>
</body>
</html>
To test:
Add /add-user-input as the action name on your context path. If you enter an age of 12 you will get the success page, if you enter anything else you will get an action error. This has been tested to work. It is possible a typo was made, but the main thing to take away is there is an error in your application, using both conventions or xml there should be no issues with what you are doing.

You can also validate with #validations too, you have access to model driven object in the validator.
#Action(value = "save-user")
#Validations(
stringLengthFields = {
#StringLengthFieldValidator(fieldName = "name", trim = true, key = "validate.required.string.length"),
#StringLengthFieldValidator(fieldName = "age", trim = true, key = "validate.required.string.length"),
#StringLengthFieldValidator(fieldName = "address.addLine1", trim = true, key = "validate.required.string.length")
})
public String save() {

Related

HTML Page Doesn't Show Up on Springboot Application

I'm trying to create a registration page. The website is very simple, homepage will be returning signup page and when clicked "Submit" it should save it to the database. But I'm having trouble with getting the page.
Applicant.java
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
import java.util.Date;
#Entity
#Table(name = "bayilik_basvuru")
public class Applicant {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#NotBlank(message = "İsim Soyisim Girilmelidir.")
#Column(name = "isim_soyisim")
private String adSoyad;
public Applicant() {
}
public String getAdSoyad() {
return adSoyad;
}
public void setAdSoyad(String adSoyad) {
this.adSoyad = adSoyad;
}
}
Controller
#Controller
public class HomeContoller {
#Autowired
private ApplicantDAO dao;
#RequestMapping("/")
public ModelAndView getApplicationPage(){
ModelAndView model = new ModelAndView();
Applicant applicant = new Applicant();
model.addObject("applicant",applicant);
model.setViewName("index");
return model;
}
#PostMapping("/save")
public String saveApplicant(#Valid Applicant applicant, BindingResult result){
if (result.hasErrors()) {
return "add-student";
}
dao.save(applicant);
return "index";
}
}
Index.html -> form's piece,
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Bayilik Ön Başvuru Formu</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <link rel="stylesheet" type="text/css" th:href="#{../css/style.css}" />
<link rel="stylesheet" type="text/css" th:href="#{../css/bootstrap-datetimepicker.min.css}" />
<link rel="stylesheet" type="text/css" th:href="#{../css/roboto-font.css}" />
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</head>
<body>
<form class="form-horizantal" th:action="#{/save}" th:object="${applicant}"
method="POST">
<div class="form-group">
<label class="col-md-4 control-label">İsim Soyisim</label>
<div class="col-md-8">
<input type="text" th:field="*{adSoyad}" class="form-control" /> <!-- it gives error at this line at the start of the th:field-->
<span th:if="${#fields.hasErrors('adSoyad')}" th:errors="*{adSoyad}" class="text-danger"></span>
</div>
</div>
</body>
</html>
I have been dealing with this for hours. I've compared it to many examples and html file looks good, controller looks good I don't know what I'm doing wrong.
In my pom file I have thymeleaf;
<dependencies>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
...
<dependendcies>
By the way the error is this;
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Thu Oct 31 01:40:47 EET 2019
There was an unexpected error (type=Internal Server Error, status=500).
Error during execution of processor 'org.thymeleaf.spring5.processor.SpringInputGeneralFieldTagProcessor' (template: "index" - line 27, col 56) // this line is the same line I showed on the index.html file
You are not using the annotation #ModelAttribute on your /save endpoint . try using it like :
#PostMapping("/save")
public String saveApplicant( #Valid #ModelAttribute("applicant") Applicant applicant, BindingResult result){
if (result.hasErrors()) {
return "add-student";
}
dao.save(applicant);
return "index";
}
In your thymeleaf template th:object="${applicant}" expression declares the model object to use for collecting the form data. So , in your controller you have to use the annotation #ModelAttribute to bind the Applicant object to the incoming form content.
Change your get method to properly add the object:
#GetMapping("/") //simpler, more informative
public String getApplicationPage(Model model){
model.addAttribute("applicant", new Applicant()); //note method name
return "index";
}
Look at Project Lombok to greatly simplify your beans. You are missing a getter and setter for your id property and an annotation of #Getter and #Setter on the class would remove a lot of boilerplate.
You would also want the #ModelAttribute annotation on your post method for the Applicant parameter.
In the future, also please post your full stack trace.
It seems like there was an hierarchy problem on the project. Code worked after I fixed the hierarchy of files.
Thanks to repliers for their attempt to help but they couldn't know the core of the problem since I didn't attach the hierarchy's screenshot on the question.

Strange behavior : Spring Session Attributes vs ModelAttribute

I got a very strange issue with Spring MVC 4.0.6, hosted on JBoss 7.2. When updating an existing user, the submitted information sometimes get transferred to the POST RequestMapping controller method (validateUpdate method below).
UserController.java
#Controller
#RequestMapping(value = "/security/user")
#SessionAttributes(value = {"userForm", "user"})
public class UserController {
private final String CREATE_VIEW = "user/createOrUpdate";
private final String READ_VIEW = "user/readOrDelete";
private final String UPDATE_VIEW = CREATE_VIEW;
private final String DELETE_VIEW = READ_VIEW;
#Autowired
private LocationService locationService;
#Autowired
private SecurityService securityService;
#Autowired
private UserValidator userValidator;
#InitBinder("userForm")
protected void initBinder(WebDataBinder binder) {
binder.setValidator(userValidator);
binder.setDisallowedFields("strId");
}
#ModelAttribute("regions")
public List<Region> populateRegions() {
Locale locale = LocaleContextHolder.getLocale();
List<Region> regions = this.locationService.lstRegions(locale.getLanguage());
return regions;
}
#RequestMapping(value = "/update/{intUserId}", method = RequestMethod.GET)
public String updateUser(#PathVariable Integer intUserId, Model model) {
User user = this.securityService.findUserByPk(intUserId);
if (user != null) {
UserForm userForm = new UserForm();
userForm.setUser(user);
model.addAttribute(userForm);
}
return UPDATE_VIEW;
}
#RequestMapping(value = "/update/validate", method = RequestMethod.POST)
public String validateUpdate(#Valid #ModelAttribute("userForm") UserForm userForm,
BindingResult result,
Model model,
RedirectAttributes redirectAttributes,
SessionStatus status) {
return this.performCreateOrUpdateOperation(userForm, result, model, redirectAttributes, status);
}
private String performCreateOrUpdateOperation(
UserForm userForm,
BindingResult result,
Model model,
SessionStatus status) {
if(result.hasErrors()) {
return UPDATE_VIEW;
} else {
User user = userForm.getUser();
this.securityService.validateCreateOrUpdateUser(result, user);
if (result.hasErrors() == false) {
if (userForm.isNew()) {
this.securityService.addUser(user);
} else {
this.securityService.updateUser(user);
}
model.addAttribute(user);
status.setComplete();
return "user/success";
} else {
return UPDATE_VIEW;
}
}
}
}
Form Bean
public class UserForm {
private String strUserIdToSearch = "";
private String strId = "0";
private String strUserId = "";
private String strFirstName = "";
private String strLastName = "";
private String strEmail = "";
private String strRegionId = "0";
private boolean booArchived = false;
public User getUser() {
User user = new User();
user.setIntId(Integer.valueOf(this.strId));
user.setStrUserId(this.strUserId);
user.setStrFirstName(this.strFirstName);
user.setStrLastName(this.strLastName);
user.setStrEmail(this.strEmail);
Region region = new Region(Integer.valueOf(this.strRegionId));
user.setRegion(region);
user.setBooArchived(this.booArchived);
return user;
}
public void setUser(User user) {
this.strUserIdToSearch = user.getStrUserId();
this.strId = String.valueOf(user.getIntId());
this.strUserId = user.getStrUserId();
this.strFirstName = user.getStrFirstName();
this.strLastName = user.getStrLastName();
this.strEmail = user.getStrEmail();
this.strRegionId = String.valueOf(user.getRegion().getIntId());
this.booArchived = user.getBooArchived();
}
...getters and setters...
}
JSP (removed styling for clarity)
crudMethod is a JSP tag returning "create", "read", "update" or "delete" depending on ${requestScope['javax.servlet.forward.request_uri']}
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%# taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<spring:url var="userFormAction" value="/rest/security/user/${crudMethod}/validate" />
<form:form id="userForm" modelAttribute="userForm" action="${userFormAction}" method="POST">
<form:hidden path="strId" />
<form:hidden path="strUserId" id="strUserId" />
<form:hidden path="strLastName" id="strLastName" />
<form:hidden path="strFirstName" id="strFirstName" />
<form:hidden path="strEmail" id="strEmail" />
<form:select id="strRegionId" path="strRegionId">
<form:option value="0" label="${strSelectRegionLabel}" />
<form:options items="${regions}" itemValue="intId" itemLabel="${strRegionLabel}" />
</form:select>
</form:form>
So, when I submit the form, and for instance, change the region to another ID in the list (let's say from 1 to 6). Sometimes it works, sometimes not. By "works", I mean I hit the success page, then I go back to see the user again. Sometimes it stays at 1, sometimes it's changed to 6.
I have found a pattern/workaround that works all the time to reproduce the issue:
Load the update form (UserController > updateUser)
Change the region from 1 to 6
Click Save. Form submits so the UserController.validateUser method is being invoked.
Got the success page. On that success page I got a link to the read operation for the user. Clicking that link, I realize that the region did not change (the main problem). There is a link to update a user on the read page.
Re-do the exact same change that I did at step #2.
Click Save. Form submits and I got the success page view.
Click the read hyperlink again, and now I see that the change worked.
Question is: WHY? Am I missing something??
Note: It's not related to the business layer. I have tested it and it's stable. That is certainly related to my use of the Spring MVC Framework.
Browser is IE11.
Thank you for help.
* UPDATE 2015-06-29 *
After a few more searches, I found that:
when it does NOT work, the request Content-Length header value is 0.
when it works, there is a value (eg: 146).
the request message body is always correct, like so:
strId=THE_ID&strUserId=THE_USERID&strLastName=THE_LASTNAME&strFirstName=THE_FIRSTNAME&strEmail=THE_EMAILADDRESS&strRegionId=THE_REGIONID&booArchived=false
Please note that "THE_REGIONID" is good every single time.
Related resources
Challenge-Response Authentication and Zero-Length Posts
Form Submit Not working on IE intermittently with Spring MVC in backend
<form:select id="intRegionId" path="strRegionId">
I think the id in the form:select is the culprit . It doesn't match the attribute name in UserForm, which is "strRegionId". And therefore it doesn't bind. Change the id value to strRegionId

Boolean condition with Thymeleaf and Spring

I'd like to add an error flag in my web page.
How can I to check if a Spring Model attribute is true or false with Thymeleaf?
The boolean literals are true and false.
Using the th:if you will end up with a code like:
<div th:if="${isError} == true">
or if you decide to go with the th:unless
<div th:unless="${isError} == false">
There is also a #bools utility class that you can use. Please refer to the user guide: http://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html#booleans
You can access model attributes by using variable expression (${modelattribute.property}).
And, you can use th:if for conditional checking.
It looks like this:
Controller:
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class MyController {
#RequestMapping("/foo")
public String foo(Model model) {
Foo foo = new Foo();
foo.setBar(true);
model.addAttribute("foo", foo);
return "foo";
}
}
HTML:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
</head>
<body>
<div th:if="${foo.bar}"><p>bar is true.</p></div>
<div th:unless="${foo.bar}"><p>bar is false.</p></div>
</body>
</html>
Foo.java
public class Foo {
private boolean bar;
public boolean isBar() {
return bar;
}
public void setBar(boolean bar) {
this.bar = bar;
}
}
Hope this helps.

Having multiple validation functions in a class with multiple actions

I want to validate forms in Struts 2 using validate functions in the action class.
I found this example : http://www.javatpoint.com/struts-2-custom-validation-workflow-interceptor
However, I want to have multiple functions (actions) in the same class. And I want to have a validation function for each of these functions. How can we do this?
Edit:
The validate() function in the example gets invoked automatically since it is one of the Validateable interface functions. If I have validate functions with other names they won't be invoked
Create validateXxx methods where Xxx is the name of the related action method.
(Whether or not this is the best option depends on the particular validations you need.)
validations using XML validation file
The naming convention of the XML validation file should be ActionClass-Validation.xml. Here our Action Class name is "Login.java" and the XML validation file name is "Login-Validation.xml".
The Login-Validation.xml file contains the following code.
view source
print?
<validators>
<field name="userName">
<field-validator type="requiredstring">
<message>User Name is required.</message>
</field-validator>
</field>
<field name="password">
<field-validator type="requiredstring">
<message key="password.required" />
</field-validator>
</field>
</validators>
The field element contains the name of the form property that needs to be validated. The filed-validator element inside the field element contains the type of validation that needs to be performed.
Here you can either specify the error message directly using the message element or you can use the properties file to define all the error messages and use the key attribute to specify the error key.
Note the properties file should also have the same name as the Action class.
The Login Action class contains the following code.
view source
print?
public class Login extends ActionSupport {
private String userName;
private String password;
public Login() {
}
public String execute() {
return SUCCESS;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
The login.jsp page contains the following code.
<%#page contentType="text/html" pageEncoding="UTF-8"%>
<%#taglib uri="/struts-tags" prefix="s" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Login Page</title>
<s:head />
</head>
<body>
<s:form action="LoginAction">
<s:textfield name="userName" label="User Name" />
<s:password name="password" label="Password" />
<s:submit value="Login" />
</s:form>
</body>
</html>

Primefaces: Editable object in accordionPanel with Dynamic = True

I am trying to achieve functionality where I am generating the tabs in according panel dynamically according to the list.
List is of say some object e.g Person
Now attribute corresponding to each person, I am showing in each tab in a form.
Values are loaded correctly but when I edit those value I am not getting updated values at the backing bean, I am getting only old values.
Can somebody please explain why ?
Here is the code.
Person Object
public class Person {
String name;
//getter setter
#Override
public String toString() {
return name;
}
}
Backing Bean
#ManagedBean(name = "editor")
public class EditorBean {
private List<Person> persons = new ArrayList<Person>();
#PostConstruct
void init() {
persons.add(new Person("dhruv"));
persons.add(new Person("tarun"));
}
public void testAction() {
System.out.println(persons);
}
// Getter Setter*****************//
public List<Person> getPersons() {
return persons;
}
public void setPersons(List<Person> persons) {
this.persons = persons;
}
}
XHTML Code:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui">
<h:head>
</h:head>
<h:body>
<h1>Hello World PrimeFaces</h1>
<h:form>
<p:accordionPanel value="#{editor.persons}" var="person" dynamic="true">
<p:tab title="#{person}">
<p:inputText value="#{person.name}"></p:inputText>
</p:tab>
</p:accordionPanel>
<p:commandButton action="#{editor.testAction()}" value="testAction"></p:commandButton>
</h:form>
</h:body>
</html>
When I run the same with with dynamic="false". This works fine. But in my real scenario I have lots of data which I cannot afford to paint in one go thats why I want to use dynamic = true.
Can somebody explain how to achieve this if not by dynamic = true ??
The reason you're having stale values in your accordion is that dynamic accordions default to cache="true". This attribute causes the accordion to not reload new/updated values. To cause an ajax refresh on tab open, set cache="false" on your accordion

Resources