1

I'm working on a graphical program in Java (not Android) using Firebase where I'm trying to look through the values of one database for a certain string, and when I find it, take the data from that first database and write it to another database.

I'm able to correctly read and write the data, but I'm having issues with getting the program to terminate because of two specific problems.

  1. If I do not "wait" for the completion listener somehow (in this case, a CountDownLatch), the program terminates before the onComplete() method of the completion listener has a chance to fire, and the value is never written to the database.

  2. If I use a CountDownLatch to wait for the onComplete() method to fire, the program never terminates, because apparently the completion listener never fires.

When I do use the CountDownLatch, the data is written, but the completion listener never fires and the program does not terminate.

Here is the method in which I am doing this:

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

    // Search the graylist for the email specified.
    CountDownLatch dataChange = 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.
                    final CountDownLatch completion = new CountDownLatch(1);
                    DatabaseReference whiteRef = FirebaseDatabase.getInstance().getReference().child("whitelist");
                    whiteRef.child(uid).setValue(email, new DatabaseReference.CompletionListener() {
                        @Override
                        public void onComplete(DatabaseError dbe, DatabaseReference dbr) {
                            completion.countDown();
                            if(dbe != null) {
                                System.err.println("Error adding user to whitelist!");
                                dbe.toException().printStackTrace();
                            }
                        }
                    });
                    try {
                        completion.await();
                        System.out.println("User added successfully!");
                    } catch(InterruptedException ie) {
                        System.err.println("ERROR: Completion latch interrupted!");
                        ie.printStackTrace();
                    }
                    break;
                }
            }
            dataChange.countDown();
        }
    });

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

The "completion" CountDownLatch is the one I am using to wait on the onComplete() method to be triggered.

Does anyone have any insight as to why the completion listener never fires despite the data being written and appearing in the database?

Darin Beaudreau
  • 375
  • 7
  • 30
  • 1
    http://stackoverflow.com/questions/31700830/is-it-possible-to-synchronously-load-data-from-firebase/31702957#31702957 and http://stackoverflow.com/questions/37098006/firebase-with-java-non-android-retrive-information/37100794#37100794 – Doug Stevenson Apr 05 '17 at 02:27
  • So you're saying to enclose my setValue() call in yet another ValueEventListener for the whitelist reference? Do I still need the completion listener? Or can I just call setValue() with the string I'm pushing? – Darin Beaudreau Apr 05 '17 at 05:13
  • 1
    I don't know for certain, but I think the main problem is that you're blocking the Firebase thread when you call `completion.await()`. Don't block the thread that calls your listeners - they should return immediately. Block your own thread until all the other work has finished. – Doug Stevenson Apr 05 '17 at 05:16
  • I'm confused. So do I use the semaphore? Because Thread.sleep() seems like an inelegant solution; not to mention I need to know how long to sleep for. – Darin Beaudreau Apr 05 '17 at 16:36
  • 1
    You can use a CountDownLatch at the top level of your program. Just don't use it in the middle of your listener callback. – Doug Stevenson Apr 05 '17 at 17:29
  • EDIT: I removed the inner CountDownLatch and moved the countDown() call of the outer latch to the onComplete() method, and now it works! – Darin Beaudreau Apr 05 '17 at 20:44

0 Answers0