Caching reponse method results - java

I have a interface for getting responses from an API server, the result is a future containing the response for a request string, my question is what is better and efficient way without repeating code to cache the results, currently in each implementation of the method I am doing map.get() from in memory cache if is cached, but it is repetitive since in each method I am writing the same. If it's useful, I'm using OkHttp, just in case it's possible to use some kind of interceptor or some other way, thanks..
interface SodyApi {
ListenableFuture<FriendsListResponse> fetchFriendsList(String repo);
ListenableFuture<GuildResponse> fetchGuild(String repo);
ListenableFuture<UsersListResponse> fetchAllUsers(String repo);
}
class SodyApiImpl implements SodyApi {
final Map<String, ListenableFuture<Response>> map = new HashMap();
public ListenableFuture<FriendsList> fetchFriendsList(String repo) {
if (map.containsKey(repo))
return map.get(repo);
// .... get response with okHttp
}
public ListenableFuture<GuildResponse> fetchGuild(String repo) {
if (map.containsKey(repo))
return map.get(repo);
// .... get response with okHttp
}
public ListenableFuture<UsersListResponse> fetchAllUsers(String repo) {
if (map.containsKey(repo))
return map.get(repo);
// .... get response with okHttp
}
}

Related

Asynchronous http request processing (non blocking way) - DeferredResult and how to remove it

I kind of hit the wall with DeferredResult. We have really old pattern where we have Interfaces that contains all rest annotations and implementation of them. Also other clients (microservices) uses those interfaces to map communicate with each others (they are importing them as a module and make proxy rest calls). But there is a problem somebody hacked a bit this approach and we had two different declarations one for clients without DeferredResult and one with it on implementation side. When we tried to reflect changes for clients there is a problem a lot of them needs to change a way of communication. So i've been thinking of removing DeferredResult from method signature and just use result.
My question is how to do it in non blocking way in Spring?
Let's say i have this kind of code
#Component
public class ExampleSO implements ExampleSOController {
private final MyServiceSO myServiceSO;
public ExampleSO(MyServiceSO myServiceSO) {
this.myServiceSO = myServiceSO;
}
#Override
public DeferredResult<SOResponse> justForTest() {
CompletableFuture<SOResponse> responseCompletableFuture = myServiceSO.doSomething();
DeferredResult<SOResponse> result = new DeferredResult<>(1000L);
responseCompletableFuture.whenCompleteAsync(
(res, throwable) -> result.setResult(res)
);
return result;
}
}
where:
#RestController
public interface ExampleSOController {
#PostMapping()
DeferredResult<SOResponse> justForTest();
}
and:
#Component
public class MyServiceSO {
public CompletableFuture<SOResponse> doSomething() {
CompletableFuture<SOResponse> completableFuture = new CompletableFuture<>();
Executors.newCachedThreadPool().submit(() -> {
Thread.sleep(500);
completableFuture.complete(new SOResponse());
return null;
});
return completableFuture;
}
}
How could i achieve something like this:
#RestController
public interface ExampleSOController {
#PostMapping()
SOResponse justForTest();
}
Without removing async benefits ?

How to send restRequest in Patch mode?

I'm working with Postman and I see that it has many modes. I was able to implement a restRequest object that "knows" how to send a request in Post or Get method.
This is part of my code:
#Override
public RestResponse sendRequest() {
return data.accept(new RequestDataVisitor<RestResponse>() {
#Override
public RestResponse visit(GetData getData) {
return new RestResponse(webTarget.request().headers(headers).get());
}
#Override
public RestResponse visit(PostFormData post) {
return new RestResponse(webTarget.request(post.getMediaType()).headers(headers).post(post.getEntity()));
}
#Override
public RestResponse visit(PostRawData post) {
return new RestResponse(webTarget.request(post.getMediaType()).headers(headers).post(post.getEntity()));
}
#Override
public RestResponse visit(DeleteData deleteData) {
return new RestResponse(webTarget.request(deleteData.getMediaType()).headers(headers).delete());
}
});
}
How do I get my webTarget to send a request in Patch mode?
You can use the method method with first argument set to "PATCH".
method is a generalized method (as opposed to post, get, delete which have the http-method hardwired) and allows you to call an arbitrary method:
webTarget.request(mediaType).headers(headers).method("PATCH", entity);
See the documentation for additional details: WebTarget.request gives you an instance of an Invocation.Builder which inherits method from SyncInvoker.
Note: depending on the JDK version and the JAX-RS library that you are using, you may run into problems when calling the PATCH method. If you do, see if any of these help:
PATCH request using Jersey Client
HttpURLConnection Invalid HTTP method: PATCH)
Invalid HTTP method: PATCH
The issue was in WebTarget object:
WebTarget target = client.target(baseUrl).path(resourcePath)
.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND,"true");
PATCH method only works when SET_METHOD_WORKAROUND is true.

Java 8: Applying a list of methods on one object

I have a particular part in code where all I want to do is the below, but I am at a loss to write in a way that doesn't involve code repetition. Is there a way that I can declare a list of methods, which can be then applied to productFeatureValidationDto. My current approach is noob-ish.
public ValidateProductFeatureResponse validateProductFeatureAgainstAllCriteria(ProductFeatureValidationDto productFeatureValidationDto) throws
ApplicationException, ParseException {
ValidateProductFeatureResponse response;
response = this.validateProductFeatureA(productFeatureValidationDto);
if(response.getStatus().equalsIgnoreCase(ResponseStatus.FAILURE.name())){
return response;
}
response = this.validateProductFeatureB(productFeatureValidationDto);
if(response.getStatus().equalsIgnoreCase(ResponseStatus.FAILURE.name())){
return response;
}
response = this.validateProductFeatureA(productFeatureValidationDto);
if(response.getStatus().equalsIgnoreCase(MPResponseStatus.FAILURE.name())){
return response;
}
response = this.validateProductFeatureC(productFeatureValidationDto);
if(response.getStatus().equalsIgnoreCase(MPResponseStatus.FAILURE.name())){
return response;
}
response = this.validateProductFeatureD(productFeatureValidationDto);
if(response.getStatus().equalsIgnoreCase(ResponseStatus.FAILURE.name())){
return response;
}
response = this.validateProductFeatureE(productFeatureValidationDto);
if(response.getStatus().equalsIgnoreCase(ResponseStatus.FAILURE.name())){
return response;
}
response = this.validateProductFeatureF(productFeatureValidationDto);
if(response.getStatus().equalsIgnoreCase(ResponseStatus.FAILURE.name())){
return response;
}
return getResponseOnValidationSuccess(productFeatureValidationDto);
}
Thanks in advance.
if you can use spring framework.
at first you can define an interface like this.
public interface ValidateProduct{
ValidateProductFeatureResponse validate(ProductFeatureValidationDto dto);
}
Your specific verification class implements this interface and register to srpingcontext
public ValidateProductFeatureResponse validateProductFeatureAgainstAllCriteria(ProductFeatureValidationDto productFeatureValidationDto) throws
ApplicationException, ParseException {
ValidateProductFeatureResponse response;
Map<String, ValidateProduct> beansOfType = applicationContext.getBeansOfType(ValidateProduct.class);
for (ValidateProduct value : beansOfType.values()) {
response = value.validate(productFeatureValidationDto);
if(response.getStatus().equalsIgnoreCase(ResponseStatus.FAILURE.name())){
return response;
}
}
return getResponseOnValidationSuccess(productFeatureValidationDto);
}
I would suggest following approach (schematic):
/**
* List of validation functions
*/
private final static List<Function<ProductFeatureValidationDto, ProductFeatureValidationDto>> VALIDATIONS = new LinkedList<>();
/**
* Fill validations list
*/
static {
VALIDATIONS.add((source) -> {
// test for feature A
return source;
});
VALIDATIONS.add((source) -> {
// test for feature B
return source;
});
VALIDATIONS.add((source) -> {
// test for feature C
return source;
});
}
/**
* Predicate for failure determination
*/
private final Predicate<ProductFeatureValidationDto> IS_FAILURE = (dto) ->
dto.getStatus().equalsIgnoreCase(ResponseStatus.FAILURE.name());
/**
* Validation method
*/
public ValidateProductFeatureResponse validateProductFeatureAgainstAllCriteria(
ProductFeatureValidationDto dto
) throws ApplicationException, ParseException {
// iterate over validation functions and invoke them on dto instance
// filter stream by failed validations
// stop on first match
Optional<ProductFeatureValidationDto> dtoOptional = VALIDATIONS.stream()
.map(action -> action.apply(dto))
.filter(IS_FAILURE)
.findFirst();
// apply fuilure / success maping depending on result
return dtoOptional.isPresent()
? getResponseOnValidationFailure(dto)
: getResponseOnValidationSuccess(dto);
}
I ended up composing my solution from the answers given by #Eiden and #Alexandra Dudkina. A big shoutout to them. Below is the crux of my whole solution.
So, I have two interfaces
IProductFeature : This is a functional interface which has only one method validate. Every constraint needs to implement this.
IProductValidationService: This is a contract specifying the core methods needed to be implemented. The method validateProductFeatureAgainstAllCriteria is part of the contract.
There is a config file where all the features have been imported and organised into lists as required for different kinds of products. This list has been kept in a map with the product type as key. So this is acting like a factory which is giving a list of constraint based on a given product type.
The concrete class implementing IProductValidationService gets the list from the config and then applies all the constraints in the list to the given dto.
This way, I have separated all the concerns into separate portions.
The practical advantages to this approach are:
You can write extensive test cases and documentation for individual features.
If in the future, there is a policy change in ProductFeatureB(for e.g.),all I have to do is create a new concrete class, call it ProductFeatureBV2 and change only config file. The policy changes can be documented as part of the class javadoc. This way without changing core validation method, I can deprecate ProductFeatureB. This makes the code extremely flexible.
Thanks a lot to the community for helping me getting this right. If there are further improvements to be made here, please suggest them.

Conditional memoization in Guava

I know how to memoize a single object. However, I'd like to memoize only if some condition is met. I'm calling a service that sometimes returns a response that is not successful. I'd like to memoize only if the service's response if successful.
MyResponse myResponse = myService.call()
boolean success = myResponse.isSuccessful();
And my cache is created like so:
private Supplier<MyResponse> cache;
private void createCache() {
this.cache = Suppliers
.memoizeWithExpiration(myService::call, timeout,
TimeUnit.MINUTES);
}
Question: Is it possible to somehow cache the response only if the response is successful using the Supplier passed to the memoizeWithExpiration method?
The only workaround I found to do this is to, when retrieving the value, call cache.get() first, check if the object stored in cache is successful, and if it's not, call createCache() again to clear it and then get the value again. This way if the subsequent service call returns a valid object, it will get stored, and if not, every subsequent call will clear the cache and call the service again.
public MyResponse getResponse() {
MyResponse myResponse = cache.get();
if (myResponse.isSuccess()) {
return myResponse;
} else {
createCache();
return cache.get();
}
}
However, in this solution, if the cache is empty and the service returns unsuccessful response, it will get called again immediately.
You can create a method callUntilSuccess in Service class or in any other suitable place (here I'm assuming it is in your Service). You could also define a maximum number of tries in this method and after that it will return null, so you could avoid calling your service indefinitely (this suggestion isn't implemented in the code supplied bellow but it is very easy to do so). As the Guava method expects a Supplier, you can even create a lambda with this logic and pass it directly to the memoizeWithExpiration method.
public MyResponse callUntilSuccess() {
MyResponse response = myService.call();
while (!response.isSuccessful()) {
response = myService.call();
}
return response;
}
Then do the memoization in this way:
private void createCache() {
this.cache = Suppliers
.memoizeWithExpiration(myService::callUntilSuccess, timeout,
TimeUnit.MINUTES);
}
Could this be what you are looking for?
private void createCache() {
this.cache = Suppliers.memoizeWithExpiration(
Suppliers.compose(
response -> (response.isSuccess() ? response : null),
myService::call
),
timeout,
TimeUnit.MINUTES
);
}
Here, it will cache the response, or null, depending on whether it was successful.
More info on compose here https://github.com/google/guava/blob/master/guava/src/com/google/common/base/Suppliers.java#L45
EDIT:
If you need to cache the value on success, and leave the cache empty on failure, while returning the failed request, then you are almost there yourself, just change return logic a bit in getResponse, like this:
public MyResponse getResponse() {
final MyResponse myResponse = cache.get();
if (!myResponse.isSuccess()) {
this.createCache(); // clear cache
}
return myResponse; // don't call .get() again!
}

auto connect in case of error

i am working on android app , I often get HTTP error 500 when i try to access the url due to bad connectivity which causes my app to fail . Response returned from URL is in JSON format so in order to Parse this json i used jackson api
JsonNode monthlyUrlNode = objectMapper.readValue(url, JsonNode.class);
In case of failure i want to reconnect to url with a delay of 30 seconds
i referred this Retry a connection on timeout in Java , but it is not of much use to me
Have you thought of using a Proxy object? Proxy let's you wrap an interface in a way that you can perform the same intervention independent of the method being called. Let's assume you've already created a client object for accessing interface SomeService. Then you can create a proxy class with a 30-second retry built in:
public class ServiceProxy implements InvocationHandler {
private final SomeService realClient;
public ServiceProxy(SomeService realClientObject) {
this.realClient = realClientObject;
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(realClient, args);
if (result instanceof URL) {
JsonNode urlNode = objectMapper.readValue(url, JsonNode.class);
if (some condition on urlNode) {
// Wait and retry
Thread.sleep(30000);
result = method.invoke(realClient, args);
}
}
return result;
}
}
You create the proxy object by passing it the original interface, like:
public class ProxyFactory {
public static SomeService get(SomeService originalClient) {
return (SomeService)Proxy.newProxyInstance(SomeService.class.getClassLoader(),
new Class[]{SomeService.class},
new ServiceProxy(originalClient));
}
}
If you need this sort of fine control, do not pass a URL to Jackson. Use an appropriate HTTP library to read the content from the URL into memory, retrying as needed, and then feed it to Jackson.

Categories