I'm was working without any problem while generating the project with RC1. After some work on the project, i realized that the CRUD fields that use select2 to autocomplete suddenly stopped working. I get error 500 when the s2 tries to fetch the data from the /entity/s2 path.
Full java console error:
2017-03-09 11:20:46.136 WARN 42495 --- [nio-8080-exec-7] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: No converter found capable of converting from type [ar.edu.um.programacion2.oficios.reference.Disponibilidad] to type [java.lang.String] (through reference chain: io.springlets.data.web.select2.Select2DataWithConversion["results"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: No converter found capable of converting from type [ar.edu.um.programacion2.oficios.reference.Disponibilidad] to type [java.lang.String] (through reference chain: io.springlets.data.web.select2.Select2DataWithConversion["results"])
2017-03-09 11:20:46.137 WARN 42495 --- [nio-8080-exec-7] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved exception caused by Handler execution: org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: No converter found capable of converting from type [ar.edu.um.programacion2.oficios.reference.Disponibilidad] to type [java.lang.String] (through reference chain: io.springlets.data.web.select2.Select2DataWithConversion["results"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: No converter found capable of converting from type [ar.edu.um.programacion2.oficios.reference.Disponibilidad] to type [java.lang.String] (through reference chain: io.springlets.data.web.select2.Select2DataWithConversion["results"])
Any of that fields reference fields are not working now. This is one of the entities used as reference in that field:
@RooJavaBean
@RooToString
@RooJpaEntity
@RooEquals(isJpaEntity = true)
public class Localidad {
public Localidad(String nombre, String descripcion) {
super();
this.nombre = nombre;
this.descripcion = descripcion;
}
/**
* TODO Auto-generated attribute documentation
*
*/
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
/**
* TODO Auto-generated attribute documentation
*
*/
@Version
private Integer version;
/**
* TODO Auto-generated attribute documentation
*
*/
@NotNull
private String nombre;
/**
* TODO Auto-generated attribute documentation
*
*/
private String descripcion;
private String googleMapsString;
/**
* TODO Auto-generated method documentation
*
* @return String
*/
public String toString() {
return getNombre();
}
public String getMapString(){
return "http://maps.googleapis.com/maps/api/staticmap?" + getGoogleMapsString() + "&key=";
}
}
The class where the referenced field is used:
@RooJavaBean
@RooToString
@RooJpaEntity
@RooEquals(isJpaEntity = true)
public class Servicio {
/**
* TODO Auto-generated attribute documentation
*
*/
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
/**
* TODO Auto-generated attribute documentation
*
*/
@Version
private Integer version;
/**
* TODO Auto-generated attribute documentation
*
*/
@NotNull
private String nombre;
/**
* TODO Auto-generated attribute documentation
*
*/
private String descripcion;
/**
* TODO Auto-generated attribute documentation
*
*/
@RooUploadedFile(contentType = "image/png")
@Lob
private byte[] logo;
/**
* TODO Auto-generated attribute documentation
*
*/
@NotNull
private String telefono;
/**
* TODO Auto-generated attribute documentation
*
*/
@NumberFormat
private float puntaje;
@OneToOne(fetch=FetchType.LAZY)
@JoinColumn(name="localidad_id")
private Localidad localidad;
@OneToOne(fetch=FetchType.LAZY)
@JoinColumn(name="disponibilidad_id")
private Disponibilidad disponibilidad;
@OneToOne(fetch=FetchType.LAZY)
@JoinColumn(name="categoria_id")
private Categoria categoria;
@OneToOne(fetch=FetchType.LAZY)
@JoinColumn(name="prestador_id")
private Prestador prestador;
@ManyToMany
@JoinTable(name = "servicio_calificacion", joinColumns = @JoinColumn(name = "servicio_id"), inverseJoinColumns = @JoinColumn(name = "calificacion_id"))
private List<CalificacionCliente> listadecalificaciones;
/**
* TODO Auto-generated method documentation
*
* @return String
*/
public String toString() {
return getNombre() + " (" + getPrestador() + ")";
}
}
One of the views were that field is used:
<!DOCTYPE html>
<html lang="en" data-layout-decorate="~{layouts/default-layout}">
<head id="head">
<title data-th-text="#{label_create_entity(#{label_servicio})}">
Create Servicio - oficios - SpringRoo Application</title>
<!-- DateTimePicker -->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-datetimepicker/2.5.4/build/jquery.datetimepicker.min.css" data-th-href="@{/webjars/datetimepicker/build/jquery.datetimepicker.min.css}" />
</head>
<body id="body">
<header role="banner">
<!-- Content replaced by layout of the page displayed -->
</header>
<!-- CONTAINER -->
<div class="container bg-container">
<!-- CONTENT -->
<!--
Only the inner content of the following tag "section" is included
within the template, in the section "content"
-->
<section data-layout-fragment="content">
<div class="container-fluid content" data-th-with="collectionLink=${@linkBuilder.of('ServiciosCollectionThymeleafController')}">
<h1 data-th-text="#{label_create_entity(#{label_servicio})}">Create Servicio</h1>
<!-- FORM -->
<form class="form-horizontal validate" method="POST" data-th-object="${servicio}" data-th-action="@{${collectionLink.to('create').with('servicio', servicio.id)}}">
<fieldset id="containerFields">
<legend class="sr-only" data-th-text="#{label_data_entity(#{label_servicio})}">Servicio data </legend>
<div class="form-group has-error has-feedback" data-z="15c04ac1" id="servicio-nombre-field" data-th-classappend="${#fields.hasErrors('nombre')}? 'has-error has-feedback'" data-th-class="form-group">
<label for="nombre" class="col-md-3 control-label" data-th-text="#{label_servicio_nombre}">nombre</label>
<div class="col-md-6">
<input id="nombre" name="nombre" data-th-value="*{{nombre}}" type="text" class="form-control" placeholder="nombre" data-th-placeholder="#{label_servicio_nombre}" data-toggle="tooltip" aria-describedby="nombreStatus" required="required" />
<span data-th-classappend="${#fields.hasErrors('nombre')}? 'glyphicon glyphicon-remove form-control-feedback'" class="glyphicon glyphicon-remove form-control-feedback" data-th-if="${#fields.hasErrors('nombre')}" aria-hidden="true"></span>
<span id="nombre-error" class="help-block" data-th-if="${#fields.hasErrors('nombre')}" data-th-errors="*{nombre}">Error message.</span>
</div>
</div>
<div class="form-group has-error has-feedback" data-z="8d2c287d" id="servicio-descripcion-field" data-th-classappend="${#fields.hasErrors('descripcion')}? 'has-error has-feedback'" data-th-class="form-group">
<label for="descripcion" class="col-md-3 control-label" data-th-text="#{label_servicio_descripcion}">descripcion</label>
<div class="col-md-6">
<input id="descripcion" name="descripcion" data-th-value="*{{descripcion}}" type="text" class="form-control" placeholder="descripcion" data-th-placeholder="#{label_servicio_descripcion}" data-toggle="tooltip" aria-describedby="descripcionStatus" />
<span data-th-classappend="${#fields.hasErrors('descripcion')}? 'glyphicon glyphicon-remove form-control-feedback'" class="glyphicon glyphicon-remove form-control-feedback" data-th-if="${#fields.hasErrors('descripcion')}" aria-hidden="true"></span>
<span id="descripcion-error" class="help-block" data-th-if="${#fields.hasErrors('descripcion')}" data-th-errors="*{descripcion}">Error message.</span>
</div>
</div>
<div class="form-group has-error has-feedback" data-z="54220f01" id="servicio-logo-field" data-th-classappend="${#fields.hasErrors('logo')}? 'has-error has-feedback'" data-th-class="form-group">
<label for="logo" class="col-md-3 control-label" data-th-text="#{label_servicio_logo}">logo</label>
<div class="col-md-3">
<input id="logo" name="logo" data-th-value="*{{logo}}" type="text" class="form-control inputmask" placeholder="logo" data-th-placeholder="#{label_servicio_logo}" data-toggle="tooltip" aria-describedby="logoStatus" data-inputmask-alias="numeric" data-inputmask-digits="2" />
<span data-th-classappend="${#fields.hasErrors('logo')}? 'glyphicon glyphicon-remove form-control-feedback'" class="glyphicon glyphicon-remove form-control-feedback" data-th-if="${#fields.hasErrors('logo')}" aria-hidden="true"></span>
<span id="logo-error" class="help-block" data-th-if="${#fields.hasErrors('logo')}" data-th-errors="*{logo}">Error message.</span>
</div>
</div>
<div class="form-group has-error has-feedback" data-z="f1acb8e1" id="servicio-telefono-field" data-th-classappend="${#fields.hasErrors('telefono')}? 'has-error has-feedback'" data-th-class="form-group">
<label for="telefono" class="col-md-3 control-label" data-th-text="#{label_servicio_telefono}">telefono</label>
<div class="col-md-6">
<input id="telefono" name="telefono" data-th-value="*{{telefono}}" type="text" class="form-control" placeholder="telefono" data-th-placeholder="#{label_servicio_telefono}" data-toggle="tooltip" aria-describedby="telefonoStatus" required="required" />
<span data-th-classappend="${#fields.hasErrors('telefono')}? 'glyphicon glyphicon-remove form-control-feedback'" class="glyphicon glyphicon-remove form-control-feedback" data-th-if="${#fields.hasErrors('telefono')}" aria-hidden="true"></span>
<span id="telefono-error" class="help-block" data-th-if="${#fields.hasErrors('telefono')}" data-th-errors="*{telefono}">Error message.</span>
</div>
</div>
<div class="form-group has-error has-feedback" data-z="3c00987d" id="servicio-localidad-field" data-th-classappend="${#fields.hasErrors('localidad')}? 'has-error has-feedback'" data-th-class="form-group" data-th-with="collectionLink=${@linkBuilder.of('LocalidadsCollectionThymeleafController')}">
<label for="localidad" class="col-md-3 control-label" data-th-text="#{label_servicio_localidad}">Localidad</label>
<div class="col-md-6">
<!-- Select2 -->
<select data-th-field="*{localidad}" class="form-control dropdown-select-ajax" data-allow-clear="true" data-data-ajax--url="${collectionLink.to('select2')}" data-ajax--cache="true" data-ajax--delay="250" data-ajax--data-type="json" data-data-placeholder="#{info_select_an_option}">
<option data-th-unless="*{localidad} == null" data-th-value="*{localidad.id}" data-th-text="*{{localidad}}" selected="selected">Localidad</option>
</select>
<span data-th-classappend="${#fields.hasErrors('localidad')}? 'glyphicon glyphicon-remove form-control-feedback'" class="glyphicon glyphicon-remove form-control-feedback" data-th-if="${#fields.hasErrors('localidad')}" aria-hidden="true"></span>
<span id="localidad-error" class="help-block" data-th-if="${#fields.hasErrors('localidad')}" data-th-errors="*{localidad}">Error message.</span>
</div>
</div>
<div class="form-group has-error has-feedback" data-z="19c21a81" id="servicio-disponibilidad-field" data-th-classappend="${#fields.hasErrors('disponibilidad')}? 'has-error has-feedback'" data-th-class="form-group" data-th-with="collectionLink=${@linkBuilder.of('DisponibilidadsCollectionThymeleafController')}">
<label for="disponibilidad" class="col-md-3 control-label" data-th-text="#{label_servicio_disponibilidad}">Disponibilidad</label>
<div class="col-md-6">
<!-- Select2 -->
<select data-th-field="*{disponibilidad}" class="form-control dropdown-select-ajax" data-allow-clear="true" data-data-ajax--url="${collectionLink.to('select2')}" data-ajax--cache="true" data-ajax--delay="250" data-ajax--data-type="json" data-data-placeholder="#{info_select_an_option}">
<option data-th-unless="*{disponibilidad} == null" data-th-value="*{disponibilidad.id}" data-th-text="*{{disponibilidad}}" selected="selected">Disponibilidad</option>
</select>
<span data-th-classappend="${#fields.hasErrors('disponibilidad')}? 'glyphicon glyphicon-remove form-control-feedback'" class="glyphicon glyphicon-remove form-control-feedback" data-th-if="${#fields.hasErrors('disponibilidad')}" aria-hidden="true"></span>
<span id="disponibilidad-error" class="help-block" data-th-if="${#fields.hasErrors('disponibilidad')}" data-th-errors="*{disponibilidad}">Error message.</span>
</div>
</div>
<div class="form-group has-error has-feedback" data-z="1d95a73d" id="servicio-categoria-field" data-th-classappend="${#fields.hasErrors('categoria')}? 'has-error has-feedback'" data-th-class="form-group" data-th-with="collectionLink=${@linkBuilder.of('CategoriasCollectionThymeleafController')}">
<label for="categoria" class="col-md-3 control-label" data-th-text="#{label_servicio_categoria}">Categoria</label>
<div class="col-md-6">
<!-- Select2 -->
<select data-th-field="*{categoria}" class="form-control dropdown-select-ajax" data-allow-clear="true" data-data-ajax--url="${collectionLink.to('select2')}" data-ajax--cache="true" data-ajax--delay="250" data-ajax--data-type="json" data-data-placeholder="#{info_select_an_option}">
<option data-th-unless="*{categoria} == null" data-th-value="*{categoria.id}" data-th-text="*{{categoria}}" selected="selected">Categoria</option>
</select>
<span data-th-classappend="${#fields.hasErrors('categoria')}? 'glyphicon glyphicon-remove form-control-feedback'" class="glyphicon glyphicon-remove form-control-feedback" data-th-if="${#fields.hasErrors('categoria')}" aria-hidden="true"></span>
<span id="categoria-error" class="help-block" data-th-if="${#fields.hasErrors('categoria')}" data-th-errors="*{categoria}">Error message.</span>
</div>
</div>
</fieldset>
<!-- buttons form -->
<div class="form-group">
<div class="col-md-9 col-md-offset-3">
<button type="submit" class="btn btn-primary" data-th-text="#{label_save}">Save</button>
<button type="reset" class="btn btn-default" onclick="window.history.back()" data-th-text="#{label_reset}">Cancel</button>
</div>
</div>
</form>
<!-- /FORM -->
</div>
</section>
<!-- /CONTENT -->
</div>
<!-- /CONTAINER -->
<footer class="container">
<!-- Content replaced by layout of the page displayed -->
</footer>
<!-- JavaScript
================================================== -->
<!-- Placed at the end of the document so that the pages load faster -->
<!-- JavaScript loaded by layout of the page displayed -->
<!--
Only the inner content of the following tag "javascript" is included
within the template, in the div "javascript"
-->
<div data-layout-fragment="javascript">
<!-- DateTimePicker -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-datetimepicker/2.5.4/build/jquery.datetimepicker.full.min.js" data-th-src="@{/webjars/datetimepicker/build/jquery.datetimepicker.full.min.js}"></script>
<script src="../../static/public/js/datetimepicker-defaults.js" data-th-src="@{/public/js/datetimepicker-defaults.js}"></script>
<!-- jquery.inputmask -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.inputmask/3.3.1/jquery.inputmask.bundle.min.js" data-th-src="@{/webjars/jquery.inputmask/min/jquery.inputmask.bundle.min.js}"></script>
<script type="text/javascript" data-th-inline="javascript">
(function(jQuery) {
jQuery(document)
.ready(
function() {
Inputmask
.extendAliases({
'numeric' : {
'groupSeparator' : /*[[#{label_inputmask_groupSeparator}]]*/'.',
'radixPoint' : /*[[#{label_inputmask_radixPoint}]]*/','
},
'currency' : {
'prefix' : /*[[#{label_inputmask_prefix}]]*/'',
'suffix' : /*[[#{label_inputmask_suffix}]]*/' €'
}
});
});
})(jQuery);
</script>
<script src="../../static/public/js/inputmask-defaults.js" data-th-src="@{/public/js/inputmask-defaults.js}"></script>
<!-- JQuery Validation -->
<script src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.15.0/jquery.validate.min.js" data-th-src="@{/webjars/jquery-validation/dist/jquery.validate.min.js}">
</script>
<script src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.15.0/additional-methods.min.js" data-th-src="@{/webjars/jquery-validation/dist/additional-methods.min.js}">
</script>
<script src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.15.0/localization/messages_es.js" data-th-src="@{/webjars/jquery-validation/src/localization/messages_}+${#locale.language}+'.js'" data-th-if="${#locale.language} != 'en'">
</script>
<script src="../../static/public/js/validation-defaults.js" data-th-src="@{/public/js/validation-defaults.js}">
</script>
<script type="text/javascript" data-th-inline="javascript">
(function(jQuery) {
jQuery(document)
.ready(
function() {
jQuery
.extend(
jQuery.validator.messages,
{
'dateformat' : /*[[#{error_invalid_date}]]*/'Please enter a correct date/time',
'inputmask' : /*[[#{error_invalid_maskValue}]]*/'Please enter a valid value',
});
});
})(jQuery);
</script>
</div>
</body>
</html>
Log.roo
// Spring Roo 2.0.0.RC1 [rev 28015e3] log opened at 2017-03-06 20:50:24
project setup --topLevelPackage ar.edu.um.programacion2.oficios --java 8
jpa setup --database MYSQL --provider HIBERNATE --hostName 127.0.0.1 --databaseName oficiosdb --userName root
entity jpa --class ~.reference.Persona
field string --fieldName username --unique
field string --fieldName password --notNull
field string --fieldName email --unique
field string --fieldName telefono
field file --fieldName avatar --contentType PNG
entity jpa --class ~.domain.Prestador --extends ~.reference.Persona
field string --fieldName razonsocial --notNull
field string --fieldName oficio --notNull
entity jpa --class ~.reference.Disponibilidad
field string --fieldName franjahoraria --notNull
field string --fieldName descripcion
entity jpa --class ~.reference.Localidad
field string --fieldName nombre --notNull
field string --fieldName descripcion
entity jpa --class ~.reference.Categoria
field string --fieldName nombre --notNull
field string --fieldName descripcion
enum type --class ~.reference.TipoCalif
enum constant --name POSITIVO
enum constant --name NEGATIVO
enum constant --name NEUTRAL
entity jpa --class ~.domain.Cliente --extends ~.reference.Persona
field string --fieldName nombre --notNull
field string --fieldName apellido --notNull
field string --fieldName domicilio
field string --fieldName domicilio2
entity jpa --class ~.domain.Calificacion
field string --fieldName comentario --notNull
field enum --fieldName tipo --type ~.reference.TipoCalif
entity jpa --class ~.domain.CalificacionCliente --extends ~.domain.Calificacion
entity jpa --class ~.domain.Servicio
field string --fieldName nombre --notNull
field string --fieldName descripcion
field file --fieldName logo --contentType PNG
field string --fieldName telefono --notNull
field number --fieldName puntaje --type float
entity jpa --class ~.domain.CalificacionPrestador --extends ~.domain.Calificacion
repository jpa --all
finder add --name findByTelefono --entity ~.domain.Servicio
finder add --name findByNombreLike --entity ~.domain.Servicio
finder add --name findByPuntaje --entity ~.domain.Servicio
finder add --name findByUsername --entity ~.domain.Cliente
finder add --name findByUsername --entity ~.domain.Prestador
finder add --name findByEmail --entity ~.domain.Cliente
finder add --name findByEmail --entity ~.domain.Prestador
service --all
web mvc setup
web mvc view setup --type THYMELEAF
web mvc controller --all --responseType THYMELEAF
web mvc templates setup --type THYMELEAF
web mvc language --code es --useAsDefault
// script --file script_roo
// Spring Roo 2.0.0.RC1 [rev 28015e3] log opened at 2017-03-06 21:19:16
help
// Spring Roo 2.0.0.RC1 [rev 28015e3] log closed at 2017-03-07 09:03:07
// Spring Roo 2.0.0.RC1 [rev 28015e3] log opened at 2017-03-07 20:12:48
// Spring Roo 2.0.0.RC1 [rev 28015e3] log closed at 2017-03-08 01:34:17
// Spring Roo 2.0.0.RC1 [rev 28015e3] log opened at 2017-03-08 01:34:37
web mvc finder --entity ~.domain.Servicio
// Spring Roo 2.0.0.RC1 [rev 28015e3] log closed at 2017-03-08 02:28:28
// Spring Roo 2.0.0.RC1 [rev 28015e3] log opened at 2017-03-08 02:28:44
web mvc finder --all
web mvc finder --all
// Spring Roo 2.0.0.RC1 [rev 28015e3] log closed at 2017-03-08 02:29:31
// Spring Roo 2.0.0.RC1 [rev 28015e3] log opened at 2017-03-08 02:29:39
// Spring Roo 2.0.0.RC1 [rev 28015e3] log closed at 2017-03-08 02:29:54
// Spring Roo 2.0.0.RC1 [rev 28015e3] log opened at 2017-03-08 02:30:11
hint
help
web mvc finder --all
web mvc finder --entity ~.domain.Cliente
web mvc finder --all
web mvc finder --all --responseType THYMELEAF
web mvc finder --entity ~.domain.Servicio --responseType THYMELEAF
// Spring Roo 2.0.0.RC1 [rev 28015e3] log closed at 2017-03-08 03:13:33
// Spring Roo 2.0.0.RC1 [rev 28015e3] log opened at 2017-03-08 03:14:23
hint
hint finders
finder add --entity ~.domain.Servicio --name findByCategoria
finder add --entity ~.domain.Servicio --name queryByCategoria
finder add --entity ~.reference.Categoria --name findByNombre
security setup --provider SPRINGLETS_JPA
entity jpa --class ~.domain.Administrador --extends ~.reference.Persona
web mvc controller --entity ~.domain.Administrador
service --entity ~.domain.Administrador
repository jpa --entity ~.domain.Administrador
service --entity ~.domain.Administrador
web mvc controller --entity ~.domain.Administrador
web mvc controller --entity ~.domain.Administrador --responseType THYMELEAF
finder add --entity ~.reference.Persona --name findByUsername
// Spring Roo 2.0.0.RC1 [rev 28015e3] log closed at 2017-03-08 08:02:54
// Spring Roo 2.0.0.RC1 [rev 28015e3] log opened at 2017-03-08 21:06:10
// Spring Roo 2.0.0.RC1 [rev 28015e3] log closed at 2017-03-09 09:30:10
// Spring Roo 2.0.0.RC1 [rev 28015e3] log opened at 2017-03-09 10:08:32
// Spring Roo 2.0.0.RC1 [rev 28015e3] log closed at 2017-03-09 15:51:20
Note: First i thought that spring security was the problem cause the user didn't had permission to list that entity, but when i gave the user permission the problem changed from an error 403 to the 500 that i have now. After that i got completely stuck. I tried generating again all the views but it got no effect.
Thanks
After your last comment about the problems that you're having with the Spring Security configuration class and the errors that are appearing you with the converters registration, I know where is your problem.
Some weeks ago, I detected a problem in Spring Security and I created the following issue in their repository:
https://github.com/spring-projects/spring-security/issues/4202
Seems like if a @Configuration
class extends the WebSecurityConfigurerAdapter
abstract class (like in the code generated by Spring Roo), some component is trying to @Autowired
a ConversionService
instance before the formatters
have been registered in the Spring context, so the addFormatters
method doesn't include any formatters on it.
A simple work-around that will solve your problem and you'll be able to use Spring Security in your project is to @Override
the setContentNegotiationStrategy
method in the generated SecurityConfiguration
class without include the @Autowired
annotation.
The following example looks how to override this method correctly. (In this sample the code is commented)
If this solves your problem, will be great that you comment on the issue saying that you have the same problem.
Hope it helps and thanks for all the provided information!