Feign client error - Page size must not be less than one - spring-boot

I have the following Rest controller method:
#GetMapping
public Page<CompanyDto> findAllCompanies(#RequestParam(value = "name", required = false) String name, Pageable pageable, JwtAuthenticationToken jwtAuthenticationToken) {
...
and the Feigh client:
#GetMapping
RestPageImpl<CompanyDto> findAllCompanies(#RequestParam(value = "name", required = false) String name, Pageable pageable, #RequestHeader("Authorization") String token);
So far everything works fine.
Now, I'd like to substitute name and pageable parameters with a single DTO object:
public class CompanyRequest {
private CompanyDto company;
Pageable pageable;
public CompanyRequest() {
}
public CompanyRequest(CompanyDto company, Pageable pageable) {
this.company = company;
this.pageable = pageable;
}
public CompanyDto getCompany() {
return company;
}
public Pageable getPageable() {
return pageable;
}
}
to something like this:
controller:
#GetMapping
public Page<CompanyDto> findAllCompanies(CompanyRequest companyRequest, JwtAuthenticationToken jwtAuthenticationToken) {
...
Feign client:
#GetMapping
RestPageImpl<CompanyDto> findAllCompanies(CompanyRequest companyRequest, #RequestHeader("Authorization") String token);
Right now the invocation of the following code:
companyApiClient.findAllCompanies(new CompanyRequest(new CompanyDto("Company1 name", null), PageRequest.of(0, 10)), accessToken);
fails with the following exception:
Caused by: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.decisionwanted.api.model.dto.page.RestPageImpl`, problem: Page size must not be less than one!; nested exception is com.fasterxml.jackson.databind.exc.ValueInstantiationException: Cannot construct instance of `com.decisionwanted.api.model.dto.page.RestPageImpl`, problem: Page size must not be less than one!
at [Source: (PushbackInputStream); line: 1, column: 1499]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:389)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:342)
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:105)
... 42 more
Caused by: com.fasterxml.jackson.databind.exc.ValueInstantiationException: Cannot construct instance of `com.decisionwanted.api.model.dto.page.RestPageImpl`, problem: Page size must not be less than one!
at [Source: (PushbackInputStream); line: 1, column: 1499]
at com.fasterxml.jackson.databind.exc.ValueInstantiationException.from(ValueInstantiationException.java:47)
at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:1754)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.wrapAsJsonMappingException(StdValueInstantiator.java:491)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.rewrapCtorProblem(StdValueInstantiator.java:514)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromObjectWith(StdValueInstantiator.java:285)
at com.fasterxml.jackson.databind.deser.ValueInstantiator.createFromObjectWith(ValueInstantiator.java:229)
at com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator.build(PropertyBasedCreator.java:202)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:490)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1310)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:331)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:164)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4482)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3487)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:378)
... 44 more
Caused by: java.lang.IllegalArgumentException: Page size must not be less than one!
at org.springframework.data.domain.AbstractPageRequest.<init>(AbstractPageRequest.java:48)
at org.springframework.data.domain.PageRequest.<init>(PageRequest.java:45)
at org.springframework.data.domain.PageRequest.of(PageRequest.java:72)
at org.springframework.data.domain.PageRequest.of(PageRequest.java:60)
at com.decisionwanted.api.model.dto.page.RestPageImpl.<init>(RestPageImpl.java:27)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
at com.fasterxml.jackson.databind.introspect.AnnotatedConstructor.call(AnnotatedConstructor.java:124)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromObjectWith(StdValueInstantiator.java:283)
... 53 more
What am I doing wrong and how to fix it?
UPDATED
This is RestPageImpl:
public class RestPageImpl<T> extends PageImpl<T> {
#JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public RestPageImpl(#JsonProperty("content") List<T> content,
#JsonProperty("number") int number,
#JsonProperty("size") int size,
#JsonProperty("totalElements") Long totalElements,
#JsonProperty("pageable") JsonNode pageable,
#JsonProperty("last") boolean last,
#JsonProperty("totalPages") int totalPages,
#JsonProperty("sort") JsonNode sort,
#JsonProperty("first") boolean first,
#JsonProperty("numberOfElements") int numberOfElements) {
super(content, PageRequest.of(number, size), totalElements);
}
public RestPageImpl(List<T> content, Pageable pageable, long total) {
super(content, pageable, total);
}
public RestPageImpl(List<T> content) {
super(content);
}
public RestPageImpl() {
super(new ArrayList<>());
}
}

Related

Spring controller can accept ZonedDatedTime as #RequestParam but not #RequestBody

The following code works
public #ResponseBody
Map<String, Object> test(#RequestParam #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) ZonedDateTime startDate,
#RequestParam #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) ZonedDateTime endDate) {
return null;
}
with this request
https://localhost:8080/api/v1/test?startDate=2000-10-31T01:30:00.000-00:00&endDate=2000-10-31T01:30:00.000-00:00
But the following code throws exception
public #ResponseBody
Map<String, Object> test(#RequestBody #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) ZonedDateTime startDate,
#RequestBody #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) ZonedDateTime endDate) {
return null;
}
with this body
{
"endDate":"2000-10-31T01:30:00.000-00:00",
"startDate":"2000-10-31T01:30:00.000-00:00"
}
has this exception
[org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Unexpected token (START_OBJECT), expected one of [VALUE_STRING, VALUE_NUMBER_INT, VALUE_NUMBER_FLOAT] for java.time.ZonedDateTime value; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT), expected one of [VALUE_STRING, VALUE_NUMBER_INT, VALUE_NUMBER_FLOAT] for java.time.ZonedDateTime value
You must use #ModelAttribute or make object or map for mapping body, because Reflection utils not have opportunity for reading function parameter names (you cant specify property name for mapping).
public class User {
private String name;
private String occupation;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#RestController
public class MyController {
#ResponseStatus(value = HttpStatus.OK)
#PostMapping(value="/myfoo")
public void process2(#ModelAttribute("email") String email) {
}
#ResponseStatus(value = HttpStatus.OK)
#PostMapping(value="/vals")
public void process(#RequestBody MultiValueMap<String, String> values) {
}
#ResponseStatus(value = HttpStatus.OK)
#PostMapping(value="/user", consumes = MediaType.APPLICATION_JSON_VALUE)
public void process2(#RequestBody User user) {
}
}
ATTENTION
always set name of property for #RequestParam, else you can take error if somebody add not only this parameter or changed function signature

JSON decoding error: Cannot deserialize value of type `java.math.BigInteger` from Object value (token `JsonToken.START_OBJECT`); (Jackson)

It is necessary to deserialize the result from Mono<ResultSumDto> to JSON, then to sent to the client as JSON.
Controller
#GetMapping("v1/sequence/{startRange}/{endRange}")
Mono<ResultSumDto > getSumFromRange(
#PathVariable BigInteger startRange,
#PathVariable BigInteger endRange) {
ResultSumDto resultSumDto = ...
return Mono.just(resultSumDto);
}
#Configuration
public class JacksonObjectMapperConfiguration {
#Autowired
public void serializeBigInteger(ObjectMapper objectMapper) {
JsonFormat.Value formatValue =
JsonFormat.Value.forShape(JsonFormat.Shape.STRING);
objectMapper
.configOverride(BigInteger.class)
.setFormat(formatValue);
}
}
#Data
#Builder
public class ResultSumDto {
private final BigInteger sumSeq;
private final BigInteger [] seqRange;
private final Boolean isCached;
}
private Mono<ResultSumDto> buildResult(SeqDto dto) {
Mono<BigInteger> sumSeq =
calculateSumRangeValuesFibonacciSequence(dto);
BigInteger bigInteger = null;
try {
bigInteger = sumSeq
.toFuture()
.get();
} catch (InterruptedException | ExecutionException e) {
log.error(e.getLocalizedMessage());
Thread.currentThread().interrupt();
}
BigInteger[] rangeGiven = new BigInteger[]
{dto.getStartRange(), dto.getEndRange()};
return Mono.just(ResultSumSeqDto.builder()
.sumSequence(bigInteger)
.sequenceRange(rangeGiven)
.isCached(false)
.build()
);
}
But I have a mistake:
org.springframework.core.codec.DecodingException: JSON decoding error: Cannot deserialize value of type java.math.BigInteger from Object value (token JsonToken.START_OBJECT); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type java.math.BigInteger from Object value (token JsonToken.START_OBJECT)
at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 1]
But after all, when I get values in endpoint, serialization to the BigInteger type goes without problems.
Who has any idea why it doesn't work and how it can be fixed. Share your knowledge on how to deserialize an array BigInteger and a field with the BigInteger type?
That's what worked in my case.
public class DeserializeResultCalculateSumSequence
extends StdDeserializer<ResultCalculateSumSequenceDto> {
public DeserializeResultCalculateSumSequence() {
this(null);
}
protected DeserializeResultCalculateSumSequence(Class<?> vc) {
super(vc);
}
#Override
public ResultCalculateSumSequenceDto deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext)
throws IOException, JacksonException {
JsonNode node = jsonParser
.getCodec()
.readTree(jsonParser);
BigInteger sumSequence = node
.get("sumSequence")
.bigIntegerValue();
ObjectMapper mapper = new ObjectMapper();
String sequenceRangeStr = node.get("sequenceRange").toString();
BigInteger[] sequenceRange = mapper
.readValue(sequenceRangeStr, BigInteger[].class);
boolean isCached = node
.get("isCached")
.asBoolean();
return ResultCalculateSumSequenceDto
.builder()
.sumSequence(sumSequence)
.sequenceRange(sequenceRange)
.isCached(isCached)
.build();
}
}
#Data
#Builder
#JsonDeserialize(using = DeserializeResultCalculateSumSequence.class)
public class ResultCalculateSumSequenceDto {
private final BigInteger sumSequence;
private final BigInteger [] sequenceRange;
private final Boolean isCached;
}

Error with JPA PagingAndSorting request parameter

I have a Controller that pages and sorts all the news in my database:
#RequestMapping(value = "/viewstatus", method = RequestMethod.GET)
ModelAndView viewStatus(ModelAndView modelAndView, #RequestParam(name = "p", defaultValue = "1") int pageNumber) {
Page<StatusUpdate> page = statusUpdateService.getPage(pageNumber);
modelAndView.getModel().put("page", page);
modelAndView.setViewName("app.viewStatus");
return modelAndView;
}
With its call to the service that works fine:
public Page<StatusUpdate> getPage(int pageNumber) {
PageRequest request = new PageRequest(pageNumber-1, pageSize, Sort.Direction.DESC, "added");
return statusUpdateDao.findAll(request);
}
But now, I would like to do the same SortingAndPaging BUT with one parameter (SiteUser). Here is my object:
#Entity
#Table(name = "status_update")
public class StatusUpdate {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Size(min=5, max=255, message="{addstatus.title.size}")
#Column(name = "title")
private String title;
#Size(min=5, max=5000, message="{addstatus.text.size}")
#Column(name = "text")
private String text;
#Column(name = "added")
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(pattern="yyyy/MM/dd hh:mm:ss")
private Date added;
#OneToOne(targetEntity = SiteUser.class)
#JoinColumn(name="user_id")
private SiteUser siteUser;
#PrePersist
protected void onCreate() {
if (added == null) {
added = new Date();
}
}
public StatusUpdate() {
}
But when I do it, it gives me this error:
Exception: org.springframework.dao.InvalidDataAccessApiUsageException: Parameter value [3] did not match expected type [com.caveofprogramming.model.entity.SiteUser (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [3] did not match expected type [com.caveofprogramming.model.entity.SiteUser (n/a)]
Failed URL: http://192.168.160.128:8080/viewmystatus
Exception message: Parameter value [3] did not match expected type [com.caveofprogramming.model.entity.SiteUser (n/a)]; nested exception is java.lang.IllegalArgumentException: Parameter value [3] did not match expected type [com.caveofprogramming.model.entity.SiteUser (n/a)]
I tired to send a String, but it did not work. I had to change it to a NativeQuery but I does not work with PagingAndSorting, it only works as a List (which is a headache to work with). So if you can help me with the ERROR of PagingAndSorting that would be great.
Here is the Controller:
#RequestMapping(value = "/viewmystatus", method = RequestMethod.GET)
ModelAndView viewMyStatus(ModelAndView modelAndView, #RequestParam(name = "p", defaultValue = "1") int pageNumber) {
SiteUser user = getUser();
Long user_id= user.getId();
Page<StatusUpdate> page = statusUpdateService.findMyStatusUpdates(user_id, pageNumber);
for(StatusUpdate statusUpdate: page){
SiteUser siteUser= statusUpdate.getSiteUser();
modelAndView.getModel().put("siteuser", siteUser);
}
modelAndView.getModel().put("page", page);
modelAndView.setViewName("app.viewStatus");
return modelAndView;
}
Here is the service:
public Page<StatusUpdate> findMyStatusUpdates(Long user_id, int pageNumber) {
PageRequest request = new PageRequest(pageNumber-1, pageSize, Sort.Direction.DESC, "added");
return statusUpdateDao.findBySiteUser(user_id, request);
}
And the DAO:
#Repository
public interface StatusUpdateDao extends PagingAndSortingRepository<StatusUpdate, Long> {
StatusUpdate findFirstByOrderByAddedDesc();
Page<StatusUpdate> findBySiteUser(Long user_id, Pageable pageable);
}
Thanks for your help!
Use a SiteUser object:
public Page<StatusUpdate> findMyStatusUpdates(Long user_id, int pageNumber) {
PageRequest request = new PageRequest(pageNumber-1, pageSize, Sort.Direction.DESC, "added");
return statusUpdateDao.findBySiteUser(new SiteUser(user_id), request);
}
You could try query by nested properties as well, but I think you must change user_id by userId, as underscore is a reserved character. Have a look to the documentation:
Spring data JPA Property expressions
Have you changed your findBySiteUser method? The one you posted looks fine but it seems is complaining about the signature. Even when PageRequest implements Pageable the signature has to be declared, explicitly, using Pageable, but the error message you are getting says PageRequest
public abstract org.springframework.data.domain.Page com.caveofprogramming.model.repository.StatusUpdateDao.findB‌​ySiteUser(com.caveof‌​programming.model.en‌​tity.SiteUser,org.sp‌​ringframework.data.d‌​omain.PageRequest)
More info:
PageRequest parameter not recognized as Pageable in Paging query

receiving json and deserializing as List of object at spring mvc controller

My code is as below:
controller
#RequestMapping(value="/setTest", method=RequestMethod.POST, consumes="application/json")
public #ResponseBody ModelMap setTest(#RequestBody List<TestS> refunds, ModelMap map) {
for(TestS r : refunds) {
System.out.println(r.getName());
}
// other codes
}
TestS pojo
public class TestS implements Serializable {
private String name;
private String age;
//getter setter
}
Json request
var z = '[{"name":"1","age":"2"},{"name":"1","age":"3"}]';
$.ajax({
url: "/setTest",
data: z,
type: "POST",
dataType:"json",
contentType:'application/json'
});
It's giving this error
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.air.cidb.entities.TestS
I am using spring 3.1.2 and jackson 2.0.4
Edit: I want to receive list of TestS objects at my controller method, and process them. I am not able to find if I am sending wrong json or my method signature is wrong.
Here is the code that works for me. The key is that you need a wrapper class.
public class Person {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
#Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
A PersonWrapper class
public class PersonWrapper {
private List<Person> persons;
/**
* #return the persons
*/
public List<Person> getPersons() {
return persons;
}
/**
* #param persons the persons to set
*/
public void setPersons(List<Person> persons) {
this.persons = persons;
}
}
My Controller methods
#RequestMapping(value="person", method=RequestMethod.POST,consumes="application/json",produces="application/json")
#ResponseBody
public List<String> savePerson(#RequestBody PersonWrapper wrapper) {
List<String> response = new ArrayList<String>();
for (Person person: wrapper.getPersons()){
personService.save(person);
response.add("Saved person: " + person.toString());
}
return response;
}
The request sent is json in POST
{"persons":[{"name":"shail1","age":"2"},{"name":"shail2","age":"3"}]}
And the response is
["Saved person: Person [name=shail1, age=2]","Saved person: Person [name=shail2, age=3]"]
This is not possible the way you are trying it. The Jackson unmarshalling works on the compiled java code after type erasure. So your
public #ResponseBody ModelMap setTest(#RequestBody List<TestS> refunds, ModelMap map)
is really only
public #ResponseBody ModelMap setTest(#RequestBody List refunds, ModelMap map)
(no generics in the list arg).
The default type Jackson creates when unmarshalling a List is a LinkedHashMap.
As mentioned by #Saint you can circumvent this by creating your own type for the list like so:
class TestSList extends ArrayList<TestS> { }
and then modifying your controller signature to
public #ResponseBody ModelMap setTest(#RequestBody TestSList refunds, ModelMap map) {
#RequestMapping(
value="person",
method=RequestMethod.POST,
consumes="application/json",
produces="application/json")
#ResponseBody
public List<String> savePerson(#RequestBody Person[] personArray) {
List<String> response = new ArrayList<String>();
for (Person person: personArray) {
personService.save(person);
response.add("Saved person: " + person.toString());
}
return response;
}
We can use Array as shown above.
Solution works very well,
public List<String> savePerson(#RequestBody Person[] personArray)
For this signature you can pass Person array from postman like
[
{
"empId": "10001",
"tier": "Single",
"someting": 6,
"anything": 0,
"frequency": "Quaterly"
}, {
"empId": "10001",
"tier": "Single",
"someting": 6,
"anything": 0,
"frequency": "Quaterly"
}
]
Don't forget to add consumes tag:
#RequestMapping(value = "/getEmployeeList", method = RequestMethod.POST, consumes="application/json", produces = "application/json")
public List<Employee> getEmployeeDataList(#RequestBody Employee[] employeearray) { ... }
I believe this will solve the issue
var z = '[{"name":"1","age":"2"},{"name":"1","age":"3"}]';
z = JSON.stringify(JSON.parse(z));
$.ajax({
url: "/setTest",
data: z,
type: "POST",
dataType:"json",
contentType:'application/json'
});
For me below code worked, first sending json string with proper headers
$.ajax({
type: "POST",
url : 'save',
data : JSON.stringify(valObject),
contentType:"application/json; charset=utf-8",
dataType:"json",
success : function(resp){
console.log(resp);
},
error : function(resp){
console.log(resp);
}
});
And then on Spring side -
#RequestMapping(value = "/save",
method = RequestMethod.POST,
consumes="application/json")
public #ResponseBody String save(#RequestBody ArrayList<KeyValue> keyValList) {
//Saving call goes here
return "";
}
Here KeyValue is simple pojo that corresponds to your JSON structure also you can add produces as you wish, I am simply returning string.
My json object is like this -
[{"storedKey":"vc","storedValue":"1","clientId":"1","locationId":"1"},
{"storedKey":"vr","storedValue":"","clientId":"1","locationId":"1"}]

JAXB Error while using in SpringREST to return a ArrayList of a domain object

I am trying to use JAXB in Spring RESTful webservice.
My code is as follows:
#RequestMapping(value = "/countries",
method = RequestMethod.GET,
headers="Accept=application/xml, application/json")
public #ResponseBody CountryList getCountry() {
logger.debug("Provider has received request to get all persons");
// Call service here
CountryList result = new CountryList();
result.setData(countryService.getAll());
return result;
}
The CountryList.java class looks like:
#XmlRootElement(name="countries")
public class CountryList {
#XmlElement(required = true)
public List<Country> data;
#XmlElement(required = false)
public List<Country> getData() {
return data;
}
public void setData(List<Country> data) {
this.data = data;
}
}
The Country.java looks like:
#XmlRootElement(name="country")
public class Country {
private Calendar createdDt;
private String updatedBy;
private String createdBy;
private Long id;
private String countryName;
private Calendar updatedDt;
// getters and setters for all attributes goes here
}
Now, when I access the method getCountry(), I am getting the following exception
Caused by: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "data"
this problem is related to the following location:
at public java.util.List com.cisco.bic.services.model.CountryList.getData()
at com.cisco.bic.services.model.CountryList
this problem is related to the following location:
at public java.util.List com.cisco.bic.services.model.CountryList.data
at com.cisco.bic.services.model.CountryList
Would anyone has any idea why is this error coming. Am I doing anything wrong in the annotaion part ??
Please help.
Regards
Saroj
You can't annotate both the getter/setter and the field, you need to decide on one of them.

Resources