Informing the user if a Firebase search was successful

I am writing a static method for a program I have been working on to view the records in the "graylist" in my Firebase database and check the user login to see if that record exists. I can find writing just fine, but due to some limitations in the way streams work when reading / writing data from / to Firebase, I cannot figure out how to tell the user if the operation was successful or not.

I have the following method:

public static void addUser() {
    final boolean [] complete = new boolean[1];
    final String email = Tools.getEmail();
    DatabaseReference grayRef = FirebaseDatabase.getInstance().getReference().child("graylist");

    // Search the graylist for the email specified.
    CountDownLatch latch = new CountDownLatch(1); // need this so Parse-Ly doesn't close before event fires
    grayRef.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onCancelled(DatabaseError dbe) {}

        @Override
        public void onDataChange(DataSnapshot snap) {
            Iterable<DataSnapshot> graylist = snap.getChildren();
            for(DataSnapshot gray : graylist) {
                String uid = gray.getKey();
                String em = gray.getValue(String.class);
                if(em.equals(email)) {
                    // We found the one we're looking for. Insert its UID into the whitelist.
                    DatabaseReference whiteRef = FirebaseDatabase.getInstance().getReference().child("whitelist");
                    whiteRef.child(uid).setValue(email, new DatabaseReference.CompletionListener() {
                        @Override
                        public void onComplete(DatabaseError dbe, DatabaseReference dbr) {
                            complete[0] = true;
                            latch.countDown();
                            if(dbe != null) {
                                System.err.println("Error adding user to whitelist!");
                                dbe.toException().printStackTrace();
                            }
                        }
                    });
                    break;
                }
            }
            if(latch.getCount() > 0) {
                complete[0] = false;
                latch.countDown();
            }
        }
    });

    try {
        latch.await(); // wait for event to fire before we move on
    } catch(InterruptedException ie) {
        System.err.println("ERROR: Add user latch interrupted!");
        ie.printStackTrace();
    }

    if(complete[0]) ParselyMain.createAlert(AlertType.CONFIRMATION, "User Added", "User added to the whitelist!");
    else ParselyMain.createAlert(AlertType.INFORMATION, "User Not Found", "That user was not found in the graylist!");
}

      

I understand how hacky it is in the final boolean array, but even that doesn't work for me. The idea was to use CountDownLatch and only count down when the write was successful. If all records from the graylist have been read and CountDownLatch is still at 1, then nothing has been written and it must count to exit the stream, but a boolean will determine if the operation succeeds or fails.

The problem is that it seems to reach the if block after the for for loop before the onComplete method is called, causing it to think the operation has failed (although my Firebase database shows otherwise) and an incorrect warning is displayed.

Can anyone think of a better way to handle this so that I can inform them correctly if the user has been whitelisted or if the user has not been found?

+3


source to share


2 answers


Firebase database operations are asynchronous, so even if you execute the above code in any way, you end up with more complex cases where most of the lines of code are synchronizing rather than handling the functionality of your expression. This will lead to unreachable code that is easy to break.

Thus, the simplest and most "natural" way to handle a database request is to handle each event in your code whenever it happens. This means informing the user of a successful update when calling the method onComplete

and informing the user of an error when all child objects have been checked and no match was found:



public static void addUser() {
    final String email = Tools.getEmail();
    DatabaseReference grayRef = FirebaseDatabase.getInstance().getReference().child("graylist");

    // Search the graylist for the email specified.
    grayRef.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onCancelled(DatabaseError dbe) {}

        @Override
        public void onDataChange(DataSnapshot snap) {
            boolean found = false;
            Iterable<DataSnapshot> graylist = snap.getChildren();
            for(DataSnapshot gray : graylist) {
                String uid = gray.getKey();
                String em = gray.getValue(String.class);
                if(em.equals(email)) {
                    found = true;
                    // We found the one we're looking for. Insert its UID into the whitelist.
                    DatabaseReference whiteRef = FirebaseDatabase.getInstance().getReference().child("whitelist");
                    whiteRef.child(uid).setValue(email, new DatabaseReference.CompletionListener() {
                        @Override
                        public void onComplete(DatabaseError dbe, DatabaseReference dbr) {
                            complete[0] = true;
                            if(dbe != null) {
                                System.err.println("Error adding user to whitelist!");
                                dbe.toException().printStackTrace();
                            } else {
                                Platform.runLater(() -> {                                  
                                    ParselyMain.createAlert(AlertType.CONFIRMATION, "User Added", "User added to the whitelist!");
                                });
                            }
                        }
                    });
                    break;
                }
            }
            if(!found) {
                Platform.runLater(() -> {
                    ParselyMain.createAlert(AlertType.INFORMATION, "User Not Found", "That user was not found in the graylist!");
                });
            }
        }
    });
}

      

0


source


I don't have enough reputation so I can't directly comment on your question. Excuse me.

Firebase has its own thread. When called, grayRef.addListenerForSingleValueEvent

Firebase opens a new thread (or just uses an already existing independent thread), so your original thread continues the block if

right away as it thinks the operation should be performed, which is what you see as the result.

Hence, you really need a listener that will communicate with your main activity as soon as Firebase finishes its work. Since you said you are not using android, you can create a specific Firebase-only processing class that also includes an interface and a listener. For example,



public class FirebaseHandler {

    // parameters
    private DatabaseReference databaseReference;

    // constructor
    public FirebaseHandler() {
        // you'll most probably need these for some actions
        databaseReference = FirebaseDatabase.getInstance().getReference();
    }

    // interface
    public interface FirebaseInterface {
        void firebaseComplete(String serverStatus, String email);
    }

    // listener
    private FirebaseInterface listener;

    // set listener
    public void setListener(FirebaseInterface listener) {
        this.listener = listener;
    }

    /* getter for references, these will be useful to avoid calling the wrong place */
    // -- / 
    public DatabaseReference getRoot() {
        return databaseReference;
    }

    // -- /email       /* specify the data structure here */
    public DatabaseReference getEmailRef() {
        return getRoot().child("email");
    } 

    // finally your method
    public static void addUser() {
        // your codes here...
        whiteRef.child(uid).setValue(email, new DatabaseReference.CompletionListener() {
            @Override
            public void onComplete(DatabaseError dbe, DatabaseReference dbr) {
                // some action here....
                if (listener != null) listener.firebaseComplete("OK", email);
            }
        });
    }
}

      

You just need to attach a listener in your activity and implement its method. So, in your UI thread ...

class Main implements FirebaseHandler.FirebaseInterface {

    FirebaseHandler firebaseHandler = new FirebaseHandler();
    firebaseHandler.setListener(this);
    firebaseHandler.addUser();

    @Override
    public void firebaseComplete(String serverStatus, String email) {
        firebaseHandler.setListener(null);      // optional to remove listener
        if (serverStatus.equals("OK") {
            ParselyMain.createAlert(AlertType.CONFIRMATION, "User Added", "User added to the whitelist!");
        } else { 
            ParselyMain.createAlert(AlertType.INFORMATION, "User Not Found", "That user was not found in the graylist!"); 
        }
    }
}

      

0


source







All Articles