Value is not updating server side events spring boot - java

I'm new to spring boot and I'm trying to create a streaming API. But when I change the content of value it doesn't get noticed by the Server Side Events emitter.
#RestController
public class SseController {
ExecutorService executor = Executors.newSingleThreadExecutor();
RestTemplate restTemplate = new RestTemplate();
String result;
//This method only returns a single value. But I want it to return value after every 5 second.
#GetMapping("/emitter")
public SseEmitter eventEmitter() {
SseEmitter emitter = new SseEmitter();
executor.execute(() -> {
try {
//I'm Sending Value To the client Using this method
emitter.send(result);
} catch (Exception e) {
emitter.completeWithError(e);
} finally {
emitter.complete();
}
});
executor.shutdown();
return emitter;
}
//I'm updating value here.
#Scheduled(fixedRate = 5000)
private void fetchData() {
result = restTemplate.getForObject("https://api.wazirx.com/api/v2/tickers", String.class);
}
}
I have tried to use thread.sleep() method but after some time it gives me an error async time out.

Related

How to make parallel calls to the same service using spring Flux

I am working on spring reactive and need to call multiple calls sequentially to other REST API using webclient.
The issue is I am able to call multiple calls to other Rest API but response am not able to read without subscribe or block.
I can't use subscribe or block due to non reactive programming. Is there any way, i can merge while reading the response and send it as flux.
Below is the piece of code where I am stuck.
public Mono<DownloadDataLog> getDownload(Token dto, Mono<DataLogRequest> request) {
Mono<GraphQlCustomerResponse> profileResponse = customerProfileHandler.getMyUsageHomeMethods(dto, null);
DownloadDataLog responseObj = new DownloadDataLog();
ArrayList<Mono<List<dataUsageLogs>>> al = new ArrayList<>();
return Mono.zip(profileResponse, request).flatMap(tuple2 -> {
Flux<List<Mono<DataLogGqlRequest>>> userequest = prepareUserRequest(getListOfMdns(tuple2.getT1()),
tuple2.getT2());
Flux.from(userequest).flatMap(req -> {
for (Mono<DataLogGqlRequest> logReq : req) {
al.add(service.execute(logReq, dto));
}
responseObj.setAl(al);
return Mono.empty();
}).subscribe();
return Mono.just(responseObj);
});
}
private Mono<DataLogGqlRequest> prepareInnerRequest(Mono<DataLogGqlRequest> itemRequest, int v1,int v2){
return itemRequest.flatMap(req -> {
DataLogGqlRequest userRequest = new DataLogGqlRequest();
userRequest.setBillDate(req.getBillDate());
userRequest.setMdnNumber(req.getMdnNumber());
userRequest.setCposition(v1+"");
userRequest.setPposition(v2+"");
return Mono.just(userRequest);
});
}

Reactive Spring Boot API wrapping Elasticsearch's async bulk indexing

I am developing prototype for a new project. The idea is to provide a Reactive Spring Boot microservice to bulk index documents in Elasticsearch. Elasticsearch provides a High Level Rest Client which provides an Async method to bulk process indexing requests. Async delivers callbacks using listeners are mentioned here. The callbacks receive index responses (per requests) in batches. I am trying to send this response back to the client as Flux. I have come up with something based on this blog post.
Controller
#RestController
public class AppController {
#SuppressWarnings("unchecked")
#RequestMapping(value = "/test3", method = RequestMethod.GET)
public Flux<String> index3() {
ElasticAdapter es = new ElasticAdapter();
JSONObject json = new JSONObject();
json.put("TestDoc", "Stack123");
Flux<String> fluxResponse = es.bulkIndex(json);
return fluxResponse;
}
ElasticAdapter
#Component
class ElasticAdapter {
String indexName = "test2";
private final RestHighLevelClient client;
private final ObjectMapper mapper;
private int processed = 1;
Flux<String> bulkIndex(JSONObject doc) {
return bulkIndexDoc(doc)
.doOnError(e -> System.out.print("Unable to index {}" + doc+ e));
}
private Flux<String> bulkIndexDoc(JSONObject doc) {
return Flux.create(sink -> {
try {
doBulkIndex(doc, bulkListenerToSink(sink));
} catch (JsonProcessingException e) {
sink.error(e);
}
});
}
private void doBulkIndex(JSONObject doc, BulkProcessor.Listener listener) throws JsonProcessingException {
System.out.println("Going to submit index request");
BiConsumer<BulkRequest, ActionListener<BulkResponse>> bulkConsumer =
(request, bulkListener) ->
client.bulkAsync(request, RequestOptions.DEFAULT, bulkListener);
BulkProcessor.Builder builder =
BulkProcessor.builder(bulkConsumer, listener);
builder.setBulkActions(10);
BulkProcessor bulkProcessor = builder.build();
// Submitting 5,000 index requests ( repeating same JSON)
for (int i = 0; i < 5000; i++) {
IndexRequest indexRequest = new IndexRequest(indexName, "person", i+1+"");
String json = doc.toJSONString();
indexRequest.source(json, XContentType.JSON);
bulkProcessor.add(indexRequest);
}
System.out.println("Submitted all docs
}
private BulkProcessor.Listener bulkListenerToSink(FluxSink<String> sink) {
return new BulkProcessor.Listener() {
#Override
public void beforeBulk(long executionId, BulkRequest request) {
}
#SuppressWarnings("unchecked")
#Override
public void afterBulk(long executionId, BulkRequest request, BulkResponse response) {
for (BulkItemResponse bulkItemResponse : response) {
JSONObject json = new JSONObject();
json.put("id", bulkItemResponse.getResponse().getId());
json.put("status", bulkItemResponse.getResponse().getResult
sink.next(json.toJSONString());
processed++;
}
if(processed >= 5000) {
sink.complete();
}
}
#Override
public void afterBulk(long executionId, BulkRequest request, Throwable failure) {
failure.printStackTrace();
sink.error(failure);
}
};
}
public ElasticAdapter() {
// Logic to initialize Elasticsearch Rest Client
}
}
I used FluxSink to create the Flux of Responses to send back to the Client. At this point, I have no idea whether this correct or not.
My expectation is that the calling client should receive the responses in batches of 10 ( because bulk processor processess it in batches of 10 - builder.setBulkActions(10); ). I tried to consume the endpoint using Spring Webflix Client. But unable to work it out. This is what I tried
WebClient
public class FluxClient {
public static void main(String[] args) {
WebClient client = WebClient.create("http://localhost:8080");
Flux<String> responseFlux = client.get()
.uri("/test3")
.retrieve()
.bodyToFlux(String.class);
responseFlux.subscribe(System.out::println);
}
}
Nothing is printing on console as I expected. I tried to use System.out.println(responseFlux.blockFirst());. It prints all the responses as a single batch at the end and not in batches at .
If my approach is correct, what is the correct way to consume it? For the solution in my mind, this client will reside is another Webapp.
Notes: My understanding of Reactor API is limited. The version of elasticsearch used is 6.8.
So made the following changes to your code.
In ElasticAdapter,
public Flux<Object> bulkIndex(JSONObject doc) {
return bulkIndexDoc(doc)
.subscribeOn(Schedulers.elastic(), true)
.doOnError(e -> System.out.print("Unable to index {}" + doc+ e));
}
Invoked subscribeOn(Scheduler, requestOnSeparateThread) on the Flux, Got to know about it from, https://github.com/spring-projects/spring-framework/issues/21507
In FluxClient,
Flux<String> responseFlux = client.get()
.uri("/test3")
.headers(httpHeaders -> {
httpHeaders.set("Accept", "text/event-stream");
})
.retrieve()
.bodyToFlux(String.class);
responseFlux.delayElements(Duration.ofSeconds(1)).subscribe(System.out::println);
Added "Accept" header as "text/event-stream" and delayed Flux elements.
With the above changes, was able to get the response in real time from the server.

Spring boot return a response from callback in services

I am trying to develop sample Rest API application using Spring boot to test my java client library (Which is a conversion of a JS Client). Java client does some async tasks and returns response as callback.
Here is example of how i am calling it from my sample application's service.
#Async
public void initializeUser(InitRequest initRequest) {
String BASE_URL = "http://sample_url";
client.initUser(BASE_URL, initRequest, new ResponseCallback<InitRequest>() {
#Override
public void onSuccess(#NonNull InitRequest arg0) {
// Return arg0 to controller
System.out.println("User Initialized: " + arg0.getId());
}
#Override
public void onError(#NonNull ResponseBody error) {
// Return error to controller
System.out.println("User Initialize failed");
}
#Override
public void validationError(#NonNull String arg0) {
// return validationError to controller
// TODO Auto-generated method stub
}
});
}
Here initUser is method implemented in my client. When i call this method from my controller. controller returns before actual request is finished and i am not able to see any response in postman.
Here is a controller method.
#RequestMapping(method=RequestMethod.POST, value = "/init" )
public void initUser( #RequestBody InitRequest initRequest) {
experimentServices.initializeUser(initRequest);
// wait for request to finish and send response to user
}
Like in javascript i can use promises or async/await to wait till the services gives response before returning to user.
I wanted to know.....
1) How to achieve something similar in Java? (Services returns some data to controller which then goes as a response to end user).
2) Java methods needs specific return response while services might gives Error, ValidationError or Successfull response object. Is there a way to properly handle all this in a single function?
Any links or documentation will help.
Thanks
In your code , the method that you are calling asynchronously is a void method, meaning it won't return anything.In Spring, annotating a method of a bean with #Async will make it execute in a separate thread i.e. the caller will not wait for the completion of the called method.
So , if you want the controller to wait till the execution is completed, If you want to wait for the result I suggest you use Future. You could take the approach where you wrap the actual return in the Future instance.I haven't tried compiling this code , but something like below :
#Async
public Future<T> initializeUser(InitRequest initRequest) {
String BASE_URL = "http://sample_url";
client.initUser(BASE_URL, initRequest, new ResponseCallback<InitRequest>() {
#Override
public void onSuccess(#NonNull InitRequest arg0) {
return new AsyncResult<InitRequest>(arg0);
System.out.println("User Initialized: " + arg0.getId());
}
#Override
public void onError(#NonNull ResponseBody error) {
return new AsyncResult<ResponseBody>(error);
System.out.println("User Initialize failed");
}
#Override
public void validationError(#NonNull String arg0) {
return new AsyncResult<String>(arg0);
// TODO Auto-generated method stub
}
});
}
and in the rest controller to receive this :
#RequestMapping(method=RequestMethod.POST, value = "/init" )
public InitRequest initUser( #RequestBody InitRequest initRequest) {
Future<T> futureResult = experimentServices.initializeUser(initRequest);
// wait for request to finish and send response to user
if (futureResult.isDone()) {
return futureResult.get();
}
}
Best way to do is to avoid busy waiting on server, so use DeferredResult:
private ExecutorService executor = ....
#RequestMapping(method=RequestMethod.POST, value = "/init" )
public DeferredResult<ResponseEntity<InitResponse>> initUser(#RequestBody InitRequest initRequest) {
final DeferredResult<ResponseEntity<InitResponse>> output = new DeferredResult<>(5000L, 5000L);
executor.submit(() -> {
InitResponse response = processAndcreateRespnse(...);
output.setResult(new ResponseEntity<>(response, HttpStatus.OK));
});
return output;
}

How to avoid waiting on main thread when adding headers to requests using retrofit?

I use this to config my retrofit:
RestAdapter restAdapter = new RestAdapter.Builder()
//add headers to requests
.setRequestInterceptor(getAuthenticatedRequestInterceptor())
.setEndpoint(BASE_URL)
.setConverter(new GsonConverter(getGson()))
.build();
and The getAuthenticatedRequestInterceptor() method adds headers to request:
public AccountRequestInterceptor getAuthenticatedRequestInterceptor() {
AccountRequestInterceptor interceptor = new AccountRequestInterceptor();
Map<String, String> headers = new HashMap<>();
String accessToken = null;
try {
accessToken = TokenProvider.getInstance(mContext).getToken();
} catch (InterruptedException e) {
}
headers.put(HeadersContract.HEADER_AUTHONRIZATION, O_AUTH_AUTHENTICATION + accessToken);
interceptor.setHeader(headers);
return interceptor;
}
getToken() method is:
private synchronized string getToken() throws InterruptedException {
if (!isRefreshing()) {
//This is very important to call notify() on the same object that we call wait();
final TokenProvider myInstance = this;
setRefreshing(true);
MyApplication.getRestClient().getAccountService().getRefreshedToken(mLoginData.getRefreshToken())
.subscribe(new Observer<LoginResponse>() {
#Override
public void onCompleted() {
synchronized (myInstance) {
setRefreshing(false);
myInstance.notifyAll();
}
}
#Override
public void onError(Throwable e) {
synchronized (myInstance) {
setRefreshing(false);
myInstance.notifyAll();
}
}
#Override
public void onNext(LoginResponse loginResponse) {
synchronized (myInstance) {
mLoginData = loginResponse;
mAccountProvider.saveLoginData(loginResponse);
myInstance.notifyAll();
}
}
});
}
this.wait();
return mLoginData.getToken();
}
The TokenProvider.getInstance(mContext).getToken() has a wait() on main thread to get the response from an async method and i know that is a bad thing to do but i need this here to wait for the response to take the token from it and then return the token.how can i do this in a separate thread to avoid waiting on the main thread?
Note:
1 - that this is called before any request with retrofit.
2 - I read this and i know i can refresh token after a fail request, but for business reasons i want to avoid having an invalid token.
3 - I call MyApplication.getRestClient().getAccountService().login(loginRequest,callback...‌​) in my Activity and before adding token everything happened in background thread. so I want to use my token and do not block the main thread.
UPDATE: I added the following Interceptor to my new OkHttp:
public class RequestTokenInterceptor implements Interceptor {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
Request newRequest;
try {
Log.d("addHeader", "Before");
String token = TokenProvider.getInstance(mContext).getToken();
if (token != null) {
newRequest = request.newBuilder()
.addHeader("Bearer", token)
.build();
} else {
// I want to cancel the request or raise an exception to catch it in onError method
// of retrofit callback.
}
} catch (InterruptedException e) {
Log.d("addHeader", "Error");
e.printStackTrace();
return chain.proceed(request);
}
Log.d("addHeader", "after");
return chain.proceed(newRequest);
}
}
Now how can i cancel the request or raise an exception to catch it in onError method of retrofit callback, if token is null?
It's a little bit strange issue but let me try to help you. :)
As you know you can refresh token after a failed request with retrofit using response interceptor.
Let's try to use interceptor before request.
public class RequestTokenInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// Here where we'll try to refresh token.
// with an retrofit call
// After we succeed we'll proceed our request
Response response = chain.proceed(request);
return response;
}
}
And when you're creating your api create a new HttpClient:
OkHttpClient client = new OkHttpClient();
client.interceptors().add(new RequestTokenInterceptor());
And add your http client to your adapter like below:
.setClient(new OkClient(client))
If this works, before every request you'll try to refresh token first and then will proceed your api request. So in ui there'll be no difference with your normal api calls.
Edit:
I'm editing my answer too. If you want to return an error in else case if token null, in else case you can create your custom response:
private Response(Builder builder) {
this.request = builder.request;
this.protocol = builder.protocol;
this.code = builder.code;
this.message = builder.message;
this.handshake = builder.handshake;
this.headers = builder.headers.build();
this.body = builder.body;
this.networkResponse = builder.networkResponse;
this.cacheResponse = builder.cacheResponse;
this.priorResponse = builder.priorResponse;
}
or simply you can return a null response. if you build your custom response and set your code not to 200 such as 401 or 400+ you'll receive that response in Retrofit's callbacks failure method. Than you can do what ever you want.
If you return null you'll get a RuntimeException i think and still you can catch response in your callback's failure method.
After you create your own response in else you can create your custom callback and catch your null response and transform your custom error how ever you want like below:
public abstract class DefaultRequestCallback<T> implements Callback<T> {
public abstract void failure(YourCustomException ex);
public abstract void success(T responseBean);
#Override
public void success(T baseResponseBean, Response response) {
if (response == null) {
// Here we catch null response and transform it to our custom Exception
failure(new YourCustomException());
}
} else {
success(baseResponseBean);
}
}
#Override
public void failure(RetrofitError error) {
// Here's your failure method.
// Also you can transform default retrofit errors to your customerrors
YourCustomException ex = new YourCustomException();
failure(ex);
}
}
This can help you i think.
Edit 2:
You can build a new Response like below. There's a builder pattern in Retrofit's Response class. You can check it from there.
Response response = new Response.Builder().setCode(401).setMessage("Error Message").build();
You could make all long actions in AsyncTask doInBackground method, while in onPre- and onPostExecute you could show/hide some progress bars when user is waiting
Ok, I think if you are calling your getAuthenticatedRequestInterceptor() on the main thread and which in turns call getInstance(),in which i feel you would be creating an object of Type TokenProvider hence when you create this object in the main thread your object.wait() runs on main thread hence to run this on a background thread probably modify your getAuthenticatedRequestInterceptor() method to execute the following lines in a new thread.
try {
accessToken = TokenProvider.getInstance(mContext).getToken();
} catch (InterruptedException e) {
}
headers.put(HeadersContract.HEADER_AUTHONRIZATION, O_AUTH_AUTHENTICATION + accessToken);
interceptor.setHeader(headers);
return interceptor;
but this will have problems for notifying your RestAdapter as the main thread will proceed executing, hence i would suggest
you call getAuthenticatedRequestInterceptor() method first in a new thread and then notify your main thread to build your RestAdapter.This will free your main thread but with the strategy you are employing you will have to wait until you receive the token to make any calls.

SimpleClientHttpRequestFactory vs HttpComponentsClientHttpRequestFactory for Http Request timeout with RestTemplate?

I am working on a project in which I need to make a HTTP URL call to my server which is running Restful Service which returns back the response as a JSON String.
Below is my main code which is using the future and callables:
public class TimeoutThreadExample {
private ExecutorService executor = Executors.newFixedThreadPool(10);
private RestTemplate restTemplate = new RestTemplate();
public String getData() {
Future<String> future = executor.submit(new Task(restTemplate));
String response = null;
try {
response = future.get(500, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return response;
}
}
Below is my Task class which implements the Callable interface and uses the RestTemplate:
class Task implements Callable<String> {
private RestTemplate restTemplate;
public Task(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public String call() throws Exception {
String url = "some_url";
String response = restTemplate.getForObject(url, String.class);
return response;
}
}
Problem Statement:
As you can see above, I am using default way of executing the URL using RestTemplate which doesn't use any Http Request timeout so that means internally it is using -1 as the read and connection timeout.
Now what I am looking to do is, I want to set up Http Request timeout using RestTemplate in my above code efficiently. And I am not sure which class I need to use for that, I can see HttpComponentsClientHttpRequestFactory and SimpleClientHttpRequestFactory so not sure which one I need to use?
Any simple example basis on my above code will help me understand better on how to set the Http Request timeout using RestTemplate.
And also does my Http Request timeout value should be less than future timeout value?
HttpComponentsClientHttpRequestFactory vs SimpleClientHttpRequestFactory. Which one to use?
Does my Http Request timeout value should be less than future timeout value?
By default RestTemplate uses SimpleClientHttpRequestFactory which depends on default configuration of HttpURLConnection.
You can configure them by using below attributes:
-Dsun.net.client.defaultConnectTimeout=TimeoutInMiliSec
-Dsun.net.client.defaultReadTimeout=TimeoutInMiliSec
If you want to use HttpComponentsClientHttpRequestFactory - it has a connection pooling configuration which SimpleClientHttpRequestFactory does not have.
A sample code for using HttpComponentsClientHttpRequestFactory:
public class TimeoutThreadExample {
private ExecutorService executor = Executors.newFixedThreadPool(10);
private static final RestTemplate restTemplate = createRestTemplate();
private static RestTemplate createRestTemplate(){
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setReadTimeout(READ_TIME_OUT);
requestFactory.setConnectTimeout(CONNECTION_TIME_OUT);
return new RestTemplate(requestFactory);
}
public String getData() {
Future<String> future = executor.submit(new Task(restTemplate));
String response = null;
try {
response = future.get(500, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return response;
}
}
SimpleClientHttpRequestFactory uses the standard JDK's HTTP library, and hence does not support methods like HttpMethod.PATCH. So it's better to use HttpComponentsClientHttpRequestFactory now than change it later when you have to.

Categories

Resources