Spring 5 Error handling of Postexchange requests - java

I use an external rest api in my spring application, I can send json post requests to create objects but when a field is incorrect or if there is a duplicate it returns a 400 bad request error, and a body saying what the problem is.
I use Spring 5 with #PostExchange in the following code:
This is used to point spring into the right direction of the external api
public interface HardwareClient {
#PostExchange("/assetmgmt/assets/templateId/C04DBCC3-5FD3-45A2-BD34-8A84CE2EAC20")
String addMonitor(#RequestBody Monitor monitor);
}
This is the helper that is autowired into the class where I have the data that needs to be sent.
#Component
public class HardwareHelper {
private Logger logger = Logger.getLogger(getClass().getName());
#Autowired
HardwareClient hardwareClient;
#Async
public Future<String> addMonitor(MonitorForm monitorForm){
try {
Monitor monitor = new Monitor(monitorForm.objectID(), monitorForm.model(), monitorForm.make(),monitorForm.serialNumber(), monitorForm.orderNumber(),monitorForm.budgetholder(),monitorForm.ownership());
hardwareClient.addMonitor(monitor);
return new AsyncResult<String>("Success");
} catch (Exception e){
logger.info("HardwareHelper.addMonitor error: " + e.getMessage());
//todo error handling
}
return null;
}
}
When an error occurs the logger will print the error but I need to be able to control what happens after based on the response. So I need to see the body of the post request that is returned after. If everything goes well an ID is returned that I can read by printing the results of the addMonitor() method, but this is obviously not possible when it throws an exception as it skips to the catch part. How do I scan the request body when an error is thrown and handle this appropriately

Related

Java controller code runs even after returning

I am having an issue which my team and I have been debugging for hours without any clue to why my code is not executing in order,
The issue now is that in my controller I call two main functions, function 1 checks for payment and if payment is not successful, it throws an error which will be propagated to my controller which catches it and returns a 302 to redirect to an error page. However, based on my logs, my code executed function 2 which is saving the user details into my database first before checking for the payment response
My team is unable to replicate this behaviour in our dev and uat environment and this issue only occurs in production, in production, the code repeatedly runs the function 2 first based on the logs printed before returning the error page to the users,
Could this be a code issue or a server setting issue that is causing this? App is hosted on cloud foundry with autoscaling with two instances by default
Code of my controller:
public ResponseEntity<Void> executePayment(
#ModelAttribute PaymentRequestDto paymentRequestDto,
#RequestParam("sessionId") String sessionId,
HttpServletResponse response) {
GrafanaUtils.addToThreadContextTraceGroupId(sessionId);
MpgsPayApiResponseDto mpgsPayApiResponseDto;
String hashedCode = Base64.getEncoder().encodeToString((sessionId.getBytes()));
response.addCookie(CookieUtils.generateCookie(Constants.REDIRECT_COOKIE_ID, hashedCode));
// Call MPGS
try {
// error thrown from this service
mpgsPayApiResponseDto = authenticationService.executePayment(paymentRequestDto, sessionId);
} catch (Exception ex) {
GrafanaUtils.addToThreadContextTraceGroupId(ExceptionConstants.MPGS_CONNECTION_EXCEPTION, ex);
return ResponseEntity.status(HttpStatus.FOUND)
.location(URI.create(paymentUiBaseUrl.concat(paymentUiErrorPath))).build(); // should return here
}
// MPGS Success
// should never reach here if above code throws exception and return
// however code still runs after above throws error and return error page
try {
authenticationService.savePaymentDetails(mpgsPayApiResponseDto, sessionId);
} catch (Exception ex) {
log.error("[executePayment] Error saving payment details");
}
return ResponseEntity.status(HttpStatus.FOUND)
.location(URI.create(paymentUiBaseUrl.concat(paymentUiConfirmPath))).build();
}
Code of my service:
public MpgsPayApiResponseDto executePayment(PaymentRequestDto paymentRequestDto, String sessionId) {
if (!("PROCEED").equals(paymentRequestDto.getResponse().getGatewayRecommendation())) {
MpgsPayApiResponseDto mpgsPayApiResponseDto = mpgsService.retrieveTransaction(paymentRequestDto.getOrder().getId(),
paymentRequestDto.getTransaction().getId());
savePaymentDetails(mpgsPayApiResponseDto, sessionId);
log.info("[executePayment] Session {}: Error making payment ", sessionId);
throw new CustomException(ExceptionConstants.MPGS_CONNECTION_EXCEPTION); // code returns here
}
...
return mpgsPayApiResponseDto;
}

Handling errors coming from gateway implementation in the use case - Clean Architecture

how can I handle exceptions coming from the gateway implementation when we are building software using the Onion Architecture?
To clarify, I've created an example using Java and SpringBoot:
#Component
#AllArgsConstructor
public class SaveAddressUseCase{
private final GetCustomerGateway getCustomerGateway;
public void execute(AddressDomain address, Long customerId){
try{
//validates the customerId supplied and returns the customer from an external service.
CustomerDomain customer = getCustomerGateway.execute(customerId);
address.setCustomer(customer);
//saves the address
}catch(CustomerNotFoundException ex) {
AddressErrorDomain addressErrorDomain = new AddressErrorDomain();
//saves the address in a error table with httpStatus 404
} catch (InternalErrorException ex) {
AddressErrorDomain addressErrorDomain = new AddressErrorDomain();
//save the address in a error table with httpStatus 500
}
}
}
This is a simple useCase that will save an address but first, it needs to get the customer of this address from an external service. If the customer is not found, I need to save the address in an error table to processes it later. The same goes if this external service is down, but it's important to differentiate between these two errors and I can handle this problem using the HttpStatus returned from my API call.
public interface GetCustomerGateway {
CustomerDomain execute(Long customerId);
}
#Component
#AllArgsConstructor
public class GetCustomerGatewayImpl implements GetCustomerGateway {
private final CustomerApi customerApi; //Feign interface to call an external service
public CustomerDomain execute(Long customerId){
try{
return customerApi.getCustomerById(customerId);
}catch(FeignException ex){
if (ex.status() == HttpStatus.NOT_FOUND.value()) {
throw new CustomerNotFoundException();
} else {
throw new InternalErrorException();
}
}
}
}
Lastly, this is my gateway implementation that just makes a call to this external service using a simple Feign interface and throws two custom exceptions that I extended from RuntimeException.
Question: Catching these two exceptions in the usecase I'm not dealing with details that only the gateway must know? Or even worse, I'm not using exceptions to control the flow of my application? How can I handle the errors coming from the Gateway implementation in a better way than I did in my example?
Obs: In this example, it's important to save the address in error table to not ruins the user experience in the client-side, and I also need to differentiate between these errors.
Thanks in advance!
Consider using #ControllerAdvice for this to keep the controller clean and focused
#ControllerAdvice
#Slf4j
public class RestExceptionHandler {
//Magic happens here
}
Inside RestExceptionHandler, you can catch all feign exceptions like this and handle them however you want
#ResponseBody
#ExceptionHandler(Throwable.class)
public final ResponseEntity<?> handleFeignExceptions(Exception ex, WebRequest request) {
if (ex instanceof FeignException) {
return handle((FeignException) ex);// define your custom handle method
}
}

Java RabbitMQ consumer.nextMessage always gets same message

We are using Java rabbitMq with spring boot in a distributed service architecture. One service gets an HTTP request and forwards it to an unkown queue for processing. At the same time it has to wait for a response on another queue before it can terminate the HTTP request. (It's a preview request that gets its work done by a renderer).
There can be more than one instance of ServiceA (the HTTP Interface) and ServiceB (the renderer) so with every preview message we also send a unique ID to be used as routing key.
I'm having trouble with the BlockingConsumer. Whenever I call consumer.nextMessage() I get the same message over and over again. This is doubly weird, as for one it should be ACKed and removed from the queue and for another the consumer shouldn't even bother with it as the unique ID we used is no longer bound to the queue. nextMessage even returns before the renderer service is done and has sent its done message back.
Here's the simplified setup:
general
All services use a global DirectExchange for all messages
#Bean
public DirectExchange globalDirectExchange() {
return new DirectExchange(EXCHANGE_NAME, false, true);
}
ServiceA (handles the HTTP request):
private Content requestPreviewByKey(RenderMessage renderMessage, String previewKey) {
String renderDoneRoutingKey= UUID.randomUUID().toString();
renderMessage.setPreviewDoneKey(renderDoneId);
Binding binding = BindingBuilder.bind(previewDoneQueue).to(globalDirectExchange)
.with(renderDoneRoutingKey);
try {
amqpAdmin.declareBinding(binding);
rabbitProducer.sendPreviewRequestToKey(renderMessage, previewKey);
return getContentBlocking();
} catch (Exception e) {
logErrorIfDebug(type, e);
throw new ApiException(BaseErrorCode.COMMUNICATION_ERROR, "Could not render preview");
} finally {
amqpAdmin.removeBinding(binding);
}
}
private Content getContentBlocking() {
BlockingQueueConsumer blockingQueueConsumer = new BlockingQueueConsumer(rabbitMqConfig.connectionFactory(), new DefaultMessagePropertiesConverter(), new ActiveObjectCounter<>(), AcknowledgeMode.AUTO, true, 1, PREVIEW_DONE_QUEUE);
try {
blockingQueueConsumer.start();
Message message = blockingQueueConsumer.nextMessage(waitForPreviewMs);
if (!StringUtils.isEmpty(message)) {
String result = new String(message.getBody());
return JsonUtils.stringToObject(result, Content.class);
}
throw new ApiException("Could not render preview");
} catch (Exception e) {
logError(e);
throw new ApiException("Could not render preview");
} finally {
blockingQueueConsumer.stop();
}
}
Service B
I'll spare you most of the code. My log says everything is going well and as soon as its done the service sends the correct message to the UUID key that was sent with the initial render request.
public void sendPreviewDoneMessage(Content content, String previewDoneKey) {
String message = JsonUtils.objectToString(content);
rabbitTemplate.convertAndSend(globalDirectExchange, previewDoneKey, message);
}
The whole thing works... Once...
The real issues seems to be the consumer setup. Why do I keep getting the same (first) message from the queue when I use nextMessage().
Doesn't creating and removing a Bindung ensure, that only messages bound to that routingKey are even received in that instance? And doesn't nextMessage() acknowledge the message and remove it from the queue?!
Thank's a lot for bearing with me and even more for any helpful answer!
BlockingQueueConsumer is not designed to be used directly; it is a component of the SimpleMessageListenerContainer, which will take care of acking the message after it has been consumed by a listener (the container calls commitIfNecessary).
There may be other unexpected side effects of using this consumer directly.
I strongly advise using the listener container to consume messages.
If you just want to receive messages on demand, use a RabbitTemplate receive() or receiveAndConvert() method instead.

NullPointerException on SimpMessagingTemplate in Spring

I am building an application that will send messages using STOMP over websockets. I want to send messages without a request being made by the client. According to the documentation, I can do this by using convertAndSend.
However when I try and do this, I get a null pointer exception. Please see code below:
public class ParseJSON {
#Autowired
private SimpMessagingTemplate template;
public void getDetails(String json) {
try {
Tweet status = sendDetails(TwitterObjectFactory.createStatus(json));
sentToWebApp(status);
} catch (TwitterException e) {
e.printStackTrace();
}
}
#Scheduled(fixedDelay = 50)
private void sentToWebApp(Tweet status) {
System.out.println(status);
this.template.convertAndSend("/tweet/update", status);
}
}
Stack Trace:
java.lang.NullPointerException: null
at org.myproject.worker.ParseJSON.sentToWebApp(ParseJSON.java:62)
at org.myproject.worker.ParseJSON.getDetails(ParseJSON.java:51)
at org.myproject.worker.TwitterClient.run(TwitterClient.java:50)
at org.myproject.Controllers.TweetController.greeting(TweetController.java:37)
Can anybody pour any light onto my situation so I'm able to send a message via websocket without encountering an exception.
Thanks in advance.
Your template variable is not properly autowired thats why its giving nullpointerException .
you can check spring configuration to autowire correctly

RESTEasy JSON Exception Response: Send list of objects

I have web client (HTML5) and backend server based on RESTEasy webservices and session beans. In my server side code I am iterating over list of objects and per object i am executing some business logic:
List<TestTO> failedTestList = new ArrayList<TestTO>();
for (TestTO testTO : testTOList) {
try {
// some weired business logic :P
} catch (Exception e) {
logger.error("Unable to create data -" + e.getMessage());
failedTestList.add(testTO);
}
}
if (!failedTestList.isEmpty()) {
// throw custom exception embedded with failed TO list
}
I have written custome exception handlers, to catch exceptions and return proper response back to client. This class looks like:
public class CustomExceptionHandler implements ExceptionMapper<CustomException> {
public CustomException getCustomErrorCode(final CustomException customException) {
// Some logic to get cause and set error code
return customException;
}
#Override
public Response toResponse(final CustomException customException) {
return Response.serverError().entity(
"{\"Error Code\":\"" + getCustomErrorCode(customException).getErrorCode() + "\", "
+ "\"Error Message\":\"" + customException.getLocalizedMessage() + "\"}").build();
}
}
I am thinking of an option to send this failed TO list back to client, so that it can understand processing of which objects got failed. I was going through different articles, but could not find anything which fits my requirement.
Please give me an idea and link to reference, on how to implement such requirement. Please note that, my client expects response in JSON format. Please let me know, if you require more information.
Thanks.

Categories

Resources