How does caching work in JAX-RS? - java

Suppose I have the following web service call using #GET method:
#GET
#Path(value = "/user/{id}")
#Produces(MediaType.APPLICATION_JSON)
public Response getUserCache(#PathParam("id") String id, #Context HttpHeaders headers) throws Exception {
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("id", id);
SqlSession session = ConnectionFactory.getSqlSessionFactory().openSession();
Cre8Mapper mapper = session.getMapper(Cre8Mapper.class);
// slow it down 5 seconds
Thread.sleep(5000);
// get data from database
User user = mapper.getUser(map);
if (user == null) {
return Response.ok().status(Status.NOT_FOUND).build();
} else {
CacheControl cc = new CacheControl();
// save data for 60 seconds
cc.setMaxAge(60);
cc.setPrivate(true);
return Response.ok(gson.toJson(user)).cacheControl(cc).status(Status.OK).build();
}
}
To experiment, I slow down the current thread 5 seconds before fetching data from my database.
When I call my web service using Firefox Poster, within 60 seconds it seemed much faster on the 2nd, 3rd calls and so forth, until it passed 60 seconds.
However, when I paste the URI to a browser (Chrome), it seemed to slow down 5s everytime. And I'm really confused about how caching is actually done with this technique. Here are my questions:
Does POSTER actually look at the header max-age and decide when to
fetch the data?
In client side (web, android....),
when accessing my web service do I need to check the header and then
perform caching manually or the browser already cached the data
itself?
Is there a way to avoid fetching data from the database
every time? I guess I would have to store my data in memory somehow,
but could it potentially run out of memory?
In this tutorial
JAX-RS caching tutorial:
How does caching actually work? The first line always fetch the data from the database:
Book myBook = getBookFromDB(id);
So how it is considered cached? Unless the code doesn't execute in top/down order.
#Path("/book/{id}")
#GET
public Response getBook(#PathParam("id") long id, #Context Request request) {
Book myBook = getBookFromDB(id);
CacheControl cc = new CacheControl();
cc.setMaxAge(86400);
EntityTag etag = new EntityTag(Integer.toString(myBook.hashCode()));
ResponseBuilder builder = request.evaluatePreconditions(etag);
// cached resource did change -> serve updated content
if (builder == null){
builder = Response.ok(myBook);
builder.tag(etag);
}
builder.cacheControl(cc);
return builder.build();
}

From your questions i see that you're mixing client side caching(http) with server side caching(database). I think the root cause for this is the different behavior you observed in firefox and chrome first i will try to clear this
When I call my web service using Firefox Poster, within 60 seconds it
seemed much faster on the 2nd, 3rd calls and so forth, until it passed
60 seconds. However, when I paste the URI to a browser (Chrome), it
seemed to slow down 5s everytime.
Example :
#Path("/book")
public Response getBook() throws InterruptedException {
String book = " Sample Text Book";
TimeUnit.SECONDS.sleep(5); // thanks #fge
final CacheControl cacheControl = new CacheControl();
cacheControl.setMaxAge((int) TimeUnit.MINUTES.toSeconds(1));
return Response.ok(book).cacheControl(cacheControl).build();
}
I have a restful webservice up and running and url for this is
http://localhost:8780/caching-1.0/api/cache/book - GET
FireFox :
First time when i accessed url ,browser sent request to server and got response back with cache control headers.
Second Request with in 60 seconds (using Enter) :
This time firefox didn't went to server to get response,instead its loaded data from cache
Third Request after 60 seconds (using Enter) :
this time firefox made request to server and got response.
Fourth Request using Refresh (F5 or ctrl F5) :
If i refresh page ( instead of hitting enter) with in 60 seconds of previous request firefox didn't load data from cache instead it made request to server with special header in request
Chrome :
Second Request with in 60 seconds (using Enter ) : This time chrome sent request again to server instead of loading data from cache ,and in request it added header cache-control = "max-age=0"
Aggregating Results :
As chrome responding differently to enter click you saw different behavior in firefox and chrome ,its nothing do with jax-rs or your http response . To summarize clients (firefox/chrome/safari/opera) will cache data for specified time period in cache control , client will not make new request to server unless time expires or until we do a force refresh .
I hope this clarifies your questions 1,2,3.
4.In this tutorial JAX-RS caching tutorial: How does caching actually
work? The first line always fetch the data from the database:
Book myBook = getBookFromDB(id);
So how it is considered cached? Unless the code doesn't execute in
top/down order.
The example you referring is not talking about minimizing data base calls instead its about saving band width over network ,Client already has data and its checking with server(revalidating) if data is updated or not if there is no update in data in response you're sending actual entity .

Yes.
When using a browser like firefox or chrome, you don't need to worry about HTTP cache because modern browsers will handle it. For example, it uses in-memory cache when using Firefox. When using Android, it depends on how you interact with the origin server. According to WebView, it's actually a browser object, but you need to handle HTTP cache on your own if using HTTPClient.
It's not about HTTP caching but your server-side logic. the common answer is using database cache so that you don't need to hit database in every HTTP request.
Actually JAX-RS just provides you ways to work with HTTP cache headers. you need to use CacheControl and/or EntityTag to do time based cache and conditional requests. for example, when using EntityTag, the builder will handle response status code 304 which you never need to worry about.

Related

Spring Boot MVC - is it good practice to forward requests to referrer?

In my Java 11, Spring Boot 2.7.4, Spring MVC application, users search for data using URL patterns similar to this:
http://host:port/mydata?office=london&name=jones (type A)
The data is displayed in tables. Each row has a delete button with an associated URL similar to this: -
http://host:port/delete?id=23 (type B)
Deletion is performed by a Controller method. This deletes the data and forwards the user to the updated View.
This works fine, but the URL in the address bar changes from type A to type B. This becomes an issue if the user decides to refresh their browser, which may happen after a period of inactivity, for example. If this happens, the type B URL is now invalid, out of context and gives an error.
To address this, I have changed the Controller method to get the original referer URL (type A) from the request headers (see code below), and forward the user request there instead.
public ModelAndView getReferer(HttpServletRequest req) {
Enumeration<String> headerNames = req.getHeaderNames();
AtomicReference<String> referer = new AtomicReference();
headerNames.asIterator().forEachRemaining(headerName -> {
if (headerName.equalsIgnoreCase("referer")) {
referer.set(req.getHeader(headerName));
}
});
if (referer.get() != null) {
return new ModelAndView("redirect:" + referer.get());
}
return null; //Note: if null is returned, the old (type B) behaviour is used.
}
This works fine and now only type A URLs appear in the address bar. However, having done some reading, I am concerned that what I am doing may not be best practice and that we cannot guarantee that the referer URL will always provided in the headers.
I would be very grateful for any advice / suggestions here. Is what I am doing ok and good practice? Or are there better ways of redirecting requests to the original URL. Many thanks for reading.

How can I send POST requests from a background service in Java?

I'm currently working on a Java application that reads from Table A (with BLOB stored), writes some data from table A to table B while uploading the BLOB data to a file server. I tested the application on a test database(with around 400 rows) and it works fine. I need to implement the application as a background service that reads table A and sends HTTP POST requests to a REST server, followed by an insertion to table B and upload to file server. After the POST request, the server needs to return HTTP 202 created. I tried something like this:
#POST
#Path("attachments")
public void moveToMinio() throws Exception {
TiedostoDaoImpl tiedostoDao = new TiedostoDaoImpl();
List<Integer> id = tiedostoDao.getDistinctCustomerId();
for (Integer userId : id){
AttachmentService.insertAndUploadService(userId);
}
}
tiedostoDao.getDistinctCustomerId() returns a list of distinct customer id from table A and passes that id to AttachmentService.insertAndUploadService() inside a for loop. This somehow gets the job done, but I doubt this isn't the right way as it returns HTTP 200 and not 202. Is this right way to send a POST request? The production database may have millions of rows, what's the right way to process all those rows without affecting the server efficiency? I've been stuck with this for a while as I'm a java newbie and would really appreciate any help/suggestion.
If you want a http response after every row is processed, first you need to divide your method into which processes one row at one time, then you can use a Response to contain your http code and entity, like this:
#POST
#Path("attachments")
public Response moveToMinio() throws Exception {
TiedostoDaoImpl tiedostoDao = new TiedostoDaoImpl();
Integer userId = tiedostoDao.getOneCustomerId();
String uploadLink = AttachmentService.insertAndUploadService(userId);
return Response.status(Response.Status.ACCEPTED).entity(uploadLink).build();
}
Please refer to this How to run a background task in a servlet based web application?
Before you return response, put the job into a global queue and let the background process do the job.

Prevent multiple HTTP POST request in angularjs

I've built a complex web app using angularjs, java and hibernate.
I'm using http request for saving the form data.
$http({
method : 'POST',
url : $scope.servlet_url,
headers : {'Content-Type' : 'application/json'},
data : $scope.deals
}).success(function(data) {
}).error(function(data) {
});
I'm using angular version 1.2.19.
When hitting the save button this request will be triggered with the data available in the form and it moves to the server where the data is saved in the database. Before saving into the database many validations are done and also some external data is fetched which are related to the form data. Hence, it takes some time to save. (approximately around 5 to 7 minutes based on the form data provided). After the data is saved i'm redirecting to another page based on the response provided.
But the response takes time in my case, and in the mean time the same request is triggered again without any clue. There's no place the request is called in the code, but the request is triggered. It's a bit confusing.
But the same code works fine if the save takes less than 5 minutes. If it exceeds 5 minutes, it goes into a infinite loop and the save happens as many times the request is triggered. The response for the first request hits the angular controller but the controller doesn't identify the response, means we can't handle the response in this case. If this happens the page gets struck and we manually need to refresh or move the page.
Is there any way to prevent the duplicate request in angularjs? If there is a way, could anyone please help me achieve it.
Thanks in advance.

Implementing Caching in REST (JAX - RS)

I am trying to learn how caching works in REST. I know all headers like Cache control, Max-age, Expires etc. I was going through example mentioned in this post.
What I know about Http cache is (I may be wrong), browser sends Http request to server, and if it has cache headers, browser will store the response in local cache. If client hits another request for the same response, browser will check the cache and if response is not expired, then it will return from cache instead of requesting to server.
Example given in this link, client hits server every time and server checks if client has expired copy or not. In this case, we hit server every time instead of retrieving data from cache.
Am I missing something here?
In mentioned post server side cache is used.
In other words:
RESTEasy Cache can avoid calling UserDatabase if it already contains requested User (by EntityTag key, based on user ID).
Everything is done on server side. It has no any connection with expire date/time request/response headers.
This might be of some help :
Cache response only for GET request when response is 200 OK,
Test environment : Jboss6.4 and maven 3.0
Dependency :
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-cache-core</artifactId>
<version>Any version after 3.0</version>
</dependency>
Code Changes : Add singleton for ServerCacheFeature in your application class.
singletons.add(new ServerCacheFeature());
Add this annotation to your function :
#Cache(maxAge=15, mustRevalidate = false, noStore = false, proxyRevalidate = false, sMaxAge = 15)
noStore can be use to enable/disable to cache resp

Reload/History behaviour in Get and Post request?

I went thru http://www.w3schools.com/tags/ref_httpmethods.asp to read
about read vs post.Here is the description
To clear confusion, I am just taking scenario where user creates the customer on page 1(with the submit button) and
navigates to success page(page 2).
For reload (say user press F5 on success page) point, Get request is said to be harmless where in post request
"Data will be re-submitted".
My understanding in both request (GET/POST), data will be resubmitted
. so in customer scenario, two customer will be created when user
press F5 on page whether its post or get. So as per my
understanding, Data will be re-submitted in both GET/POST request and
none is harmless.Please correct my understanding if it is wrong?
For History point. It is said in GET request ,"Parameters remain in browser history" and for POST request
"Parameters are not saved in browser history". My question is if request parameters are not saved in
browser history in post request, how on click of F5 on success page duplicate customer is created. Are they stored
at some other location instead of browser history in post request?
I'll try to explain point by point:
About GET being harmless: Method GET is supossed to de idempotent, that means: given the same url and the same parameters it always should return the same result (user=34,date=01-07-2013 should return the same page) and SHOULDN'T change anything (do nothing more than a sort of query with "user" and "date"). Of course is quite common to break this rule and actually change the internal state (do an update or the like) that is the case that you're mentioning (page1 --> page2 creating something). POST requests don't have that requirement and are meant to change the internal state.
About parameters remaining in browser history: What they really mean is that in the GET request parameters are contained in the URL itself ( mysite.com?user=34,date=01-07-2013 ) so if you save the URL you also saving the parameters. In a POST request parameters go in the body of the request rather than as part of the URL; so you're right, old browsers used to only store the URL, nowadays browsers are optimized to store those POST parameters in an internal cache.

Categories

Resources