I have this form which shows several lists of objects inside datatables. Through this form, an admin user can define the permissions for an specific user over those objects.
For instance, this is the product permissions dataTable jsp code:
<sf:form action="${action}" class="form-horizontal" method="post" modelAttribute="permisosList" enctype="multipart/form-data" >
...
<table class="table table-striped table-bordered table-hover" id="tabla_permisos6">
<thead>
<tr>
<th><%=Languages.getString("Producto")%></th>
<th><%=Languages.getString("Permiso")%></th>
</tr>
</thead>
<tbody>
<c:forEach items="${permisosList.permisosProducto}" var="permiso" varStatus="counter">
<tr>
<sf:input path="permisosProducto[${counter.index}].producto.idProd" type="hidden"/>
<td style="vertical-align:middle;"><c:out value="${permiso.producto.nombre}"/></td>
<td align="center">
<sf:select class="form-control" path="permisosProducto[${counter.index}].permiso" type="number">
<sf:option value="0">
<c:out value="No visible" />
</sf:option>
<sf:option value="1">
<c:out value="Visible" />
</sf:option>
<sf:option value="2">
<c:out value="Descarga" />
</sf:option>
</sf:select>
</td>
</tr>
</c:forEach>
</tbody>
</table>
...
Some of these lists have hundreds or thousands of objects inside, so when I submit the form, I get the spring BindException:
org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 4 errors
Field error in object 'permisosList' on field 'permisosProducto[137].producto.idProd': rejected value []; codes [typeMismatch.permisosList.permisosProducto[137].producto.idProd,typeMismatch.permisosList.permisosProducto.producto.idProd,typeMismatch.permisosProducto[137].producto.idProd,typeMismatch.permisosProducto.producto.idProd,typeMismatch.idProd,typeMismatch.int,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [permisosList.permisosProducto[137].producto.idProd,permisosProducto[137].producto.idProd]; arguments []; default message [permisosProducto[137].producto.idProd]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'int' for property 'permisosProducto[137].producto.idProd'; nested exception is java.lang.NumberFormatException: For input string: ""]
Field error in object 'permisosList' on field 'permisosProducto[338].permiso': rejected value []; codes [typeMismatch.permisosList.permisosProducto[338].permiso,typeMismatch.permisosList.permisosProducto.permiso,typeMismatch.permisosProducto[338].permiso,typeMismatch.permisosProducto.permiso,typeMismatch.permiso,typeMismatch.int,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [permisosList.permisosProducto[338].permiso,permisosProducto[338].permiso]; arguments []; default message [permisosProducto[338].permiso]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'int' for property 'permisosProducto[338].permiso'; nested exception is java.lang.NumberFormatException: For input string: ""]
Field error in object 'permisosList' on field 'permisosProducto[573].permiso': rejected value []; codes [typeMismatch.permisosList.permisosProducto[573].permiso,typeMismatch.permisosList.permisosProducto.permiso,typeMismatch.permisosProducto[573].permiso,typeMismatch.permisosProducto.permiso,typeMismatch.permiso,typeMismatch.int,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [permisosList.permisosProducto[573].permiso,permisosProducto[573].permiso]; arguments []; default message [permisosProducto[573].permiso]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'int' for property 'permisosProducto[573].permiso'; nested exception is java.lang.NumberFormatException: For input string: ""]
Field error in object 'permisosList' on field 'permisosProducto[808].permiso': rejected value []; codes [typeMismatch.permisosList.permisosProducto[808].permiso,typeMismatch.permisosList.permisosProducto.permiso,typeMismatch.permisosProducto[808].permiso,typeMismatch.permisosProducto.permiso,typeMismatch.permiso,typeMismatch.int,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [permisosList.permisosProducto[808].permiso,permisosProducto[808].permiso]; arguments []; default message [permisosProducto[808].permiso]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'int' for property 'permisosProducto[808].permiso'; nested exception is java.lang.NumberFormatException: For input string: ""]
I have already checked that the info at those indexes is OK, so there is no empty fields anywhere (then why once the data is submitted there are empty fields?). I have also noticed that those indexes aren't always the same ones. Maybe if I restart tomcat or I retry minuts later, the indexes in the error are different. And only happens with lists having at least 200+ objects. And the exception is thrown before reaching the Controller.
This is my PermisoList class:
public class PermisoList {
List<PermisoProductoForm> permisosProducto;
List<PermisoCarpetaForm> permisosCarpeta;
List<PermisoTipoDocForm> permisosTipoDoc;
List<PermisoAgenteClienteForm> permisosAgenteCliente;
List<PermisoIdiomaForm> permisosIdioma;
List<PermisoPaisForm> permisosPais;
List<PermisoEmpresaForm> permisosEmpresa;
List<PermisoUsuarioProductoForm> permisosUsuarioProducto;
...
}
My PermisoProductoForm class:
public class PermisoProductoForm {
private Producto producto;
private int permiso;
...
}
And my Producto class:
@Entity
@Table(name = "Producto")
public class Producto {
@Id
@GeneratedValue
private int idProd;
private String codigo;
private String nombre;
private String marca;
private boolean activo;
@ManyToOne
@JoinColumn(name = "idFamilia")
private Familia familia;
@OneToMany(mappedBy = "producto", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Documento> documentacion;
@ManyToOne
@JoinColumn(name = "idEmpresa")
private Empresa empresa;
@OneToMany(mappedBy = "primaryKey.producto", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<PermisoUsuarioProducto> permisosProducto;
@OneToMany(mappedBy = "productoRelacionado", fetch = FetchType.LAZY)
private List<TemaForo> temasForo;
...
}
Also, I have already tried some solutions I've found like adding this code to the controller:
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.setAutoGrowCollectionLimit(2048);
}
Or adding enctype="multipart/form-data"
inside the form tag in the jsp code.
Does anyone know why is this happening or know how to solve this issue? I'm 100% stuck right now.
Edit: I've done what @angcap answered (change int to Integer types and add the CustomEditor in the initBinder method) and now this BindException is no longer throwing. So controller is reached.
What happens now is that those indexes that had an empty string now have a 'null' value (due to the CustomEditor). As I said, I've checked the info before submitting the form, I can see that all the objects at the lists are OK, there is not an element with an empty field.
So why is Spring doing this to the data when the list is quite large? And it seems to do it to random indexes...
Edit 2: This is a TOMCAT issue! I've deployed on a Pivotal tc Server (v3.1) and with the binder.setAutoGrowCollectionLimit(2048);
solution, works without any exception or empty string or null. Fully functional.
So the problem is in the tomcat (v8.0) configuration. Does anyone know anything about this?
Edit 3: I accept @angcap answer because it really solves my initial issue. By doing what he says the BindException is avoided.
It seems you are posting empty strings for number fields idProd
and permiso
. Check posted data or try to register CustomNumberEditor allowing empyValues using @initBinder in your controller.