java can't find local variable in reterofit 2 api call - java

I have done an API call to retrieve a list of messages, then i want to check if each message has a flag of 2 and then if it does do another API call to receive information on whether the message has been "paid" and if it has alter the object message to paid = true;
Here is my failed attempt.
for (int i = 0; i < chatHistory.getData().size(); i++) {
final ChatMessage chatMessage = chatHistory.getData().get(i).getBody();
if (chatMessage.flag.equals("2")) {
RestClient.getInstance().getApiService().getPaymentRequest(chatMessage.payment_request_id, new Callback<SinglePaymentRequest>() {
#Override
public void success(SinglePaymentRequest singlePaymentRequest, Response response) {
Payment payment = singlePaymentRequest.getPayment();
if(payment.getStatus().equals("paid")) {
chatMessage.isPaid=true;
}
}
#Override
public void failure(RetrofitError error) {
System.out.println("fail");
}
});
}
chatMessages.add(chatMessage);
Log.e("chat history", chatMessage.from);
}
addData(chatMessages);
The problem I am facing is that the api call cannot find local variable chatmessage, any ideas as to why this is?
Thanks

Notice the bit of code new Callback<SinglePaymentRequest>() that creates your new Callback object? It does not have access to the variables outside it, for good reason too.
What you should be doing, is calling a setter method that's part of your container class (the one that is the parent of the Callback) that will, in turn manipulate the values that you want to change.

Related

How to create a custom BodyPublisher for Java 11 HttpRequest

I'm trying to create a custom BodyPublisher that would deserialize my JSON object. I could just deserialize the JSON when I'm creating the request and use the ofByteArray method of BodyPublishers but I would rather use a custom publisher.
public class CustomPublisher implements HttpRequest.BodyPublisher {
private byte[] bytes;
public CustomPublisher(ObjectNode jsonData) {
...
// Deserialize jsonData to bytes
...
}
#Override
public long contentLength() {
if(bytes == null) return 0;
return bytes.length
}
#Override
public void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber) {
CustomSubscription subscription = new CustomSubscription(subscriber, bytes);
subscriber.onSubscribe(subscription);
}
private CustomSubscription implements Flow.Subscription {
private final Flow.Subscriber<? super ByteBuffer> subscriber;
private boolean cancelled;
private Iterator<Byte> byterator;
private CustomSubscription(Flow.Subscriber<? super ByteBuffer> subscriber, byte[] bytes) {
this.subscriber = subscriber;
this.cancelled = false;
List<Byte> bytelist = new ArrayList<>();
for(byte b : bytes) {
bytelist.add(b);
}
this.byterator = bytelist.iterator();
}
#Override
public void request(long n) {
if(cancelled) return;
if(n < 0) {
subscriber.onError(new IllegalArgumentException());
} else if(byterator.hasNext()) {
subscriber.onNext(ByteBuffer.wrap(new byte[]{byterator.next()));
} else {
subscriber.onComplete();
}
}
#Override
public void cancel() {
this.cancelled = true;
}
}
}
This implementation works, but only if subscriptions request method gets called with 1 as a parameter. But that's what happens when I am using it with the HttpRequest.
I'm pretty sure this is not any way preferred or optimal way of creating the custom subscription but I have yet to found better way to make it work.
I would greatly appreciate if anyone can lead me to a better path.
You are right to avoid making a byte array out of it, as that would create memory issues for large objects.
I wouldn’t try to write a custom publisher. Rather, just take advantage of the factory method HttpRequest.BodyPublishers.ofInputStream.
HttpRequest.BodyPublisher publisher =
HttpRequest.BodyPublishers.ofInputStream(() -> {
PipedInputStream in = new PipedInputStream();
ForkJoinPool.commonPool().submit(() -> {
try (PipedOutputStream out = new PipedOutputStream(in)) {
objectMapper.writeTree(
objectMapper.getFactory().createGenerator(out),
jsonData);
}
return null;
});
return in;
});
As you have noted, you can use HttpRequest.BodyPublishers.ofByteArray. That is fine for relatively small objects, but I program for scalability out of habit. The problem with assuming code won’t need to scale is that other developers will assume it is safe to pass large objects, without realizing the impact on performance.
Writing your own body publisher will be a lot of work. Its subscribe method is inherited from Flow.Publisher.
The documentation for the subscribe method starts with this:
Adds the given Subscriber if possible.
Each time your subscribe method is called, you need to add the Subscriber to some sort of colllection, you need to create an implementation of Flow.Subscription, and you need to immediately pass it to the subscriber’s onSubscribe method. Your Subscription implementation object needs to send back one or more ByteBuffers, only when the Subscription’s request method is called, by invoking the corresponding Subscriber’s (not just any Subscriber’s) onNext method, and once you’ve sent all of the data, you must call the same Subscriber’s onComplete() method. On top of that, the Subscription implementation object needs to handle cancel requests.
You can make a lot of this easier by extending SubmissionPublisher, which is a default implementation of Flow.Publisher, and then adding a contentLength() method to it. But as the SubmissionPublisher documentation shows, you still have a fair amount of work to do, for even a minimal working implementation.
The HttpRequest.BodyPublishers.of… methods will do all of this for you. ofByteArray is okay for small objects, but ofInputStream will work for any object you could ever pass in.

how to return value from this public function?

Examle below must return user.photo_100 (and user.photo_100 response String, I logged it), but var wallPostUserAvatar set null. Looks like I missed something, but I can't find it out.
public class WallPostExtends {
private String wallPostUserAvatar;
public String getUserPhotoLink(int user_id){
//Prepare request for userName and photo
final VKRequest request = VKApi.users().get(VKParameters.from(VKApiConst.USER_ID, user_id, VKApiConst.FIELDS, "photo_100"));
request.executeWithListener(new VKRequest.VKRequestListener() {
#Override
public void onError(VKError error) {
}
#Override
public void onComplete(VKResponse response) {
super.onComplete(response);
//Work with UserName and photo response
VKApiUserFull user = ((VKList<VKApiUserFull>) response.parsedModel).get(0);
wallPostUserAvatar = user.photo_100;
}
});
Log.d("photo link: ", wallPostUserAvatar); //Here is NULL. What am I doing wrong?
return wallPostUserAvatar; //How to return here "user.photo_100" ?
}
}
I think i came to know the reason why wallPostUserAvatar is null , because the you are assigning the value in onComplete() and the execution flow is not how you think, the log you printed will be executed first and then depends on what is happening in executeWithListener() will execute after.
I have a solution for this, i will share with you.
You can use callback mechanism here.
A typical example would be using Interface
public interface Callbacks {
public void successCallback(String response);
public void failCallback(String response);
}
Then you can use this interface as follows:
public void getUserPhotoLink(int user_id,final Callbacks callback){
request.executeWithListener(new VKRequest.VKRequestListener() {
#Override
public void onError(VKError error) {
callbacks.failCallback("fail");
}
#Override
public void onComplete(VKResponse response) {
super.onComplete(response);
//Your code
callback.successCallback(user.photo_100);
}
Then you can call you function getUserPhotoLink() as
getUserPhotoLink(userid value,new Callbacks() {
#Override
public void successCallback(String success) {
//You will get your desired result here
}
#Override
public void failCallback(String fail) {
});
Hope it will help you
I don't know the VKApi, but it looks like its an asynchronous call based on the name and the fact you pass in a listener. That means you CANT return the string, because you won't have it until the asynchronous call completes. Instead you'd need to do any processing of it in onComplete.
In short:
Sorry to tell you, but you cannot return any value right there.
The VKRequestListener is called asynchronously, while the method returns immediately.
You will have to find a more suitable solution for this, like handing over the object you want to make the String accessable to and call a set-Method in the onComplete block.
If this is not being run on the UI thread, try putting a while(wallPostUserAvatar!=null); after the call to the log function. This would work because, as is, you probably have a race condition between the return and the API request and the return statement will almost always win that race.

how do I suspend a request in Play Framework?

I'm playing around with the Play Framework (v2.2.2), and I'm trying to figure out how to suspend an HTTP request. I'm trying to create a handshake between users, meaning, I want user A to be able to fire off a request and wait until user B "connects". Once the user B has connected, user A's request should return with some information (the info is irrelevant; let's just say some JSON for now).
In another app I've worked on, I use continuations to essentially suspend and replay an HTTP request, so I have something like this...
#Override
public JsonResponse doGet(HttpServletRequest request, HttpServletResponse response) {
Continuation reqContinuation = ContinuationSupport.getContinuation(request);
if (reqContinuation.isInitial()) {
...
reqContinuation.addContinuationListener(new ContinuationListener() {
public void onTimeout(Continuation c) {...}
public void onComplete(Continuation c) {...}
});
...
reqContinuation.suspend();
return null;
}
else {
// check results and return JsonResponse with data
}
}
... and at some point, user B will connect and the continuation will be resumed/completed in a different servlet. Now, I'm trying to figure out how to do this in Play. I've set up my route...
GET /test controllers.TestApp.test()
... and I have my Action...
public static Promise<Result> test() {
Promise<JsonResponse> promise = Promise.promise(new Function0<JsonResponse>() {
public JsonResponse apply() {
// what do I do now...?
// I need to wait for user B to connect
}
});
return promise.map(new Function<JsonResponse, Result>() {
public Result apply(JsonResponse json) {
return ok(json);
}
});
}
I'm having a hard time understanding how to construct my Promise. Essentially, I need to tell user A "hey, you're waiting on user B, so here's a promise that user B will eventually connect to you, or else I'll let you know when you don't have to wait anymore".
How do I suspend the request such that I can return a promise of user B connecting? How do I wait for user B to connect?
You need to create a Promise that can be redeemed later. Strangely, the Play/Java library (F.java) doesn't seem to expose this API, so you have to reach into the Scala Promise class.
Create a small Scala helper class for yourself, PromiseUtility.scala:
import scala.concurrent.Promise
object PromiseUtility {
def newPromise[T]() = Promise[T]()
}
You can then do something like this in a controller (note, I don't fully understand your use case, so this is just a rough outline of how to use these Promises):
if (needToWaitForUserB()) {
// Create an unredeemed Scala Promise
scala.concurrent.Promise<Json> unredeemed = PromiseUtility.newPromise();
// Store it somewhere so you can access it later, e.g. a ConcurrentMap keyed by userId
storeUnredeemed(userId, unredeemed);
// Wrap as an F.Promise and, when redeemed later on, convert to a Result
return F.Promise.wrap(unredeemed.future()).map(new Function<Json, Result>() {
#Override
public Result apply(Json json) {
return ok(json);
}
});
}
// [..]
// In some other part of the code where user B connects
scala.concurrent.Promise<Json> unredeemed = getUnredeemed(userId);
unredeemed.success(jsonDataForUserB);

How to return List of Object from server in GWT

I am using the tutorial explaining the usage of asyncdataprovider to create the celltable http://www.mytechtip.com/2010/11/gwt-celltable-example-using_8168.html. Tutorial mentioned that you can return the list of object from the server.
// Associate an async data provider to the table
AsyncDataProvider<Contact> provider = new AsyncDataProvider<Contact>() {
#Override
protected void onRangeChanged(HasData<Contact> display) {
final int start = display.getVisibleRange().getStart();
int length = display.getVisibleRange().getLength();
AsyncCallback<List<Contact>> callback = new AsyncCallback<List<Contact>>() {
#Override
public void onFailure(Throwable caught) {
Window.alert(caught.getMessage());
}
#Override
public void onSuccess(List<Contact> result) {
updateRowData(start, result);
}
};
// The remote service that should be implemented
remoteService.fetchPage(start, length, callback);
}
};
Could someone please tell me how can I return a list of object from the server.
I thinkt this tutorial will help you:
http://www.tutorialspoint.com/gwt/gwt_rpc_communication.htm
Another good place to look for information is:
http://www.gwtproject.org/doc/latest/tutorial/RPC.html
Please don't forget you only can pass serializable objects through RPC. So you can't use List types or you should serialize it.

Need help with callbacks and anonymous classes in Java

I am using some third party library to connect to a server via async protocol and get response back. For example method to get userid by username looks like this:
public int getUserid(String username) {
int userid = 0;
connection.call("getUserid", new Responder() {
public void onResult(final int result) {
System.out.println("userid: " + result);
//how to assign received value to userid and return it?
}
}, username);
//wait for response
while (userid == 0) {
try{
Thread.sleep(100);
} catch (Exception e) {}
}
return userid;
}
The problem is I can't assign returned "result" from server response to "userid" variable from the method (in order to return it after). How to solve this? I probably can assign it to some class variable rather than method variable but I want to keep it within method scope so I don't have to deal with concurrency issues.
Thanks.
If I understand your question correctly, you're asking how you can write a variable from inside an anonymous class.
Anonymous classes can only access final variables, and can't directly "write" them.
A straightforward solution that is "good enough" is to create sort of a ValueBox class with a single value field and a getter and setter. You can then instantiate a new one in the function as a final variable, and have your anonymous class access it. The anonymous class will use its getter and setter to write/read.
The fact that the variable is final just means that you can't aim the reference anywhere else, but you can still change the contents of the referred object from either function.
The bigger problem you are going to have is in waiting until the callback has been called. This sort of wait-sleep might be good enough, but you may want to consider timeouts, threads, etc, depending on what you're trying to achieve.
In addition, this all assumes that you are never going to call this twice on the connection. Otherwise, you need to provide more info to us on your synchronization model.
Here's some sample code:
public int getUserid(String username) {
final ValueBox<Integer> userid = new ValueBox<Integer>();
connection.call("getUserid", new Responder() {
public void onResult(final int result) {
System.out.println("userid: " + result);
userId.setValue(result);
//how to assign received value to userid and return it?
}
}, username);
//wait for response
while (userid.isEmpty()) {
try{
Thread.sleep(100);
} catch (Exception e) {}
}
return userid.getValue();
}
The simplest change is to use something like java.util.concurrent.SynchronousQueue. But possibly you want to provide an event driven interface yourself.

Categories