Параллельное изменение Exception Stitch MongoDB логин - PullRequest
0 голосов
/ 06 апреля 2020

Я создаю приложение на основе Stitch Mongodb с методом аутентификации по электронной почте / паролю. Мне удалось зарегистрировать пользователя и отправить электронное письмо с подтверждением. После регистрации и отправки электронного письма я перенаправляю пользователя на страницу входа напрямую. Затем я перехожу на свою электронную почту и подтверждаю учетную запись и возвращаюсь в приложение для входа в систему, но каждый раз, когда я делаю это, он не входит напрямую, это дает мне исключение одновременной модификации. Это проблема, которую я могу решить, или пользователь должен немного подождать, прежде чем войти в систему?

Ошибка выглядит следующим образом:

2020-04-06 19:13:17.631 7255-7255/com.example.goonernews E/Error Logging in:: java.util.ConcurrentModificationException
        at java.util.ArrayList$Itr.next(ArrayList.java:860)
        at com.mongodb.stitch.android.core.internal.StitchAppClientImpl.onRebindEvent(StitchAppClientImpl.java:313)
        at com.mongodb.stitch.android.core.internal.StitchAppClientImpl.onUserLoggedIn(StitchAppClientImpl.java:332)
        at com.mongodb.stitch.android.core.auth.internal.StitchAuthImpl.onUserLoggedIn(StitchAuthImpl.java:326)
        at com.mongodb.stitch.android.core.auth.internal.StitchAuthImpl.onUserLoggedIn(StitchAuthImpl.java:48)
        at com.mongodb.stitch.core.auth.internal.CoreStitchAuth.doLogin(CoreStitchAuth.java:640)
        at com.mongodb.stitch.core.auth.internal.CoreStitchAuth.loginWithCredentialInternal(CoreStitchAuth.java:382)
        at com.mongodb.stitch.android.core.auth.internal.StitchAuthImpl.access$000(StitchAuthImpl.java:48)
        at com.mongodb.stitch.android.core.auth.internal.StitchAuthImpl$1.call(StitchAuthImpl.java:105)
        at com.mongodb.stitch.android.core.auth.internal.StitchAuthImpl$1.call(StitchAuthImpl.java:102)
        at com.mongodb.stitch.core.internal.common.ThreadDispatcher$1.run(ThreadDispatcher.java:57)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:459)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:764)

Страница входа выглядит следующим образом:

package com.example.goonernews;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.BounceInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.mongodb.stitch.android.core.Stitch;
import com.mongodb.stitch.android.core.StitchAppClient;
import com.mongodb.stitch.android.core.auth.StitchUser;
import com.mongodb.stitch.android.core.auth.providers.userpassword.UserPasswordAuthProviderClient;
import com.mongodb.stitch.android.services.mongodb.remote.RemoteMongoClient;
import com.mongodb.stitch.android.services.mongodb.remote.RemoteMongoCollection;
import com.mongodb.stitch.core.auth.providers.userpassword.UserPasswordCredential;
import com.mongodb.stitch.core.services.mongodb.remote.RemoteInsertOneResult;

import org.bson.Document;

public class LoginPage extends AppCompatActivity {
    TextView SignUpMessage;
    ImageView BouncingLogo;
    EditText Username;
    EditText Password;
    Button Login;
    private StitchUser currentUser;
    public static StitchAppClient client;

    private RemoteMongoClient mongoClient;
    private RemoteMongoCollection usersCollection;

    @Override
    protected void onStart() {
        super.onStart();
        if (ContextCompat.checkSelfPermission(getApplicationContext(), android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getApplicationContext(), android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_COARSE_LOCATION}, 101);
        }
        if(client==null)
            client = Stitch.initializeAppClient("goonernews-ocisk");
        client = Stitch.getAppClient("goonernews-ocisk");
        mongoClient = client.getServiceClient(RemoteMongoClient.factory, "mongodb-atlas");
        usersCollection = mongoClient.getDatabase("ArsenalLebanon").getCollection("Users");
        currentUser = client.getAuth().getUser();
        if(currentUser != null){
            startActivity(new Intent(LoginPage.this, Homepage.class));
            finish();
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login_page);
        SignUpMessage = findViewById(R.id.SignUpMessage);
        BouncingLogo = findViewById(R.id.bouncinglogo);
        Username = findViewById(R.id.username);
        Password = findViewById(R.id.password);
        Login = findViewById(R.id.login_button);
        String signupmessage = "Don't have an account? Sign Up";
        SpannableString Ssignupmessage = new SpannableString(signupmessage);
        ForegroundColorSpan fcsRed = new ForegroundColorSpan(Color.RED);
        ForegroundColorSpan fcsBlack = new ForegroundColorSpan(Color.BLACK);
        Ssignupmessage.setSpan(fcsRed,0,22, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        ClickableSpan goToSignUp = new ClickableSpan() {
            @Override
            public void onClick(@NonNull View widget) {
                Intent RegisterActivity = new Intent(getApplicationContext(), RegistrationPage.class);
                startActivity(RegisterActivity);
            }
        };
        Ssignupmessage.setSpan(goToSignUp,23,30, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        Ssignupmessage.setSpan(fcsBlack,23,30,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        SignUpMessage.setText(Ssignupmessage);
        SignUpMessage.setMovementMethod(LinkMovementMethod.getInstance());
        BouncingLogo.clearAnimation();
        TranslateAnimation translateAnimation = new TranslateAnimation(0,0,0,getDisplayHeight()/10);
        translateAnimation.setStartOffset(500);
        translateAnimation.setDuration(3000);
        translateAnimation.setFillAfter(true);
        translateAnimation.setInterpolator(new BounceInterpolator());
        translateAnimation.setAnimationListener(new Animation.AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {
                Log.i("Animation", "Starting button dropdown animation");
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
                // TODO Auto-generated method stub
            }

            @Override
            public void onAnimationEnd(Animation animation) {

            }
        });
        BouncingLogo.startAnimation(translateAnimation);
        Login.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String email = Username.getText().toString();
                String password = Password.getText().toString();
                boolean cancel = false;
                View focusView = null;
                if (TextUtils.isEmpty(email)) {
                    Username.setError("Please enter an Email Address!");
                    focusView = Username;
                    cancel = true;
                } else if (!MiscFunc.isEmailValid(email)) {
                    Username.setError("Please enter a valid Email Address!");
                    focusView = Username;
                    cancel = true;
                }

                if (!TextUtils.isEmpty(password) && !MiscFunc.isPasswordValid(password)) {
                    Password.setError("Please enter a valid Password!");
                    focusView = Password;
                    cancel = true;
                }
                if (cancel) {
                    focusView.requestFocus();
                }
                else {
                    new getLoginTask().execute(email,password);
                }
            }
        });
    }

    @SuppressLint("StaticFieldLeak")
    private class getLoginTask extends AsyncTask<String, Void, Void> {
        private byte statusCode;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
           }

        @Override
        protected Void doInBackground(String... params) {
            try {
                Log.e("Email: ",params[0]);
                Log.e("Password: ",params[1]);
                UserPasswordCredential credential = new UserPasswordCredential(params[0], params[1]);
                client.getAuth().loginWithCredential(credential).addOnCompleteListener(new OnCompleteListener<StitchUser>() {
                    @Override
                    public void onComplete(@NonNull final Task<StitchUser> task) {
                        if (task.isSuccessful()){
                            statusCode = 1;
                            onPostExecute();
                        }
                        else{
                            statusCode = 0;
                            Log.e("Error Logging in: ","",task.getException());
                            onPostExecute();
                        }
                    }
                });
            } catch (Exception e) {
                Log.e("stitch-auth", "Authentication Failed!");
            }
            return null;
        }

        protected void onPostExecute() {
            if (statusCode == 1) {
                Log.d("stitch-auth", "Authentication Successful.");
                StitchUser newAccount = client.getAuth().getUser();
                try
                {
                    String full_name = getIntent().getStringExtra("FullName");
                    String full_Birthday = getIntent().getStringExtra("Birthday");
                    if(newAccount!= null && full_name!= null && full_Birthday!= null) {
                        String id = newAccount.getId();
                        Document newUser = new Document()
                                .append("UserID", id)
                                .append("FullName", full_name)
                                .append("Birthday", full_Birthday);
                        final Task<RemoteInsertOneResult> insertTask = usersCollection.insertOne(newUser);
                        insertTask.addOnCompleteListener(new OnCompleteListener<RemoteInsertOneResult>() {
                            @Override
                            public void onComplete(@NonNull Task<RemoteInsertOneResult> task) {
                                if (task.isSuccessful()) {
                                    Log.d("app", String.format("successfully inserted item with id %s",
                                            task.getResult().getInsertedId()));
                                } else {
                                    Log.e("app", "failed to insert document with: ", task.getException());
                                }
                            }
                        });
                    }
                }
                catch (Exception e)
                {
                    Log.e("No name or birthday","");
                }
                startActivity(new Intent(LoginPage.this, Homepage.class));
                finish();
            } else {
                Log.e("stitch-auth", "Authentication Failed.");
                Toast.makeText(LoginPage.this, "Ugh...! Error in recognizing.", Toast.LENGTH_SHORT).show();
                Password.setText("");
            }
        }
    }

    private int getDisplayHeight() {
        return this.getResources().getDisplayMetrics().heightPixels;
    }

    private void changePassword() {
        String email = Username.getText().toString();
        UserPasswordAuthProviderClient emailPassClient = Stitch.getDefaultAppClient().getAuth().getProviderClient(UserPasswordAuthProviderClient.factory);
        if (TextUtils.isEmpty(email)) {
            Username.setError("Please enter your email!");
        } else {
            emailPassClient.sendResetPasswordEmail(email)
                    .addOnCompleteListener(new OnCompleteListener<Void>() {
                        @Override
                        public void onComplete(@NonNull final Task<Void> task) {
                            if (task.isSuccessful()) {
                                Log.d("stitch-auth", "Successfully sent password reset email");
                                Toast.makeText(LoginPage.this, "Password reset mail sent.", Toast.LENGTH_SHORT).show();
                            } else {
                                Log.e("stitch-auth", "Error sending password reset email:", task.getException());
                                Toast.makeText(LoginPage.this, "Account not found!", Toast.LENGTH_SHORT).show();
                            }
                        }
                    });
        }
    }
}

1 Ответ

0 голосов
/ 08 апреля 2020

Из полученной вами параллельной ошибки, которая показывает, что за один раз несколько операций обновляют один и тот же документ, что приводит к тому, что параллелизм не поддерживается.

Я бы предложил вам использовать транзакции обновлять документы, поскольку в транзакциях параллелизм данных поддерживается с помощью блокировок, полученных на документах.

https://docs.mongodb.com/manual/core/transactions/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...