Java EE FirebaseApp 名称 [DEFAULT] 已存在

Java EE FirebaseApp name [DEFAULT] already exists

我遇到了与 Firebase 和 Java EE 有关的问题。

我目前正在为我的项目编写一些 Java servlet,我是第一次使用 Firebase,因为我想尝试一些新东西。

我的实际问题如下: 我有一个 servlet,它负责在用户数据库中交换 iOS 设备令牌。这是向设备发送远程推送通知所必需的。 我在 google 教程中这样做了,但我遇到了以下异常:

java.lang.IllegalStateException: FirebaseApp name [DEFAULT] already exists!

我访问 Firebase 数据库的方式是通过 Java SDK。

我使用以下代码执行此操作:

连接方式

    // gets called by the servlet to configure Firebase
    public static void connect() {
      try {
        // for authentication purpose
        Map<String, Object> auth = new HashMap<>();
        auth.put("uid", "my-service-account");

        // Setting up the FirebaseOptions object
        // constant FIREBASE_DATABASE_URL = "url to my database"
        // constant FIREBASE_KEY_PATH = "path to my json key"
        options = new FirebaseOptions.Builder()
                  .setDatabaseUrl(FIREBASE_DATABASE_URL)
                  .setServiceAccount(new FileInputStream(FIREBASE_KEY_PATH))
                  .setDatabaseAuthVariableOverride(auth)
                  .build();

        FirebaseApp.initializeApp(options);

        // calling the method for exchanging the token
        exchangeIosDeviceToken("testmail@example.com", "5bf53173c9ef0a37638f3ddaa59cf2c0687c14ca0dcd47ccf57f9f09bd6368ab");
      } catch (FileNotFoundException ex) {
        ex.printStackTrace();
      }
    }

exchangeIosDeviceToken 方法

    public static boolean exchangeIosDeviceToken(String email, String newDeviceToken) {
      FirebaseDatabase database = FirebaseDatabase.getInstance();

      // getting a reference to my "employee" child
      DatabaseReference employeeReference = database.getReference("/employee");

      Map<String, Object> employeeUpdates = new HashMap<>();
      // updating the device token with child "iosDeviceToken" of "employee"
      employeeUpdates.put(email+"/iosDeviceToken", newDeviceToken);

      // update the actual children
      employeeReference.updateChildren(employeeUpdates);
      return true;
    }

有趣的是,当我尝试在独立的 main class 中执行此代码(用 main 方法替换 connect 方法)时,代码运行正常。

在你说 "there are tons of questions related to this topic" 之类的话之前......它们几乎都与 Android 相关,而与我的问题相关的问题很少有人回答。

此致

问题解决了。

问题是: 每次收到请求时,我都会调用 connect 方法。

解法: 只调用一次连接方法。 (ServletContextListener)

我解决这个问题的方法是调用 FirebaseApp.getInstance().delete();

对于以后的用户,可以这样检查默认应用是否初始化。

    FirebaseApp firebaseApp = null;
    List<FirebaseApp> firebaseApps = FirebaseApp.getApps();
    if(firebaseApps!=null && !firebaseApps.isEmpty()){
        for(FirebaseApp app : firebaseApps){
            if(app.getName().equals(FirebaseApp.DEFAULT_APP_NAME))
                firebaseApp = app;
        }
    }
    else
        firebaseApp = FirebaseApp.initializeApp(options);   

出现此异常是因为您正在尝试再次创建 [DEFAULT] FirebaseApp,只需在初始化之前添加验证以检查它是否存在,如下所示:

if(FirebaseApp.getInstance(FirebaseApp.DEFAULT_APP_NAME) != null) {
    return;
}

我的解决方案

    package com.zs.configuration;

    import com.google.auth.oauth2.GoogleCredentials;
    import com.google.firebase.FirebaseApp;
    import com.google.firebase.FirebaseOpt`enter code here`ions;
    import com.google.firebase.auth.FirebaseAuth;

    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    import javax.servlet.annotation.WebListener;
    import java.io.FileInputStream;

    @WebListener
    public class MyAppServletContextListener
            implements ServletContextListener{

        @Override
        public void contextDestroyed(ServletContextEvent arg0) {
            System.out.println("ServletContextListener destroyed");
        }

        //Run this before web application is started
        @Override
        public void contextInitialized(ServletContextEvent arg0) {
            try {
                FileInputStream serviceAccount = new FileInputStream(getClass().getClassLoader().getResource("zs.json").getFile());

                FirebaseOptions options = new FirebaseOptions.Builder()
                        .setCredentials(GoogleCredentials.fromStream(serviceAccount))
                        .setDatabaseUrl("https://zs.firebaseio.com")
                        .build();


                FirebaseApp firebaseApp = FirebaseApp.initializeApp(options);
                FirebaseAuth.getInstance(firebaseApp);

            }catch (Exception exc){
                System.out.println("Firebase exception "+exc);

            }
            System.out.println("ServletContextListener started");
        }
    }

在我的申请中class

    @ServletComponentScan
    @SpringBootApplication
    public class ZSApplication {


        public static void main(String[] args) {

            SpringApplication.run(ZSApplication.class, args);

        }

    }

总结所有解决方案,顺便说一句,这里提出了非常好的解决方案:Running code after Spring Boot starts请在下面找到适合我的 spring-boot (2.6.3) 实现。

注意:请在下方替换为您的应用和文件名。

将 firebase 初始化方法 (FirebaseApp.initializeApp(options)) 直接放在主要方法上(正如我在本教程中发现的那样:https://www.youtube.com/watch?v=8jK9O0lwem0)它导致了错误,只是因为调试我注意到,由于某种原因,方法 FirebaseApp.initializeApp(options) 的调用;已完成多次。

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Objects;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@SpringBootApplication
public class YourAppNameApplication {

    public static void main(String[] args) {
        SpringApplication.run(YourAppNameApplication.class, args);
    }

    /**
     * Initialize the firebase SDK to integrate with the firebase application. Used
     * for check the clients UIDs authentications.
     * 
     * @throws IOException in case the firebase configuration JSON file is not
     *                     present on the path.
     */
    @EventListener(ApplicationReadyEvent.class)
    public void initFirebaseSDK() throws IOException {
        ClassLoader classLoader = YourAppNameApplication.class.getClassLoader();

        File file = new File(
                Objects.requireNonNull(classLoader.getResource("your_file_firebase.json")).getFile());
        FileInputStream serviceAccount = new FileInputStream(file.getAbsolutePath());

        @SuppressWarnings("deprecation")
        FirebaseOptions options = new FirebaseOptions.Builder()
                .setCredentials(GoogleCredentials.fromStream(serviceAccount))
                .setDatabaseUrl("https://your_firebase_app_name.firebaseio.com").build();

        FirebaseApp.initializeApp(options);

        if (!(FirebaseApp.getApps().isEmpty())
                && FirebaseApp.getInstance(FirebaseApp.DEFAULT_APP_NAME).getName() != null) {
            log.info("Firebase SDK has been initialised with the app name: "
                    + FirebaseApp.getInstance(FirebaseApp.DEFAULT_APP_NAME).getName());
        } else {
            log.error(
                    "Firebase SDK has NOT been initialised. This is a serious error. Please contact the administrator of the app.");
        }
    }

}