Google Cloud Endpoint execute async code in endpoints - java

I am trying to authenticate client token created by Firebase authentication library in Android in GCE endpoint.
The guide of how to do this can be found here
Basically I need to call this code snippet from the end point (i.e. server backend code not android code).
FirebaseAuth.getInstance().verifyIdToken(idToken)
.addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
#Override
public void onSuccess(FirebaseToken decodedToken) {
String uid = decodedToken.getUid();
// ...
}
});
Let say I want to execute that code and return the user to android client code. How should I do that?
This is my sample code that does not make sense. But it demonstrate what I want to do!
#ApiMethod(name = "serverAuth")
public MyUser serverAuth(#Named("token") String token) {
FirebaseAuth.getInstance().verifyIdToken(token)
.addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
#Override
public void onSuccess(FirebaseToken decodedToken) {
String uid = decodedToken.getUid();
String email = decodedToken.getEmail();
String name = decodedToken.getName();
Map<String, Object> claims = decodedToken.getClaims();
String claimString = "";
for (Object claim : claims.values()) {
claimString += claims.toString();
}
MyUser user = new MyUser(uid, email, name, claimString);
//How to return this user?
}
});
//This is compile error since user varriable does not exist here
return user;
}
I have google search how to execute async code in GCE endpoints. But getting nowhere with that. What I get is something about code execution that is blocking until done and then return the user. But how to code so that async code as above become blocking?

CountDownLatch is the magic class you need. It will let you wait till the OnSuccessListener is actually completed.
Adapt your method this way: (I removed the steps that lead to MyUser's creation in order to focus on important points.)
#ApiMethod(name = "serverAuth")
public MyUser serverAuth(#Named("token") String token) {
final List<MyUser> users = new ArrayList<>();
final CountDownLatch cdl = new CountDownLatch(1);
FirebaseAuth.getInstance().verifyIdToken(token)
.addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
#Override
public void onSuccess(FirebaseToken decodedToken) {
// ... init uid, email, name and claimString
users.add(new MyUser(uid, email, name, claimString));
cdl.countDown();
}
});
try {
cdl.await(); // This line blocks execution till count down latch is 0
} catch (InterruptedException ie) {
}
if (users.size() > 0) {
return users.get(0);
} else {
return null ;
}
}
This is the basic version of what you need. IMHO, it requires 2 more improvements :
You should also take the possibility of failure into account :
FirebaseAuth.getInstance().verifyIdToken(token)
.addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
#Override
public void onSuccess(FirebaseToken decodedToken) {
cdl.countDown();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// log error, ...
cdl.countDown();
}
});
You should also take the possibility that none of the listeners are called. In this situation your method will never return. To avoid that, you can set a timeout on the await() method :
try {
// This line blocks execution till count down latch is 0
// or after 30 seconds.
cdl.await(30l, TimeUnit.SECONDS);
} catch (InterruptedException ie) {
}
That's it. Hope this may help.

Related

Performing Callbacks one after another

I am new to doing asynchronous programming in Android Java. I am wondering if there is a way to run another Callback after an initial Callback function has completed. Right now, I think they are running in parallel even though the second relies on the first.
First Callback:
// GETTING USER
private interface FirestoreUserCallback {
void onCallback (User myUser);
}
private void getUser(final FirestoreUserCallback firestoreCallback) {
Task<DocumentSnapshot> task = fStore.collection("users").document(fAuth.getCurrentUser().getUid()).get();
task.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
user = documentSnapshot.toObject(User.class);
firestoreCallback.onCallback(user);
Log.d(TAG, "user created");
}
});
task.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "user creation failed");
}
});
}
Second Callback:
// GETTING ALL DOCUMENTS
private interface FirestoreDocumentCallback {
void onCallback (List<TableEntries> myEntries);
}
private void getDocuments (final FirestoreDocumentCallback firestoreDocumentCallback) {
fStore.collection("result")
.document(Integer.toString(user.getCompanyNumber())) // need to use User object returned from the first Callback
.collection("SAM").get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
List<TableEntries> results = new ArrayList<>();
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
// add objects to results ArrayList ...
Log.d(TAG, document.getId() + " => " + document.getData());
}
firestoreDocumentCallback.onCallback(results);
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
}
onCreate:
getUser(new FirestoreUserCallback () {
#Override
public void onCallback(User myUser) {
user = myUser;
}
});
getDocuments(new FirestoreDocumentCallback() {
#Override
public void onCallback(List<TableEntries> myEntries) {
entries = myEntries;
}
});
getDocuments() relies on the user variable being given its value from the first Callback. I'm receiving this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'double java.lang.Double.doubleValue()' on a null object reference
Callbacks are looking fine. You just need to check if your value is null or not before accessing it. Just add a null check
if(doubleValue!=null)
Using RxJava. First, we fetch the user and then fetch the documents. Rx-Java has an operator flatmap. flatmap is used to execute the sequential tasks, where the second task is dependent on the data from the first task.
final CompositeDisposable disposable = new CompositeDisposable();
//function to fetch user data
Single<User> getUser(){
return API.getUserData(...);
}
//function to fetch ducuments
Sinlge<UserDetail> getDocuments(int userId){
return API.getUserDetail(userId, ...);
}
//Subscribe
disposable.add(getUser()
.flatmap(user-> return getDocuments(...))
.subscribeOn(Scheduler.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObservable(){
#Override
public void onSuccess(UserDetail userDetail){
Log.v("Api result", "Successful";
//Do some work
}
#Override
public void onError(Throwable e)
Log.v("Api result", "Error Returned");
}
}));
If either of the API call fails, onError() is called. If first API fails, second API call is not executed and onError() is called.
The simplest solution for your use-case is to pass both queries to Tasks.whenAllSuccess() method, as explained in my answer from the following post:
Firestore - Merging two queries locally
So once the task is complete, you can use the elements from both queries. Another solution might be to use Android Jetpack with LiveData along with ViewModel, as the Android team recommends.

How to get PaymentMethodNonce and deviceData on Braintree Android PayPal Vault Payment

Am trying to implement Braintree Vault PayPal payment, the problem am facing here is getting the paymentMethodNonce my event listener createdListener to capture nonce doesn't get called using vault, but everything works fine using checkout. I can't charge customer without a paymentMethodNonce, please can anyone assist me.
mBraintreeFragment = BraintreeFragment.newInstance(this,"TOKEN_FROM_SERVER");
PayPalRequest request = new PayPalRequest().localeCode("US").billingAgreementDescription("Your agreement description");
PayPal.requestBillingAgreement(mBraintreeFragment, request);
mBraintreeFragment.addListener(createdListener);
mBraintreeFragment.addListener(cancelListener);
mBraintreeFragment.addListener(errorListener);
DataCollector.collectDeviceData(mBraintreeFragment, new BraintreeResponseListener<String>() {
#Override
public void onResponse(String deviceData) {
Log.e("PayPal", deviceData);
try {
JSONObject json = new JSONObject(deviceData);
deviceDataInfo = json.getString("correlation_id");
Log.e("PayPal", deviceDataInfo);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
My Listeners
PaymentMethodNonceCreatedListener createdListener = new PaymentMethodNonceCreatedListener() {
#Override
public void onPaymentMethodNonceCreated(PaymentMethodNonce paymentMethodNonce) {
String nonce = paymentMethodNonce.getNonce();
Log.d("PayPal", "nonce id " + nonce);
}
};
BraintreeCancelListener cancelListener = new BraintreeCancelListener() {
#Override
public void onCancel(int requestCode) {
Log.d("CreditCard", "Braintree Error Code " + requestCode);
}
};
BraintreeErrorListener errorListener = new BraintreeErrorListener() {
#Override
public void onError(Exception error) {
if (error instanceof ErrorWithResponse) {
ErrorWithResponse errorWithResponse = (ErrorWithResponse) error;
BraintreeError cardErrors = errorWithResponse.errorFor("creditCard");
if (cardErrors != null) {
List<BraintreeError> errors = cardErrors.getFieldErrors();
String err = Objects.requireNonNull(errors.get(0).getMessage());
Log.d("CreditCard", errors.toString());
}
}
}
};
Instead of adding manually your listeners to that request, it's better to just implement the interface from braintree.
For example, if you want to use the onPaymentMethodNonceCreated() just add "implements PaymentMethodNonceCreatedListener" after your class name.
public class "YourClass" implements PaymentMethodNonceCreatedListener {
//...
}
And then override the method that now the Android Studio is warning you:
#Override
public void onPaymentMethodNonceCreated(PaymentMethodNonce paymentMethodNonce) {
String nonce = paymentMethodNonce.getNonce();
//...
}
This way you can go for everyone of your listeners! Good luck!

Waiting for asynchronous I/O on Android

I have been trying for awhile to figure out an issue with Asynchronous i/o in an android application that I am working on.
This application is required to download data to from a series of tables from Microsoft Dynamics CRM.
Once the data has been down it must preform a series of operations on the data to fill out some forms.
My problem is that I must wait for the downloads to be complete in order to start the update process.
If I add a any form of wait to my code it seems that it blocks indefinitely and never executes the callback.
I have tried methods using AtomicBooleans, AtomicIntegers, and CountDownLatchs with no success.
Here is an example using an AtomicInteger.
final CountDownLatch latch = new CountDownLatch(1);
OrganizationServiceProxy orgService;
orgService = new OrganizationServiceProxy(Constant.ENDPOINT, CRMLogin.getRequestInterceptor());
ColumnSet columnSet = new ColumnSet();
columnSet.AddColumns(AccountEntry.FETCH_COLS);
orgService.Retrieve(AccountEntry.ENTITY, UUID.fromString(accountid), columnSet, new Callback<Entity>() {
#Override
public void success(Entity entity, Response response) {
Account account = new Account();
//Load the existing fields for the account
account.load(index);
String activityid = account.getValue(AccountEntry.ACTIVITY_ID);
String recordid = account.getValue(AccountEntry.RECORD_ID);
String name = account.getValue(AccountEntry.ACCOUNT_NAME);
//Overload the fields for the account
account.load(entity);
//Reset overloaded fields on the account.
account.setValue(AccountEntry.ACTIVITY_ID, activityid);
account.setValue(AccountEntry.RECORD_ID, recordid);
account.setValue(AccountEntry.ACCOUNT_NAME, name);
//overwrite the record in the database.
account.setValue(AccountEntry.SYNCED, "1");
account.update();
Log.d("pullAccount>>>", accountid + " " + "pulled.");
latch.countDown();
}
#Override
public void failure(RetrofitError error) {
Log.d("pullAccount>>>", accountid + " " + error.getMessage());
latch.countDown();
}
});
try{
latch.await(); //THIS BLOCKS FOREVER AND EVER
}
catch (Exception e){
}
Of note is the CallBack is implemented using Retrofit.
Any guidance would be greatly appreciated.
Thank you.
Look at AsyncTask it will handle what you want in a way that Android is optimized for. There is example usage here
http://developer.android.com/reference/android/os/AsyncTask.html
EDIT:
I kinda threw this together, let me know if it works as you would expect
public class AsyncOrganizationService extends AsyncTask<Void, Void, Entity> {
#Override
protected Entity doInBackground(Void... params) {
final CountDownLatch blocker = new CountDownLatch(1);
OrganizationServiceProxy orgService;
orgService = new OrganizationServiceProxy(Constant.ENDPOINT, CRMLogin.getRequestInterceptor());
ColumnSet columnSet = new ColumnSet();
columnSet.AddColumns(AccountEntry.FETCH_COLS);
final SettableFuture<Entity> result = SettableFuture.create();
orgService.Retrieve(AccountEntry.ENTITY, UUID.fromString(accountid), columnSet, new SortedList.Callback<Entity>() {
#Override
public void success(Entity entity, HttpHelper.Response response) {
result.set(entity);
blocker.countDown();
}
});
try {
blocker.await();
return result.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
#Override
protected void onPostExecute(Entity entity) {
Account account = new Account();
//Load the existing fields for the account
account.load(index);
String activityid = account.getValue(AccountEntry.ACTIVITY_ID);
String recordid = account.getValue(AccountEntry.RECORD_ID);
String name = account.getValue(AccountEntry.ACCOUNT_NAME);
//Overload the fields for the account
account.load(entity);
//Reset overloaded fields on the account.
account.setValue(AccountEntry.ACTIVITY_ID, activityid);
account.setValue(AccountEntry.RECORD_ID, recordid);
account.setValue(AccountEntry.ACCOUNT_NAME, name);
//overwrite the record in the database.
account.setValue(AccountEntry.SYNCED, "1");
account.update();
Log.d("pullAccount>>>", accountid + " " + "pulled.");
}
Im using Guava's SettableFuture class (http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/util/concurrent/SettableFuture.html). Guava is quite an amazing library - if you're not using it you should consider doing so. Otherwise, you could whip something up really quick

Parse: Duplicate records for the a single saveInBackground Call

I have a single incident where a complete duplicate of a entry was made into the database (the same user comment appeared twice). They had different object IDs but were otherwise the exact same. It was slower than usual to finish the posting and only happened once out of dozens of comments, so I want to say it was a Parse issue during the saveInBackground call. Even so, I expect a service like Parse to be a little more robust. As my first time working with Android though, I also can't be sure nothing is wrong on my end. Any help? Also just any criticisms? This is the method called when the user hits a comment submission button:
private void submitComment() {
String text = commentText.getText().toString().trim();
Intent intent = getIntent();
String ID = intent.getStringExtra("imageID");
String parentID = intent.getStringExtra("parent");
// Set up a progress dialog
final ProgressDialog loadingDialog = new ProgressDialog(CommentSubmitActivity.this);
loadingDialog.setMessage(getString(R.string.publishing_comment));
loadingDialog.show();
Comment comment = new Comment();
comment.setText(text);
comment.setUser((ParseUser.getCurrentUser()));
if (ID.equals("#child")) {
comment.setParent(parentID);
comment.setImage("#child");
ParseQuery<ParseObject> query = ParseQuery.getQuery("Comment");
query.getInBackground(parentID, new GetCallback<ParseObject>() {
public void done(ParseObject parentComment, ParseException e) {
if (e == null) {
int numChild = parentComment.getInt("numChild");
parentComment.put("numChild", ++numChild);
parentComment.saveInBackground();
} else {
Log.d("numChild: ", "error");
}
}
});
} else {
comment.setImage(ID);
comment.put("numChild", 0);
ParseQuery<ParseObject> query = ParseQuery.getQuery("ImageUpload");
query.getInBackground(ID, new GetCallback<ParseObject>() {
public void done(ParseObject image, ParseException e) {
if (e == null) {
int numComments = image.getInt("numComments");
image.put("numComments", ++numComments);
image.saveInBackground();
} else {
Log.d("numComments: ", "error");
}
}
});
}
comment.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
loadingDialog.dismiss();
finish();
}
}
});
}
I encountered similar problem like yours.
I created an app where user can create account and add photo to it and list of objects (friends in my case).
Once when I was testing it user was created twice.
I went through my code and my my suspicions are connected with async calls.
I see that you use asynchronous parse api in you application so no fragment of code is waiting for response and blocking the rest of operations.
You cannot control when parse server will response.
What I did I just put all synchronous requests in my custom async code (AsyncTask in Android).
Hope that my answer somehow meeets your expectations.

is there better way using rxjava with multiple requests?

I'm new in rxjava or rxandroid, and looking for a better way dealing with multiple requests. I need to get the token from server and use the result as a parameter to do login verification and if it returns success then get the sessionId through getSessionId method.
I've considered about zip or merge, but I don't think it'll work. So can you give me an idea or I don know , train of thought?
Thank you.
Here's my code:
private void getToken(final String name , final String pwd){
api.newToken()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<TokenModel>() {
#Override public void call(TokenModel tokenModel) {
String token = tokenModel.request_token;
if (!"".equals(token)){
login(token, name, pwd);
}else {
Timber.e("got token failed");
}
}
});
}
private void login(String token, String name, String pwd){
api.validateToken(token, name, pwd)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<TokenModel>() {
#Override public void call(TokenModel tokenModel) {
String token = tokenModel.request_token;
if (!"".equals(token)){
getSessionId(token);
}else {
Timber.e("got token failed");
}
}
});
}
private void getSessionId(String token){
api.newSessionn(token)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<TokenModel>() {
#Override public void onCompleted() {
//go to home activity
}
#Override public void onError(Throwable e) {
//handle error
}
#Override public void onNext(TokenModel tokenModel) {
//store session id
}
});
}
Your first subscription call your second subscription, ...
You can avoid this using flapmap operator.
api.newToken(...)
.flapMap(token -> api.validateToken(token))
.flapMap(token -> api.newSession(token)).subscribe()
New observable in a subscription can offen be replaced by a flatMap call.
If you want to manage your error, in a flatMap, if the token is invalid, your can return an error observable instead of returning new api call observable.
.flatMap(token -> if(token.isValid){ return api.newCall(); } else { return Observable.error(...); ;)

Categories

Resources