I need to make a library in which I will have synchronous and asynchronous methods in it.
Core Logic of my Library -
The customer will use our library and they will call it by passing DataKey
builder object. We will then construct a URL by using that DataKey
object and make a HTTP client call to that URL by executing it and after we get the response back as a JSON String, we will send that JSON String back to our customer as it is by creating DataResponse
object.
I will have synchronous and asynchronous methods. Some customer will call the executeSynchronous
method to get the same feature and some customer will call our executeAsynchronous
method and with the executeAsynchronous
method, they will call future.get
in there code itself.
Below is my interface -
public interface Client {
// for synchronous
public DataResponse executeSynchronous(DataKey dataKey);
// for asynchronous
public Future<DataResponse> executeAsynchronous(DataKey dataKey);
}
Below is my DataResponse
class -
public class DataResponse {
private String response;
private DataErrorEnum error;
private DataStatusEnum status;
// constructor here
// and getters here
}
Below is my DataStatusEnum
class -
public enum DataStatusEnum {
SUCCESS, ERROR;
}
Below is my DataErrorEnum
class -
public enum DataErrorEnum {
NONE(200, "NONE", "Response is success."),
SERVER_DOWN(3145, "Server Down", "some long message here which can give more details."),
CLIENT_ERROR(3123, "Client Error", "some long message here which can give more details."),
TIMEOUT_ON_CLIENT(3187, "Client Timeout", "some long message here which can give more details.");
private final int code;
private final String status;
private final String description;
// constructor and getters here
}
And then I have my DataClient
which implements the above Client
interface.
public class DataClient implements Client {
private RestTemplate restTemplate = new RestTemplate();
private ExecutorService service = Executors.newFixedThreadPool(10);
// for synchronous call
@Override
public DataResponse executeSynchronous(DataKey dataKey) {
DataResponse dataResponse = null;
try {
Future<String> future = executeAsynchronous(dataKey);
dataResponse = future.get(dataKey.getTimeout(), TimeUnit.MILLISECONDS);
} catch (TimeoutException ex) {
PotoLogging.logErrors(ex, DataErrorEnum.TIMEOUT_ON_CLIENT, dataKey);
dataResponse = new DataResponse(null, DataErrorEnum.TIMEOUT_ON_CLIENT, DataStatusEnum.ERROR);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, dataKey);
dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
}
return dataResponse;
}
//for asynchronous call
@Override
public Future<DataResponse> executeAsynchronous(DataKey dataKey) {
Future<DataResponse> future = null;
try {
Task task = new Task(dataKey, restTemplate);
future = executor.submit(task);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, dataKey);
}
return future;
}
}
Now below is my simple class which will perform the actual task -
public class Task implements Callable<DataResponse> {
private DataKey dataKey;
private RestTemplate restTemplate;
public Task(DataKey dataKey, RestTemplate restTemplate) {
this.dataKey = dataKey;
this.restTemplate = restTemplate;
}
@Override
public DataResponse call() throws Exception {
DataResponse dataResponse = null;
String response = null;
try {
String url = createURL();
response = restTemplate.getForObject(url, String.class);
// it is a successful response
dataResponse = new DataResponse(response, DataErrorEnum.NONE, DataStatusEnum.SUCCESS);
} catch (RestClientException ex) {
PotoLogging.logErrors(ex, DataErrorEnum.SERVER_DOWN, dataKey);
dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, dataKey);
dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
}
return dataResponse;
}
// create a URL by using dataKey object
private String createURL() {
String url = somecode;
return url;
}
}
Problem Statement:-
As I mentioned above, some customers will call executeSynchronous
method to get the data for that user id which they are passing in DataKey
object and some customers will call executeAsynchronous
method with DataKey
object but in latter case, they will do future.get
in their code base.
If you see my executeSynchronous
method, I am doing future.get
after calling executeAsynchronous
method and if there is any TimeoutException
, then I am logging using PotoLogging
class which is specific in our company and that logs will go to some other service here which we use to look all our error logs on the dashboard. And it mainly depends how we are logging it with what names so that we can see those names in the dashboard.
Now the problem is customer within our company can also call executeAsynchronous
method but that means, they will do future.get
in their code base and that can also result in TimeoutException
in their code but I cannot force them to log the same way as I am doing it. So my question is - Is there any way that I can get callback if there is any TimeoutException
so that I can log it like this if anyone is calling executeAsynchronous
method of my library in their code base -
PotoLogging.logErrors(ex, DataErrorEnum.TIMEOUT_ON_CLIENT, dataKey);
I need to do this so that my library can log TimeoutException
in the tool we have in our company in the way we want it. Otherwsie, I need to tell each of the customer to do log it like this so that we can see it in our dashboard. How can I get the callback from asynchronous call and still leverage all the feature of being asynchronous?
What is the best way to do this?