mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
Java bindings (#147)
This commit is contained in:
150
bindings/java/src/lib.rs
Normal file
150
bindings/java/src/lib.rs
Normal file
@@ -0,0 +1,150 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use anyhow::Result;
|
||||
use jni::objects::{JClass, JObject, JString};
|
||||
use jni::sys::{jlong, jstring};
|
||||
use jni::JNIEnv;
|
||||
|
||||
use regorus::{Engine, Value};
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_newEngine(
|
||||
_env: JNIEnv,
|
||||
_class: JClass,
|
||||
) -> jlong {
|
||||
let engine = Engine::new();
|
||||
Box::into_raw(Box::new(engine)) as jlong
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_addPolicy(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
path: JString,
|
||||
rego: JString,
|
||||
) {
|
||||
let _ = throw_err(env, |env| {
|
||||
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
|
||||
let path: String = env.get_string(&path)?.into();
|
||||
let rego: String = env.get_string(®o)?.into();
|
||||
engine.add_policy(path, rego)?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_addPolicyFromFile(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
path: JString,
|
||||
) {
|
||||
let _ = throw_err(env, |env| {
|
||||
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
|
||||
let path: String = env.get_string(&path)?.into();
|
||||
engine.add_policy_from_file(path)?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_addDataJson(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
data: JString,
|
||||
) {
|
||||
let _ = throw_err(env, |env| {
|
||||
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
|
||||
let data: String = env.get_string(&data)?.into();
|
||||
engine.add_data_json(&data)?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_addDataJsonFromFile(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
path: JString,
|
||||
) {
|
||||
let _ = throw_err(env, |env| {
|
||||
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
|
||||
let path: String = env.get_string(&path)?.into();
|
||||
engine.add_data(Value::from_json_file(path)?)?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_setInputJson(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
input: JString,
|
||||
) {
|
||||
let _ = throw_err(env, |env| {
|
||||
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
|
||||
let input: String = env.get_string(&input)?.into();
|
||||
engine.set_input_json(&input)?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_setInputJsonFromFile(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
path: JString,
|
||||
) {
|
||||
let _ = throw_err(env, |env| {
|
||||
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
|
||||
let path: String = env.get_string(&path)?.into();
|
||||
engine.set_input(Value::from_json_file(&path)?);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_evalQuery(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
query: JString,
|
||||
) -> jstring {
|
||||
let res = throw_err(env, |env| {
|
||||
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
|
||||
let query: String = env.get_string(&query)?.into();
|
||||
let results = engine.eval_query(query, false)?;
|
||||
let output = env.new_string(serde_json::to_string(&results)?)?;
|
||||
Ok(output.into_raw())
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(val) => val,
|
||||
Err(_) => JObject::null().into_raw(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "system" fn Java_com_microsoft_regorus_Engine_destroyEngine(
|
||||
_env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
) {
|
||||
let _engine = Box::from_raw(engine_ptr as *mut Engine);
|
||||
}
|
||||
|
||||
fn throw_err<T>(mut env: JNIEnv, mut f: impl FnMut(&mut JNIEnv) -> Result<T>) -> Result<T> {
|
||||
match f(&mut env) {
|
||||
Ok(val) => Ok(val),
|
||||
Err(err) => {
|
||||
env.throw(err.to_string())?;
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
114
bindings/java/src/main/java/com/microsoft/regorus/Engine.java
Normal file
114
bindings/java/src/main/java/com/microsoft/regorus/Engine.java
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
* Licensed under the MIT License.
|
||||
**/
|
||||
|
||||
package com.microsoft.regorus;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class Engine implements AutoCloseable {
|
||||
// Methods exposed from Rust side, you can run
|
||||
// `javac -h . src/main/java/com/microsoft/regorus/Engine.java` to update
|
||||
// expected native header at `bindings/java/com_microsoft_regorus_Engine.h`
|
||||
// if you update the native API.
|
||||
private static native long newEngine();
|
||||
private static native void addPolicy(long enginePtr, String path, String rego);
|
||||
private static native void addPolicyFromFile(long enginePtr, String path);
|
||||
private static native void addDataJson(long enginePtr, String data);
|
||||
private static native void addDataJsonFromFile(long enginePtr, String path);
|
||||
private static native void setInputJson(long enginePtr, String input);
|
||||
private static native void setInputJsonFromFile(long enginePtr, String path);
|
||||
private static native String evalQuery(long enginePtr, String query);
|
||||
private static native void destroyEngine(long enginePtr);
|
||||
|
||||
// Pointer to Engine allocated on Rust's heap, all native methods works on
|
||||
// engine expects this pointer. It is free'd in `close` method.
|
||||
private final long enginePtr;
|
||||
|
||||
public Engine() {
|
||||
enginePtr = newEngine();
|
||||
}
|
||||
|
||||
public void pubAddPolicy(String path, String rego) {
|
||||
addPolicy(enginePtr, path, rego);
|
||||
}
|
||||
|
||||
public void pubAddDataJson(String path) {
|
||||
addDataJson(enginePtr, path);
|
||||
}
|
||||
|
||||
public void pubSetInputJson(String path) {
|
||||
setInputJson(enginePtr, path);
|
||||
}
|
||||
|
||||
public String pubEvalQuery(String path) {
|
||||
return evalQuery(enginePtr, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
destroyEngine(enginePtr);
|
||||
}
|
||||
|
||||
// Loading native library from jar is adapted from:
|
||||
// https://github.com/apache/opendal/blob/93e5f65bbf30df2fed4bdd95bb0685c73c6418c2/bindings/java/src/main/java/org/apache/opendal/NativeLibrary.java
|
||||
// https://github.com/apache/opendal/blob/93e5f65bbf30df2fed4bdd95bb0685c73c6418c2/bindings/java/src/main/java/org/apache/opendal/Environment.java
|
||||
private static final String classifier;
|
||||
static {
|
||||
final StringBuilder classifierBuilder = new StringBuilder();
|
||||
final String os = System.getProperty("os.name").toLowerCase();
|
||||
if (os.startsWith("windows")) {
|
||||
classifierBuilder.append("windows");
|
||||
} else if (os.startsWith("mac")) {
|
||||
classifierBuilder.append("osx");
|
||||
} else {
|
||||
classifierBuilder.append("linux");
|
||||
}
|
||||
classifierBuilder.append("-");
|
||||
final String arch = System.getProperty("os.arch").toLowerCase();
|
||||
if (arch.equals("aarch64")) {
|
||||
classifierBuilder.append("aarch_64");
|
||||
} else {
|
||||
classifierBuilder.append("x86_64");
|
||||
}
|
||||
classifier = classifierBuilder.toString();
|
||||
|
||||
loadNativeLibrary();
|
||||
}
|
||||
|
||||
private static void loadNativeLibrary() {
|
||||
try {
|
||||
// try dynamic library - the search path can be configured via "-Djava.library.path"
|
||||
System.loadLibrary("regorus_java");
|
||||
return;
|
||||
} catch (UnsatisfiedLinkError ignore) {
|
||||
// ignore - try from classpath
|
||||
}
|
||||
|
||||
final String libraryPath = bundledLibraryPath();
|
||||
try (final InputStream is = Engine.class.getResourceAsStream(libraryPath)) {
|
||||
if (is == null) {
|
||||
throw new RuntimeException("cannot find " + libraryPath);
|
||||
}
|
||||
final int dot = libraryPath.indexOf('.');
|
||||
final File tmpFile = File.createTempFile(libraryPath.substring(0, dot), libraryPath.substring(dot));
|
||||
tmpFile.deleteOnExit();
|
||||
Files.copy(is, tmpFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
System.load(tmpFile.getAbsolutePath());
|
||||
} catch (IOException exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static String bundledLibraryPath() {
|
||||
final String libraryName = System.mapLibraryName("regorus_java");
|
||||
return "/native/" + classifier + "/" + libraryName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
* Licensed under the MIT License.
|
||||
**/
|
||||
package com.microsoft.regorus;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
import junit.framework.TestCase;
|
||||
import junit.framework.Assert;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
public class EngineTest extends TestCase
|
||||
{
|
||||
public void test_engine()
|
||||
{
|
||||
String resJson;
|
||||
try (Engine engine = new Engine()) {
|
||||
engine.pubAddPolicy(
|
||||
"hello.rego",
|
||||
"package test\nmessage = concat(\", \", [input.message, data.message])"
|
||||
);
|
||||
engine.pubAddDataJson("{\"message\":\"World!\"}");
|
||||
engine.pubSetInputJson("{\"message\":\"Hello\"}");
|
||||
resJson = engine.pubEvalQuery("data.test.message");
|
||||
}
|
||||
|
||||
Gson gson = new Gson();
|
||||
Map res = gson.fromJson(resJson, Map.class);
|
||||
ArrayList results = (ArrayList) res.get("result");
|
||||
ArrayList expressions = (ArrayList) ((Map) results.get(0)).get("expressions");
|
||||
Map expression = (Map) expressions.get(0);
|
||||
Assert.assertEquals("Hello, World!", expression.get("value"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user