I have a controller that calls a webservice to start a batch job, when the result is returned, it should call another REST API based on this result. Then it should wait for the new result, and return this second result to user:
#RestController
public class LaunchController {
#PostMapping(path = "/launch", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<LaunchResult> launch(#Valid #RequestBody LaunchParams params) {
// in launch() I call the first REST API
LaunchResult result = myService.launch(params);
// here I need to call another REST API
AnotherResult result2 = callAnotherWebAPIBasedOnThisResult(result);
return ResponseEntity.ok(result2);
}
Now I want to know that is it good practice to do it like this (synchronously) and all in one controller ? Exist other way of doing this ?
Your controller is perfectly fine as it does not have any application logic inside and it actually calls the service methods. But It lacks the exception handling. You have catch with proper exceptions with try catch block or throws keyword.
The decision to convert the endpoint to an asychronous one depends on a few factors :
Is the batch job going to take time to be executed.
Can this process be converted to an asynchronous one.
Does the use case expect the user to wait until the action is completed.
If the your answer is yes, it's better to convert the endpoint to an ayschronous one and update the user with the details later after all processes including the batch processes are completed . It's always better NOT to keep the user waiting for a response. Non-blocking requests makes sense when you are dealing with a lot of data and processing needed for this data. Also, by making this request asynchronous you will have better control over the processing stages and provide the user with better statistics incase any of the processing stage resulted in failure. For instance the batch job could fail or the second rest api call could result in an error.
Related
I am working on Spring-Rest application in which we have a request(Parent request) having few requests inside it(child request(varies from 10-50)).
We have to call another API with those child request and recieve response/request (child response) which we have to integrate into main response(parent response).
I am able to code upto this scenario but the problem is that it is taking too much time and that makes our API slow.
I am looking out for a way by which we can make call parallel so that the API can perform faster.
I have already used parallel stream with custom thread but the problem which I am encountering is that I am not able to store any value when I am doing
childRequests
.prallelStream()
.forEach((request) ->
{my logic to call other api and store result into childResponse }
)
I am getting that "childResponse needs to be final or effectively final"
I'm learning more about using Spring Webflux and experimenting with testing a simple async webservice call. I've looked at several examples and I can't see what I'm doing wrong. I have a service that makes a call to a third party API and all I want to do is output the Json response returned. I'm not converting the response into model objects just yet but this will be the next step if I can get basics working first. The code doesn't log any of the output of the webservice call and I've also tried sending to System.out::println and that also doesn't work. The output in the test only includes the following log output
023-01-04 00:53:46.622 INFO 19938 --- [ main] c.r.io.service.impl.ListlyServiceImpl : Starting call to Listly API
2023-01-04 00:53:52.395 INFO 19938 --- [ main] c.r.io.service.impl.ListlyServiceImpl : Exiting service call to Listly
However , when I put a break point on
listlyResponse.subscribe(listlyResp ->
log.info(listlyResp));
I can actually see the correct contents of the response from the web service call. Any ideas on what I'm doing wrong? This is the code
#Service
public class ListlyServiceImpl implements ListlyService {
private final static Logger log = LoggerFactory.getLogger(ListlyServiceImpl.class);
private final String baseUrl = "https://list.ly/api/v4";
#Override
public void callListlyService(String searchUrl) {
if (searchUrl == null) {
throw new RuntimeException("Search URL cannot be null");
}
log.info("Starting call to Listly API");
Mono<String> listlyResponse = WebClient.create(baseUrl)
.get()
.uri(uriBuilder -> uriBuilder
.path("/meta")
.queryParam("url","{url}")
.build(searchUrl))
.retrieve()
.bodyToMono(String.class);
listlyResponse.subscribe(listlyResp ->
log.info(listlyResp));
// listlyResponseFlux.subscribe(System.out::println);
log.info("Exiting service call to Listly");
}
}
I'm expecting to be able to output the contents of the web service call to the log output which is not working for some reason.
It is not clear without seeing your entire project, because I cannot see where you actually call your callListlyService() method.
But I can see you're calling it from Main thread.
I think you misunderstood the reactive paradigm.
As soon as you call subscribe() on reactive chain you should know that within the chain may happen the thread switching (depending on operators on your chain/some reactive API that may do thread switching)
In case of WebClient the retrive() subscribes from netty threads by default and the next executions will happen on reactor-%s thread
So when you call your service method, it makes subsribe() and returns immediately showing you log "Exiting service call to Listly"
and your method returns immediately, so you cannot see result of your call.
Just to see your result you can call block() on your reactive chain, that will force your calling thread to block and waiting the response from your WebClient. But this approach is not recommended in reactor because you're losing benefits from reactive-way. In real cases you should return Publisher<> from your method and , for example, subscribe by yourself where you need/use Spring WebFlux returning Publishers in your controller methods leaving subsribe process to WebFlux
One another way just for experimanetal purposes: you can place Thread.sleep(n) in the end of your test, where n - time in millis for your Main thread to wait. The time should be greater than your actual web-call
I have a requirement and I am bit confused about its design.
Requirement: iOS makes a call to backend(java), backend makes a call to the cloud API which return a token for future calls. The cloud API might take approximately 6 to 10 seconds to return the actual result, so instead of waiting for 6 to 10 seconds it gives a token back and let the caller(in my case the backend java server) to pull the results.
Current Approach: iOS calls the backend(java server), the backend calls cloud API and get's the token, then it sleeps the thread for 1 second and once the thread is invoked it hit the cloud API to get the status, if the status is not completed thread.sleep is invoked again and this continues till the cloud API call give's the complete result. Once the cloud API returns the result the backend returns the result to iOS.
The approach is not scalable and was done to test the cloud API but now we need a more scalable approach.
This is what I am thinking about iOS calls backend, backend calls the API and send back the result to iOS(it displays some static screen just to keep users engaged) and in the mean time it puts the object in Spring Thread pool Executor. The executor hits the API every one second and update the iOS through push notification and this continues till we get the final result from cloud API.
This is better then existing approach but even this doesn't look scalable and thread pool executor will get exhausted after some time(making it slow) and also thread.sleep is also not a good option.
I thought about using AWS SQS but it doesn't provide real time processing and running background jobs every 1 second doesn't seem to be a good option.
I am also exploring Apache Kafka and trying to understand whether it can fit to my use case.
Let me know if someone has tacked the similar kind of use case.
Here #EventListener in tandem with #Scheduled can be utilized, if Spring 4.2 (or newer) version is used.
First Create an event object say APIResult which will hold the API result
public class APIResult extends ApplicationEvent {
public APIResult(Object result) {
super(source);
}
}
Next register a listener for the event published as APIResult
#Component
public class MyListener {
#EventListener
public void handleResult(APIResult result) {
// do something ...
}
}
Next create a scheduled process which will hold the token(s) for which result is not yet retrieved
#Component
public class MyScheduled {
private final ApplicationEventPublisher publisher;
private List<String> tokens = new ArrayList<>();
#Autowired
public MyScheduled (ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
#Scheduled(initialDelay=1000, fixedRate=5000) // modify it as per requirement
public void callAPIForResult() {
// call the API and get result for each token(s) ....
this.publisher.publishEvent(new APIResult(result));
}
// method to add & remove tokens
}
The overall process flow should be like
Application submit a request to API and collect the respective token.
Token is passed to scheduled service to fetch the result.
In its next run the scheduled service iterates over the available token(s) and call API to fetch the results (if result is available publish the event else continue)
The published event is intercepted by registered listener; which itself process the result or delegates as applicable
This approach will transparently fetch results without messing with the business logic and at same time leveraging the standard framework features viz. scheduling and asynchronous event publishing & processing.
Although I have not tested this but it should work, at least giving an idea on how to implement. The setup is tested with Spring boot ver. 1.5.1.RELEASE which is backed by Spring's 4.3.6.RELEASE
Do let know in comments if any further information is required.
Reference - Application Event in Spring (link)
I am thinking about using Spring ConcurrentTaskExecutor(let's call it cloudApiCall) and as soon as I received the token from Cloud API, I will submit a future job to the executor and return the token to the Mobile Client. The thread associated with ConcurrentTaskExecutor will pick the job, call the Cloud API and submit the response to the another ConcurrentTaskExecutor(let's call it pushNotification) which will be responsible for pushing the silent notification to the Mobile client. The thread associated ConcurrentTaskExecutor(cloudApiCall), will also check the status of the call, if the future call is required, it will submit the job back to ConcurrentTaskExecutor(cloudApiCall). This will continue till we get the complete response.
I am new to Java Spring Framework, I am Rails developer I have requirement in java spring like I need to do background jobs but after the response send to the end User. It should not wait for the jobs to complete. But the jobs should run every time action completes.
Is a webservice app. We have Service, Bo and DAO layers and we are logging any exceptions occurred while processing the user data in database before response send to user, but now we want to move(Exception handling) after response send to user to increase the performance.
I remember in rails we have callbacks/filters after the action executed it calls the methods we want to executed. Same is available in java Spring?
Thanks,
Senthil
I assume the use case is something like a user requests a long-running task, and you want to return a response immediately and then launch the task in the background.
Spring can help with this. See
http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/scheduling.html
In particular see the #Async annotation.
With respect to the client getting a response back following the async processing (exception or otherwise), you can do it, but it's extra work.
Normally the immediate response would include some kind of ID that the client could come back with after some period of time. (For example, when you run a search against the Splunk API, it gives you a job ID, and you come back later with that job ID to check on the result). If this works, do that. The client has to poll but the implementation is the simplest.
If not, then you have to have some way for the client to listen for the response. This could be a "reply-to" web service endpoint on the client (perhaps passed in with the original request as a custom X-Reply-To HTTP header), or it could be a message queue, etc.
I'm trying to set up a servlet that I can use to call webservices asynchronously. For the most part, it is working fine. I have a servlet with a doGet method and a js that calls it. I have a callback method in the js that the servlet correctly calls when it has finished doing its thing.
The complication is that one of the web services I am calling is also asynchronous, and I would like to be able to essentially call the js callback method a second time after the asynchronous ws callback has finished. For example, if you have a status field, when you call the synchronous web service, it immediately updates to "Beginning Synchronous Call" and then when the servlet callback arrives it changes to the callback value, which is the result of the web service.
When you call the asynchronous web service, the update field immediately updates to "Beginning Asynchronous Call", and shortly receives the first callback from the servlet indicating that the web service has been requested, so we update the field to "Processing Web Service" or whatever. The problem is that once the web service finishes and calls back to the servlet, I can't seem to figure out how to send the result to the js callback method.
I'm pretty new at AJAX and servlets, so maybe this is a horrible way to accomplish what I want.
The web services are both being called in the Servlet, mostly using Netbeans auto-generated WS calls. The WS calls themselves work fine, but once I get the result of the asynchronous WS, I am stuck inside of the handleResponse method of the webservice callback and no longer have any reference to the response element for the document I want to update.
I tried to store the original response variable as a static member variable and use it in the handleResponse method like so:
javax.xml.ws.AsyncHandler<WsClients.Op11Response> asyncHandler = new javax.xml.ws.AsyncHandler<WsClients.Op11Response>() {
public void handleResponse(javax.xml.ws.Response<WsClients.Op11Response> asyncResponse) {
try {
storedResponse.setContentType("text/xml");
String returnString = asyncResponse.get().getReturn();
storedResponse.getWriter().write("<returnData><content>"
+ returnString + "</content></returnData>");
} catch (Exception ex) {
}
}
};
This will not compile with a debugger attached and does not seem to be able to assign a reference anyway.
Is there a better way to do this?
The nature of HTTP is that you cannot send anything back to the client unless client requested this information either by polling or by keeping the connection open.
The operation to start the asynchronous call ends immediately and you need to return from the servlet doGet method (while technically you can stay in the servlet call until your async call finishes I wouldn't recommend that as it ties up the server resources. It is generally a good practice to return from the servlet as soon as you can).
The best course of action would be:
Have internal data structure (e.g. HashMap with appropriate synchronization) to hold the asynchronous calls that are executing.
When you start a new call, assign it pseudo-random key and return it from the initial call.
Using the above key, have browser-side javascript AJAX calls periodically poll the status of the call and display the results.
Do not forget to clean up finished or stale calls (for example by running a timer thread).
When you comfortable with the polling implementation in step 3 above, you may want to consider Comet, a.k.a. long poll to replace client-side polling.
Servlet cannot send response again. HTTP protocol is synchronous, and only client can initiate a request-response exchange.
For async updates you need to perform polling from the client side to the server side, and accumulate messages on the server side (in the sessions) until client picks them up or they expire.