Java controller code runs even after returning - java

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

Related

Spring 5 Error handling of Postexchange requests

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

DeferredResult deferring HTTP thread but not returning to browser

I get some of the idea of async services, but haven't coded many, so the mechanics especially in Java are new to me. Basically, I have a long running service that I want to fork off to another thread and be able to check in on the status of it using a different service. For now, I can get the work started, I don't have a way to check in on it yet. But worse:
In the POST service asyncUploadSoftLayerFile below:
#RestController
#RequestMapping("/")
public class MyController {
...
#ResponseStatus(HttpStatus.CREATED)
#PostMapping("/async-files")
public DeferredResult<ResponseEntity<JobExecutionResult>> asyncUploadSoftLayerFile(#RequestParam MultipartFile file) throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, IOException, JobExecutionAlreadyRunningException{
logger.info("Received async-CompleteableFuture request");
DeferredResult<ResponseEntity<JobExecutionResult>> output = new DeferredResult<ResponseEntity<JobExecutionResult>>();
ForkJoinPool.commonPool().submit(() -> {
logger.info("Processing in separate thread: Thread-ID="+Thread.currentThread().getId());
JobExecutionResult jobExecutionResult = null;
try {
myReallyLongRunningProcess();
} catch (JobParametersInvalidException | JobExecutionAlreadyRunningException | JobRestartException
| JobInstanceAlreadyCompleteException | IOException e) {
logger.error("Error processing /async-files upload in Thread-ID: "+Thread.currentThread().getId(),e);
throw new RuntimeException(e);
}
if (!"COMPLETED".equals(jobExecutionResult.getExitStatus())) {
throw new UploadFileException(file.getOriginalFilename() + " exit status: " + jobExecutionResult.getExitStatus());
}
ResponseEntity<JobExecutionResult> responseEntity = ResponseEntity.ok(jobExecutionResult);
output.setResult(responseEntity);
});
return output;
}
}
Spring does do it's deferred thing, and I can see it spawned the work off to another thread. But it did not return back to the caller. Instead I saw:
2021-07-27 05:20:00 DEBUG o.s.web.servlet.DispatcherServlet - Exiting but response remains open for further handling
So it only partially gave me what I wanted. How can I get Spring to spawn the work off to another thread, (and ideally be able to refer to that process by another web service call), but immediately return some sort of response to the web browser?
This is part of an effort to start work but track the percentage complete as it goes along.

javax.net.ssl.SSLHandshakeException: Connection reset by peer

I use following code to call an Azure mobile backend API in my Android app,
try {
mobileClient.invokeApi("CustomTransaction", senderToCheck,
Boolean.class, new ApiOperationCallback<Boolean>() {
#Override
public void onCompleted(Boolean result,
Exception error, ServiceFilterResponse response) {
if (error == null) {
CheckSender(result);
} else {
dial.dismiss();
Crouton.makeText(MyActivity.this,
"Eror Occured with service",
Style.ALERT).show();
}
}
});
} catch (SecurityException e) {
Log.d(TAG, "CouldNotConnectToSocket", e);
e.printStackTrace();
} catch (IllegalArgumentException e) {
Log.d(TAG, "CouldNotConnectToSocket", e);
e.printStackTrace();
}
Other information:
CustomTransaction - API Controller name;
senderToCheck - JSON parsable data transfer object;
Boolean.class - return type; and 4th parameter is the callback method
All objects are JSON parsable and this worked like several days ago.
So this API call/Azure call always times out giving a What does "connection reset by peer" mean? ,SSLHandShakeExceptionand and most of the time Connect gets Timed out.
Main cause for the problem is com.microsoft.windowsazure.mobileservices.MobileServiceException: Error while processing request.
I tried re-publishing my asp.net web app several times but it never hits controller action where my debugger point is placed when debugging the service call remotely.
I checked if my service is down, found it is up & running then checked Azure management portal logs, found out traceApi messages of some controller action methods. and of SQL Cpu usages and Data out packet sizes., but I never gets a proper reply from anywhere to solve this problem for two weeks now.
In case,if I am correct, think the solution for this problem lies in http://www.webapper.com/blog/index.php/2007/02/09/troubleshooting-javaxnetsslsslhandshakeexception/ but Im not pretty sure on doing it.
Please advise me on getting this fixed

Is this a bad practice?

Is the following code considered a bad practice? Do you think it can be done otherwise?
The goal is to always update the status, either with success (i.e invocation to service.invoke(id);returns normally ) or with failure...
#Autowired
private Service service;
public void onMessage(Message message) {
String id = null;
String status = "FAILED";
try {
id = ((TextMessage) message).getText();
status = service.invoke(id); //can throw unchecked exception
} catch (final JMSException e) {
throw new RuntimeException(e);
} finally {
if (StringUtils.isNumeric(id)) {
service.update(id, status);
}
}
}
It depends on your Use-case, whether you have to perform a step or not based on previous step. Using finally may execute your second step regardless what exception you may receive.
I would recommend having the second step outside try...catch block so that you'll update only when you have got any exception you've Expected and continue to your second step, else, your method will throw and exit.
i think you should not use implementation of message listener , you should wire them independent of spring tech . just pojo based . use <jms:listener-container > with <jms:listener>

BlazeDS - AMFConnection.call giving HTTP 400 status

I'm trying to use BlazeDS's AMFConnection class to connect to pyamf, but when I call AMFConnection.call(), I get HTTP status 400 (Bad Request - "The request body was unable to be successfully decoded."). I'm more or less following this example: (pyamf.org/wiki/ClientHowTo ... sorry, I'm a new user so I guess I can't use hyperlinks. append a "http://" to those if you want to follow them)
Here's my code:
package amfconnectiontest;
import flex.messaging.io.amf.client.AMFConnection;
import flex.messaging.io.amf.client.exceptions.*;
public class Main {
public static void main(String[] args) {
AMFConnection amfConnection = new AMFConnection();
String url = "http://demo.pyamf.org/gateway/recordset";
String service = "service.getLanguages";
try
{
amfConnection.connect(url);
}
catch (ClientStatusException cse)
{
System.out.println(cse);
return;
}
// Make a remoting call and retrieve the result.
try
{
Object result = amfConnection.call(service);
System.out.println("results: " + result.toString());
}
catch (ClientStatusException cse)
{
System.out.println(cse);
}
catch (ServerStatusException sse)
{
System.out.println(sse);
}
// Close the connection.
amfConnection.close();
}
}
Any ideas?
The ability to en/decode BlazeDS specific messages (implementing ISmallMessage) has landed on the PyAMF trunk (r2726 and up). See the related ticket - http://pyamf.org/ticket/581
This version or one very similar is likely to become 0.5. If you need to connect to a BlazeDS service I would suggest checking out the trunk.

Categories

Resources