]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
java: support ceph_get_osd_addr
authorNoah Watkins <noahwatkins@gmail.com>
Fri, 22 Mar 2013 19:42:47 +0000 (12:42 -0700)
committerNoah Watkins <noahwatkins@gmail.com>
Sun, 24 Mar 2013 17:51:06 +0000 (10:51 -0700)
Adds a few JNI utilities from the Android project (license: Apache) to
help with IP address conversions. These functions are also updated to
work in our environment (use Ceph exception utilities, edit header
paths).

Signed-off-by: Noah Watkins <noahwatkins@gmail.com>
src/Makefile.am
src/java/java/com/ceph/fs/CephMount.java
src/java/native/JniConstants.cpp [new file with mode: 0644]
src/java/native/JniConstants.h [new file with mode: 0644]
src/java/native/ScopedLocalRef.h [new file with mode: 0644]
src/java/native/libcephfs_jni.cc
src/java/test/com/ceph/fs/CephMountTest.java

index fd54df867e214e5a6e98dabb95ea2c425c59c344..12bc53da6da9bd1879dc44dae8e5dae90e15ece4 100644 (file)
@@ -561,7 +561,11 @@ endif
 ##  - The JNI library is here
 ##  - The Java source Makefile.am is in src/java
 if ENABLE_CEPHFS_JAVA
-libcephfs_jni_la_SOURCES = java/native/libcephfs_jni.cc
+libcephfs_jni_la_SOURCES = \
+       java/native/libcephfs_jni.cc \
+       java/native/ScopedLocalRef.h \
+       java/native/JniConstants.cpp \
+       java/native/JniConstants.h
 libcephfs_jni_la_LIBADD = libcephfs.la $(EXTRALIBS)
 libcephfs_jni_la_CPPFLAGS = $(JDK_CPPFLAGS)
 libcephfs_jni_la_CXXFLAGS = ${AM_CXXFLAGS}
index dcc263063fedc099efc17898e05eebfe532f4ddf..1dd373c759f10cf4379e87ea4496ec4db43ab02f 100644 (file)
@@ -21,6 +21,7 @@ package com.ceph.fs;
 
 import java.io.IOException;
 import java.io.FileNotFoundException;
+import java.net.InetAddress;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.Arrays;
@@ -1032,4 +1033,21 @@ public class CephMount {
   }
 
   private static native String[] native_ceph_get_osd_crush_location(long mountp, int osd);
+
+  /**
+   * Get the network address of an OSD.
+   *
+   * @param osd The OSD device id.
+   * @return The network address.
+   */
+  public InetAddress get_osd_address(int osd) {
+    rlock.lock();
+    try {
+      return native_ceph_get_osd_addr(instance_ptr, osd);
+    } finally {
+      rlock.unlock();
+    }
+  }
+
+  private static native InetAddress native_ceph_get_osd_addr(long mountp, int osd);
 }
diff --git a/src/java/native/JniConstants.cpp b/src/java/native/JniConstants.cpp
new file mode 100644 (file)
index 0000000..cd54c99
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include "JniConstants.h"
+#include "ScopedLocalRef.h"
+
+#include <stdlib.h>
+
+jclass JniConstants::inet6AddressClass;
+jclass JniConstants::inetAddressClass;
+jclass JniConstants::inetSocketAddressClass;
+jclass JniConstants::stringClass;
+
+static jclass findClass(JNIEnv* env, const char* name) {
+    ScopedLocalRef<jclass> localClass(env, env->FindClass(name));
+    jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get()));
+    if (result == NULL) {
+        fprintf(stderr, "failed to find class '%s'", name);
+        abort();
+    }
+    return result;
+}
+
+void JniConstants::init(JNIEnv* env) {
+    inet6AddressClass = findClass(env, "java/net/Inet6Address");
+    inetAddressClass = findClass(env, "java/net/InetAddress");
+    inetSocketAddressClass = findClass(env, "java/net/InetSocketAddress");
+    stringClass = findClass(env, "java/lang/String");
+}
diff --git a/src/java/native/JniConstants.h b/src/java/native/JniConstants.h
new file mode 100644 (file)
index 0000000..acee867
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef JNI_CONSTANTS_H_included
+#define JNI_CONSTANTS_H_included
+
+#include <jni.h>
+
+/**
+ * A cache to avoid calling FindClass at runtime.
+ *
+ * Class lookup is relatively expensive (2.5us on passion-eng at the time of writing), so we do
+ * all such lookups eagerly at VM startup. This means that code that never uses, say,
+ * java.util.zip.Deflater still has to pay for the lookup, but it means that on a device the cost
+ * is definitely paid during boot and amortized. A central cache also removes the temptation to
+ * dynamically call FindClass rather than add a small cache to each file that needs one. Another
+ * cost is that each class cached here requires a global reference, though in practice we save
+ * enough by not having a global reference for each file that uses a class such as java.lang.String
+ * which is used in several files.
+ *
+ * FindClass is still called in a couple of situations: when throwing exceptions, and in some of
+ * the serialization code. The former is clearly not a performance case, and we're currently
+ * assuming that neither is the latter.
+ *
+ * TODO: similar arguments hold for field and method IDs; we should cache them centrally too.
+ */
+struct JniConstants {
+    static void init(JNIEnv* env);
+
+    static jclass inet6AddressClass;
+    static jclass inetAddressClass;
+    static jclass inetSocketAddressClass;
+    static jclass stringClass;
+};
+
+#define NATIVE_METHOD(className, functionName, signature) \
+    { #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName) }
+
+#endif  // JNI_CONSTANTS_H_included
diff --git a/src/java/native/ScopedLocalRef.h b/src/java/native/ScopedLocalRef.h
new file mode 100644 (file)
index 0000000..71d5776
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef SCOPED_LOCAL_REF_H_included
+#define SCOPED_LOCAL_REF_H_included
+
+#include "jni.h"
+
+#include <stddef.h>
+
+// A smart pointer that deletes a JNI local reference when it goes out of scope.
+template<typename T>
+class ScopedLocalRef {
+public:
+    ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) {
+    }
+
+    ~ScopedLocalRef() {
+        reset();
+    }
+
+    void reset(T ptr = NULL) {
+        if (ptr != mLocalRef) {
+            if (mLocalRef != NULL) {
+                mEnv->DeleteLocalRef(mLocalRef);
+            }
+            mLocalRef = ptr;
+        }
+    }
+
+    T release() __attribute__((warn_unused_result)) {
+        T localRef = mLocalRef;
+        mLocalRef = NULL;
+        return localRef;
+    }
+
+    T get() const {
+        return mLocalRef;
+    }
+
+private:
+    JNIEnv* mEnv;
+    T mLocalRef;
+
+    // Disallow copy and assignment.
+    ScopedLocalRef(const ScopedLocalRef&);
+    void operator=(const ScopedLocalRef&);
+};
+
+#endif  // SCOPED_LOCAL_REF_H_included
index 8da54989e25a498527916dfa3a38d49ae6958533..5d9e26abca2851a9410d85a77f135982e2c50b89 100644 (file)
 #include <errno.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <sys/un.h>
 #include <jni.h>
 
+#include "ScopedLocalRef.h"
+#include "JniConstants.h"
+
 #include "include/cephfs/libcephfs.h"
 #include "common/dout.h"
 
@@ -331,6 +335,8 @@ static void setup_field_ids(JNIEnv *env, jclass clz)
        if (!cephfileextent_ctor_fid)
                return;
 
+  JniConstants::init(env);
+
 #undef GETFID
 
        cephmount_instance_ptr_fid = env->GetFieldID(clz, "instance_ptr", "J");
@@ -2795,3 +2801,143 @@ out:
 
   return path;
 }
+
+/*
+ * sockaddrToInetAddress uses with the following license, and is adapted for
+ * use in this project by using Ceph JNI exception utilities.
+ *
+ * ----
+ *
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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.
+ */
+jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage& ss, jint* port) {
+    // Convert IPv4-mapped IPv6 addresses to IPv4 addresses.
+    // The RI states "Java will never return an IPv4-mapped address".
+    const sockaddr_in6& sin6 = reinterpret_cast<const sockaddr_in6&>(ss);
+    if (ss.ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) {
+        // Copy the IPv6 address into the temporary sockaddr_storage.
+        sockaddr_storage tmp;
+        memset(&tmp, 0, sizeof(tmp));
+        memcpy(&tmp, &ss, sizeof(sockaddr_in6));
+        // Unmap it into an IPv4 address.
+        sockaddr_in& sin = reinterpret_cast<sockaddr_in&>(tmp);
+        sin.sin_family = AF_INET;
+        sin.sin_port = sin6.sin6_port;
+        memcpy(&sin.sin_addr.s_addr, &sin6.sin6_addr.s6_addr[12], 4);
+        // Do the regular conversion using the unmapped address.
+        return sockaddrToInetAddress(env, tmp, port);
+    }
+
+    const void* rawAddress;
+    size_t addressLength;
+    int sin_port = 0;
+    int scope_id = 0;
+    if (ss.ss_family == AF_INET) {
+        const sockaddr_in& sin = reinterpret_cast<const sockaddr_in&>(ss);
+        rawAddress = &sin.sin_addr.s_addr;
+        addressLength = 4;
+        sin_port = ntohs(sin.sin_port);
+    } else if (ss.ss_family == AF_INET6) {
+        const sockaddr_in6& sin6 = reinterpret_cast<const sockaddr_in6&>(ss);
+        rawAddress = &sin6.sin6_addr.s6_addr;
+        addressLength = 16;
+        sin_port = ntohs(sin6.sin6_port);
+        scope_id = sin6.sin6_scope_id;
+    } else if (ss.ss_family == AF_UNIX) {
+        const sockaddr_un& sun = reinterpret_cast<const sockaddr_un&>(ss);
+        rawAddress = &sun.sun_path;
+        addressLength = strlen(sun.sun_path);
+    } else {
+        // We can't throw SocketException. We aren't meant to see bad addresses, so seeing one
+        // really does imply an internal error.
+        //jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+        //                     "sockaddrToInetAddress unsupported ss_family: %i", ss.ss_family);
+        cephThrowIllegalArg(env, "sockaddrToInetAddress unsupposed ss_family");
+        return NULL;
+    }
+    if (port != NULL) {
+        *port = sin_port;
+    }
+
+    ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(addressLength));
+    if (byteArray.get() == NULL) {
+        return NULL;
+    }
+    env->SetByteArrayRegion(byteArray.get(), 0, addressLength,
+            reinterpret_cast<const jbyte*>(rawAddress));
+
+    if (ss.ss_family == AF_UNIX) {
+        // Note that we get here for AF_UNIX sockets on accept(2). The unix(7) man page claims
+        // that the peer's sun_path will contain the path, but in practice it doesn't, and the
+        // peer length is returned as 2 (meaning only the sun_family field was set).
+        //
+        // Ceph Note: this isn't supported. inetUnixAddress appears to just be
+        // something in Dalvik/Android stuff.
+        cephThrowInternal(env, "OSD address should never be a UNIX socket");
+        return NULL;
+        //static jmethodID ctor = env->GetMethodID(JniConstants::inetUnixAddressClass, "<init>", "([B)V");
+        //return env->NewObject(JniConstants::inetUnixAddressClass, ctor, byteArray.get());
+    }
+
+    if (addressLength == 4) {
+      static jmethodID getByAddressMethod = env->GetStaticMethodID(JniConstants::inetAddressClass,
+          "getByAddress", "(Ljava/lang/String;[B)Ljava/net/InetAddress;");
+      if (getByAddressMethod == NULL) {
+        return NULL;
+      }
+      return env->CallStaticObjectMethod(JniConstants::inetAddressClass, getByAddressMethod,
+          NULL, byteArray.get());
+    } else if (addressLength == 16) {
+      static jmethodID getByAddressMethod = env->GetStaticMethodID(JniConstants::inet6AddressClass,
+          "getByAddress", "(Ljava/lang/String;[BI)Ljava/net/Inet6Address;");
+      if (getByAddressMethod == NULL) {
+        return NULL;
+      }
+      return env->CallStaticObjectMethod(JniConstants::inet6AddressClass, getByAddressMethod,
+          NULL, byteArray.get(), scope_id);
+    } else {
+      abort();
+      return NULL;
+    }
+}
+
+/*
+ * Class:     com_ceph_fs_CephMount
+ * Method:    native_ceph_get_osd_addr
+ * Signature: (JI)Ljava/net/InetAddress;
+ */
+JNIEXPORT jobject JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1get_1osd_1addr
+  (JNIEnv *env, jclass clz, jlong j_mntp, jint osd)
+{
+       struct ceph_mount_info *cmount = get_ceph_mount(j_mntp);
+       CephContext *cct = ceph_get_mount_context(cmount);
+  struct sockaddr_storage addr;
+  int ret;
+
+       CHECK_MOUNTED(cmount, NULL);
+
+  ldout(cct, 10) << "jni: get_osd_addr: osd " << osd << dendl;
+
+  ret = ceph_get_osd_addr(cmount, osd, &addr);
+
+  ldout(cct, 10) << "jni: get_osd_addr: ret " << ret << dendl;
+
+  if (ret < 0) {
+    handle_error(env, ret);
+    return NULL;
+  }
+
+  return sockaddrToInetAddress(env, addr, NULL);
+}
index 2922f589eba9280ea8d3b949cc9e701c47d6855f..d218d2fe14b5d0fa70fdb9d76033c2dde748ed98 100644 (file)
@@ -22,6 +22,7 @@ package com.ceph.fs;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.net.InetAddress;
 import java.util.UUID;
 import org.junit.*;
 import static org.junit.Assert.*;
@@ -993,4 +994,10 @@ public class CephMountTest {
       assertTrue(b.getName().length() > 0);
     }
   }
+
+  @Test
+  public void test_get_osd_address() throws Exception {
+    InetAddress addr = mount.get_osd_address(0);
+    assertTrue(addr.getHostAddress().length() > 0);
+  }
 }