RequestMapping isn't reaching in the controller and thus list isn't showing on the JSP page - spring

I'm developing a simple application of tender management in which I'm using J2EE, Spring-mvc and CRUD operations on Eclipse IDE
I've to retrieve the list of tenders from the database and show in the JSP using controller. The JSP page is mapped in the #RequestMapping but the mapping isn't executing and hence the code following it isn't reaching.
I've tried non- crud operations before and have used different methods including ModelAndView and String but the issue is with mapping which isn't able to run when loading the required JSP. No error is being shown.
Here is the mapping call in the Controller
#RequestMapping("/viewTendersByUser")
public String viewTender(Model m) {
System.out.println("in edit tender user controller");
List<tender> tender = userDao.getTenderByCreator(name);
m.addAttribute("tender", tender);
System.out.println("in ctender after view tender");
System.out.println(""+tender);
return "viewTendersByUser";
}
This is the method in DAO which I'm calling but isn't actually reached
#Override
public List<tender> getTenderByCreator(String name) {
String sql = "select * from tenderdb where publisher =
'"+name+"'";
return jdbcTemplate.query(sql,new RowMapper<tender>(){
public tender mapRow(ResultSet rs, int row) throws SQLException {
tender t = new tender();
t.settName(rs.getString(1));
t.setOpening(rs.getTimestamp(2));
t.setClosing(rs.getTimestamp(3));
t.setMinBid(rs.getDouble(4));
t.settDesc(rs.getString(5));
return t;
}
});
}
And this is the JSP I'm trying to show data in:
<h3>List of Tenders</h3>
<table border="2" width="70%" cellpadding="2">
<tr><th>Tender Name</th><th>Opening Date</th><th>Closing Date</th><th>Minimum Bid</th><th>Tender Description</th><th>Edit</th><th>Delete</th></tr>
<c:forEach var="row" items="${tender}">
<tr>
<td>${row.tName}</td>
<td>${row.opening}</td>
<td>${row.closing}</td>
<td>${row.minBid}</td>
<td>${row.tDesc}</td>
<td>Edit</td>
<td>Delete</td>
</tr>
</c:forEach>
</table>
Expected results should be the list of tenders from the database onto the viewTendersByUser.jsp.
Actual result is the JSP page being loaded but the contents to be received from controller aren't showing which traces to mapping not being called and actually nothing is happening.
Even an error or exception would be easy to debug but there's no output.

Related

How can I get data from from table2 by sending corresponding ID from table1 using thymleaf

getFunction() in database class:
public Review getReviews(Long id) {
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
String query = "SELECT * FROM reviews WHERE id = :id";
namedParameters.addValue("id", id);
BeanPropertyRowMapper<Review> reviewMapper = new BeanPropertyRowMapper<Review>(Review.class);
List<Review> reviews = jdbc.query(query, namedParameters, reviewMapper);
if (reviews.isEmpty()) {
return null;
} else {
return reviews.get(0);
}
}
Controller class:
#GetMapping("/viewReview")
public String viewReviews(Model model,Long id) {
List<Review> review = da.getReviews(id);
model.addAttribute("reviewList", review);
return "view-book";
}
HTML:
<tr th:each="book : ${bookList}">
<td th:text="${book.title}"></td>
<td th:text="${book.author}"></td>
<td> View</td>
</tr>
I have two tables 'books' and 'reviews'. When I click the first book it should display the corresponding review of the book on the next page. But instead shows 404 error. Im guessing Im missing something in my database class but not sure what.
I'm not very familiar with Thymeleaf, but looking at your Controller (and the error message), the problem could be with your Controller than Database class.
Your viewReviews method is mapped for /viewReview, but you are trying to navigate to /viewReview/BOOK_ID, which is why you are getting the 404 error message.
Try updating your Controller as:
#GetMapping("/viewReview/{id}")
public String viewReviews(Model model, #PathVariable("id") Long id) {
List<Review> review = da.getReviews(id);
model.addAttribute("reviewList", review);
return "view-book";
}

Spring & Thymeleaf: Refresh single row in table

I refresh content of a table with:
<tr id="rows" th:each="entity: ${entities}">
<!-- ... -->
</tr>
and on server side:
#RequestMapping(value = "/replace", method = RequestMethod.GET)
public String replace(Model model) {
model.addAttribute("entities", entities);
return "/ :: #rows";
}
calling the method with:
$.get('/replace', function(fragment) {
// fragment gives me all rows of the table
}
which works as expected. However, I do not want to update all rows every time, but only a single one, so my idea was to use a dynamic id as follows:
<tr th:id="'row' + ${entity.id}" th:each="entity, iterStat: ${entities}">
<!-- ... -->
</tr>
and on server side:
#RequestMapping(value = "/replace", method = RequestMethod.GET)
public String replace(#RequestParam("id") int id, Model model) {
model.addAttribute("entities", entities);
return "/ :: #row" + id;
}
calling the method with:
$.get('/replace', {id: id} function(fragment) {
// fragment is empty
}
but that doesn't work. Looking a the html code and server side, the id is correct. Is it not possible to use a th:id? What would be a workaround if I only want to update one row in a table and not all rows? Thanks.
I also tried the same with th:fragment - it works without ${entity.id} and does not with.
Using th:fragment should do the trick. I normally use them to accomplish what you are trying to do. This is an example of how to implement it.
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/xhtml">
<head></head>
<body>
<th:block th:fragment="entity-row">
<tr th:id="'row'+${entity.id}">
<td th:text=${entity.id}></td>
<!-- Whatever other element you need. -->
</tr>
</th:block>
</body>
Add the code above in a whole a new html file. Remember, we are expecting one single entity, so we don't need the th:each. Now in order to fetch the fragment, you would need to the following in your Controller.
#RequestMapping(value = "/replace/fragment", method = RequestMethod.GET)
#ResponseBody
public String replaceSingleRow(#RequestParam("id") int id, Model model) {
Entity entity = entityService.findById(id);
model.addAttribute("entity", entity);
return "file :: entity-row";
}
Where file would be the name of the html file that contains the fragment. Of course, I am assuming you are passing only the desired id to our controller.
Very late response, but I think this is the ultimate solution.
Thymeleaf fragment
<tr th:fragment="entity-row" th:id="'row' + ${entity.id}" th:each="entity, iterStat: ${entities}">
<!-- ... -->
</tr>
Controller code
#RequestMapping(value = "/replace", method = RequestMethod.GET)
public String replace(#RequestParam("id") int id, Model model) {
Entity entity = entityService.findById(id);
model.addAttribute("entities", Arrays.asList(entity)); // sending the row as list, so that the loop can iterate and will produce a single row
return "file :: entity-row";
}
You have the desired single row html in your ajax response, now you can replace the existing row
$.get('/replace', {id: id} function(fragment) {
$("#row" + id).replaceWith(fragment);
}
I was able to send only one entity to the view with:
Entity entity = entityService.findById(id);
model.addAttribute("entities", entity);
It is for sure not the prettiest way, but it gives the desired result.

How to check if a particular row has validation error for a multi row validation in Spring using BindingResult

The current logic will check if the BindingResult has errors and the display the data and errors in a jsp.
The logic needed is to check Errors for each row and display only those rows containing validation errors and update the rows which don't have validation errors.
#Autowired
private IncidentExtractStgService incidentExtractStgService;
#RequestMapping(value = "/validatingIncidentList", method = RequestMethod.POST)
public String ValidateIncidentList( #Valid #ModelAttribute("incidentsForm") IncidentsForm incidentsForm,
BindingResult bindingResult,RedirectAttributes redirectAttributes) {
if (bindingResult.hasErrors()) {
for(ObjectError error: bindingResult.getAllErrors()){
System.out.println(error);
}
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.incidentsForm", bindingResult);
redirectAttributes.addFlashAttribute("incidentsForm", incidentsForm);
return "redirect:/validateIncidentList";
}
else
{
for(IncidentExtractStg ie : incidentsForm.getIncidents()) {
ie.setValidated(1);
incidentExtractStgService.update(ie);
System.out.println(ie.getNumber()+" "+ie.getWaitTime());
}
return "redirect:/validateIncidentList";
}
The below code snippet will check if the model contains attribute "incidetsForm",if so will send the same to example.jsp which in-turn will display the data and validation errors.
#RequestMapping(value = "/validateIncidentList", method = RequestMethod.GET)
public String incidentList(Model model) {
if (!model.containsAttribute("incidentsForm")) {
List<IncidentExtractStg> incidents = incidentExtractStgDao.validateList();
incidentsForm.setIncidents(incidents);
model.addAttribute("incidentsForm", incidentsForm);
return "example";
}
model.addAttribute("errormessage","Please Check the Validation Errors column for Errors");
return "example";
}
Example.jsp code snippet
<c:forEach var="ie" items="${incidentsForm.incidents}" varStatus="status">
<tr>
<td><form:input path="incidents[${status.index}].id" value="${ie.id}" readonly ="true"/></td>
<td><form:errors path="incidents[${status.index}].id" cssClass="error" /></td>
<td><form:input path="incidents[${status.index}].number" value="${ie.number}"/></td>
<td><form:errors path="incidents[${status.index}].number" cssClass="error" /></td>
</tr>
IncidentsForm.java:
import java.util.List;
import javax.validation.Valid;
import com.infosys.sla.model.IncidentExtractStg;
public class IncidentsForm {
#Valid
private List<IncidentExtractStg> incidents;
public List<IncidentExtractStg> getIncidents() {
return incidents;
}
public void setIncidents(List<IncidentExtractStg> incidents) {
this.incidents = incidents;
}
}
IncidentExtractStg.java snippet
#Entity
#Table(name="incident_extract_stg")
public class IncidentExtractStg {
#Id
#Column(name="ies_id")
private int id;
#NotBlank(message="number cannot be empty")
#Pattern(regexp="[A-Za-z0-9]*",message="number can contain only alphabets and numbers")
#Column(name="ies_number")
private String number;
First of all, if I were you I will extract all the logic inside a Service Layer. To proceed, you could create an interface IncidentService and its own concrete implementation IncidentServiceImpl in which you can safely will handle your needs. Controllers are definitely not done to do everything.
Then, what are your needs?
"check Errors for each row and display only those rows containing validation errors and update the rows which don't have validation errors"
A method inside your Service Layer could be like that:
public void handleErrors(IncidentsForm incidentsForm, BindingResult bindingResult){
List<String> fieldsInErrorState = new ArrayList<String>(10);
if (bindingResult.hasErrors()) { //
Map<String, Object> bindingModel = bindingResult.getModel();
for (Map.Entry<String, Object> entry : bindingModel.entrySet()) {
String key = entry.getKey();
//Object value = entry.getValue(); you don't need to parse that unless you want specific domain model handlers to run
//you need to store the key as a form field which is in error state
fieldsInErrorState.add(key);
//you already have all the stuff to parse and display errors in your JSP
//thanksfully to bindingResult and JSTL tags.
}
ContactMessageForm cmForm2 = new ContactMessageForm();
// get the list of the fields inside your form
Field[] declaredFields = ContactMessageForm.class.getDeclaredFields();
for (Field field : declaredFields) {
if (!fieldsInErrorState.contains(field.getName())) {
if (field.getName().equalsIgnoreCase("firstname")) {
cmForm2.setFirstname(contactMessageForm.getFirstname());
}
if (field.getName().equalsIgnoreCase("lastname")) {
cmForm2.setLastname(contactMessageForm.getLastname());
}
//etc for each properties of your form object.
}
// then store your dbmodel object
// BUT i think you must be carefull to your data integrity... It is maybe not safe to save an object like that with bypassing some stuff...
// Your form was built like that maybe for a good reason looking at your objects graph.
// If your form is too big, then split it in small parts, it will be much easy to handle, to update, and to work with daily.
}
}
}
Of course you need to customize that code, don't forget to add the throws IntrospectionException to your service method, and you are on the good way.
Cheers!
As the logic is to display only those rows containing validation errors, a new List is created to store the rows which are having at least one validation error.
A new BindingResult is created to store the Errors against the index of the new List.(If this is not done then the error message wont be displayed against the displayed row).
The below logic is to check for field error against each field of the row and from the jsp you can see the filed name has "incidents[${status.index}].id".
- Counter i to get the row count
- Counter j is to set the index for BindingResult.
BeanPropertyBindingResult result2 = new BeanPropertyBindingResult(incidentsForm, bindingResult.getObjectName();
List<IncidentExtractStg> incidents= new ArrayList<IncidentExtractStg>();
int i=0;// to get the row count
int j=0;// to set the index
for(IncidentExtractStg ies : incidentsForm.getIncidents())
{
int count=0;
Field[] declaredFields = IncidentExtractStg.class.getDeclaredFields();
for (Field field : declaredFields)
{
if (bindingResult.hasFieldErrors("incidents["+i+"]."+field.getName()))
{
for (FieldError error: bindingResult.getFieldErrors("incidents["+i+"]."+field.getName()))
{
result2.addError(new FieldError(error.getObjectName(), "incidents["+j+"]."+field.getName(), error.getRejectedValue(), error.isBindingFailure(), error.getCodes(), error.getArguments(), error.getDefaultMessage()));
}
count++;
}
}
if(count>0)
{
j++;
incidents.add(ies);
}
else
{
ies.setValidated(1);
incidentExtractStgService.update(ies);
}
i++;
}
i=0;
j=0;
if (bindingResult.hasErrors()) {
incidentsForm.setIncidents(incidents);
System.out.println("error block");
for (FieldError error: result2.getFieldErrors()) {
System.out.println("field errors are "+error.getField());
System.out.println("field errors are "+error);
}
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.incidentsForm", result2);
redirectAttributes.addFlashAttribute("incidentsForm", incidentsForm);
return "redirect:/validateIncidentList";
}
E.g: If in row number 30 ,incidents[30].number field has validation error. Then i=30 and count>0 and j=0.
Hence the entire row incidents[30] will be saved at index 0 in the newly initialized list and the bindingresult will also be added at index 0. If the J is not being set to the result2 and bindingresult is used then it will still point to index 30 and error message will not be displayed against the field which is now stored at index 0.
Now this updated IncidentsForm and result2 will be send to jsp page which will display only those rows having validations errors and there respective error messages.

Spring MVC: How to test whether param exists when there's no value?

I want to display an error message with my custom login.jsp form. When there's an error, the url is ../loginForm?error without any value assigned to error. (This seems to be the behavior of Spring Security.) If there's no error, the url is simply ../loginForm (without the parameter). In the controller I can capture the parameter with #RequestParam, but how do I check whether or not error is passed? In other words, how can I test a parameter alone without a value?
Here's the controller code I have now:
#RequestMapping("/loginForm")
public String showLoginForm(#RequestParam(value="error", defaultValue="false")
boolean error,
Model model)
{
if (error == true)
{
model.addAttribute("loginError", "Invalid username and password.");
}
return "/user/loginForm";
}
...and here's the JSP snippet:
<c:if test="${not empty loginError}">
<tr>
<td><c:out value="${loginError}" /></td>
</tr>
</c:if>
At this point I'm not including the Security configuration I have set up, since everything else seems to be working and I want to keep this focused on the issue at hand.
Thanks in advance for any suggestions!
Ok, I figured it out (while taking a break). The #RequestParam only works when there's actually a parameter available for mapping. If no such parameter is passed in, it's useless. So instead, I checked the Map provided by ServletRequest:
#RequestMapping("/loginForm")
public String showLoginForm(ServletRequest request, Model model)
{
Map<String, String[]> paramMap = request.getParameterMap();
if (paramMap.containsKey("error"))
{
model.addAttribute("loginError", "Invalid username and password.");
}
return "/user/loginForm";
}
It works fine now.
There is another way to do that. Just create one more method where #RequestMapping will check presence of "error" parameter, add required attribute and return view. Both methods could exist together.
#RequestMapping(value = "/loginForm", params = {"error"})
public String loginError(Model model)
{
model.addAttribute("loginError", "Invalid username and password.");
return "/user/loginForm";
}
#RequestMapping(value = "/loginForm")
public String login()
{
return "/user/loginForm";
}

Spring portlet mvc Validation fails during submission of the editted form

I have a form with few validations on it.
During new form submission, if validation fails I can see those error messages.
but, during editing the form when I change the field to blank intentionally and submit the form error messages are not shown on Jsp page but I can get the errorcount in controller as 1 .
<portlet:actionURL var="actionUrl">
<portlet:param name="action" value="editCommunity"/>
<portlet:param name="communityID" value="${community.id}"/>
</portlet:actionURL>
<liferay-ui:tabs names="Details" />
<form:form commandName="community" method="post" action="${actionUrl}">
<form:hidden path="id"/>
<div><form:errors cssClass="portlet-msg-error" path="*"/></div>
<table class="manager-detail">
<tr>
<th class="portlet-form-field-label">
<label for="community_label_name"><spring:message code="community.label.name"/></label>
<span class="manager-field-required">*</span>
</th>
<td><form:input id="community_label_name" cssClass="portlet-form-input-field" path="name" size="30" maxlength="80" /></td>
</tr>
My edit controller method.....
rendering edit form
#RequestMapping(params = "action=editCommunity")
public String showEditCommunityForm(final RenderRequest request,
#RequestParam(value="communityID") Long id, final Model model)
throws CommunityNotFoundException {
final ThemeDisplay themeDisplay = (ThemeDisplay) request
.getAttribute(WebKeys.THEME_DISPLAY);
model.addAttribute("community", communityService.getCommunity(id));
return "communityEdit";
}
edited form is submitted
#RequestMapping(params = "action=editCommunity")
public void submitEditCommunityForm(final ActionRequest request,
final ActionResponse response,
#ModelAttribute("community") Community community,
BindingResult result, Model model) throws SystemException, PortalException {
communityValidator.validate(community, result);
if (result.hasErrors()) {
System.out.println("validation errors size..."+result.getErrorCount());
//model.addAttribute("community", community);
response.setRenderParameter("action", "editCommunity");
response.setRenderParameter("communityID", String.valueOf(community
.getId()));
}
}
It is not full code but a block
I have tried couple of things like,
changing the http method from post to POST, but nothing works. Validation perfectly works during form creation, but not during edit.
Am I missing anything? please give me suggestions.
Cheers
Vamshi
Preserving the validation error messages can be a real pain!
I have tried a lot of things - from configuring the redirect behavior of the portlet container to using jsr303 instead of spring validation.
The only solution I have consistently had and success implementing is really ugly:
Do the validation in an action method.
If errors are encountered save the BindingResult/Errors-object with "your own key" in the Spring model and interrupt the action handling.
You are redirected to the render method
There you pick up the Errors-object and put it back to the key where "Spring validation" expects it.
In code this looks something like this:
#ActionMapping
public void invite(#ModelAttribute MyFormBean myFormBean,
BindingResult result, Model model) {
// validate indata
myValidator.validate(myFormBean, result);
// Workaround to preserve Spring validation errors
if (result.hasErrors()) {
model.addAttribute("errors", result);
return;
}
...
}
#RequestMapping
public String showForm(#ModelAttribute("myFormBean") MyFormBean myFormBean,
Model model) {
...
// Workaround to get the errors form-validation from actionrequest
Errors errors = (Errors) model.asMap().get("errors");
if (errors != null) {
model.addAttribute(
"org.springframework.validation.BindingResult.myFormBean", errors);
}
return "myForm";
}
The information stored in the Model under "org.springframework.validation.BindingResult.*" are deleted automatically between the action processing and the render processing, and by preserving it explicitly in "errors" the information will be available to the view.
This is an ugly solution, you have to know more than you want about how the implementation really works, it is counter intuitive and if not properly commented this code could easily be removed by someone not familiar with the problem, but it is not a lot of code and it works.
You can omit the #ModelAttribute in the render phase and retrieve it from the model:
#ActionMapping
public void invite(#ModelAttribute MyFormBean myFormBean,
BindingResult result, Model model) {
// validate indata
myValidator.validate(myFormBean, result);
...
}
#RequestMapping
public String showForm(Model model) {
MyFormBean myFormBean = (MyFormBean)model.asMap().get("myFormBean");
...
return "myForm";
}

Resources