Why do I get IllegalArgumentException Cannot convert value of type String to required type Product, in Spring? - spring

I receive the exception
Failed to convert property value of
type [java.lang.String] to required
type [beans.Product] for property
product; nested exception is
java.lang.IllegalArgumentException:
Cannot convert value of type
[java.lang.String] to required type
[beans.Product] for property product:
no matching editors or conversion
strategy found
in the Errors errors object even before my DetailProductValidator starts validating through the validate method.
I don't understand why Spring does that. I don't have any input field that is mapped directly to the product property/object. I just use the product object's properties in the jsp. For example, I use:
<form:options items="${dpBackObj.product.colorMap}"/>
<!-- or -->
${dpBackObj.product.priceInDollars}
but I never use:
<form:input path="product"/>
Can anyone please explain why this happens? And maybe inform me of a simple solution?
The bean configuration for the controller is:
<!-- DETAIL PRODUCT FORM CONTROLLER -->
<bean id="productDetailFormController" name="/detail.htm /addToCart.htm"
class="detailProduct.DetailProductFormController">
<property name="sessionForm" value="true" />
<property name="commandName" value="dpBackObj" />
<property name="commandClass" value="detailProduct.DetailProductBackingObject" />
<property name="validator">
<bean class="detailProduct.DetailProductValidator" />
</property>
<property name="formView" value="detail" />
<property name="successView" value="redirect:/viewCart.htm" />
<property name="cartService" ref="cartServiceImpl"/>
</bean>
The backing object for the DetailProductFormController is:
public class DetailProductBackingObject {
private String quantityOverflowError;
private Product product;
private int quantity;
private ShoppingCart shoppingCart;
private long sizeId;
private long colorId;
public DetailProductBackingObject() {
this.product = new Product();
this.sizeId = -1;
this.colorId = -1;
}
//getters and setters
}
If you need some other info, I will provide. I am using Spring 2.5.5.
Kind Regards,
Despot
EDIT1 (due to request from axtavt):
<form:form method="post" commandName="dpBackObj">
<table width="730" border="0" cellspacing="0" cellpadding="0">
<c:if test="${!empty dpBackObj.quantityOverflowError}">
<tr>
<td>
<c:out value="${dpBackObj.quantityOverflowError}"/>
</td>
</tr>
</c:if>
<spring:bind path="dpBackObj.*">
<c:if test="${not empty status.errorMessages}">
<div class="val-summary text-error" id="errorDivId">
<div style="" class="val-summary text-error" id="errorDivId">
<fmt:message key="detail.error.header"/>
<ul>
<c:forEach items="${status.errorMessages}" var="error">
<li><c:out value="${error}"/></li>
</c:forEach>
</ul>
</div>
</div>
</c:if>
</spring:bind>
<tr>
<td width="310" align="left" valign="top">
<img src="${imagesPath}/${dpBackObj.product.largeImageUrl}" alt="${dpBackObj.product.description}" />
</td>
<td width="420" align="left" valign="top">
<div id="tls_detPName">
<c:out value="${dpBackObj.product.name}"></c:out>
</div>
<div >
<strong class="numeric">${dpBackObj.product.priceInDollars}</strong>
</div>
<div id="tls_detPDescLong">
${dpBackObj.product.largeDescription}
<br />
</div>
<div >
<table cellpadding="2" border="0">
<tr>
<td align="right">
<label for="p_sizes" class="label"><fmt:message key="viewCart.Size"/></label>
</td>
<td>
<form:select path="sizeId" >
<form:option value="-1" label="x"/>
<form:options items="${dpBackObj.product.sizeMap}"/>
</form:select>
</td>
</tr>
<tr>
<td align="right">
<label for="p_colors" class="label"><fmt:message key="viewCart.Color"/></label>
</td>
<td>
<form:select path="colorId" >
<form:option value="-1" label="y"/>
<form:options items="${dpBackObj.product.colorMap}"/>
</form:select>
</td>
</tr>
</table>
</div>
<div id="tls_addToCart">
<div >
<label for="quantityId" class="label"><fmt:message key="viewCart.Quantity"/>:</label>
<form:input path="quantity" onkeypress="return checkForNumber(this, event)" maxlength="10" size="3" id="quantityId" cssClass="textbox-center"/>
<input type="image" name="addToCartButtonName" src="${imagesPath}/addToCartBtn.jpg" />
</div>
</div>
</td>
</tr>
</table>
</form:form>
EDIT2 (due to JacobM's request):
This is my Validator:
public class DetailProductValidator implements Validator {
public boolean supports(Class clazz) {
return DetailProductBackingObject.class.equals(clazz);
}
public void validate(Object obj, Errors errors) {
DetailProductBackingObject detailProductBackingObject = (DetailProductBackingObject) obj;
if (detailProductBackingObject.getSizeId() == -1) {
errors.rejectValue("sizeId", "error.detail.jsp.choose.size", null, "Input size.");
}
}
}
When I reach the line DetailProductBackingObject detailProductBackingObject = I already have the error.
The converting of the request parameters to the backing object properties happens in http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/web/servlet/mvc/BaseCommandController.html . This is what Spring says about the conversion:
Populating using request parameters
and PropertyEditors: Upon receiving a
request, any BaseCommandController
will attempt to fill the command
object using the request parameters.
This is done using the typical and
well-known JavaBeans property
notation. When a request parameter
named 'firstName' exists, the
framework will attempt to call
setFirstName([value]) passing the
value of the parameter. Nested
properties are of course supported.
For instance a parameter named
'address.city' will result in a
getAddress().setCity([value]) call on
the command class.
It's important to realise that you are
not limited to String arguments in
your JavaBeans. Using the
PropertyEditor-notion as supplied by
the java.beans package, you will be
able to transform Strings to Objects
and the other way around. For instance
setLocale(Locale loc) is perfectly
possible for a request parameter named
locale having a value of en, as long
as you register the appropriate
PropertyEditor in the Controller (see
initBinder() for more information on
that matter.
Validators: After the controller has
successfully populated the command
object with parameters from the
request, it will use any configured
validators to validate the object.
Validation results will be put in a
Errors object which can be used in a
View to render any input problems.

Since I can't see anything wrong with the form, the only possible reason I can imagine is that you have a parameter named product in the URL of your form page.
If so, you can change your URLs or use DataBinder.setDisallowedFields() to disable the attempt to bind that parameter.

Related

SpringBoot Web, JPA, JSP with JSTL - type conversion error

I am trying to use Spring Boot Web with a JSP and JSTL combination. All is well, except the Update method for the JPA queries.
When I submit the form for the edit page, I always get a Type Conversion error for my #Entity which has an #Id column of datatype Long. The control does not even come to the Controller Method with the URL pattern /update, even when I make it hidden in the form:hidden tag.
I am clueless, how to go past this issue?
FYI, I am pasting the relevant sections of the code.
Entity - Quote.java
#Entity
#Data
public class Quote { //implements Auditable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "quote")
#NonNull
private String quote;
...
//other attributes omitted for brevity
}
Controller Method - QuoteController.java
#RequestMapping(value = "/update",method = RequestMethod.POST)
public String updateQuote(#ModelAttribute("quote") Quote quote, Model model,
BindingResult bindingResult) {
LOGGER.info("/update POST request received, quote :: " + quote);
if (bindingResult.hasErrors()) {
LOGGER.error("/update POST request has errors :: " + bindingResult.getAllErrors());
model.addAttribute("error", bindingResult.getAllErrors());
return "qms/edit";
}
quoteService.updateQuote(quote);
return "qms/list";
}
JSP - edit.jsp
<%# include file="header.jsp"%>
<h3>Edit a Quote</h3>
<c:choose>
<c:when test="${error}">
<div>
<span class='error'>No matching quotes. Try again!</span>
</div>
<div>
<b>Errors : </b> : ${error}
</div>
</c:when>
<c:otherwise>
<c:url var="update_quote_url" value="/qms/update"/>
<form:form action="${update_quote_url}" method="post" modelAttribute="quote">
<form:errors path="*" cssClass="errorBox"/>
<table class='table table-striped'>
<form:hidden path="id"/>
<tr>
<td>
<form:label path="id">Id: </form:label>
</td>
<td>
<form:label path="id">${quote.id}</form:label>
</td>
</tr>
<tr>
<td>
<form:label path="quote">Quote: </form:label>
</td>
<td>
<form:input type="text" path="quote" size="50"
placeholder="Beginning is half done!"
value="${quote.quote}"/>
</td>
</tr>
<tr>
<td>
<form:label path="category">Category: </form:label>
</td>
<td>
<form:input type="text" path="category" size="50"
placeholder="Motivation" value="${quote.category}"/>
</td>
</tr>
<tr>
<td>
<form:label path="author">Author Name: </form:label>
</td>
<td>
<form:input type="text" path="author" size="50"
placeholder="Raghavan Muthu" value="${quote.author}"/>
</td>
</tr>
<tr>
` <td colspan="2" class='colspan2'>
<input type="submit" value="Update"/>
</td>`
</tr>
</table>
</form:form>
</c:otherwise>
</c:choose>
<%# include file="footer.jsp"%>
Error
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Mon Sep 20 14:21:03 IST 2021
There was an unexpected error (type=Bad Request, status=400).
Failed to convert value of type 'java.lang.String' to required type 'com.raghsonline.springbootweb.qms.model.Quote'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Long] for value 'When you learn a little, you feel you know a lot. But when you learn a lot, you realize that you know very little.'; nested exception is java.lang.NumberFormatException: For input string: "Whenyoulearnalittle,youfeelyouknowalot.Butwhenyoulearnalot,yourealizethatyouknowverylittle."
org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'com.raghsonline.springbootweb.qms.model.Quote'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Long] for value 'When you learn a little, you feel you know a lot. But when you learn a lot, you realize that you know very little.'; nested exception is java.lang.NumberFormatException: For input string: "Whenyoulearnalittle,youfeelyouknowalot.Butwhenyoulearnalot,yourealizethatyouknowverylittle."
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:79)
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:53)
at org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:696)
Question
Why it always considers the #Id Column?
Usage of DTO - a different issue
I have used a DTO which has all the attributes of the main Entity except the #Id column, in the Controller method. However, it has a different issue. It inserts a new record in DB because it does not have the ID attribute.
#RequestMapping(value = "/update",method = RequestMethod.POST)
public String updateQuote(#ModelAttribute("quote") QuoteDTO quoteDTO, Model model,
BindingResult bindingResult) {
LOGGER.info("/update POST request received, quote :: " + quoteDTO);
if (bindingResult.hasErrors()) {
LOGGER.error("/update POST request has errors :: " + bindingResult.getAllErrors());
model.addAttribute("error", bindingResult.getAllErrors());
return "qms/edit";
}
//Convert from the DTO to Model
Quote quote = getModelFromDTO(quoteDTO);
quoteService.updateQuote(quote);
return "qms/list";
}
Any help will be highly appreciated.
Thanks.
A few things solved this issue.
Added the type field for the ID column in the element, where it resolved the type mismatch issue. Thanks to Mohammed Khan for the hint. I kinda ignored it earlier.
Also there was a clash with the actual quote ('quote') of the Model object and also the reference variable of the ModelAttribute ('quote'). I updated the model object to 'quoteObj`, which also helped the issue to be resolved.
Still the /id in the #PathVariable was not working well.. I have updated the #PathVariable in the URL, where instead of having it as /qms/update/{id} , now it is just /qms/update because I get the Model Attribute in the Request Body.
Additionally I enabled the Web Development tool (Network mode/Tab) on the browser to inspect the REQ and RESP elements for each URL being hit. It helped me ensure that the right values are actually passed from the UI to Controller.
My final method now looks like this.
#RequestMapping(value = "/update",method = RequestMethod.POST)
public String updateQuote(#ModelAttribute("quoteObj") Quote quoteObj) {
LOGGER.info("/update POST request received, quote :: " + quoteObj);
quoteService.updateQuote(quoteObj);
return "qms/list";
}
And the form element looks like this.
<tr>
<td>
<form:label path="id">Id: </form:label>
</td>
<td>
<%-- ${quote.id} --%>
<form:input path="id" type="number" value="${quote.id}"/>
</td>
</tr>

Extra character(,) is added while using Textbox in spring

I am new to JSP and Spring. I want to insert a userID(U0005) using textbox in spring form but it is storing(,U0005). From where the "," is inserted?
The code I have written is:
/* In Register.jsp: */
<c:url var="addAction" value="/libUsr/add"></c:url>
<form:form action="${addAction}" commandName="libUsr">
<table>
<tr>
<td>
<form:label path="id">
<spring:message text="ID" />
</form:label>
</td>
<td><form:input path="id" required="true" /></td>
</tr>
</table>
</form:form>
/*
In UserController.java:
*/
#RequestMapping(value= "/libUsr/add", method = RequestMethod.POST)
public String addLibUsr(#ModelAttribute("libUsr") LibUsr libUsr){
libUsrDAO.saveOrUpdate(libUsr);
return "redirect:/register";
}
/*
In DAOImpl:
Saving the data through DAOs
*/
#Transactional
public void saveOrUpdate(LibUsr libusr) {
sessionFactory.getCurrentSession().saveOrUpdate(libusr);
}
Your problem is with this tag:
<form:label path="id">
As you are adding the attribute path to one label, which wont store any value, spring try to get the value and returns empty,yourID.
Change the path attribute of your label to:
<form:label for="id">
<spring:message text="ID" />
</form:label>
If you want to show the value of the id into that label use:
<label>${yourObject.id}</label>
or
<label th:value="${yourObject.id}"></label>
Path attribute is just for input, select, checkbox...not static values as label, span...

Spring form validation - errors are not displayed

I have following situation:
I try to create a simple form to edit information about a movie. Therefor I use spring mvc with jsp.
For the validation I use the JSR 303 hibernate implementation (hibernate-validator 4.2.0).
My problem is that if there are validation errors (BindResults hasErrors method returns true) I can only display the errors with
<form:errors path="*" />
and not fieldspecific. like with:
<form:errors path="title" />
I have no idea why but all errors are displayed in at the path="*" errortag, but none at the fieldspecific ones. The only errors which are displayed right to the field are some which brought an exception i.e.
Failed to convert property value of type java.lang.String to required type int for property runtime; nested exception is java.lang.NumberFormatException: For input string: "")
the important parts of my jsp file look like this:
<%# taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<form:form commandName="movie">
<fieldset>
<form:errors path="*" cssClass="formErrorBlock"/>
<table>
<tr>
<td><form:label path="title">Title:</form:label></td>
<td>
<form:input path="title"/>
<form:errors path="title" cssClass="formFieldError"/>
</td>
</tr>
<tr>
<td><form:label path="runtime">Runtime:</form:label></td>
<td>
<form:input path="runtime"/><br />
<form:errors path="runtime" cssClass="formFieldError" />
</td>
</tr>
<tr>
<td><form:label path="imdbId">IMDB-ID:</form:label></td>
<td>
<form:input path="imdbId"/><br />
<form:errors path="imdbId" cssClass="formFieldError" />
</td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" />
</td>
</tr>
</table>
</fieldset>
</form:form>
My Controller:
#Controller
public class MovieController {
#Autowired
private MovieService _movieService;
//some other methods...
#RequestMapping(value="/movie/{pMovieId}/edit", method = RequestMethod.GET)
public String showForm(ModelMap pModel, #PathVariable Integer pMovieId) {
Movie movie = _movieService.getMovieById(pMovieId);
pModel.addAttribute("movie", movie);
return "edit/movie";
}
#RequestMapping(value="/movie/{pMovieId}/edit", method = RequestMethod.POST)
public String editMovieSave(#Valid Movie pMovie, BindingResult pResult, #PathVariable Integer pMovieId, ModelMap pModel) {
Movie movie = _movieService.getMovieById(pMovieId);
if(pResult.hasErrors()) {
return "edit/movie";
}
//save
//redirect to moviepage
return "redirect:/movie/" + pMovieId;
}
}
and finally my movie class:
public class Movie implements Serializable{
private int _id;
#NotEmpty
#Size(min=3)
private String _title;
#NotEmpty
private String _filename;
#Pattern(regexp="tt+[0-9]{7}")
private String _imdbId;
#Min(value=1)
private int _runtime;
//getters and setters....
}
I know this question has been asked a lot of times before, but none of the answers worked with me.
thanks for your help
I solved it by renaming the attributes (removing the underlines). However it was like "jpprade" said (maybe there are accessed by reflection).
For those who can't rename the class attributes (in case of some codingconventions, ...) can annotate the getter methods.

Spring Framework 3.0.5 - spring form data binding - comma delimiter

I have a group editation form:
<form:form method="POST" action="" commandName="group">
<table>
<tr>
<td><form:label path="name">Name</form:label></td>
<td><form:input path="name" disabled="disabled" /></td>
<td><form:errors path="name" class="error" /></td>
</tr>
<tr>
<td><form:label path="description">Description</form:label></td>
<td><form:input path="description" /></td>
<td><form:errors path="description" class="error" /></td>
</tr>
<tr>
<td><form:label path="gidNumber">GID</form:label></td>
<td><form:input path="gidNumber" /></td>
<td><form:errors path="gidNumber" class="error" /></td>
</tr>
<tr>
<td colspan="3"><input type="submit" /></td>
</tr>
</table>
<form:hidden path="members" />
</form:form>
This form is binding object of class Group. Problem is with binding the member attribute to the hidden field. The deffinition of the memeber attribute in Group class is below. The group class is also being used by Spring LDAP ODM manager, (thus the annotations there).
#Attribute(name="member", syntax="1.3.6.1.4.1.1466.115.121.1.12")
private List<String> members = new ArrayList<String>();
The content of this array list is usually something like:
1: uid=user1,ou=users,dc=example,dc=com
2: uid=lilcuttie2,ou=users,dc=example,dc=com
3: uid=naprostejmagor,ou=users,dc=example,dc=com
4: uid=crazyuser,ou=users,dc=brazzers,dc=com
...
When the content is binded to hidden field it is concatenated to the comma delimited and then when it is beeing splited again to the arraylist entries, of course each entry is broken to four separate entries.
I've tried to use custom converter for ArrayList, but it messed up some otheer stuf like loading resource messages for locales..
Do you have any suggestions how to deal with it in any non-ugly way - so that I dont have to wrap my DAO classes nor modify them.
Thanks,
/Jakub
Have a small wrapper type over your members element, say:
public class MembersWrapper{
private List<String> members;
.....
}
Now, you can provide a custom converter to convert this MembersWrapper instance to a String and back - the conversion can be to say colon delimited across each element of the list, something that you can parse back later:
public class MemberWrapperToStringConverter implements Converter<MemberWrapper, String>{
...
}
public class StringToMemberWrapperConverter implements Converter<String, MemberWrapper>{
....
}
and register these converters with Spring MVC:
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="..MemberWrapperToStringConverter "/>
<bean class="..StringToMemberWrapperConverter "/>
</set>
</property>
</bean>
An alternative will be providing a custom property editor through #InitBinder annotation:
#InitBinder
public void initBinder(WebDataBinder binder){
binder.registerCustomEditor(Wrapper.class, new PropertyEditorSupport(){
#Override public String getAsText() {
//....
}
#Override public void setAsText(String text) throws IllegalArgumentException {
//....
}
});
}
The logic in these converters should be straightforward, convert the list to some string form, that you can parse back to list easily, say between list elements just add a colon or a semi-colon.
As I am not clear about your problem I have made some assumptions and providing this answer on basis of that.
Well you can use to loop your entries in the members list and for each entry in the list you can create a separate hidden field for that.
Hope this helps you.
Cheers.
You don't need to put the formtag for hidden field now when you are using just use the simple input tag for hidden fields but in the name property you need to specify as following.
<input type="hidden" name="members${status.index}"/>
in this way it will bind the same value back to the controller.
I think this should help you.
Cheers.

Spring MVC 3 Validation will not work when attributes added to model

I'm trying to get Spring annotation based validation working in a simple webapp. I'm using Spring 3.0.5, Tiles 2.2.2. In one specific case I can get the validation error to appear next to the field, however as soon as I add any objects to the model it stops working. Ideally, after the POST, I want to redirect to a GET of the form with the validation errors included. This is the setup:
I have a simple domain object.
public class DomainObject {
#NotEmpty
private String name;
private Date created;
private Date lastModified;
...
}
I have a controller with a GET method which adds all existing DomainObjects to the model and returns a view that displays them, and contains a very simple form for creating them. It also has a POST method for creating a new DomainObject.
#Controller
#RequestMapping("/")
public class DomainObjectController {
#Autowired
private DomainObjectService domainObjectService;
#RequestMapping("form.htm")
public String home(Model model) {
model.addAttribute("objects", domainObjectService.getAll());
model.addAttribute(new DomainObject());
return "form";
}
#RequestMapping(value="new_object.do", method=RequestMethod.POST)
public String newObject(#Valid DomainObject domainObject, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
//model.addAttribute("objects", domainObjectService.getAll());
//model.addAttribute(new DomainObject());
return "form";
}
domainObjectService.saveNew(domainObject);
model.addAttribute("objects", domainObjectService.getAll());
model.addAttribute(new DomainObject());
return "form";
}
}
Here's the view:
<form:form commandName="domainObject" action="new_object.do" method="post>
<spring:message code="name" />: <form:input path="name" />
<input type="submit" value="<spring:message code="create.object"/>" /><form:errors path="name" cssClass="error"/></form:form>
</div>
<table class="centered">
<col width=50 />
<col width=225 />
<col width=200 />
<col width=200 />
<thead>
<tr>
<td id="id" class="sortable"><spring:message code="id" /></td>
<td id="name" class="sortable"><spring:message code="name" /></td>
<td id="created" class="sortable"><spring:message code="created" /></td>
</tr>
</thead>
<tbody>
<c:forEach var="obj" items="${objects}">
<tr>
<td class="id">${obj.id}</td>
<td>${obj.name}</td>
<td>${obj.created}</td>
</tr>
</c:forEach>
</tbody>
</table>
With this setup, if I leave the name field empty, the validation error is picked up and correctly displayed to the right of the field. However, the table is always empty, as no objects are added to the model. If I add objects to the model, by uncommenting the lines
//model.addAttribute("objects", domainObjectService.getAll());
//model.addAttribute(new DomainObject());
the table is populated but the validation error no longer appears. I can't work this one out.
As another unwanted side effect, any relative links I have in the view now no longer work (for example, a link which changes the locale href="?lang=de").
So, what could be causing the validation message to disappear when I add data to the model? And is it possible for me to redirect to the original form while keeping hold of the validation messages?
Thanks,
Russell
The validation error is attached to the object that is invalid. If you replace the object that is invalide by an new one:
model.addAttribute(newDomainObject());
then the error messages is not attached to this object.

Resources