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.
Related
I have an API controller:
#RestController
#RequestMapping("api")
public class ListingRestController {
#PostMapping("/listings/edit/{id}")
public void editListing(ListingForm newListing, #PathVariable Integer id, Model model) {
ListingDto newListingDto = new ListingDto(newListing.getId(), newListing.getUserId(), newListing.getTitle());
model.addAttribute("submitURL", String.format("edit/%s", id));
listingService.deleteListingById(id);
listingService.addListing(newListingDto);
}
}
It takes a POST request from api/listings/edit/{id}. Also it should get the data from form inputs ListingForm newListing.
The problem is, the form is defined in a route listings/edit/{id}, so the controller method cannot get data. Any idea how I can get form data from route listings/edit/{id} in my API controller?
You must define GET api beside Api Post to get data. First, client call API GET to get data from server and place data to the form. After finish editing data, call API POST to post data to server.
You can write #RequestBody annotation to ListeningForm parameter
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?
I have a service which calls a dozen other services. This reads from a Kafka topic using a #StreamListener in a controller class. For traceability purposes, the same headers(original request ID) from the Kafka message need to be forwarded to all the other services as well
Traditionally, with a #PostMapping("/path") or GetMapping, a request context is generated, and one can access the headers from anywhere using RequestContextHolder.currentRequestAttributes() and I would just pass the HttpHeaders object into a RequestEntity whenever I need to make an external call
However in a StreamListener, no request context is generated and trying to access the RequestContextHolder results in an exception
Here's an example of what I tried to do, which resulted in an exception:
public class Controller {
#Autowired Service1 service1
#Autowired Service2 service2
#StreamListener("stream")
public void processMessage(Model model) {
service1.execute(model);
service2.execute(model);
}
}
public class Service {
RestTemplate restTemplate;
public void execute(Model model){
// Do some stuff
HttpHeaders httpHeaders = RequestContextHolder.currentRequestAttributes().someCodeToGetHttpHeaders();
HttpEntity<Model> request = new HttpEntity(model, httpHeaders);
restTemplate.exchange(url, HttpMethod.POST, request, String.class);
}
}
My current workaround is to change the StreamListener to a PostMapping and have another PostMapping which calls that so a request context can be generated. Another option was to use a ThreadLocal but it seems just as janky
I'm aware of the #Headers MessageHeaders annotation to access the stream headers, however, this isn't accessible easily without passing the headers down to each and every service and would affect many unit tests
Ideally, I need a way to create my own request context (or whatever the proper terminology is) to have a place to store request scoped objects (the HttpHeader) or another thread safe way to have request headers passed down the stack without adding a request argument to service.execute
I've found a solution and am leaving it here for anyone else trying to achieve something similar
If your goal is to forward a bunch of headers end-to-end through REST controllers and Stream listeners, you might want to consider using Spring Cloud Sleuth
Add it to your project through your maven or gradle configuration:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
Specifically, in Spring Cloud Sleuth there is a feature to forward headers or "baggage" by setting the property spring.sleuth.propagation-keys in your application.properties. These key-value pairs are persisted through the entire trace, including any downstream http or stream calls which also implement the same propagation keys
If these fields need to be accessed on a code level, you can get and set them using the ExtraFieldPropagation static functions:
ExtraFieldPropagation.set("country-code", "FO"); // Set
String countryCode = ExtraFieldPropagation.get("country-code"); // Get
Note that the ExtraFieldPropagation setter cannot set a property not present in the defined spring.sleuth.propagation-keys so arbitrary keys won't be accepted
You can read up on the documentation for more information
I am building a RESTful web service that can be consumed by a browser or another web service.
I am willing to reduce the bandwidth through caching, however i want the method to be executed and send the actual data only if it's different than the last modified cache.
From my understanding of the #cacheable annotation, the method is only executed once and the output is cached until the cache expires .
Also #CachePut executes everytime and updates the cache but does it send the cache again even if it's not updated?
summary is: i need the client to be able to send the last modified date of it's cache and only get a new data if it has been modified.
Also how does Spring handle the client side caching and if-modified-since headers? does i need to save the last modified time or it is automatically handled ?
No, you need to do it by yourself.
You need to annotate your "fetch" method with #Cacheable(docs) and then, annotate "update" method with #CacheEvict (docs) in order to "drop" your cache. So when you would fetch your data next time after its modification, it will be fresh.
Alternatively, you can create another method with #CacheEvict and manually call it from "update" method.
The cache related annotations (#Cacheable, #CacheEvict etc) will only deal with the cache being maintained by application. Any http response header like last-modified etc has to be managed seperately. Spring MVC provides a handy way to deal with it (docs).
The logic to calculate the last modified time has to be obviously application specific.
An example of its usage would be
MyController {
#Autowire
CacheService cacheService;
#RequestMapping(value = "/testCache", method = RequestMethod.GET)
public String myControllerMethod(WebRequest webRequest, Model model, HttpServletResponse response) {
long lastModified = // calculate as per your logic and add headers to response
if (request.checkNotModified(lastModified)) {
// stop processing
return null;
} else {
return cacheService.getData(model);
}
}
#Component
public class CacheService{
#Cacheable(value = "users", key = "#id")
public String getData(Model model) {
//populate Model
return "dataview";
}
I am developing a REST API in Spring Boot which I am providing the response within mostly 1-3 sec.My Controller is like below:
#RestController
public class ApiController {
List<ApiObject> apiDataList;
#RequestMapping(value="/data",produces={MediaType.APPLICATION_JSON_VALUE},method=RequestMethod.GET)
public ResponseEntity<List<ApiObject>> getData(){
List<ApiObject> apiDataList=getApiData();
return new ResponseEntity<List<ApiObject>>(apiDataList,HttpStatus.OK);
}
#ResponseBody
public List<ApiObject> getApiData(){
List<ApiObject> apiDataList3=new List<ApiObject> ();
//do the processing
return apiDataList3;
}
}
So I have a 300 users concurrently loading the API.I performed the load test with JMeter and it was ok.But still there were some failures(not all API calls were served).So how do I overcome this?How to imlement any queue on the API calls which arrives or any other methods to ensure each API call is responded with data?
Do you mean that you would like to make sure all the requests return the data?! If yes, you can use #Async and get the CompletableFuture. Then in your Controller, you can use the CompletableFuture to get the response. In case there are some failure, you can set the timeout for that and catch the exception to log the error.
Hope this help.