I am building two microservices which are supposed to talk to each other. I am using Eureka as service registry.
Microservice 1 - Microservice1.java
@SpringBootApplication
public class Microservice1Application {
public static void main(String[] args) {
SpringApplication.run(Microservice1Application.class, args);
}
}
Microservice1Controller.java
@RestController
@RequestMapping("/getdata")
public class Microservice1Controller {
@GetMapping(value = "/")
public ResponseEntity<Microservice1ResponseWrapper<List<Customer1>>> getAll() {
List<Customer1> list = //get data from repository
return new ResponseEntity<Microservice1ResponseWrapper<List<Customer1>>>(new Microservice1ResponseWrapper<List<Customer1>>(Microservice1ResponseStatus.SUCCESS,list);
}
}
Microservice1ResponseWrapper.java - this is generic wrapper
public class Microservice1ResponseWrapper<T> {
private Microservice1ResponseStatus status;
private T data;
//constructor, getter and setters
}
applicationProperties.yaml
spring:
application:
name: microservice1
server:
port: 8073
public class Customer1 implements Serializable {
private static final long serialVersionUID = 1L;
private Long custId;
private String custName;
private String firstName;
private String lastName;
private Long age;
public Customer1() {
}
//getter, setter and toString
}
Microservice2 Microservice2 that will get data from Microservice1
@SpringBootApplication
public class Microservice2Application {
public static void main(String[] args) {
SpringApplication.run(Microservice2Application.class, args);
}
}
@Configuration
class Config {
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Microservice2Controller.java
@RestController
@RequestMapping("/fetchdata")
public class Microservice2Controller {
@Autowired
private RestTemplate restTemplate;
@GetMapping(value = "/")
public ResponseEntity<Microservice2ResponseWrapper<List<Customer2>>> getAll() {
String getAllUrl = "http://microservice1/getdata/";
ParameterizedTypeReference<Microservice2ResponseWrapper<List<Customer2>>> parameterizedTypeReference =
new ParameterizedTypeReference<Microservice2ResponseWrapper<List<Customer2>>>(){};
ResponseEntity<Microservice2ResponseWrapper<List<Customer2>>> listData =
restTemplate.exchange(getAllUrl, HttpMethod.GET, null,parameterizedTypeReference);
return listData;
}
}
Microservice2ResponseWrapper.java - this is generic wrapper
public class Microservice2ResponseWrapper<T> {
private Microservice2ResponseStatus status;
private T data;
//constructor, getter and setters
}
applicationProperties.yaml
spring:
application:
name: microservice2
server:
port: 8074
Customer1(in Microservice1) and Customer2(Microservice2) are almost identical objects.
Customer2.java in Microservice2
public class Customer2 implements Serializable {
private static final long serialVersionUID = 1L;
private Long custId;
private String custName;
private String firstName;
private String lastName;
private Long age;
public Customer2() {
}
//getter, setter and toString
}
When I run Microservice1 : http://localhost:8073/getdata it gets data from database and works fine. Here is the response I see on screen:
<Microservice1ResponseWrapper>
<status>SUCCESS</status>
<data>
<custId>1</custId>
<custName>string1</custName>
<firstName>string1</firstName>
<lastName>string1</lastName>
<age>30</age>
</data>
</Microservice1ResponseWrapper>
When I run Microservice2 : http://localhost:8074/fetchdata it should go to Microservice 1 and get data.
However, I am getting error like:
org.springframework.web.client.RestClientException: Error while extracting response for type
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:117)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:994)
at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:977)
Caused by: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.rest.Customer2` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('1'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.rest.Customer2` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('1')
at [Source: (PushbackInputStream); line: 1, column: 61] (through reference chain: com.rest.wrapper.Microservice2ResponseWrapper["data"]->java.util.ArrayList[0])
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:245)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:227)
at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:102)
... 77 more
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.rest.Customer2` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('1')
at [Source: (PushbackInputStream); line: 1, column: 61] (through reference chain: com.rest.wrapper.Microservice2ResponseWrapper["data"]->java.util.ArrayList[0])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1343)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1032)
at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:371)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:323)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1373)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:171)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:286)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:245)
at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:27)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:369)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3084)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:239)
If I make following change in Microservice2 Controller then I see 2 issues: 1) start getting LinkedHashMap error. java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to java.util.List 2) It doesn't pull all records, it just pulls last element from List. e.g. There are 2 users, then it just shows last one user and not all.
ParameterizedTypeReference<Microservice2ResponseWrapper> parameterizedTypeReference =
new ParameterizedTypeReference<Microservice2ResponseWrapper>(){};
ResponseEntity<Microservice2ResponseWrapper> listData =
restTemplate.exchange(getAllUrl, HttpMethod.GET, null,parameterizedTypeReference);
List ls = (List) listData.getBody().getData();
//if I print listData.getBody().getData() then it just shows only one record of users.
Am I making any mistake in ParameterizedTypeReference or resttemplate exchange call?
NOTE: If I run these two microservices without Eureka registry, they work absolutely fine. But the moment I introduce Eureka and register these two services with Eureka, I get issue as stated above. For this I just made the change to Miroservice2 controller: String getAllUrl = "http://localhost:8073/getdata/";
Comment From: jamesfernando94
Also had this or a similar issue when trying to use restTemplate.exchange() inside of a generic method
Comment From: rstoyanchev
Am I making any mistake in ParameterizedTypeReference or resttemplate exchange call?
I don't think so.
If I run these two microservices without Eureka registry, they work absolutely fine.
This isn't anything we can fix in the Spring Framework then.
If you do decide to open an issue against Spring Cloud (e.g. spring-cloud-netflix), I would advise that you provide an actual sample instead of a long description that in the end likely still requires putting together a sample in order to debug it. I would also suggest going a little further to try and narrow the issue. For example what are the actual request details that go out from service 2 to service 1 and what is the actual response, with and without Eureka. This might even help you to figure out the issue.