## - 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}
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;
}
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);
}
--- /dev/null
+/*
+ * 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");
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
#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"
if (!cephfileextent_ctor_fid)
return;
+ JniConstants::init(env);
+
#undef GETFID
cephmount_instance_ptr_fid = env->GetFieldID(clz, "instance_ptr", "J");
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);
+}
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.net.InetAddress;
import java.util.UUID;
import org.junit.*;
import static org.junit.Assert.*;
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);
+ }
}