extern "C" NS_EXPORT void JNICALL
XPCOM_NATIVE(shutdownXPCOM) (JNIEnv *env, jobject, jobject aServMgr)
{
  nsresult rv;
  nsIServiceManager* servMgr = nullptr;
  if (aServMgr) {
    // Get native XPCOM instance
    nsISupports* instancePtr = nullptr;
    rv = JavaObjectToNativeInterface(env, aServMgr,
            NS_GET_IID(nsIServiceManager), (void**) &instancePtr);
    NS_ASSERTION(NS_SUCCEEDED(rv) && instancePtr != nullptr,
                 "Failed to get XPCOM obj for ServiceMgr.");
    if (NS_SUCCEEDED(rv)) {
      rv = instancePtr->QueryInterface(NS_GET_IID(nsIServiceManager),
                                       (void**) &servMgr);
      NS_ASSERTION(NS_SUCCEEDED(rv), "QI for nsIServiceManager failed");
    }

    // Even if we failed to get the matching xpcom object, we don't abort this
    // function.  Just call NS_ShutdownXPCOM with a null service manager.
  }

  // Free globals before calling NS_ShutdownXPCOM(), since we need some
  // XPCOM services.
  FreeJavaGlobals(env);

  rv = NS_ShutdownXPCOM(servMgr);
  if (NS_FAILED(rv))
    ThrowException(env, rv, "NS_ShutdownXPCOM failed");
}
extern "C" NS_EXPORT void JNICALL
GRE_NATIVE(termEmbedding) (JNIEnv *env, jobject)
{
  // Free globals before calling XRE_TermEmbedding(), since we need some
  // XPCOM services.
  FreeJavaGlobals(env);

  XRE_TermEmbedding();
}
extern "C" NS_EXPORT void JNICALL
GRE_NATIVE(initEmbedding) (JNIEnv* env, jobject, jobject aLibXULDirectory,
                           jobject aAppDirectory, jobject aAppDirProvider)
{
  nsresult rv = InitEmbedding_Impl(env, aLibXULDirectory, aAppDirectory,
                                   aAppDirProvider);

  if (NS_FAILED(rv)) {
    ThrowException(env, rv, "Failure in initEmbedding");
    FreeJavaGlobals(env);
  }
}
extern "C" NS_EXPORT jobject JNICALL
XPCOM_NATIVE(initXPCOMNative) (JNIEnv* env, jobject, jobject aMozBinDirectory,
                         jobject aAppFileLocProvider)
{
  jobject servMan;
  nsresult rv = InitXPCOM_Impl(env, aMozBinDirectory, aAppFileLocProvider,
                               &servMan);
  if (NS_SUCCEEDED(rv))
    return servMan;

  ThrowException(env, rv, "Failure in initXPCOM");
  FreeJavaGlobals(env);
  return nullptr;
}
/******************************
 *  InitializeJavaGlobals
 ******************************/
PRBool
InitializeJavaGlobals(JNIEnv *env)
{
  if (gJavaXPCOMInitialized)
    return PR_TRUE;

  // Save pointer to JavaVM, which is valid across threads.
  jint rc = env->GetJavaVM(&gCachedJVM);
  if (rc != 0) {
    NS_WARNING("Failed to get JavaVM");
    goto init_error;
  }

  jclass clazz;
  if (!(clazz = env->FindClass("java/lang/System")) ||
      !(systemClass = (jclass) env->NewGlobalRef(clazz)) ||
      !(hashCodeMID = env->GetStaticMethodID(clazz, "identityHashCode",
                                             "(Ljava/lang/Object;)I")))
  {
    NS_WARNING("Problem creating java.lang.System globals");
    goto init_error;
  }

  if (!(clazz = env->FindClass("java/lang/Boolean")) ||
      !(booleanClass = (jclass) env->NewGlobalRef(clazz)) ||
      !(booleanValueMID = env->GetMethodID(clazz, "booleanValue", "()Z")) ||
      !(booleanInitMID = env->GetMethodID(clazz, "<init>", "(Z)V")))
  {
    NS_WARNING("Problem creating java.lang.Boolean globals");
    goto init_error;
  }

  if (!(clazz = env->FindClass("java/lang/Character")) ||
      !(charClass = (jclass) env->NewGlobalRef(clazz)) ||
      !(charValueMID = env->GetMethodID(clazz, "charValue", "()C")) ||
      !(charInitMID = env->GetMethodID(clazz, "<init>", "(C)V")))
  {
    NS_WARNING("Problem creating java.lang.Character globals");
    goto init_error;
  }

  if (!(clazz = env->FindClass("java/lang/Byte")) ||
      !(byteClass = (jclass) env->NewGlobalRef(clazz)) ||
      !(byteValueMID = env->GetMethodID(clazz, "byteValue", "()B")) ||
      !(byteInitMID = env->GetMethodID(clazz, "<init>", "(B)V")))
  {
    NS_WARNING("Problem creating java.lang.Byte globals");
    goto init_error;
  }

  if (!(clazz = env->FindClass("java/lang/Short")) ||
      !(shortClass = (jclass) env->NewGlobalRef(clazz)) ||
      !(shortValueMID = env->GetMethodID(clazz, "shortValue", "()S")) ||
      !(shortInitMID = env->GetMethodID(clazz, "<init>", "(S)V")))
  {
    NS_WARNING("Problem creating java.lang.Short globals");
    goto init_error;
  }

  if (!(clazz = env->FindClass("java/lang/Integer")) ||
      !(intClass = (jclass) env->NewGlobalRef(clazz)) ||
      !(intValueMID = env->GetMethodID(clazz, "intValue", "()I")) ||
      !(intInitMID = env->GetMethodID(clazz, "<init>", "(I)V")))
  {
    NS_WARNING("Problem creating java.lang.Integer globals");
    goto init_error;
  }

  if (!(clazz = env->FindClass("java/lang/Long")) ||
      !(longClass = (jclass) env->NewGlobalRef(clazz)) ||
      !(longValueMID = env->GetMethodID(clazz, "longValue", "()J")) ||
      !(longInitMID = env->GetMethodID(clazz, "<init>", "(J)V")))
  {
    NS_WARNING("Problem creating java.lang.Long globals");
    goto init_error;
  }

  if (!(clazz = env->FindClass("java/lang/Float")) ||
      !(floatClass = (jclass) env->NewGlobalRef(clazz)) ||
      !(floatValueMID = env->GetMethodID(clazz, "floatValue", "()F")) ||
      !(floatInitMID = env->GetMethodID(clazz, "<init>", "(F)V")))
  {
    NS_WARNING("Problem creating java.lang.Float globals");
    goto init_error;
  }

  if (!(clazz = env->FindClass("java/lang/Double")) ||
      !(doubleClass = (jclass) env->NewGlobalRef(clazz)) ||
      !(doubleValueMID = env->GetMethodID(clazz, "doubleValue", "()D")) ||
      !(doubleInitMID = env->GetMethodID(clazz, "<init>", "(D)V")))
  {
    NS_WARNING("Problem creating java.lang.Double globals");
    goto init_error;
  }

  if (!(clazz = env->FindClass("java/lang/String")) ||
      !(stringClass = (jclass) env->NewGlobalRef(clazz)))
  {
    NS_WARNING("Problem creating java.lang.String globals");
    goto init_error;
  }

  if (!(clazz = env->FindClass("org/mozilla/interfaces/nsISupports")) ||
      !(nsISupportsClass = (jclass) env->NewGlobalRef(clazz)))
  {
    NS_WARNING("Problem creating org.mozilla.interfaces.nsISupports globals");
    goto init_error;
  }

  if (!(clazz = env->FindClass("org/mozilla/xpcom/XPCOMException")) ||
      !(xpcomExceptionClass = (jclass) env->NewGlobalRef(clazz)))
  {
    NS_WARNING("Problem creating org.mozilla.xpcom.XPCOMException globals");
    goto init_error;
  }

  if (!(clazz = env->FindClass("org/mozilla/xpcom/internal/XPCOMJavaProxy")) ||
      !(xpcomJavaProxyClass = (jclass) env->NewGlobalRef(clazz)) ||
      !(createProxyMID = env->GetStaticMethodID(clazz, "createProxy",
                                   "(Ljava/lang/Class;J)Ljava/lang/Object;")) ||
      !(isXPCOMJavaProxyMID = env->GetStaticMethodID(clazz, "isXPCOMJavaProxy",
                                                    "(Ljava/lang/Object;)Z")) ||
      !(getNativeXPCOMInstMID = env->GetStaticMethodID(xpcomJavaProxyClass,
                                                       "getNativeXPCOMInstance",
                                                       "(Ljava/lang/Object;)J")))
  {
    NS_WARNING("Problem creating org.mozilla.xpcom.internal.XPCOMJavaProxy globals");
    goto init_error;
  }

  if (!(clazz = env->FindClass("java/lang/ref/WeakReference")) ||
      !(weakReferenceClass = (jclass) env->NewGlobalRef(clazz)) ||
      !(weakReferenceConstructorMID = env->GetMethodID(weakReferenceClass, 
                                           "<init>","(Ljava/lang/Object;)V")) ||
      !(getReferentMID = env->GetMethodID(weakReferenceClass,
                                          "get", "()Ljava/lang/Object;")) ||
      !(clearReferentMID = env->GetMethodID(weakReferenceClass, 
                                            "clear", "()V")))
  {
    NS_WARNING("Problem creating java.lang.ref.WeakReference globals");
    goto init_error;
  }

  if (!(clazz = env->FindClass("org/mozilla/xpcom/internal/JavaXPCOMMethods")) ||
      !(javaXPCOMUtilsClass = (jclass) env->NewGlobalRef(clazz)) ||
      !(findClassInLoaderMID = env->GetStaticMethodID(clazz,
                    "findClassInLoader",
                    "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Class;")))
  {
    NS_WARNING("Problem creating org.mozilla.xpcom.internal.JavaXPCOMMethods globals");
    goto init_error;
  }

#ifdef DEBUG_JAVAXPCOM
  if (!(clazz = env->FindClass("java/lang/Class")) ||
      !(getNameMID = env->GetMethodID(clazz, "getName","()Ljava/lang/String;")))
  {
    NS_WARNING("Problem creating java.lang.Class globals");
    goto init_error;
  }

  if (!(proxyToStringMID = env->GetStaticMethodID(xpcomJavaProxyClass,
                                                  "proxyToString",
                                     "(Ljava/lang/Object;)Ljava/lang/String;")))
  {
    NS_WARNING("Problem creating proxyToString global");
    goto init_error;
  }
#endif

  gNativeToJavaProxyMap = new NativeToJavaProxyMap();
  if (!gNativeToJavaProxyMap || NS_FAILED(gNativeToJavaProxyMap->Init())) {
    NS_WARNING("Problem creating NativeToJavaProxyMap");
    goto init_error;
  }
  gJavaToXPTCStubMap = new JavaToXPTCStubMap();
  if (!gJavaToXPTCStubMap || NS_FAILED(gJavaToXPTCStubMap->Init())) {
    NS_WARNING("Problem creating JavaToXPTCStubMap");
    goto init_error;
  }

  {
    nsresult rv = NS_OK;
    PRUint32 size = NS_ARRAY_LENGTH(kJavaKeywords);
    gJavaKeywords = new nsTHashtable<nsDepCharHashKey>();
    if (!gJavaKeywords || NS_FAILED(gJavaKeywords->Init(size))) {
      NS_WARNING("Failed to init JavaKeywords HashSet");
      goto init_error;
    }
    for (PRUint32 i = 0; i < size && NS_SUCCEEDED(rv); i++) {
      if (!gJavaKeywords->PutEntry(kJavaKeywords[i])) {
        rv = NS_ERROR_OUT_OF_MEMORY;
      }
    }
    if (NS_FAILED(rv)) {
      NS_WARNING("Failed to populate JavaKeywords hash");
      goto init_error;
    }
  }

  gJavaXPCOMLock = PR_NewLock();
  gJavaXPCOMInitialized = PR_TRUE;
  return PR_TRUE;

init_error:
  // If we encounter an error during initialization, then free any globals that
  // were allocated, and return false.
  FreeJavaGlobals(env);
  return PR_FALSE;
}