How do I not lose my RequestAttributes on new thread? - java

Using Spring 4, in a simple POST API, I am attempting to immediately return a response and continue some more processing in the background.
#RestController
#RequestMapping("/somewhere")
public class SomeController {
SomeService someService;
#PostMapping(value = "/something")
public ResponseModel getResponseAndDoOtherProcessing() {
ResponseModel response = someService.getInitialResponse();
// Async function
someService.doOtherProcessing(response);
return response;
}
}
The first function getInitialResponse() would return an object to be sent back to the client but I want to do more processing using the response I sent.
So I define an Async function for my controller to call on another thread as to not wait on it for sending the response to the client (#EnableAsync is on a configurable file):
#Async
#Override
public void doOtherProcessing(ResponseModel) {
// do some stuff
...
// get the request attributes for some other pieces
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
...
}
But since this on another thread, it looks to lose the request attributes:
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
I have searched a few things such as using MODE_INHERITABLETHREADLOCAL and read some debugging pieces such as this but haven't had any luck.
How do I keep the scope of my request to do other processing? Can anyone point me in the right direction?

Related

Invoke Spring Controller from Spring Integration flow

Hi I have a little problem. I want to invoke spring controller manually but I have an exception. Firstly, let me show you some integration flow and controller:
#Bean
public IntegrationFlow flow() {
return IntegrationFlows.from(
Amqp.inboundAdapter(rabbitMqConfig.connectionFactory(), queue)
.acknowledgeMode(AcknowledgeMode.MANUAL)
.errorChannel("errorChannel")
.concurrentConsumers(2)
.maxConcurrentConsumers(3))
.transform(Transformers.fromJson(Event.class))
.transform(new EventToRequestTransformer())
.handle(Request.class, (request, headers) -> controller.trigger(request))
.<ResponseEntity, HttpStatus>transform(ResponseEntity::getStatusCode)
.routeToRecipients(some routing)
.get();
}
#Controller
public class SomeController {
#RequestMapping(value = "/trigger", method = RequestMethod.POST)
public ResponseEntity<Response> trigger(#RequestBody Request request)
{
//some logic
}
}
When I'm running my app and sending an event I am getting exception on line:
.handle(Request.class, (request, headers) -> controller.trigger(request))
Exception:
nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet
Could someone please tell me what is wrong and how to fix that? I thought I can just invoke controller method like it was coming from simple POJO.
You are mixing concerns and try to call Web tier from the service layer.
If the logic is like that, then design of the app is wrong.
You should extract some service from the controller logic and call it from the Web, as well as from there on the Integration level.
According your stack trace it looks like you try to get access to the request scope object. Well, and that is exactly what happens to #Controller beans, I guess.

Server Sent Event with Spring with having a thread being freed from request

I need to show off an example of server sent events. I learned about it in a spring talk. People used Webflux there to show the reactive principles. I understood the part on how this will free thread resources because the request thread won't be blocked until the job is done and the server returns the response.
I have an example here but actually I don't really know how I can make this thread resource example be clear enough.
I do not want to use the WebFlux framework here. Just need to know what to put into a separate thread here - if necessary at all?!
As you can see I have a GetMapping to subscribe to the event stream. And then I have a GetMapping to launch or fire an event. This example is fast for sure but should be considered as heavy database call.
So I actually need to have the whole logic be separated in another thread right? So the request thread is free as soon as possible?
#RestController
public class EventStreamRequestHandler {
#Autowired
ObjectMapper objectMapper;
SseEmitter sseEmitter = new SseEmitter(1000000L);
#GetMapping("/get/event/stream")
public SseEmitter getStream() {
return this.sseEmitter;
}
#GetMapping("/launch/event")
public void fireEvent() throws IOException {
Person peter = new Person("Peter", "25");
String valueAsString = objectMapper.writeValueAsString(peter);
SseEmitter.SseEventBuilder sseEventBuilder = SseEmitter.event()
.id("foo")
.name("awesome-event")
.data(valueAsString);
sseEmitter.send(sseEventBuilder);
}
}
Yes, Server sent events are supposed to send messages to the client asynchronously without client keep on polling for message.
The sending of messages from client to server needs to be done asynchronously. With the way you have done it. When a GET request is sent to /get/event/stream an SseEmitter will be created but messages will only be sent when a GET request is sent to /launch/event. And the GET request thread for /launch/event will be used to send the message.
Sometime back I wrote post to send SSE messages using a different thread. I hope this helps.
But I don't recommend storing the SseEmitter in an instance variable as it will overridden by multiple requests. You must at least make it ThreadLocal

Insert to database using #Async in Spring

I'm writing a web application which offers image files upload.
After the file's uploaded (using JavaScript) the user needs to submit a form.
After submitting, image files are processed (making thumbnails and so on ), that's why I wanted to pass the HttpServletRequest to an #Async service.
Unfortunately when I use #Async my application doesn't insert anything in the database. There is no error also. It's just not happening.
When I remove #Async, it works fine, but the form submission takes a long time.
I also use #EnableAsync after #Service.
What is wrong ?
#ResponseBody
public String addAdvertisement(Model model, HttpServletRequest request){
addAdvertisementService.addAdvertisement(request);
return "OK";
}
AND ASYNC SERVICE
#Service
#EnableAsync
public class AddAdvertisementService {
public String addAdvertisement(HttpServletRequest request){
...
System.out.println(" REQUEST " );
int customerId = customerNotRegisteredService.addCustomer("", ipAddress, email, phoneNumber);
REQUEST is displayed on the console, but "addCustomer" is not invoked...
This could be due to scoped nature of request object.
With #Async you are processing it in a different thread. So servlet thread is free and your request is complete.
To do is properly either clone your request and pass cloned object or you can use Future object and block main thread.
Cloning HTTP request and response is possible via HttpServletResponseWrapper class http://docs.oracle.com/javaee/1.3/api/javax/servlet/http/HttpServletResponseWrapper.html.

How to invoke async controller logic after returning response using Spring?

I need to process request asynchronously in time - after receiving request I must return a response with status 200 to confirm that the request have reached it's goal, and then proceed with some magic to happen in service. I tried few ways to reach it, but every time response was sent after logic part ended in some other thread.
Is there a way to reach it using Spring? Or should I rather think about other approach to this problem?
The Spring Framework provides abstractions for asynchronous execution and scheduling of tasks
You can look at this => http://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html
You need to use deferredResult http://docs.spring.io/spring-framework/docs/3.2.0.BUILD-SNAPSHOT/api/org/springframework/web/context/request/async/DeferredResult.html
You will create a deferredResult object and you will return to the client. then asynchronously you will execute the logic and as soon as you finish you will inform the client that the request it´s done.
This technique is also know as "http long polling"
#RequestMapping("/")
#ResponseBody
public DeferredResult<String> square() throws JMSException {
final DeferredResult<String> deferredResult = new DeferredResult<>();
runInOtherThread(deferredResult);
return deferredResult;
}
private void runInOtherThread(DeferredResult<String> deferredResult) {
//seconds later in other thread...
deferredResult.setResult("HTTP response is: 42");
}

Request right after Response?

I am a bit lost with the following scenario:
My webservice consumes POST requests with form data. I have to reply with 200 OK or otherwise the senders thinks the request failed.
Immediately after answering with 200 I would like to proceed to call another webservice on a remote host with some of the data I have just received.
My webservice consumes the POST request with the #GET annotation. That works I can read all the form data. To call the other webservice I used the Jersey Client API. That works fine too.
I just can't figure out how to switch from switching from one call to another. Everything is programmed with Jersey 2 and deployed in Tomcat, so no real Application Server. There is no full Java EE stack available.
Am I missing some middleware? Do I need to implement a custom event-loop or some message broker?
Not sure if there's any "standard" way to handle this, but there's a CompletionCallback we can register with an AyncResponse.
CompletionCallback:
A request processing callback that receives request processing completion events.
A completion callback is invoked when the whole request processing is over, i.e. once a response for the request has been processed and sent back to the client or in when an unmapped exception or error is being propagated to the container.
The AsyncResponse is meant to handle requests asynchronously , but we can immediately call resume to treat it like it was synchronous. A simple example would be something like
#Path("/callback")
public class AsyncCallback {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public void postWithAsync(#Suspended AsyncResponse asyncResponse,
SomeObject object) {
asyncResponse.register(new CompletionCallback() {
#Override
public void onComplete(Throwable error) {
if (error == null) {
System.out.println("Processing new Request");
} else {
System.out.println("Exception in Request handling");
}
}
});
Response response = Response.ok("Success").build();
// Carry on like nothing happened
asyncResponse.resume(response);
}
}
You can see more explanation at Asynchronous Server-side Callbacks

Categories