diff --git a/.gitignore b/.gitignore index dafb7b0..5b3d0a7 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,9 @@ cmake_install.cmake # idea .idea/ + +.DS_Store +*iml +target/ + +build/ diff --git a/java/pom.xml b/java/pom.xml new file mode 100644 index 0000000..d1bfebf --- /dev/null +++ b/java/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + org.restrpc + restrpc + 1.0-SNAPSHOT + + + + commons-io + commons-io + 2.5 + + + com.google.guava + guava + 29.0-jre + + + org.slf4j + slf4j-api + 1.7.21 + + + org.testng + testng + 7.0.0 + + + + + + native_dependencies + + + + diff --git a/java/src/main/java/org/restrpc/client/AsyncRpcFunction.java b/java/src/main/java/org/restrpc/client/AsyncRpcFunction.java new file mode 100644 index 0000000..a771616 --- /dev/null +++ b/java/src/main/java/org/restrpc/client/AsyncRpcFunction.java @@ -0,0 +1,28 @@ +package org.restrpc.client; + +import java.util.concurrent.CompletableFuture; + +public interface AsyncRpcFunction { + + CompletableFuture invoke(); + + + CompletableFuture invoke(Arg1Type arg1); + + + CompletableFuture invoke(Arg1Type arg1, Arg2Type arg2); + + + CompletableFuture invoke(Arg1Type arg1, Arg2Type arg2, Arg3Type arg3); + + + + CompletableFuture invoke(Arg1Type arg1, Arg2Type arg2, Arg3Type arg3, Arg4Type arg4); + + + CompletableFuture invoke(Arg1Type arg1, Arg2Type arg2, Arg3Type arg3, Arg4Type arg4, Arg5Type arg5); + + + CompletableFuture invoke(Arg1Type arg1, Arg2Type arg2, Arg3Type arg3, Arg4Type arg4, Arg5Type arg5, Arg6Type arg6); + +} diff --git a/java/src/main/java/org/restrpc/client/AsyncRpcFunctionImpl.java b/java/src/main/java/org/restrpc/client/AsyncRpcFunctionImpl.java new file mode 100644 index 0000000..105b590 --- /dev/null +++ b/java/src/main/java/org/restrpc/client/AsyncRpcFunctionImpl.java @@ -0,0 +1,59 @@ +package org.restrpc.client; + +import java.util.concurrent.CompletableFuture; + +public class AsyncRpcFunctionImpl implements AsyncRpcFunction { + + private RpcClient rpcClient; + + private String funcName; + + public AsyncRpcFunctionImpl(RpcClient rpcClient, String funcName) { + this.rpcClient = rpcClient; + this.funcName = funcName; + } + + public CompletableFuture invoke() { + return internalInvoke(new Object[0]); + } + + public CompletableFuture invoke(Arg1Type arg1) { + Object[] args = new Object[] {arg1}; + return internalInvoke(args); + } + + public + CompletableFuture invoke(Arg1Type arg1, Arg2Type arg2) { + Object[] args = new Object[] {arg1, arg2}; + return internalInvoke(args); + } + + public + CompletableFuture invoke(Arg1Type arg1, Arg2Type arg2, Arg3Type arg3) { + Object[] args = new Object[] {arg1, arg2, arg3}; + return internalInvoke(args); + } + + + public + CompletableFuture invoke(Arg1Type arg1, Arg2Type arg2, Arg3Type arg3, Arg4Type arg4) { + Object[] args = new Object[] {arg1, arg2, arg3, arg4}; + return internalInvoke(args); + } + + public + CompletableFuture invoke(Arg1Type arg1, Arg2Type arg2, Arg3Type arg3, Arg4Type arg4, Arg5Type arg5) { + Object[] args = new Object[] {arg1, arg2, arg3, arg4, arg5}; + return internalInvoke(args); + } + + public + CompletableFuture invoke(Arg1Type arg1, Arg2Type arg2, Arg3Type arg3, Arg4Type arg4, Arg5Type arg5, Arg6Type arg6) { + Object[] args = new Object[] {arg1, arg2, arg3, arg4, arg5, arg6}; + return internalInvoke(args); + } + + private CompletableFuture internalInvoke(Object[] args) { + return rpcClient.invoke(args); + } +} diff --git a/java/src/main/java/org/restrpc/client/JniUtils.java b/java/src/main/java/org/restrpc/client/JniUtils.java new file mode 100644 index 0000000..3ece0da --- /dev/null +++ b/java/src/main/java/org/restrpc/client/JniUtils.java @@ -0,0 +1,67 @@ +package org.restrpc.client; + +import com.google.common.base.Strings; +import com.google.common.collect.Sets; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.lang.reflect.Field; +import java.util.Set; + +public class JniUtils { + + private static final Logger LOGGER = LoggerFactory.getLogger(JniUtils.class); + + private static Set loadedLibs = Sets.newHashSet(); + + /** + * Loads the native library specified by the libraryName argument. + * The libraryName argument must not contain any platform specific + * prefix, file extension or path. + * + * @param libraryName the name of the library. + */ + public static synchronized void loadLibrary(String libraryName) { + if (!loadedLibs.contains(libraryName)) { + LOGGER.debug("Loading native library {}.", libraryName); + // Load native library. + String fileName = System.mapLibraryName(libraryName); + final File file = LibraryFileUtils.getFile("/tmp/restrpc", fileName); + System.load(file.getAbsolutePath()); + LOGGER.debug("Native library loaded."); + resetLibraryPath(file.getAbsolutePath()); + loadedLibs.add(libraryName); + } + } + + /** + * This is a hack to reset library path at runtime. + */ + public static synchronized void resetLibraryPath(String libPath) { + if (Strings.isNullOrEmpty(libPath)) { + return; + } + String path = System.getProperty("java.library.path"); + String separator = System.getProperty("path.separator"); + if (Strings.isNullOrEmpty(path)) { + path = ""; + } else { + path += separator; + } + path += String.join(separator, libPath); + + // This is a hack to reset library path at runtime, + // see https://stackoverflow.com/questions/15409223/. + System.setProperty("java.library.path", path); + // Set sys_paths to null so that java.library.path will be re-evaluated next time it is needed. + final Field sysPathsField; + try { + sysPathsField = ClassLoader.class.getDeclaredField("sys_paths"); + sysPathsField.setAccessible(true); + sysPathsField.set(null, null); + } catch (NoSuchFieldException | IllegalAccessException e) { + LOGGER.error("Failed to set library path.", e); + } + } +} diff --git a/java/src/main/java/org/restrpc/client/LibraryFileUtils.java b/java/src/main/java/org/restrpc/client/LibraryFileUtils.java new file mode 100644 index 0000000..8d95ab3 --- /dev/null +++ b/java/src/main/java/org/restrpc/client/LibraryFileUtils.java @@ -0,0 +1,48 @@ +package org.restrpc.client; + +import com.google.common.base.Preconditions; +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.nio.channels.FileLock; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class LibraryFileUtils { + + public static final String RESTRPC_LIBRARY_NAME = "restrpc_jni"; + + public static File getFile(String destDir, String fileName) { + final File dir = new File(destDir); + if (!dir.exists()) { + try { + FileUtils.forceMkdir(dir); + } catch (IOException e) { + throw new RuntimeException("Couldn't make directory: " + dir.getAbsolutePath(), e); + } + } + String lockFilePath = destDir + File.separator + "file_lock"; + try (FileLock ignored = new RandomAccessFile(lockFilePath, "rw") + .getChannel().lock()) { + File file = new File(String.format("%s/%s", destDir, fileName)); + if (file.exists()) { + return file; + } + + // File does not exist. + try (InputStream is = LibraryFileUtils.class.getResourceAsStream("/" + fileName)) { + Preconditions.checkNotNull(is, "{} doesn't exist.", fileName); + Files.copy(is, Paths.get(file.getCanonicalPath())); + } catch (IOException e) { + throw new RuntimeException("Couldn't get temp file from resource " + fileName, e); + } + return file; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/java/src/main/java/org/restrpc/client/NativeRpcClient.java b/java/src/main/java/org/restrpc/client/NativeRpcClient.java new file mode 100644 index 0000000..7cbd35d --- /dev/null +++ b/java/src/main/java/org/restrpc/client/NativeRpcClient.java @@ -0,0 +1,57 @@ +package org.restrpc.client; + +import java.util.concurrent.CompletableFuture; + +public class NativeRpcClient implements RpcClient { + + static { + JniUtils.loadLibrary("restrpc_jni"); + } + private long rpcClientPointer = -1; + + public NativeRpcClient() { + rpcClientPointer = nativeNewRpcClient(); + } + + public void connect(String serverAddress) { + if (rpcClientPointer == -1) { + throw new RuntimeException("no init"); + } + nativeConnect(rpcClientPointer, serverAddress); + } + + public AsyncRpcFunction asyncFunc(String funcName) { + if (funcName == null) { + throw new NullPointerException("Rpc function name should be null."); + } + return new AsyncRpcFunctionImpl(this, funcName); + } + + public CompletableFuture invoke(Object[] args) { + if (rpcClientPointer == -1) { + throw new RuntimeException("no init"); + } + + nativeInvoke(rpcClientPointer, null); +// return nativeInvoke(rpcClientPointer, ); + return null; + } + + public void close() { + if (rpcClientPointer == -1) { + throw new RuntimeException("no init"); + } + + nativeDestroy(rpcClientPointer); + this.rpcClientPointer = -1; + } + + + private native long nativeNewRpcClient(); + + private native void nativeConnect(long rpcClientPointer, String serverAddress); + + private native long nativeInvoke(long rpcClientPointer, byte[][] encodedFuncNameAndArgs); + + private native void nativeDestroy(long rpcClientPointer); +} diff --git a/java/src/main/java/org/restrpc/client/RpcClient.java b/java/src/main/java/org/restrpc/client/RpcClient.java new file mode 100644 index 0000000..c0c5a3e --- /dev/null +++ b/java/src/main/java/org/restrpc/client/RpcClient.java @@ -0,0 +1,14 @@ +package org.restrpc.client; + +import java.util.concurrent.CompletableFuture; + +public interface RpcClient { + + void connect(String serverAddress); + + AsyncRpcFunction asyncFunc(String funcName); + + CompletableFuture invoke(Object[] args); + + void close(); +} diff --git a/java/src/test/java/org/restrpc/test/BasicClientTest.java b/java/src/test/java/org/restrpc/test/BasicClientTest.java new file mode 100644 index 0000000..8516b6c --- /dev/null +++ b/java/src/test/java/org/restrpc/test/BasicClientTest.java @@ -0,0 +1,14 @@ +package org.restrpc.test; + +import org.restrpc.client.NativeRpcClient; +import org.restrpc.client.RpcClient; +import org.testng.annotations.Test; + +public class BasicClientTest { + + @Test + public void testBasic() { + RpcClient rpcClient = new NativeRpcClient(); + rpcClient.asyncFunc("111").invoke(); + } +} diff --git a/jni/CMakeLists.txt b/jni/CMakeLists.txt new file mode 100644 index 0000000..77e1608 --- /dev/null +++ b/jni/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.1) +project(example) + +set(ASIO_STANDALONE 1) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -std=c++11") + +SET(ENABLE_SSL OFF) + +if (ENABLE_SSL) + add_definitions(-DCINATRA_ENABLE_SSL) + message(STATUS "Use SSL") +endif() + +find_package(JNI REQUIRED) +#find_package(Boost COMPONENTS system filesystem REQUIRED) +include_directories( + # "/usr/local/include" + "../include" + "../jni" + ${JNI_INCLUDE_DIRS} +# "/usr/local/opt/openjdk/include/" + "/Users/qingwang/workspace/opensource/rest_rpc/third/msgpack/include") + +INCLUDE_DIRECTORIES(SYSTEM "/Users/qingwang/workspace/opensource/dousi/core/build/external/boost/src/boost_ep") +INCLUDE_DIRECTORIES(SYSTEM "/Users/qingwang/workspace/opensource/rest_rpc/third/msgpack/include") + +add_library(restrpc_jni SHARED org_restrpc_client_NativeRpcClient.cc) diff --git a/jni/org_restrpc_client_NativeRpcClient.cc b/jni/org_restrpc_client_NativeRpcClient.cc new file mode 100644 index 0000000..a94a323 --- /dev/null +++ b/jni/org_restrpc_client_NativeRpcClient.cc @@ -0,0 +1,52 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ + +#include "org_restrpc_client_NativeRpcClient.h" +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_restrpc_client_NativeRpcClient + * Method: nativeNewRpcClient + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_org_restrpc_client_NativeRpcClient_nativeNewRpcClient + (JNIEnv *, jobject) { + return 10009; +} + +/* + * Class: org_restrpc_client_NativeRpcClient + * Method: nativeConnect + * Signature: (JLjava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_org_restrpc_client_NativeRpcClient_nativeConnect +(JNIEnv *, jobject, jlong, jstring) { +} + +/* + * Class: org_restrpc_client_NativeRpcClient + * Method: nativeInvoke + * Signature: (J[[B)J + */ +JNIEXPORT jlong JNICALL Java_org_restrpc_client_NativeRpcClient_nativeInvoke + (JNIEnv *, jobject, jlong, jobjectArray) { + return 20009; +} + +/* + * Class: org_restrpc_client_NativeRpcClient + * Method: nativeDestroy + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_org_restrpc_client_NativeRpcClient_nativeDestroy +(JNIEnv *, jobject, jlong) { + +} + +#ifdef __cplusplus +} +#endif diff --git a/jni/org_restrpc_client_NativeRpcClient.h b/jni/org_restrpc_client_NativeRpcClient.h new file mode 100644 index 0000000..eb7e772 --- /dev/null +++ b/jni/org_restrpc_client_NativeRpcClient.h @@ -0,0 +1,45 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_restrpc_client_NativeRpcClient */ + +#ifndef _Included_org_restrpc_client_NativeRpcClient +#define _Included_org_restrpc_client_NativeRpcClient +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_restrpc_client_NativeRpcClient + * Method: nativeNewRpcClient + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_org_restrpc_client_NativeRpcClient_nativeNewRpcClient + (JNIEnv *, jobject); + +/* + * Class: org_restrpc_client_NativeRpcClient + * Method: nativeConnect + * Signature: (JLjava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_org_restrpc_client_NativeRpcClient_nativeConnect + (JNIEnv *, jobject, jlong, jstring); + +/* + * Class: org_restrpc_client_NativeRpcClient + * Method: nativeInvoke + * Signature: (J[[B)J + */ +JNIEXPORT jlong JNICALL Java_org_restrpc_client_NativeRpcClient_nativeInvoke + (JNIEnv *, jobject, jlong, jobjectArray); + +/* + * Class: org_restrpc_client_NativeRpcClient + * Method: nativeDestroy + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_org_restrpc_client_NativeRpcClient_nativeDestroy + (JNIEnv *, jobject, jlong); + +#ifdef __cplusplus +} +#endif +#endif