0

I'm currently working with Firebase Realtime Database. I'm trying to read the data once when a certain method in my program gets called.

This is a project for a Discord bot. (https://github.com/DV8FromTheWorld/JDA)

The data is being read, however the execution order is wrong. I make the call to get the data but the program continues even-though the methods haven't been called yet properly.

To showcase my error I will include the code. Within the code I do printlines to illustrate what happens (or the order everything gets executed in).

I have attempted to work with Threads to fix this but I haven't managed to get it to work.

The code:

public class userLogs extends ListenerAdapter {

private FirebaseDatabase db;
private JDA jda;
private Stack<offense> allOffenses = new Stack<>();
public userLogs(FirebaseDatabase db, JDA jda){
    this.db = db;
    this.jda = jda;
}

@Override
public  void onSlashCommand(SlashCommandEvent event)
{

    if (!event.getName().equals("logs")) return; // make sure we handle the right command

    this.allOffenses = new Stack<>();
    // Selected user you want the logs from
    String userID = event.getOption("user").getAsUser().getId();


    new Thread(() -> {
        /** GET ALL MUTES*/
        db.getReference("Mutes").addListenerForSingleValueEvent(
                new ValueEventListener() {

                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {

                        System.out.println("I am now getting all logs from the database.");

                        for(DataSnapshot ds : dataSnapshot.getChildren()){
                            String offenderID = ds.child("offenderID").getValue(String.class);

                            if(offenderID.equals(userID)) {
                                // Get moderator
                                String modID = ds.child("moderatorID").getValue(String.class);
                                User moderator = jda.getUserById(modID);

                                // Get reason
                                String reason = ds.child("reason").getValue(String.class);

                                Duration duration = Duration.ofSeconds(ds.child("duration").getValue(Long.class));

                                saveOffense(new offense("Mute", moderator.getAsTag(), reason, duration));
                            }

                        }
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
    }).start();

    System.out.println("I just got out of the Thread. The stack size is: " + allOffenses.size());

    // Create embed
    EmbedBuilder eb = new EmbedBuilder();
    eb.setTitle("Logs for user: " + jda.getUserById(userID).getAsTag());
    StringBuilder sb = new StringBuilder();

    for(int i = 0; i < allOffenses.size(); i++){

        sb.append("Type: \n").append(allOffenses.get(i).getOffenseName());
        sb.append("Moderator: \n" + allOffenses.get(i).getModerator());
        sb.append("Reason: \n" + allOffenses.get(i).getReason());

        Duration dur = allOffenses.get(i).getDuration();
        if(dur != Duration.ZERO){
            sb.append("Duration: \n" + dur);
        }

    }

    eb.setDescription(sb.toString());

    event.getChannel().sendMessageEmbeds(eb.build()).queue();

}

void saveOffense(offense off){
    System.out.println("I just got in the method where I put a found offense into the stack.");
    this.allOffenses.push(off);
}

}

This outputs the following whenever the method gets called: (There are 3 offenses logged so that is correct)

enter image description here

As you can see it exists the thread before it executes the code within it. I'm very confused about this and have spent the past 5+ hours trying to fix this.

I hope that I've explained the issue correctly. Thank you all very much for your help!

  • Is that an Android project? If yes, have you tried to use an async approach as explained in this [answer](https://stackoverflow.com/questions/47847694/how-to-return-datasnapshot-value-as-a-result-of-a-method/47853774)? – Alex Mamo Jan 07 '22 at 12:38
  • This is not an Android project, it's a Discord bot @AlexMamo – JustAnotherPatrick Jan 07 '22 at 12:38
  • You need to put all your code inside the thread if you want it to execute in sequence. – Minn Jan 07 '22 at 13:09
  • This is the expected behavior: data is loaded from Firebase (and most modern cloud APIs) asynchronously (on a different thread already, you don't need to spawn a `Thread` for that in your code), and your main code continues while the data is being loaded. Then when the data is available, your `onDataChange` gets called with it and you can use it. This means that: **any code that needs the data from Firebase has to be inside `onDataChange`**, be called from there, or otherwise synchronized. See the answers I linked for the options. – Frank van Puffelen Jan 07 '22 at 15:53

0 Answers0