Marcelo Delgado
Marcelo Delgado

Reputation: 11

Listener's onDataChange() is not called by Firebase. Could not use new Google-Firebase on Java client, not Android

I've being trying for a week right now to migrate from Firebase to google-firebase on a Java Client. One important detail is that the client will run in a controlled environment so I can allow some key to be stored locally.

After my studies I could find three ways based on this tutorial.

As a "server" using the service account key the onDataChange() callback is not being called. This is the solution I put more effort and the one I still believe. I'll put my code forward on this question. I'm suspecting that I'm not handling the asynchronous call right.

My objective question about this example is, are the examples in this tutorial self contained or is there something I should implement on a Java program to make this work and I'm missing?

This example code:

class Conf
{
    public String activeMQPassword;
    public String activeMQPath;
    public String activeMQUser;
    public String jdbcPassword;
    public String jdbcUrl;
    public String jdbcUser;
    public String jmsserver;
    public String logPath;
    public String s3AccessKey;
    public String s3Path;
    public String s3SecretKey;

    public Conf(String activeMQPassword, String activeMQPath, String activeMQUser, String jdbcPassword, String jdbcUrl, String jdbcUser, String jmsserver, String logPath, String s3AccessKey, String s3Path, String s3SecretKey)
    {
        this.activeMQPassword = activeMQPassword;
        this.activeMQPath = activeMQPath;
        this.activeMQUser = activeMQUser;
        this.jdbcPassword = jdbcPassword;
        this.jdbcUrl = jdbcUrl;
        this.jdbcUser = jdbcUser;
        this.jmsserver = jmsserver;
        this.logPath = logPath;
        this.s3AccessKey = s3AccessKey;
        this.s3Path = s3Path;
        this.s3SecretKey = s3SecretKey;
    }

}



FirebaseOptions  options = new FirebaseOptions.Builder()
                .setServiceAccount(new FileInputStream("c:/chaveFB/TesteNovaApi-0efd51a8ad0c.json"))
                .setDatabaseUrl("https://testenovaapi.firebaseio.com/")
                //.setDatabaseAuthVariableOverride(auth) //AQUI SETAMOS A RESTRIÇÃO DE ACESSO CRIADA LOGO ACIMA.
                .build();
FirebaseApp.initializeApp(options);

DatabaseReference ref = FirebaseDatabase
            .getInstance()
            .getReference("/conf");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        System.out.println("LA2LA2LA2LA2LA2LA2LA2LA2LA2LA2");
        Conf conf = dataSnapshot.getValue(Conf.class);
        System.out.println(conf.activeMQPassword);
        System.out.println(conf.activeMQPath);
        System.out.println(conf.activeMQUser);
        System.out.println(conf.jdbcPassword);
        System.out.println(conf.jdbcUser);
        System.out.println(conf.jmsserver);
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {

    }
});

I was able to find an answer in a Stack Overflow topic that leads to this PDF to solve the callback problem, but this solution seems to be very Android driven since I could not follow lots of steps in my Java program.

As an Android or web client, I'm was not able to import the libs needed to my Java application.

Using the API, In the past I was using the API with custom token to download a json and retrieve data. But since the database secret keys were deprecated, the new java library works only in "server mode" so the custom-key created by this server library was not able to retrieve data. Firebase was responding with an error like user-id key expected received custom-key.

If I could make this approach to work, is OK for me since where the Java client is being installed is controlled and safe enough. And it was my older approach.

I must be terribly wrong, and I prefer to be. But my sensation is that I'm locked in a case that is not yet covered or well documented. A Java program that acts like a client and it's not Android or web.

Upvotes: 1

Views: 423

Answers (1)

Doug Stevenson
Doug Stevenson

Reputation: 317657

If you have a java program that's invoked on the command line via some class's main function, you have to prevent that main function from returning before the database listener fires. Otherwise, the program will immediately terminate and the listener will never fire. It's important to remember that all Firebase listeners are asynchronous, and Firebase manages its own daemon thread for communicating with the server.

For the case of making a simple program wait until a listener triggers, you can use this sort of pattern to block the main thread to wait for the listener:

CountDownLatch latch = new CountDownLatch(1);
ref.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        System.out.println("onDataChange: " + dataSnapshot);
        latch.countDown();
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        System.out.println("onCanceled: " + databaseError);
        latch.countDown();
    }
});
latch.await();

Using a CountDownLatch is not your only option. You can do whatever you want to make sure the process doesn't stop, including simply sleeping the main thread for longer than it takes for the listener to fire.

Upvotes: 3

Related Questions