I'm writing a android chat application with socket.io-client-java.I want to check whether the client user exist at first.So I need to send a command like "user/exist" to server url and get the response from server.I need to wait the server response then can go to next step.But the socket.io use the asynchronous callback.For getting the response synchronous I known the Furture and Callable only.So I tried the way using code as below:
//this is request method using socket.io
public JSONObject request(final String method,final String url,final JSONObject data){
final JSONObject responseObj = new JSONObject();
if (mSocket.connected()) {
mSocket.emit(method, reqObj, new Ack() {
#Override
public void call(Object... objects) {
System.out.println("get Ack");
try {
responseObj.put("body", (JSONObject) objects[0]);
}catch (JSONException e){
e.printStackTrace();
}
}
})
}
}
//this is Callable call implement
#Override
public JSONObject call(){
return request("get","https://my-chat-server/user/exist",new JSONObject());
}
//this is call method in activity
ExecutorService executor = Executors.newCachedThreadPool();
Future<JSONObject> response = executor.submit(mApiSocket);
executor.shutdown();
JSONObject respObj = new JSONObject();
JSONObject respBody = new JSONObject();
try {
respObj = response.get();
respBody = respObj.getJSONObject("body");
}catch (ExecutionException e){
}catch(InterruptedException e1){
}catch(JSONException e2){
}
But it dose not work.The respObj is null.
How can i get the reponse synchronous?
I am a green hand on java and forgive my poor chinese english.
Any help would be appreciated!
I known the js can use Promise and await like below:
//request method
static request(method, url, data) {
return new Promise((resolve, reject) => {
this.socket.emit(method,
{
url: url,
method,
data,
},
async (res) => {
if (res.statusCode == 100) {
resolve(res.body, res);
} else {
throw new Error(`${res.statusCode} error: ${res.body}`);
reject(res.body, res);
}
}
)
})
}
//call method
response = await mSocket.request('get','https://my-chat-server/user/exist', {
first_name: 'xu',
last_name: 'zhitong',
});
I'm not sure this is the best way but we can wait for the callback as follows:
#Nullable
Object[] emitAndWaitForAck(#NotNull String event, #Nullable Object[] args,
long timeoutMillis) {
Object[][] response = new Object[1][1];
Semaphore lock = new Semaphore(0);
socketClient.emit(event, args, ackArgs -> {
response[0] = ackArgs;
lock.release();
});
try {
boolean acquired = lock.tryAcquire(timeoutMillis, TimeUnit.MILLISECONDS);
if (acquired) {
return response[0];
}
} catch (InterruptedException ignored) {
}
return null;
}
Assuming your socket.io server returns one argument containing the body (or null) you would call it something like this:
String method = "get";
String url = "https://my-chat-server/user/exist";
long timeoutMillis = 5000;
Object[] args = emitAndWaitForAck(method, new String[]{url}, timeoutMillis);
JSONObject response = (JSONObject) args[0];
Related
I'm trying to do something similar to the answer of this question
I'm using Volley rather than Retrofit, and have adapted my code accordingly. For me the callback handlers never actually fire, although the counDownLatch does timeout after the specified number of seconds. I suspect the handlers never fire because the countDownLatch.awaiting is using all the processing on the current thread. Or am I missing something else?
public void queryUmbrellaServer() {
ArrayList<String> identifiers = getHardwareIdentifiers(context);
VolleyLog.DEBUG = true;
CountDownLatch countDownLatch = new CountDownLatch(identifiers.size());
// creating a new variable for our request queue
final RequestQueue[] queue = {Volley.newRequestQueue(context)};
queue[0].start();
for (int i = 0; i < identifiers.size(); i++) {
String url = umbrellaServerUrl + identifiers.get(i) + "/";
Log.i(LOG_TAG, "Inside Loop " + url);
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, url, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
countDownLatch.countDown();
String serverName = response.getString("mdm_server_url");
String registrationUrl = response.getString("registration_url");
String isDeviceOwner = response.getString("device_owner");
Toast.makeText(context, "Retrieved server name from umbrella server: " + serverName, Toast.LENGTH_LONG).show();
setMdmInfo(serverName, registrationUrl, isDeviceOwner);
Log.i(LOG_TAG, "Successful response");
//queue[0].stop();
results.add(isDeviceOwner.toString());
} catch (JSONException e) {
e.printStackTrace();
Log.d(LOG_TAG, "Error in parsing response");
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
countDownLatch.countDown();
if(error.networkResponse.data!=null) {
String jsonString = new String(error.networkResponse.data);
//Log.d(LOG_TAG, jsonString);
try {
JSONObject jsonObject = new JSONObject(jsonString);
String msg = jsonObject.getString("message");
if (!msg.equals("Device not found")) {
Log.d(LOG_TAG, "Error in retrieving response from server");
//queue[0].stop();
results.add("false");
}
else {
Log.d(LOG_TAG, "No device found");
results.add("false");
}
} catch (JSONException e) {
e.printStackTrace();
Log.d(LOG_TAG, "Error in retrieving response from server");
//queue[0].stop();
results.add("UNSET");
}
}
else {
Log.d(LOG_TAG, "Error in retrieving response from server");
//queue[0].stop();
results.add("UNSET");
}
}
});
// Add the request to the RequestQueue.
queue[0].add(jsonObjectRequest);
}
try {
countDownLatch.await(1L * identifiers.size(), TimeUnit.SECONDS); // join thread with timeout of second for each item
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(LOG_TAG,"outside loop" + results);
}
I expected the results arrayList should get populated before the countDownLatch countdown completes, but I haven't been able to get any results.
I have a Spring Boot application where I created a POST method that sends data in a streaming fashion to the caller. Code below:
#RequestMapping(value = "/mapmatchstreaming", method = RequestMethod.POST)
public ResponseEntity<StreamingResponseBody> handleRequest(#RequestParam(value = "data", required = true) String data, #RequestParam(value = "mnr", required = true) Boolean mnr) {
logger.info("/mapmatchstreaming endpoint");
try {
Semaphore semaphore = new Semaphore(1);
ObjectMapper mapper = new ObjectMapper();
StreamingResponseBody responseBody = new StreamingResponseBody() {
#Override
public void writeTo (OutputStream outputStream) throws IOException {
// For each map
DataReader dataReader = new DataReader(data, "2020.06.011");
for(String mapRoot: dataReader.getMapsFolders()) {
dataReader = new DataReader(data, "2020.06.011");
DistributedMapMatcherStreaming distributedMapMatcher = new DistributedMapMatcherStreaming(dataReader.getTraces(), mapRoot, dataReader.getBoundingBox());
distributedMapMatcher.mapMatchBatch(new DistributedMapMatcherResult() {
#Override
public void onCorrectlyMapMatched(MapMatchedTrajectory mapMatchedTrajectory) {
try {
semaphore.acquire();
outputStream.write(mapper.writeValueAsString(mapMatchedTrajectory).getBytes());
outputStream.flush();
}
catch (Exception e) {
e.printStackTrace();
logger.error(String.format("Writing to output stream error: %s", e.getMessage()));
} finally{
semaphore.release();
}
}
});
}
}
};
return new ResponseEntity<StreamingResponseBody>(responseBody, HttpStatus.OK);
}
catch (Exception e) {
logger.error(String.format("Map-matching result ERROR: %s", ExceptionUtils.getStackTrace(e)));
return new ResponseEntity<StreamingResponseBody>(HttpStatus.BAD_REQUEST);
}
}
It works nicely, but the problem is that if multiple calls arrive to this method, all of them are run in parallel even if I have set server.tomcat.threads.max=1. In the non-streaming version, every next call waits for the current one to complete.
Is it possible to have blocking streaming calls in Spring? Thanks.
EDIT: I temporarily solved by using a global semaphore with only 1 permit, but I don't think this is the ideal solution.
I want to make my soap request calls parallel in java.
I'm new to multithreading not getting the way to do it.
public List<TB600Model.Response> getTableDesc(final List<TB600Model.Request> requests)
{
List<TB600Model.Response> responses = new ArrayList<>();
for (TB600Model.Request request : requests)
{
responses.add(
this.modifyDescription(
this.getDescription(
request.getSite()
,request.getDescType()
,request.getKeyData()
,request.getEffMdy()
)
,request.getDescType()
,request.getKeyData()
)
);
}
return responses;
}
enter image description here
For Java 8+ Try to use this method instead :
requests.parallelStream().forEach(request -> {...});
put the code for response.add instead of ...
For earlier version :
ExecutorService es = Executors.newFixedThreadPool(10);
List<Response> responses = new ArrayList<>();
for(Request request: requests){
Future<Response> future = es.submit(new Callable<Response>() {
#Override
public Response call() throws Exception {
//create your response here and return
}
});
try {
responses.add(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
I am trying to get information from my AsyncHttpClient in my Android app, and I need to use an interface to set the variable so I can use it in my main method. However, when I run System.out.println(PostResponse);, I am getting null.
I don't understand why, because if I put the line in my callback() method, I get the values.
From my main method:
try {
JSONArray PostResponse = PerformPostRequest(new OnJSONResponseCallback() {
#Override
public JSONArray onJSONResponse(boolean success, JSONArray response) {
System.out.println("Response: " + response); //This is returning the correct value
return response;
}
}, PostData);
System.out.println("Useable: " + PostResponse); //This is returning null.
} catch (Exception e) {
e.printStackTrace();
}
The interface:
public interface OnJSONResponseCallback {
public JSONArray onJSONResponse(boolean success, JSONArray response);
}
The AsyncHttpClient:
public JSONArray PerformPostRequest(final OnJSONResponseCallback callback, JSONObject PostData) {
//To authenticate against the API we need the user's credentials
String Email = getSharedPreferences(ctx).getString("Email","");
String Password = getSharedPreferences(ctx).getString("Password","");
final JSONArray[] ResponseStorage = new JSONArray[1];
//Add the credentials to post data
try{
PostData.put("email", Email);
PostData.put("password", Password);
} catch (Exception e){
e.printStackTrace();
}
//Then we need to put the post data into request parameters so we can send them in the call.
RequestParams RequestParameters = new RequestParams();
RequestParameters.put("data", PostData);
//This is the client we will use to make the request.
AsyncHttpClient client = new AsyncHttpClient();
client.post(AppHost + "MyMeetings.php", RequestParameters, new AsyncHttpResponseHandler() {
#Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
try {
String ResponseString = new String(responseBody);
ResponseStorage[0] = new JSONArray(ResponseString);
System.out.println(ResponseStorage[0] + "<============="); //Returns with the array
callback.onJSONResponse(true, ResponseStorage[0]);
} catch (Exception e) {
Log.e("Exception", "JSONException on success: " + e.toString());
}
}
#Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
try {
Toast.makeText(ctx, "Error: " + statusCode, Toast.LENGTH_LONG).show();
} catch (Exception e) {
Log.e("Exception", "JSONException on failure: " + e.toString());
}
}
});
JSONArray ResponseArray = new JSONArray();
try{
System.out.println(ResponseStorage[0] + "<==============="); //Returning null?
ResponseArray = ResponseStorage[0];
} catch (Exception e){
e.printStackTrace();
}
System.out.println("ResponseArray" + ResponseArray); //Returns null
return ResponseArray;
}
Where am I going wrong? I think it is something to do with my call in the main method.
Edit:
1) I tried to return the ResponseArray (set in onsuccess) but I can't return it from onsuccess because it is public void. When I tried to change it to public JSONArray, I get an incompatible return type error.
2) I have updated the method so it returns something other than null, however, it still returns as null, even when I am printing it inside the AsyncHttp.
The general idea behind the asynchronous calls is that:
the asynchronous method call (in your case PerformPostRequest) returns immediately and does not return the expected result - instead it returns either just an accept confirmation or an object from which you can sometimes in the future get the result (such as an instance of a Future)
you provide the method a callback interface (in your case OnJSONResponseCallback) or the method returns an instance of a callback interface, and you check regularly if there is already a result ready.
You should not expect that the asynchronous method returns the result immediately, this is exactly the opposite of asynchronous call.
Here is the rough idea expressed by pictures. It is just an overall picture of the whole idea, so the implementation details may be quite different!
I was trying to set the variable from the PerformPostRequest(), which by default does not get called. At the top of my class, I set a
public JSONArray[] PostResponse = new JSONArray[1];
and updated the bit where I was calling the post request to the following:
//Make a post request
try {
JSONObject PostData = new JSONObject();
PostData.put("action","test");
PerformPostRequest(new OnJSONResponseCallback(){
#Override
public JSONArray onJSONResponse(boolean success, JSONArray response) {
PostResponse[0] = response;
System.out.println(PostResponse[0]); //This will be replaced by calling the method I want to call.
return PostResponse[0];
}
}, PostData);
System.out.println(PostResponse[0]);
} catch (Exception e){
e.printStackTrace();
}
How can I retrieve a file from the following method:
public void getJson(String pathToFile) {
String userUrl = DOMAIN_URL+pathToFile;
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(userUrl));
JSONArray outerJsonArray;
try {
Request request = builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
}
public void onResponseReceived(Request request, Response response) {
if (200 == response.getStatusCode()) {
try {
JSONValue jsonValue = JSONParser.parse(response.getText());
JSONArray jsonArray = jsonValue.isArray();
if (jsonArray != null) {
} else {
throw new JSONException();
}
} catch (JSONException e) {
}
} else {
}
}
});
} catch (RequestException e) {
}
}
Now it is just void. I should retriwe the jsonArray variable
You simply can't.
The request is asynchronous, so your method has to be asynchronous (non-blocking) too. You can pass something like an AsyncCallback (to reuse an existing interface) to your getJson method, and have your RequestCallback call it... back, with the JSONArray.
Technically speaking, that would be possible, but that's a design choice in GWT to not allowing blocking AJAX requests.
Embrace asynchrony!
Just change the declaration to
public JSONArray getJson(String pathToFile) {
After the line
JSONArray jsonArray = jsonValue.isArray();
you can add
return jsonArray;
Now you have to be careful. Obviously, you have some error checking conditions in case the array is null or an error occurs during reading. It is up to you to design how to handle them: if the array is null, should you throw an exception or return the null value? If there is an error reading, should you throw an exception, print an error, or return a null value? These are considerations that your external interface will dictate.