Jersey async request handling with programmatical resource registration - java

We used
org.glassfish.jersey.server.model.ResourceMethod$Builder
to register the method and the handler.
ResourceMethod.Builder methodBuilder = resourceBuilder.addMethod(httpMethod);
methodBuilder.produces(restContext.getProduceContent()).handledBy(inflector);
methodBuilder.consumes(restContext.getConsumeContent()).handledBy(inflector);
The handler class implements the org.glassfish.jersey.process.Inflector<ContainerRequestContext, Response>
public class CommonMethodInflector implements Inflector<ContainerRequestContext, Response>
{
#Override
public Response apply(ContainerRequestContext request)
{
//sync bloc
//using resqest object we do processing in different maner
incRestFeRequestCounters(request.getMethod());
Response response = processIncoming(request);`enter code here`
}
}
Could you please help us in creating the async handler.
our requirement in short:
At runtime only we know the http method and other resources to register.
So, we can not use annotations for resource & httpMethod registration.
We need only programmatic resource registration.
In handler We need the request object so that we can access what method and what json body in it.
we need to make the async response as we are doing huge operation in the processing request.

The first thing you need to do is to suspend the resource method:
ResourceMethod.Builder methodBuilder = resourceBuilder.addMethod(httpMethod)
.suspend(AsyncResponse.NO_TIMEOUT, TimeUnit.Seconds);
Then you have few choices how to process the request in async mode:
"Arbitrary" handler class
builder.addMethod("GET")
// Suspend without time limit.
.suspended(AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS)
.handledBy(MyHandler.class, MyHandler.class.getMethod("handle", AsyncResponse.class));
and
public class MyHandler {
public void handle(final #Suspended AsyncResponse response) {
Executors.newSingleThreadExecutor().submit(new Runnable() {
#Override
public void run() {
// Simulate long-running operation.
try {
Thread.sleep(1000);
} catch (final InterruptedException ie) {
// NOOP
}
response.resume("Hello World!");
}
});
}
}
Inflector class
Resource.builder("helloworld")
.addMethod("GET")
// Suspend without time limit.
.suspended(AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS)
// Can be registered only as a class otherwise the
// #Context injection would be out of request scope.
.handledBy(MyAsyncInflector.class);
and
public class MyAsyncInflector implements Inflector<ContainerRequestContext, Response> {
#Context
private AsyncResponse response;
#Override
public Response apply(final ContainerRequestContext context) {
Executors.newSingleThreadExecutor().submit(new Runnable() {
#Override
public void run() {
// Simulate long-running operation.
try {
Thread.sleep(1000);
} catch (final InterruptedException ie) {
// NOOP
}
response.resume("Hello World!");
}
});
return null;
}
}
Annonymous Inflector
Resource.builder("helloworld")
.addMethod("GET")
// Suspend without time limit.
.suspended(AsyncResponse.NO_TIMEOUT, TimeUnit.SECONDS)
.handledBy(new Inflector<ContainerRequestContext, Response>() {
#Inject
private javax.inject.Provider<AsyncResponse> responseProvider;
#Override
public Response apply(final ContainerRequestContext context) {
// Make sure we're in request scope.
final AsyncResponse response = responseProvider.get();
Executors.newSingleThreadExecutor().submit(new Runnable() {
#Override
public void run() {
// Simulate long-running operation.
try {
Thread.sleep(1000);
} catch (final InterruptedException ie) {
// NOOP
}
response.resume("Hello World!");
}
});
return null;
}
});

Related

Android use OkHttpClient on main thread

In my main activity, i am opening a new activity like this:
someActivityResultLauncher.launch(myIntent);
In my secondary activity i am invoking a REST api with like this:
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), someRequest.toString());
Request request = new Request.Builder().url(myURL).post(body).build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(#NonNull Call call, #NonNull IOException e) {
System.out.println(e);
}
#Override
public void onResponse(#NonNull Call call, #NonNull Response response) throws IOException {
System.out.println(response.body().string());
}
});
After that i close the secondary activity returning an object with data i retrieved from the REST api to the main activity.
The problem is that the HTTP call executes on the seperate thread and the main thread is executed before the HTTP call, which means the secondary activity returns the object before it is filled with data.
I tried execute() in order to wait for the HTTP response before returning to the main activity, but this generates an android.os.NetworkOnMainThreadException.
How can i force it to wait for a response before returning to the main activity?
You can use java.util.concurrent.CountDownLatch:
CountDownLatch latch = new CountDownLatch(1);
OkHttpClient client = new OkHttpClient();
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), someRequest.toString());
Request request = new Request.Builder().url(myURL).post(body).build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(#NonNull Call call, #NonNull IOException e) {
System.out.println(e);
//Add line here
latch.countDown();
}
#Override
public void onResponse(#NonNull Call call, #NonNull Response response) throws IOException {
System.out.println(response.body().string());
//Add line here
latch.countDown();
}
});
try {
//Await method will stop main thread until calling Latch.countDown();
latch.await(10, //Write here timeout you want
TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Do your stuff with UI here

Complete CompletableFuture with another CompletableFuture result

I'm doing async http call such way
public CompletableFuture<String> doPost(String path, String json) {
CompletableFuture<String> result = new CompletableFuture<>();
Request request = new Request.Builder().url(this.address + path).post(RequestBody.create(json, JSON)).build();
httpClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e) {
result.completeExceptionally(new TerminationException());
}
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
result.complete(response.body().string());
}
});
}
But it is possible that response will have one of the codes that I'll need to retry and the code should be
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
if (!retries.contains(responce.code()) {
result.complete(response.body().string());
} else {
// Do retry here
}
}
In retry I want to call doPost recursively and use it's return value as a result of initial call.
So it returns some completable future, how complete initial CF with it's result in async way (without doint .get())?
Thanks.
You can use delegation, e.g.
public CompletableFuture<String> doPost(String path, String json) {
CompletableFuture<String> result = new CompletableFuture<>();
doPostImpl(this.address + path, json, result, 10);
return result;
}
private void doPostImpl(
String url, String json, CompletableFuture<String> result, int maxRetries) {
Request request = new Request.Builder()
.url(url).post(RequestBody.create(json, JSON)).build();
httpClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e) {
result.completeExceptionally(new TerminationException());
}
#Override
public void onResponse(
#NotNull Call call, #NotNull Response response) throws IOException {
if(maxRetries <= 0 || !retries.contains(response.code())) {
result.complete(response.body().string());
} else {
doPostImpl(url, json, result, maxRetries - 1);
}
}
});
}
The front-end method delegates to a method receiving the target future. When retrying, the implementation method is invoked again with the same future. Therefore, there is no need to transfer results from one future to another.
Taking the question literally, you can transfer the result of a future to another using
future2.whenComplete((value,throwable) -> {
if(throwable != null) future1.completeExceptionally(throwable);
else future1.complete(value);
});
but this could create a dependency chain as long as the number of retries. As shown above, there is no need for that.

Spring gRPC - How intercept an exception with body info

I have a project using spring gRPC. I created a interceptor, when services response succsesfully, the interceptor work fine, but when the service catch an exception, the interceptor not work.
The service:
#GrpcService
public class ClientAppImpl extends ClientAppGrpc.ClientAppImplBase {
#Autowired MyService myService;
#Override
public void myMethod(Request request, StreamObserver<CreateDigitalCertificateResponse> responseObserver) {
try {
Response response = myService.doStuff(request);
responseObserver.onNext(response);
responseObserver.onCompleted();
} catch(Exception ex) {
responseObserver.onError(Status.INTERNAL.withDescription(exception.getMessage()).withCause(exception).asRuntimeException());
}
}
}
The interceptor:
#GrpcGlobalServerInterceptor
public class GrpcServerInterceptor implements ServerInterceptor {
public static final String REQUEST_ID_HEADER = "request-id-bin";
public static final Metadata.Key<byte[]> REQUEST_ID_METADATA_KEY = Metadata.Key.of(REQUEST_ID_HEADER, Metadata.BINARY_BYTE_MARSHALLER);
private static final Map<String, GrpcCall> CALLS = new HashMap<>();
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
ForwardingServerCall<ReqT, RespT> responseServerCall = new ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT>(call) {
#Override
public void sendMessage(RespT response) {
String callId = new String(headers.get(REQUEST_ID_METADATA_KEY));
GrpcCall grpcCall = CALLS.get(callId);
grpcCall.setResponse(response);
GrpcCallProcessor grpcCallProcessor = new GrpcCallProcessor(grpcCall);
grpcCallProcessor.processCall();
super.sendMessage(response);
}
};
ServerCall.Listener<ReqT> listenerWithContext = Contexts.interceptCall(Context.current(), responseServerCall, headers, next);
return new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(listenerWithContext) {
#Override
public void onMessage(ReqT request) {
String callId = UUID.randomUUID().toString();
headers.put(REQUEST_ID_METADATA_KEY, callId.getBytes());
GrpcCall grpcCall = new GrpcCall();
grpcCall.setCall(call);
grpcCall.setHeaders(headers);
grpcCall.setRequest(request);
CALLS.put(callId, grpcCall);
super.onMessage(request);
}
};
}
}
When the service not catch an exception, the interceptor work fine, and method sendMessage is called. But when the service catch an exception, the method sendMessage is not called.
Is there any way for intercept an exception, and get de request body in the interceptor?
Thanks!
If myService.doStuff() throws an exception, there is not any message being sent. sendMessage() won't be called, but close(Status, Metadata) still will be.
The current usage of headers is broken. Metadata is not thread-safe and you do not know what the current "owner" of the object is doing with it. headers.get(REQUEST_ID_METADATA_KEY) should be performed within interceptCall() directly, before returning.
It isn't entirely clear what the purpose of onMessage() is, but it looks like maybe you should make a copy of the metadata (new Metadata().merge(headers) within interceptCall().

Making multiple asynchronous HTTP 2.0 requests with okhttp3

I am trying to use okhttp (3.4.1) to implement a HTTP 2.0 based client. Part of my requirement is to implement multiple asynchronous HTTP requests to different URLs with a callback to handle the response at a later time.
However, with my current implementation, I see that I cannot get all my asynchronous requests to use the same TCP connection unless I make a blocking HTTP request from my main thread at the beginning.
I understand that the enque() method used for asynchronous calls engages the dispatcher which seems to spawn a new thread for each of the requests.
Here is my code snippet:
OkHttpClient client = new OkHttpClient();
My async Get Request method looks as follows:
public void AsyncGet(String url) throws Exception {
Request request = new Request.Builder().url(url).build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
new Thread() {
#Override
public void run() {
/* Some code */
}
}.start();
}
});
}
My synchronous Get Request is as follows:
public Response SyncGet(String url) throws Exception {
Request request = new Request.Builder().url(url).build();
Call call = client.newCall(request);
Response response = call.execute();
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
return response;
}
Making a call sequence like the following triggers 2 TCP connections to the server.
AsyncGet(Url);
AsyncGet(Url2);
However, a call sequence like the following makes use of the same TCP connection.
SyncGet(Url);
AsyncGet(Url);
AsyncGet(Url2);
I have not debugged this but, it looks like OkHttp forces us to make a blocking HTTP request on the main thread first to possibly obtain the TCP connection context and then share that with other threads? Or, am I missing something?
You can call async to and set sector to each call later on based on the sector you can distinguish the response of call. Try this code; I hope it will help you!
Create a separate class for api call:
public class RestApi {
protected RestApiResponse serverResponse;
protected Context c;
public RestApi(RestApiResponse serverResponse, Context c) {
this.serverResponse = serverResponse;
this.c = c;
}
public void postDataWithoutAuth(String url,String sector) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
Log.e("response", call.request().body().toString());
serverResponse.onErrorLoaded(call.request().body().toString(), sector);
}
#Override
public void onResponse(Call call, Response response) throws IOException {
serverResponse.onResponseLoaded(response, sector);
}
});
}
}
Then create an interface for callback
public interface RestApiResponse {
public void onResponseLoaded(Response response, String sector);
public void onErrorLoaded(String response, String sector);
}
and access it from your Activity like this
RestApi apicall = new RestApi(this, getBaseContext());
apicall.postDataWithoutAuthUrlEncoded("ur url","your sector");

Return value from inner class in a method

I am building a Login system for an Android app. I am using OkHttp to connect to my server and get a JSON response.
I have defined a class with the login return data (right now just a true/false response based on whether the user exists in the database), and then written the code to connect to the server, as shown below:
class UserLogin {
boolean status;
public void setStatus(boolean status) {
this.status = status;
}
public boolean getStatus() {
return status;
}
}
public class ClientServerInterface {
OkHttpClient client = new OkHttpClient();
boolean login(Request request) {
final Gson gson = new Gson();
client.newCall(request).enqueue(new Callback() {
UserLogin login;
#Override
public void onFailure(Call call, IOException e) {
}
#Override
public void onResponse(Call call, Response response) throws IOException {
login = gson.fromJson(response.body().charStream(), UserLogin.class);
login.setStatus(login.status);
}
});
// need to return the boolean response (status) here
}
}
The code which passes the Request variable to the login method works perfectly. I want login to return a boolean response so that I can pass that to other methods in other classes.
However, because the UserLogin object is defined in the callback I can't access it in the parent method. I have made a getStatus method but not sure how to use it properly to get the status in the main login method.
The code which passes the Request variable to the login method works
perfectly. I want login to return a boolean response so that I can
pass that to other methods in other classes.
you can't. enqueue executes the code in Async way. You don't know when the callback is invoked. What you could do is to add the Callback as parameter to your login method. E.g.
boolean login(Request request, final Callback callback) {
and either pass it to enqueue,
client.newCall(request).enqueue(callback);
or call the callback manually. E.g.
#Override
public void onResponse(Call call, Response response) throws IOException {
if (callback != null) {
callback.onResponse(call, response);
}
}
in both cases the caller of login will receive the callback on the provided object and, accordingly to the content it receives, can decide wha actions undertake
You can do this using a SynchronousQueue:
final SynchronousQueue<Boolean> queue = new SynchronousQueue<>();
client.newCall(request).enqueue(new Callback() {
UserLogin login;
#Override
public void onFailure(Call call, IOException e) {
queue.put(false);
}
#Override
public void onResponse(Call call, Response response) throws IOException {
queue.put(true);
}
});
return queue.take();
Add loginStatus variable to class like below and one more to indicate login operation completion.
public class ClientServerInterface {
OkHttpClient client = new OkHttpClient();
private boolean loginStatus = false;
private boolean isLoginOperationDone = false;
boolean login(Request request) {
final Gson gson = new Gson();
client.newCall(request).enqueue(new Callback() {
UserLogin login;
#Override
public void onFailure(Call call, IOException e) {
}
#Override
public void onResponse(Call call, Response response) throws IOException {
login = gson.fromJson(response.body().charStream(), UserLogin.class);
loginStatus = login.setStatus(login.status);
isLoginOperationDone = true;
}
});
// need to return the boolean response (status) here
while( !isLoginOperationDone )
{
//not to do anything.
}
return loginStatus;
}
}
Note that this might be a little hacky but will do solve your problem.
The way to go is with AsyncTask. Override the doInBackground method to perform the http requests and get the result by overriding the onPostExecute method.
Read more here: http://developer.android.com/reference/android/os/AsyncTask.html
An even better way to go is to run a background Service for all your API calls.

Categories

Resources