This commit is contained in:
Qing Wang
2020-10-21 13:53:46 +08:00
parent 9ae931cd65
commit b66ca0cde9
12 changed files with 457 additions and 0 deletions
+40
View File
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.restrpc</groupId>
<artifactId>restrpc</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>7.0.0</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>native_dependencies</directory>
</resource>
</resources>
</build>
</project>
@@ -0,0 +1,28 @@
package org.restrpc.client;
import java.util.concurrent.CompletableFuture;
public interface AsyncRpcFunction {
CompletableFuture<Object> invoke();
<Arg1Type>
CompletableFuture<Object> invoke(Arg1Type arg1);
<Arg1Type, Arg2Type>
CompletableFuture<Object> invoke(Arg1Type arg1, Arg2Type arg2);
<Arg1Type, Arg2Type, Arg3Type>
CompletableFuture<Object> invoke(Arg1Type arg1, Arg2Type arg2, Arg3Type arg3);
<Arg1Type, Arg2Type, Arg3Type, Arg4Type>
CompletableFuture<Object> invoke(Arg1Type arg1, Arg2Type arg2, Arg3Type arg3, Arg4Type arg4);
<Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type>
CompletableFuture<Object> invoke(Arg1Type arg1, Arg2Type arg2, Arg3Type arg3, Arg4Type arg4, Arg5Type arg5);
<Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type, Arg6Type>
CompletableFuture<Object> invoke(Arg1Type arg1, Arg2Type arg2, Arg3Type arg3, Arg4Type arg4, Arg5Type arg5, Arg6Type arg6);
}
@@ -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<Object> invoke() {
return internalInvoke(new Object[0]);
}
public <Arg1Type> CompletableFuture<Object> invoke(Arg1Type arg1) {
Object[] args = new Object[] {arg1};
return internalInvoke(args);
}
public <Arg1Type, Arg2Type>
CompletableFuture<Object> invoke(Arg1Type arg1, Arg2Type arg2) {
Object[] args = new Object[] {arg1, arg2};
return internalInvoke(args);
}
public <Arg1Type, Arg2Type, Arg3Type>
CompletableFuture<Object> invoke(Arg1Type arg1, Arg2Type arg2, Arg3Type arg3) {
Object[] args = new Object[] {arg1, arg2, arg3};
return internalInvoke(args);
}
public <Arg1Type, Arg2Type, Arg3Type, Arg4Type>
CompletableFuture<Object> invoke(Arg1Type arg1, Arg2Type arg2, Arg3Type arg3, Arg4Type arg4) {
Object[] args = new Object[] {arg1, arg2, arg3, arg4};
return internalInvoke(args);
}
public <Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type>
CompletableFuture<Object> invoke(Arg1Type arg1, Arg2Type arg2, Arg3Type arg3, Arg4Type arg4, Arg5Type arg5) {
Object[] args = new Object[] {arg1, arg2, arg3, arg4, arg5};
return internalInvoke(args);
}
public <Arg1Type, Arg2Type, Arg3Type, Arg4Type, Arg5Type, Arg6Type>
CompletableFuture<Object> 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<Object> internalInvoke(Object[] args) {
return rpcClient.invoke(args);
}
}
@@ -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<String> loadedLibs = Sets.newHashSet();
/**
* Loads the native library specified by the <code>libraryName</code> argument.
* The <code>libraryName</code> 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);
}
}
}
@@ -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);
}
}
}
@@ -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<Object> 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);
}
@@ -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<Object> invoke(Object[] args);
void close();
}
@@ -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();
}
}