I am using a Library called milo, it is programmed with the Java 8 attribute like the use of CompletableFuture.
And now I want to get data from REST using OkHttp.But I don't know how to implement this with CompletableFuture.
Below is my code
#Override
public void run(OpcUaClient client, CompletableFuture<OpcUaClient> future) throws Exception {
// synchronous connect
client.connect().get();
List<NodeId> nodeIds = ImmutableList.of(new NodeId(2, "HelloWorld/ScalarTypes/Int32"));
OkHttpClient okHttpClient = new OkHttpClient();
Request.Builder requestBuilder = new Request.Builder().url("http://localhost:8080/greeting");
for (int i = 0; i < 10; i++) {
Request request = requestBuilder.build();
Call call= okHttpClient.newCall(request);
final GreetingModel[] greetingModel = {new GreetingModel()};
call.enqueue(new Callback() {
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e) {
logger.error("Writing is wrong");
}
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
String json = response.body().string();
logger.info("Connecting is good");
greetingModel[0] = JSON.parseObject(json, GreetingModel.class);
}
});
Variant v = new Variant(greetingModel[0].getId());
// don't write status or timestamps
DataValue dv = new DataValue(v, null, null);
// write asynchronously....
CompletableFuture<List<StatusCode>> f =
client.writeValues(nodeIds, ImmutableList.of(dv));
// ...but block for the results so we write in order
List<StatusCode> statusCodes = f.get();
StatusCode status = statusCodes.get(0);
if (status.isGood()) {
logger.info("Wrote '{}' to nodeId={}", v, nodeIds.get(0));
}
}
future.complete(client);
}
}
And the figure shows the result.The writing operation is ahead the data acquisition from okhttp.
result
Related
I have the following method that runs some web services commands in Android.
Response.Listener Success = new Response.Listener<JSONObject >() {#Override
public void onResponse(JSONObject response) {
JSONObject S = response;
// do something with the response
}};
Response.ErrorListener Fail = new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
if (error.networkResponse.statusCode == 400) {
Success.notify(); // <<<<< According to some technical custom logic, this should be considered as a successful REST command. Hence run Success Response Listener
}
else {
// operation failed, do something
}
}
};
JSONObject J = new JSONObject();
J.put("param", some parameters);
RequestQueue queue = Volley.newRequestQueue(Act);
JsonObjectRequest JsonRequestObject = new JsonObjectRequest(Method, URL, J, Success, Fail);
queue.add(JsonRequestObject);
According to some custom technical logic, the requests that get 400 as a response should be considered as successful and hence run the success calback.
I tried this code but it crashes at the line :
Success.notify(); // <<<<<
Does anyone know how can I execute the Success call back manually please ?
Thanks
Cheers,
Instead of notify() you should call onResponse() like Success.onResponse() with null argument or with some JSONObject mapped from error.networkResponse.
More generalized way - create custom Response Listener (to listen to the success/error events in one place):
public abstract class ResponseListener<T> implements Response.Listener<T>,
Response.ErrorListener {
#Override
public final void onErrorResponse(VolleyError error) {
if (error.networkResponse.statusCode == 400) {
// redirect to onResponse()
onResponse(null /* or some response T object mapped from [error.networkResponse] */);
} else {
onError(error);
}
}
public abstract void onError(VolleyError error);
}
...
final ResponseListener<JSONObject> responseListener =
new ResponseListener<JSONObject>() {
#Override
public void onResponse(#Nullable JSONObject response) {
// do something with the response
}
#Override
public void onError(VolleyError error) {
// operation failed, do something
}
};
//
final JsonObjectRequest JsonRequestObject =
new JsonObjectRequest(Method, URL, J, responseListener, responseListener);
//
I calling to the api with the basic retrofit Call object:
public interface dataApi {
#GET("animal/cats")
Call<AllAnimals> getAllData(
#Query("api_key") String apiKey
);
}
And I can get the response inside my view model like this:
call.enqueue(new Callback<AllAnimals>() {
#Override
public void onResponse(Call<AllAnimals> call, Response<AllAnimals> response) {
animals.setValue(response.body());
}
#Override
public void onFailure(Call<AllAnimals> call, Throwable t) {
Log.i(TAG, "onFailure: " + t);
}
});
Nothing speical here.
I've several problem with this approach
FIRST - if I give the wrong api key for example, the response should give me a response with the code of the problem, instead I just get null body.
SECOND I am planning to have more api calls, and it's a huge code duplication to handle errors every call I wrote.
How can I implement custom error handling for this situation, that will be apply to other calls too?
I think you can use okhttp interceptor and define yourself ResponseBody converter to fix your problem.
First,intercept you interested request and response;
Second,check the response,if response is failed then modify the response body to empty。
define a simple interceptor
Interceptor interceptor = new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String url = request.url().toString();
System.out.println(request.url());
okhttp3.Response response = chain.proceed(request);
if (!response.isSuccessful() && url.contains("animal/cats")) {
// request failed begin to modify response body
response = response.newBuilder()
.body(ResponseBody.create(MediaType.parse("application/json"), new byte[] {}))
.build();
}
return response;
}
};
define self ResponseBody converter
most code from com.squareup.retrofit2:converter-jackson we just add two lines:
final class JacksonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final ObjectReader adapter;
JacksonResponseBodyConverter(ObjectReader adapter) {
this.adapter = adapter;
}
#Override public T convert(ResponseBody value) throws IOException {
try {
if (value.contentLength() == 0) {
return null;
}
return adapter.readValue(value.charStream());
} finally {
value.close();
}
}
}
the below code is added:
if (value.contentLength() == 0) {
return null;
}
Can any one please help me to write a unit test case for this method returning RxJava Future object , I am able to write and mock for a method returning Single.
public Future<JsonObject> fetchVendorDetailsVendorIdAsFuture(String serviceURI, Map<String, String> headerMap) {
if(vbConnectorCircuitBreaker == null){
vbConnectorCircuitBreaker= CircuitBreakers.getVbConnectorCircuitBreaker();
}
return vbConnectorCircuitBreaker.execute(future -> {
// get ok http client
OkHttpClient client = okHTTPClientHelper.getOkHTTPClient();
if(client != null){
try{
MediaType mediaType = MediaType.parse("application/json");
Headers headers = Headers.of(headerMap);
Request request = new Request.Builder()
.url(serviceURI)
.get()
.headers(headers)
.build();
Call call = client.newCall(request);
call.enqueue(new Callback() {
public void onResponse(Call call, Response response)
throws IOException {
String jsonData = response.body().string();
JsonObject jsonObject = new JsonObject(jsonData);
future.complete(jsonObject);
}
public void onFailure(Call call, IOException e) {
future.complete(null);
}
});
} catch(Exception exception) {
future.complete(null);
}
} else {
future.complete(null);
}
});
}
You can try using okhttp's MockWebServer.
That way, your Call can emit a real http request and you will be able to handle the server's response.
You can create the mocked server's response yourself using mockWebServer.enqueue(new MockResponse() ... )
There are a lot of different ways to write tests for this kind of problem and here is my suggestion:
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
// other imports omitted
#ExtendWith(VertxExtension.class)
#Slf4j
public class VendorDetailsTest {
private VendorDetailsVerticle sut;
private MockWebServer mockWebServer;
#BeforeEach
public void setUp() {
sut = new VendorDetailsVerticle();
mockWebServer = new MockWebServer();
}
#Test
public void testExecuteService(final Vertx vertx, final VertxTestContext testContext)
throws InterruptedException {
// given -----
final JsonObject serverResponsePayload = new JsonObject().put("futureCompleted", true);
mockWebServer.enqueue(new MockResponse()
.setBody(serverResponsePayload.encode())
.setResponseCode(200)
.setHeader("content-type", "application/json"));
// when -----
final Future<JsonObject> jsonObjectFuture =
sut.fetchVendorDetailsVendorIdAsFuture(mockWebServer.url("/").toString(), new HashMap<>());
// then -----
final RecordedRequest recordedRequest = mockWebServer.takeRequest();
assertEquals("GET", recordedRequest.getMethod());
assertEquals(1, mockWebServer.getRequestCount());
testContext.assertComplete(jsonObjectFuture)
.map(val -> {
assertEquals("{'futureCompleted': true}", val.encode());
testContext.completeNow();
return val;
})
.onComplete(onComplete -> {
assertTrue(onComplete.succeeded());
log.info("done");
})
.onFailure(onError -> Assertions.fail());
}
}
This test will of course need a little bit of customization to run in your project, but I hope it will give a picture on how to approach testing RxJava's futures.
I need to send an array of Strings using a Retrofit call. To do that I decided to create an object like this one:
public class SendEmailsList {
ArrayList<String> emails;
public SendEmailsList(ArrayList<String> emails) {
this.emails = emails;
}
}
And my JSON String must be like this:
{
"emails": ["email#server.com","email1#server.com","email2#server.com"]
}
This is the POST method defined in my interface:
#POST("/v2/companies/{companyId}/invite")
Call<ArrayList<String>> inviteMembers(#Path("companyId") String companyId, #Body SendEmailsList emails);
And this is the method that makes the Retrofit call:
public void SendNetworkRequest() {
OkHttpClient.Builder okhttpBuilder = new OkHttpClient.Builder();
okhttpBuilder.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request.Builder newRequest = request.newBuilder().header("Authorization", "Bearer " + token);
return chain.proceed(newRequest.build());
}
});
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(BuildConfig.ENDPOINT)
.client(okhttpBuilder.build())
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.build();
CompanyService invite = retrofit.create(CompanyService.class);
Call<ArrayList<String>> call = invite.inviteMembers("5602eb7ce49c9cd70409f206", new SendEmailsList(invitedEmails));
call.enqueue(new Callback<ArrayList<String>>() {
#Override
public void onResponse(Call<ArrayList<String>> call, Response<ArrayList<String>> response) {
System.out.println("Internal Users: " + response.code());
}
#Override
public void onFailure(Call<ArrayList<String>> call, Throwable t) {
// Log error here since request failed
Log.e("Internal Users Activity", t.toString());
}
});
}
But I am getting this error:
12-21 14:36:49.953 27953-27953/com.construct.test E/Internal Users Activity: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
How can I figure out what is going on?
I got the following response from my server: status code 201 Created.
There is no actual response (returned object, etc.), so there is not need to create a POJO class.
So, I don't know how I should handle this status code without creating a POJO class. Is there any option to make write the code without using a POJO class?
Retrofit API has Response class that can encapsulate your response.
As long as you don't want to bother with the response data you can implement your service as:
interface CustomService {
#GET("whatever")
Call<Response<Void>> getAll();
// Or using RxJava:
#GET("whatever")
Single<Response<Void>> getRxAll();
}
Then implement your callback:
private Callback<Response<Void>> responseHandler = new Callback<Response<Void>>() {
#Override
public void onResponse(Call<Response<Void>> call, Response<Response<Void>> response) {
final int code = response.code();
// TODO: Do whatever you want with the response code.
}
#Override
public void onFailure(Call<Response<Void>> call, Throwable t) {
// TODO: Handle failure.
}
}
Or reactive consumer:
private Consumer<Response<Void>> responseRxHandler = new Consumer<Response<Void>>() {
#Override
public void accept(Response<Void> response) throws Exception {
final int responseCode = response.code();
// TODO: Do whatever you want with the response code.
}
};
Debugging result:
You can try the following code.
Can get the response without a POJO class by getting using ResponseBody format and then you can parse it normally like ordinary JSON parsing.
Api Call:
Call<ResponseBody> call = service.callLogin(AppConstants.mApiKey, model_obj);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if(response.code() == 201)
{
JSONObject jobjresponse = null;
try {
jobjresponse = new JSONObject(mResponse.body().string());
String status = jobjresponse.getString("status");
JSONObject result = jobjresponse.getJSONObject("results");
String msg = result.getString(“msg”);
} catch (JSONException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
Retrofit Interface class:
public interface RetrofitInterface {
#Headers({"Content-Type: application/json", "Cache-Control: max-age=640000"})
#POST("v1/auth/")
public Call<ResponseBody> callLogin(#Query("key") String key, #Body LoginModel body);
public static final Retrofit retrofit = new Retrofit.Builder()
.baseUrl(“base url”)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
Sample Response:
{ "status":"true", "result":{"msg”:”created successfully”} }