how to cast response to responseEntity - spring

Hello I have a problem with my service says that "Internal Server Error Exception occured! Exception details: IdentificationResponse cannot be cast to org.springframework.http.ResponseEntity"" any help on this matter ?
my code blocks below;
my IdentificationService Impl;
Slf4j
#Service
#RequiredArgsConstructor
public class IdentificationServiceImpl implements IdentificationService {
private final RestTemplate restTemplate;
#Value("${platform.url}")
private String platform;
private final ServiceConfig serviceConfig;
#Override
public ResponseEntity<IdentificationResponse> createIdentification(Request request) {
IdentificationRequest identificationRequest = new IdentificationRequest(serviceConfig.getIssuerKey(), serviceConfig.getSecureKey());
identificationRequest.setOperation(ClientConstants.CREATE_IDENTIFICATION_ADD);
HttpEntity<IdentificationRequest> requestHttpEntity = new HttpEntity<>(IdentificationRequest);
ResponseEntity<IdentificationResponse> response = restTemplate.exchange(platform, HttpMethod.POST, requestHttpEntity, IdentificationResponse.class);
return HandleResponse.handleResponse(response);
}
my handle Response class below;
public static <T> T handleResponse(ResponseEntity response) {
HttpStatus code = response.getStatusCode();
if (code == HttpStatus.OK || code == HttpStatus.CREATED) {
return (T) response.getBody();
} else if (code == HttpStatus.NO_CONTENT) {
return null;
} else if (code == HttpStatus.BAD_REQUEST)
throw new BadRequestException("BadRequest Exception occured while requesting! Exception details: " + response.getBody());
else if (code == HttpStatus.UNAUTHORIZED)
throw new UnauthorizedException("Unauthorized Exception occured while requesting! Exception details: " + response.getBody());
else
throw new HttpClientException("Exception occured! Exception details: " + response.getBody(), code.value());
}
}
my IdentificationRequest class below;
#Data
public class IdentificationRequest {
#Setter(AccessLevel.NONE)
#NotNull
#JsonProperty("IssuerKey")
private String issuerKey;
#Setter(AccessLevel.NONE)
#NotNull
#JsonProperty("SecurityKey")
private String secureKey;
#NotNull
#JsonProperty("TransactionId")
private String transactionId;
#NotNull
#JsonProperty("TransactionDate")
#DateTimeFormat(pattern = "YYYY-MM-DD HH:mm:ss.SSS")
private String transactionDate;
#NotNull
#JsonProperty("Msisdn")
#Pattern(regexp = "^905.{9}", message = "must be of 12 char/digit")
private Long msisdn;
private String operation;
#NotNull
#JsonProperty("Package")
private String Package;
#NotNull
#JsonProperty("STB")
private Boolean isStb;
#JsonProperty("STB_SerialNumber")
private String stbSerialNumber;
#JsonProperty("STB_MacAddress")
private String stbMacAddress;
public IdentificationRequest(String issuerKey, String secureKey) {
this.issuerKey = issuerKey;
this.secureKey = secureKey;
}
}
my Request class below;
#Data
public class Request {
#JsonProperty("OrderId")
private String orderId;
#JsonProperty("OrderItemId")
private String orderItemId;
#JsonProperty("ProcessInstanceId")
private String processId;
#JsonProperty("step_id")
private String stepId;
#JsonProperty("step_name")
private String stepName;
my Response class below;
#Data
public class IdentificationResponse {
#JsonProperty("IsSuccessful")
private Boolean isSuccessful;
#JsonProperty("Code")
private Integer code;
#JsonProperty("Message")
private String message;
#JsonProperty("Data")
private Object data;
}
Do I need a map to response to response Entity?

As per your code, when you call response.getBody() method you'll get IdentificationResponse and it is converting it to ResponseEntity which is throwing ClassCastException.
if (code == HttpStatus.OK || code == HttpStatus.CREATED)
return (T) response.getBody();
You can either return response directly instead of response.getBody() to resolve your issue or you can follow the below implementation to your method if you want your createIdentification method to return IdentificationResponse instance
return HandleResponse.handleResponse(response, IdentificationResponse.class);
public static <T> T handleResponse(ResponseEntity response, Class<T> type) {
HttpStatus code = response.getStatusCode();
if (code == HttpStatus.OK || code == HttpStatus.CREATED)
return (T) response.getBody();
}
You can find some more info related to generics here

Basically you are trying to cast an object to an incompatible one, the cast could be done if both objects implement the same interface or if the hierarchy of objects allows it, if this cast cannot be achieved, it is a good idea to create a instance of the response object based on the value of the object that you retrieved.

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;
}

Builder Pattern and Dependecy Injection - how to fix problem

I wanted to create a class based on the builder pattern. Using the static method build. Which would return a properly built object based on initial validation checking whether a given object exists in the database.
#Component
#Data
#Builder
public class GetBookedSeatsRequest {
#Autowired
private MovieRepository movieRepository;
#Autowired
public CinemaRepository cinemaRepository;
#Autowired
public PropertiesMovieRepository propertiesMovieRepository;
private String cinemaName;
private String movieName;
private String movieRoom;
#JsonFormat(pattern="yyyy-MM-dd; HH:mm:ss",shape = JsonFormat.Shape.STRING)
private LocalDateTime localDateTime;
private List<Integer> wantedSeats;
public GetBookedSeatsRequest build(ReservationModel reservationModel) throws CinemaNotFoundException, MovieNotFoundException, PropertyMovieNotFoundException {
boolean cinemaExist = cinemaRepository.existsByCinemaName(reservationModel.getCinemaName());
if (!cinemaExist) {
throw new CinemaNotFoundException("Cinema doesn't exist");
}
boolean movieExist = movieRepository.existsByMovieName(reservationModel.getMovieName());
if (!movieExist) {
throw new MovieNotFoundException("Movie doesn't exist");
}
boolean roomExist = movieRepository.existsByMovieRoom(reservationModel.getMovieRoom());
if (!roomExist) {
throw new MovieNotFoundException("Movie Romm doesn't exist");
}
boolean existData = propertiesMovieRepository.existsByStartTimeOfTheMovie(reservationModel.getDateAndTime());
if (!existData) {
throw new PropertyMovieNotFoundException("This data doesn't exist");
}
// boolean existSeats = movieRepository.existsBySeating(reservationModel.getSeatsToBooked());
// if (!existSeats) {
// throw new MovieNotFoundException("This seats doesn't exist");
// }
GetBookedSeatsRequest correct = GetBookedSeatsRequest.builder()
.cinemaName(reservationModel.getCinemaName())
.movieName(reservationModel.getMovieName())
.movieRoom(reservationModel.getMovieRoom())
.localDateTime(reservationModel.getDateAndTime())
.wantedSeats(reservationModel.getSeatsToBooked())
.build();
return correct;
}
}
#Data
#AllArgsConstructor
public class ReservationModel {
private String cinemaName;
private String movieName;
private String movieRoom;
#JsonFormat(pattern="yyyy-MM-dd; HH:mm:ss",shape = JsonFormat.Shape.STRING)
private LocalDateTime dateAndTime;
private List<Integer> seatsToBooked;
}
But I still got some erros. What am I doing wrong, I am learing Spring Boot. Thanks for help
Description:
Parameter 3 of constructor in com.cinema.booking.aop.GetBookedSeatsRequest required a bean of type 'java.lang.String' that could not be found.
Action:
Consider defining a bean of type 'java.lang.String' in your configuration.

unable to get the response entity from an api with mockmvc

I have an issue when I try to run my controller's unit test class. I get always a empty body in the response and I don't manage to find why.
I put here the code. Maybe someone with an external vision will be able to see the reason.
the controller:
#ResponseBody
#PostMapping(path = "/upload", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<Object> uploadFile(#RequestParam("file") MultipartFile multipartFileData, #RequestParam(name="jobId", required = false) String jobId) {
JobStatus result;
try {
result = this.fileService.uploadFileChunk(multipartFileData, 1, 1, jobId);
}catch (ExecutionException|InterruptedException|IOException ex){
Thread.currentThread().interrupt();
return new ResponseEntity<>(ex,HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<>(result,HttpStatus.OK);
}
the unit test class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes= FileUploadServiceRestController.class)
public class FileUploadServiceControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#MockBean
private FileUploadServiceImpl fileService;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
#Test
public void testUploadFile()
throws Exception {
MockMultipartFile file
= new MockMultipartFile(
"file",
"hello.txt",
MediaType.TEXT_PLAIN_VALUE,
"Hello, World!".getBytes()
);
JobStatus job = new JobStatus("uuid", ConstantUtil.JOB_STARTED);
when(fileService.uploadFileChunk(Mockito.any(MultipartFile.class),Mockito.eq(1),Mockito.eq(1),Mockito.isNull())).thenReturn(job);
mockMvc.perform(MockMvcRequestBuilders.multipart("/file/upload").file(file))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk());
}
}
and the object which will be transfered:
public class JobStatus implements Serializable {
private static final long serialVersionUID = -4405865740177389860L;
private String jobId;
private String status;
public JobStatus() {
}
public JobStatus(String jobId, String status) {
this.jobId = jobId;
this.status = status;
}
public String getJobId() {
return jobId;
}
public void setJobId(String jobId) {
this.jobId = jobId;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
For information, this controller works well when I call it from the client. I can see that the mock is well returned when I put a breakpoint at the end of the controller, but the response body stay empty.
I add here the result of print if it could help:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /file/upload
Parameters = {}
Headers = [Content-Type:"multipart/form-data"]
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = org.iso.fileservice.controller.FileUploadServiceRestController
Method = org.iso.fileservice.controller.FileUploadServiceRestController#uploadFile(MultipartFile, String)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotWritableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 500
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
thanks, Mathieu
Just ran into this today.
I found the answer on Why MockMvc request retrieve empty responseBody while test succeed?
I just added My restController with #Autowired instead of #InjectMocks, after that I started to retrieve the Response Entity instead of a 500 status response

Spring Controller Test: Postman vs JUnit - Field error request rejected value [null]

I'm a beginner on Spring framework, trying to test the Controller.
The funny thing is, using Postman, I got the correct response, but not in JUnit where receive Actual :400 (bad request) instead of Expected :200.
This is due to empty field passengerCount because appears null. The class of the request is different of the response. This latter doesn't have a field for the passenger.
Controller
#Validated
#RestController
#RequestMapping("flights")
public class BusyFlightsController {
CrazyAirDatabase crazyAirService;
#Autowired
public BusyFlightsController(CrazyAirDatabase crazyAirService) {
this.crazyAirService = new CrazyAirDatabase();
}
#RequestMapping(value = "/crazy-air-response", method = RequestMethod.GET, produces = "application/json")
public List<CrazyAirResponse> getCrazyAirResponse(
#Valid CrazyAirRequest crazyAirRequest,
#RequestParam("origin") String origin,
#RequestParam("destination") String destination,
#RequestParam("departureDate") String departureDate,
#RequestParam("returnDate") String returnDate,
#RequestParam("passengerCount") int passengerCount
) {
crazyAirRequest = new CrazyAirRequest(origin, destination, departureDate, returnDate,
passengerCount);
return crazyAirService.getCrazyAirResponse(crazyAirRequest);
}
}
CrazyAirRequest class
public class CrazyAirRequest {
#IATACodeConstraint
private String origin;
#IATACodeConstraint
private String destination;
private String departureDate;
private String returnDate;
private int passengerCount;
public CrazyAirRequest(String origin, String destination, String departureDate,
String returnDate, int passengerCount) {
this.origin = origin;
this.destination = destination;
this.departureDate = departureDate;
this.returnDate = returnDate;
this.passengerCount = passengerCount;
}
// Getters
}
CrazyAirResponse class
public class CrazyAirResponse {
private String airline;
private double price;
private String cabinClass;
private String departureAirportCode;
private String destinationAirportCode;
private String departureDate;
private String arrivalDate;
public CrazyAirResponse(String airline, double price, String cabinClass, String departureAirportCode,
String destinationAirportCode, String departureDate, String arrivalDate) {
this.airline = airline;
this.price = price;
this.cabinClass = cabinClass;
this.departureAirportCode = departureAirportCode;
this.destinationAirportCode = destinationAirportCode;
this.departureDate = departureDate;
this.arrivalDate = arrivalDate;
}
// Getters
}
Repo CrazyAirDatabase
#Component
public class CrazyAirDatabase implements CrazyAirService {
List<CrazyAirResponse> list;
public CrazyAirDatabase() {
list = new ArrayList<>(
Arrays.asList(
new CrazyAirResponse("Ryanair", 125, "E", "LHR",
"BRN", "2018-10-08", "2020-10-08")
);
}
#Override
public List<CrazyAirResponse> getCrazyAirResponse(CrazyAirRequest request) {
return list.stream()
.filter(t -> t.getDepartureAirportCode().equals(request.getOrigin()) &&
t.getDestinationAirportCode().equals(request.getDestination()) &&
t.getDepartureDate().equals(request.getDepartureDate()) &&
t.getArrivalDate().equals(request.getReturnDate())
)
.collect(Collectors.toList());
}
}
Test
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class BusyFlightsControllerTest {
#Autowired
MockMvc mockMvc;
#Mock
CrazyAirRequest crazyAirRequest;
#InjectMocks
private BusyFlightsController controller;
#Mock
CrazyAirService service;
#Before
public void before() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testino() throws Exception {
crazyAirRequest = new CrazyAirRequest("LHR",
"BRN", "2018-10-08", "2020-10-08", 120);
List<CrazyAirResponse> crazyAirResponse = Arrays.asList(new CrazyAirResponse("Ryanair", 125,
"E", "LHR",
"BRN", "2018-10-08", "2020-10-08")
);
when(service.getCrazyAirResponse(crazyAirRequest)).thenReturn(crazyAirResponse);
ObjectMapper objectMapper = new ObjectMapper();
String airplane = objectMapper.writeValueAsString(crazyAirResponse);
ResultActions result = mockMvc.perform(get("/flights/crazy-air-response")
.contentType(MediaType.APPLICATION_JSON)
.content(airplane)
);
result.andExpect(status().isOk());
}
}
If I put this:
ResultActions result = mockMvc.perform(get("/flights/crazy-air-response?origin=LHR&destination=CTA&departureDate=some&returnDate=some&passengerCount=1")
.contentType(MediaType.APPLICATION_JSON)
.content(airplane)
);
Test is passed.
Then, need I perform Postman first, and after to copy and paste the query to pass the test?

Resources