All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.firebase.database.core.RepoManager Maven / Gradle / Ivy

/*
 * Copyright 2017 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.firebase.database.core;

import com.google.firebase.FirebaseApp;
import com.google.firebase.database.DatabaseException;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.InternalHelpers;
import io.netty.util.concurrent.FastThreadLocal;

import java.util.HashMap;
import java.util.Map;

public class RepoManager {

  private static final RepoManager instance;

  static {
    instance = new RepoManager();
  }

  private final Map> repos = new HashMap<>();

  public RepoManager() {}

  /**
   * Used for legacy unit tests. The public API should go through FirebaseDatabase which calls
   * createRepo.
   */
  public static Repo getRepo(Context ctx, RepoInfo info) throws DatabaseException {
    return instance.getLocalRepo(ctx, info);
  }

  public static Repo createRepo(Context ctx, RepoInfo info, FirebaseDatabase database)
      throws DatabaseException {
    return instance.createLocalRepo(ctx, info, database);
  }

  public static void destroy(Context ctx) {
    try {
      instance.destroyInternal(ctx);
    } finally {
      ctx.stop();
      // Clean up Netty thread locals, which will also clean up any dangling threadDeathWatcher
      // daemons. See https://github.com/netty/netty/issues/7310 for more context.
      FastThreadLocal.removeAll();
    }
  }

  public static void interrupt(Context ctx) {
    instance.interruptInternal(ctx);
  }

  public static void interrupt(final Repo repo) {
    repo.scheduleNow(
        new Runnable() {
          @Override
          public void run() {
            repo.interrupt();
          }
        });
  }

  public static void resume(final Repo repo) {
    repo.scheduleNow(
        new Runnable() {
          @Override
          public void run() {
            repo.resume();
          }
        });
  }

  public static void resume(Context ctx) {
    instance.resumeInternal(ctx);
  }

  private Repo getLocalRepo(Context ctx, RepoInfo info) throws DatabaseException {
    ctx.freeze(); // No-op if it's already frozen
    String repoHash = "https://" + info.host + "/" + info.namespace;
    synchronized (repos) {
      if (!repos.containsKey(ctx) || !repos.get(ctx).containsKey(repoHash)) {
        // Calling this should create the repo.
        InternalHelpers.createDatabaseForTests(
            FirebaseApp.getInstance(), info, (DatabaseConfig) ctx);
      }
      return repos.get(ctx).get(repoHash);
    }
  }

  private Repo createLocalRepo(Context ctx, RepoInfo info, FirebaseDatabase database)
      throws DatabaseException {
    ctx.freeze(); // No-op if it's already frozen
    String repoHash = "https://" + info.host + "/" + info.namespace;
    synchronized (repos) {
      if (!repos.containsKey(ctx)) {
        Map innerMap = new HashMap<>();
        repos.put(ctx, innerMap);
      }
      Map innerMap = repos.get(ctx);
      if (!innerMap.containsKey(repoHash)) {
        Repo repo = new Repo(info, ctx, database);
        innerMap.put(repoHash, repo);
        return repo;
      } else {
        throw new IllegalStateException("createLocalRepo() called for existing repo.");
      }
    }
  }

  private void interruptInternal(final Context ctx) {
    RunLoop runLoop = ctx.getRunLoop();
    if (runLoop != null) {
      runLoop.scheduleNow(
          new Runnable() {
            @Override
            public void run() {
              synchronized (repos) {
                boolean allEmpty = true;
                if (repos.containsKey(ctx)) {
                  for (Repo repo : repos.get(ctx).values()) {
                    repo.interrupt();
                    allEmpty = allEmpty && !repo.hasListeners();
                  }
                  if (allEmpty) {
                    ctx.stop();
                  }
                }
              }
            }
          });
    }
  }

  private void destroyInternal(final Context ctx) {
    RunLoop runLoop = ctx.getRunLoop();
    if (runLoop != null) {
      // RunLoop gets initialized before any Repo is created. Therefore we can assume that when
      // the RunLoop is not present, there's nothing to clean up.
      runLoop.scheduleNow(new Runnable() {
        @Override
        public void run() {
          synchronized (repos) {
            if (repos.containsKey(ctx)) {
              for (Repo repo : repos.get(ctx).values()) {
                repo.interrupt();
              }
            }
          }
        }
      });
    }
  }


  private void resumeInternal(final Context ctx) {
    RunLoop runLoop = ctx.getRunLoop();
    if (runLoop != null) {
      runLoop.scheduleNow(
          new Runnable() {
            @Override
            public void run() {
              synchronized (repos) {
                if (repos.containsKey(ctx)) {
                  for (Repo repo : repos.get(ctx).values()) {
                    repo.resume();
                  }
                }
              }
            }
          });
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy