You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by be...@apache.org on 2011/06/05 11:29:36 UTC

svn commit: r1132352 - /incubator/mesos/trunk/src/java/jni/convert.cpp

Author: benh
Date: Sun Jun  5 09:29:35 2011
New Revision: 1132352

URL: http://svn.apache.org/viewvc?rev=1132352&view=rev
Log:
Fixed Mesos Java library for environments where the Mesos classes are
not available through the system classloader, such as Scala. (Ported
the solution that existed before the protobuf switch).

Modified:
    incubator/mesos/trunk/src/java/jni/convert.cpp

Modified: incubator/mesos/trunk/src/java/jni/convert.cpp
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/src/java/jni/convert.cpp?rev=1132352&r1=1132351&r2=1132352&view=diff
==============================================================================
--- incubator/mesos/trunk/src/java/jni/convert.cpp (original)
+++ incubator/mesos/trunk/src/java/jni/convert.cpp Sun Jun  5 09:29:35 2011
@@ -1,6 +1,7 @@
 #include <jni.h>
 
 #include <string>
+#include <assert.h>
 
 #include <mesos/mesos.hpp>
 
@@ -10,6 +11,125 @@ using namespace mesos;
 
 using std::string;
 
+// Facilities for loading Mesos-related classes with the correct ClassLoader.
+// Unfortunately, JNI's FindClass uses the system ClassLoader when it is
+// called from a C++ thread, but in Scala (and probably other Java
+// environments too), this ClassLoader is not enough to locate mesos.jar.
+// Instead, we try to capture Thread.currentThread()'s context ClassLoader
+// when the Mesos library is initialized, in case it has more paths that
+// we can search. We store this in mesosClassLoader and access it through
+// FindMesosClass(). We initialize the mesosClassLoader variable in
+// JNI_OnLoad and uninitialize it in JNI_OnUnLoad (see below).
+//
+// This code is based on Apache 2 licensed Android code obtained from
+// http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/jni/AndroidRuntime.cpp;h=f61e2476c71191aa6eabc93bcb26b3c15ccf6136;hb=HEAD
+namespace {
+
+jweak mesosClassLoader = NULL; // Initialized in JNI_OnLoad later in this file.
+
+
+jclass FindMesosClass(JNIEnv* env, const char* className)
+{
+  if (env->ExceptionCheck()) {
+      fprintf(stderr, "ERROR: exception pending on entry to "
+                      "FindMesosClass()\n");
+      return NULL;
+  }
+
+  if (mesosClassLoader == NULL) {
+    return env->FindClass(className);
+  }
+
+  /*
+   * JNI FindClass uses class names with slashes, but ClassLoader.loadClass
+   * uses the dotted "binary name" format. Convert formats.
+   */
+  std::string convName = className;
+  for (int i = 0; i < convName.size(); i++) {
+    if (convName[i] == '/')
+      convName[i] = '.';
+  }
+
+  jclass javaLangClassLoader = env->FindClass("java/lang/ClassLoader");
+  assert(javaLangClassLoader != NULL);
+  jmethodID loadClass = env->GetMethodID(javaLangClassLoader,
+    "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
+  assert(loadClass != NULL);
+  jclass cls = NULL;
+
+  /* create an object for the class name string; alloc could fail */
+  jstring strClassName = env->NewStringUTF(convName.c_str());
+  if (env->ExceptionCheck()) {
+    fprintf(stderr, "ERROR: unable to convert '%s' to string\n", convName.c_str());
+    goto bail;
+  }
+
+  /* try to find the named class */
+  cls = (jclass) env->CallObjectMethod(mesosClassLoader, loadClass,
+                                       strClassName);
+  if (env->ExceptionCheck()) {
+    fprintf(stderr, "ERROR: unable to load class '%s' from %p\n",
+      className, mesosClassLoader);
+    cls = NULL;
+    goto bail;
+  }
+
+bail:
+  return cls;
+}
+
+} /* namespace { */
+
+
+// Called by JVM when it loads our library
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* jvm, void* reserved)
+{
+  // Grab the context ClassLoader of the current thread, if any
+
+  JNIEnv* env;
+  if (jvm->GetEnv((void **)&env, JNI_VERSION_1_2)) {
+    return JNI_ERR; /* JNI version not supported */
+  }
+
+  jclass javaLangThread, javaLangClassLoader;
+  jmethodID currentThread, getContextClassLoader, loadClass;
+  jobject thread, classLoader;
+
+  /* find this thread's context class loader; none of this is expected to fail */
+  javaLangThread = env->FindClass("java/lang/Thread");
+  assert(javaLangThread != NULL);
+  javaLangClassLoader = env->FindClass("java/lang/ClassLoader");
+  assert(javaLangClassLoader != NULL);
+  currentThread = env->GetStaticMethodID(javaLangThread,
+    "currentThread", "()Ljava/lang/Thread;");
+  getContextClassLoader = env->GetMethodID(javaLangThread,
+    "getContextClassLoader", "()Ljava/lang/ClassLoader;");
+  assert(currentThread != NULL);
+  assert(getContextClassLoader != NULL);
+  thread = env->CallStaticObjectMethod(javaLangThread, currentThread);
+  assert(thread != NULL);
+  classLoader = env->CallObjectMethod(thread, getContextClassLoader);
+  if (classLoader != NULL) {
+    mesosClassLoader = env->NewWeakGlobalRef(classLoader);
+  }
+
+  return JNI_VERSION_1_2;
+}
+
+
+// Called by JVM when it unloads our library
+JNIEXPORT void JNICALL JNI_OnUnLoad(JavaVM* jvm, void* reserved)
+{
+  JNIEnv *env;
+  if (jvm->GetEnv((void **)&env, JNI_VERSION_1_2)) {
+    return;
+  }
+  if (mesosClassLoader != NULL) {
+    env->DeleteWeakGlobalRef(mesosClassLoader);
+    mesosClassLoader = NULL;
+  }
+}
+
 
 template <>
 jobject convert(JNIEnv* env, const string& s)
@@ -29,7 +149,7 @@ jobject convert(JNIEnv* env, const Frame
   env->SetByteArrayRegion(jdata, 0, data.size(), (jbyte*) data.data());
 
   // FrameworkID frameworkId = FrameworkID.parseFrom(data);
-  jclass clazz = env->FindClass("org/apache/mesos/Protos$FrameworkID");
+  jclass clazz = FindMesosClass(env, "org/apache/mesos/Protos$FrameworkID");
 
   jmethodID parseFrom =
     env->GetStaticMethodID(clazz, "parseFrom",
@@ -52,7 +172,7 @@ jobject convert(JNIEnv* env, const Execu
   env->SetByteArrayRegion(jdata, 0, data.size(), (jbyte*) data.data());
 
   // ExecutorID executorId = ExecutorID.parseFrom(data);
-  jclass clazz = env->FindClass("org/apache/mesos/Protos$ExecutorID");
+  jclass clazz = FindMesosClass(env, "org/apache/mesos/Protos$ExecutorID");
 
   jmethodID parseFrom =
     env->GetStaticMethodID(clazz, "parseFrom",
@@ -75,7 +195,7 @@ jobject convert(JNIEnv* env, const TaskI
   env->SetByteArrayRegion(jdata, 0, data.size(), (jbyte*) data.data());
 
   // TaskID taskId = TaskID.parseFrom(data);
-  jclass clazz = env->FindClass("org/apache/mesos/Protos$TaskID");
+  jclass clazz = FindMesosClass(env, "org/apache/mesos/Protos$TaskID");
 
   jmethodID parseFrom =
     env->GetStaticMethodID(clazz, "parseFrom",
@@ -98,7 +218,7 @@ jobject convert(JNIEnv* env, const Slave
   env->SetByteArrayRegion(jdata, 0, data.size(), (jbyte*) data.data());
 
   // SlaveID slaveId = SlaveID.parseFrom(data);
-  jclass clazz = env->FindClass("org/apache/mesos/Protos$SlaveID");
+  jclass clazz = FindMesosClass(env, "org/apache/mesos/Protos$SlaveID");
 
   jmethodID parseFrom =
     env->GetStaticMethodID(clazz, "parseFrom",
@@ -121,7 +241,7 @@ jobject convert(JNIEnv* env, const Offer
   env->SetByteArrayRegion(jdata, 0, data.size(), (jbyte*) data.data());
 
   // OfferID offerId = OfferID.parseFrom(data);
-  jclass clazz = env->FindClass("org/apache/mesos/Protos$OfferID");
+  jclass clazz = FindMesosClass(env, "org/apache/mesos/Protos$OfferID");
 
   jmethodID parseFrom =
     env->GetStaticMethodID(clazz, "parseFrom",
@@ -139,7 +259,7 @@ jobject convert(JNIEnv* env, const TaskS
   jint jvalue = state;
 
   // TaskState state = TaskState.valueOf(value);
-  jclass clazz = env->FindClass("org/apache/mesos/Protos$TaskState");
+  jclass clazz = FindMesosClass(env, "org/apache/mesos/Protos$TaskState");
 
   jmethodID valueOf =
     env->GetStaticMethodID(clazz, "valueOf",
@@ -162,7 +282,7 @@ jobject convert(JNIEnv* env, const TaskD
   env->SetByteArrayRegion(jdata, 0, data.size(), (jbyte*) data.data());
 
   // TaskDescription task = TaskDescription.parseFrom(data);
-  jclass clazz = env->FindClass("org/apache/mesos/Protos$TaskDescription");
+  jclass clazz = FindMesosClass(env, "org/apache/mesos/Protos$TaskDescription");
 
   jmethodID parseFrom =
     env->GetStaticMethodID(clazz, "parseFrom",
@@ -185,7 +305,7 @@ jobject convert(JNIEnv* env, const TaskS
   env->SetByteArrayRegion(jdata, 0, data.size(), (jbyte*) data.data());
 
   // TaskStatus status = TaskStatus.parseFrom(data);
-  jclass clazz = env->FindClass("org/apache/mesos/Protos$TaskStatus");
+  jclass clazz = FindMesosClass(env, "org/apache/mesos/Protos$TaskStatus");
 
   jmethodID parseFrom =
     env->GetStaticMethodID(clazz, "parseFrom",
@@ -208,7 +328,7 @@ jobject convert(JNIEnv* env, const Slave
   env->SetByteArrayRegion(jdata, 0, data.size(), (jbyte*) data.data());
 
   // SlaveOffer offer = SlaveOffer.parseFrom(data);
-  jclass clazz = env->FindClass("org/apache/mesos/Protos$SlaveOffer");
+  jclass clazz = FindMesosClass(env, "org/apache/mesos/Protos$SlaveOffer");
 
   jmethodID parseFrom =
     env->GetStaticMethodID(clazz, "parseFrom",
@@ -231,7 +351,7 @@ jobject convert(JNIEnv* env, const Execu
   env->SetByteArrayRegion(jdata, 0, data.size(), (jbyte*) data.data());
 
   // ExecutorInfo executor = ExecutorInfo.parseFrom(data);
-  jclass clazz = env->FindClass("org/apache/mesos/Protos$ExecutorInfo");
+  jclass clazz = FindMesosClass(env, "org/apache/mesos/Protos$ExecutorInfo");
 
   jmethodID parseFrom =
     env->GetStaticMethodID(clazz, "parseFrom",
@@ -254,7 +374,7 @@ jobject convert(JNIEnv* env, const Execu
   env->SetByteArrayRegion(jdata, 0, data.size(), (jbyte*) data.data());
 
   // ExecutorArgs args = ExecutorArgs.parseFrom(data);
-  jclass clazz = env->FindClass("org/apache/mesos/Protos$ExecutorArgs");
+  jclass clazz = FindMesosClass(env, "org/apache/mesos/Protos$ExecutorArgs");
 
   jmethodID parseFrom =
     env->GetStaticMethodID(clazz, "parseFrom",