mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
Java publishing (#151)
* bindings/java: Add prefix to native methods * bindings/java: Add javadocs and missing methods to Engine * Setup publishing uber-JAR via GitHub workflow * bindings/java: Update README * bindings/java: Improve native library loading from JAR * bindings/java: Fix usage of `working-directory` * bindings/java: Pass required `distribution` parameter to `actions/setup-java@v4` * bindings/java: Use Corretto distribution This is because Microsoft doesn't provide JDK8, see https://learn.microsoft.com/en-us/java/openjdk/download#openjdk-8. * bindings/java: Install GCC toolchain for `aarch64-unknown-linux-gnu` * bindings/java: Upload artifacts with different names from each step * bindings/java: Upload built JARs to GitHub
This commit is contained in:
@@ -9,7 +9,7 @@ use jni::JNIEnv;
|
||||
use regorus::{Engine, Value};
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_newEngine(
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeNewEngine(
|
||||
_env: JNIEnv,
|
||||
_class: JClass,
|
||||
) -> jlong {
|
||||
@@ -18,7 +18,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_newEngine(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_addPolicy(
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddPolicy(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
@@ -35,7 +35,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_addPolicy(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_addPolicyFromFile(
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddPolicyFromFile(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
@@ -50,7 +50,20 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_addPolicyFromFile(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_addDataJson(
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeClearData(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
) {
|
||||
let _ = throw_err(env, |_env| {
|
||||
let engine = unsafe { &mut *(engine_ptr as *mut Engine) };
|
||||
engine.clear_data();
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddDataJson(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
@@ -65,7 +78,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_addDataJson(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_addDataJsonFromFile(
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeAddDataJsonFromFile(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
@@ -80,7 +93,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_addDataJsonFromFile(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_setInputJson(
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetInputJson(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
@@ -95,7 +108,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_setInputJson(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_setInputJsonFromFile(
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeSetInputJsonFromFile(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
@@ -110,7 +123,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_setInputJsonFromFile(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_evalQuery(
|
||||
pub extern "system" fn Java_com_microsoft_regorus_Engine_nativeEvalQuery(
|
||||
env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
@@ -131,7 +144,7 @@ pub extern "system" fn Java_com_microsoft_regorus_Engine_evalQuery(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "system" fn Java_com_microsoft_regorus_Engine_destroyEngine(
|
||||
pub unsafe extern "system" fn Java_com_microsoft_regorus_Engine_nativeDestroyEngine(
|
||||
_env: JNIEnv,
|
||||
_class: JClass,
|
||||
engine_ptr: jlong,
|
||||
|
||||
@@ -13,77 +13,163 @@ import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Regorus Engine.
|
||||
*/
|
||||
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);
|
||||
private static native long nativeNewEngine();
|
||||
private static native void nativeAddPolicy(long enginePtr, String path, String rego);
|
||||
private static native void nativeAddPolicyFromFile(long enginePtr, String path);
|
||||
private static native void nativeClearData(long enginePtr);
|
||||
private static native void nativeAddDataJson(long enginePtr, String data);
|
||||
private static native void nativeAddDataJsonFromFile(long enginePtr, String path);
|
||||
private static native void nativeSetInputJson(long enginePtr, String input);
|
||||
private static native void nativeSetInputJsonFromFile(long enginePtr, String path);
|
||||
private static native String nativeEvalQuery(long enginePtr, String query);
|
||||
private static native void nativeDestroyEngine(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;
|
||||
|
||||
/**
|
||||
* Creates a new Regorus Engine.
|
||||
*/
|
||||
public Engine() {
|
||||
enginePtr = newEngine();
|
||||
enginePtr = nativeNewEngine();
|
||||
}
|
||||
|
||||
public void pubAddPolicy(String path, String rego) {
|
||||
addPolicy(enginePtr, path, rego);
|
||||
/**
|
||||
* Adds an inline Rego policy.
|
||||
*
|
||||
* @param filename Filename of this Rego policy.
|
||||
* @param rego Rego policy.
|
||||
*/
|
||||
public void addPolicy(String filename, String rego) {
|
||||
nativeAddPolicy(enginePtr, filename, rego);
|
||||
}
|
||||
|
||||
public void pubAddDataJson(String path) {
|
||||
addDataJson(enginePtr, path);
|
||||
/**
|
||||
* Adds a Rego policy from given path.
|
||||
*
|
||||
* @param path Path of the Rego policy.
|
||||
*/
|
||||
public void addPolicyFromFile(String path) {
|
||||
nativeAddPolicyFromFile(enginePtr, path);
|
||||
}
|
||||
|
||||
public void pubSetInputJson(String path) {
|
||||
setInputJson(enginePtr, path);
|
||||
/**
|
||||
* Clears the data document.
|
||||
*/
|
||||
public void clearData() {
|
||||
nativeClearData(enginePtr);
|
||||
}
|
||||
|
||||
public String pubEvalQuery(String path) {
|
||||
return evalQuery(enginePtr, path);
|
||||
/**
|
||||
* Adds inline data document from given JSON.
|
||||
* The specified data document is merged into existing data document.
|
||||
* It will throw an error if new data conflicts with the existing document.
|
||||
*
|
||||
* Example:
|
||||
* addDataJson("[]") - Throws as it's not an object.
|
||||
* addDataJson('{"a": 1}') - Fine
|
||||
* addDataJson('{"b": 2}') - Fine, now {"a": 1, "b": 2}
|
||||
* addDataJson('{"b": 3}') - Throws as `b` conflicts.
|
||||
*
|
||||
* @see clearData
|
||||
*
|
||||
* @throws RuntimeException If data conflicts with the existing document
|
||||
* or data is not an object.
|
||||
*
|
||||
* @param data Inline data document.
|
||||
*/
|
||||
public void addDataJson(String data) throws RuntimeException {
|
||||
nativeAddDataJson(enginePtr, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds data document from given JSON file.
|
||||
* The specified data document is merged into existing data document.
|
||||
* It will throw an error if new data conflicts with the existing document.
|
||||
*
|
||||
* @see addDataJson
|
||||
* @see clearData
|
||||
*
|
||||
* @throws RuntimeException If data conflicts with the existing document
|
||||
* or data is not an object.
|
||||
*
|
||||
* @param path Path to JSON data document.
|
||||
*/
|
||||
public void addDataJsonFromFile(String path) throws RuntimeException {
|
||||
nativeAddDataJsonFromFile(enginePtr, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets inline JSON input.
|
||||
*
|
||||
* @param input inline JSON input.
|
||||
*/
|
||||
public void setInputJson(String input) {
|
||||
nativeSetInputJson(enginePtr, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets JSON input from given path.
|
||||
*
|
||||
* @param path Path to JSON input.
|
||||
*/
|
||||
public void setInputJsonFromFile(String path) {
|
||||
nativeSetInputJsonFromFile(enginePtr, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates given Rego query and returns a JSON string as a result.
|
||||
*
|
||||
* @param query The Rego query.
|
||||
*
|
||||
* @return Query results as a JSON string.
|
||||
*/
|
||||
public String evalQuery(String query) {
|
||||
return nativeEvalQuery(enginePtr, query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
destroyEngine(enginePtr);
|
||||
nativeDestroyEngine(enginePtr);
|
||||
}
|
||||
|
||||
// Loading native library from jar is adapted from:
|
||||
// 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("-");
|
||||
// Build a Rust target triple, like: 'aarch64-unknown-linux-gnu'.
|
||||
final StringBuilder targetTripleBuilder = new StringBuilder();
|
||||
|
||||
final String arch = System.getProperty("os.arch").toLowerCase();
|
||||
if (arch.equals("aarch64")) {
|
||||
classifierBuilder.append("aarch_64");
|
||||
targetTripleBuilder.append("aarch64");
|
||||
} else {
|
||||
classifierBuilder.append("x86_64");
|
||||
targetTripleBuilder.append("x86_64");
|
||||
}
|
||||
classifier = classifierBuilder.toString();
|
||||
targetTripleBuilder.append("-");
|
||||
|
||||
loadNativeLibrary();
|
||||
final String os = System.getProperty("os.name").toLowerCase();
|
||||
if (os.startsWith("windows")) {
|
||||
targetTripleBuilder.append("pc-windows-msvc");
|
||||
} else if (os.startsWith("mac")) {
|
||||
targetTripleBuilder.append("apple-darwin");
|
||||
} else {
|
||||
targetTripleBuilder.append("unknown-linux-gnu");
|
||||
}
|
||||
|
||||
loadNativeLibrary(targetTripleBuilder.toString());
|
||||
}
|
||||
|
||||
private static void loadNativeLibrary() {
|
||||
private static void loadNativeLibrary(String targetTriple) {
|
||||
try {
|
||||
// try dynamic library - the search path can be configured via "-Djava.library.path"
|
||||
System.loadLibrary("regorus_java");
|
||||
@@ -92,10 +178,14 @@ public class Engine implements AutoCloseable {
|
||||
// ignore - try from classpath
|
||||
}
|
||||
|
||||
final String libraryPath = bundledLibraryPath();
|
||||
// Native libraries will be bundles into JARs like:
|
||||
// `aarch64-apple-darwin/libregorus_java.dylib`
|
||||
final String libraryName = System.mapLibraryName("regorus_java");
|
||||
final String libraryPath = "/" + targetTriple + "/" + libraryName;
|
||||
|
||||
try (final InputStream is = Engine.class.getResourceAsStream(libraryPath)) {
|
||||
if (is == null) {
|
||||
throw new RuntimeException("cannot find " + libraryPath);
|
||||
throw new RuntimeException("Cannot find " + libraryPath + "\nSee https://github.com/microsoft/regorus/tree/main/bindings/java for help.");
|
||||
}
|
||||
final int dot = libraryPath.indexOf('.');
|
||||
final File tmpFile = File.createTempFile(libraryPath.substring(0, dot), libraryPath.substring(dot));
|
||||
@@ -106,9 +196,4 @@ public class Engine implements AutoCloseable {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static String bundledLibraryPath() {
|
||||
final String libraryName = System.mapLibraryName("regorus_java");
|
||||
return "/native/" + classifier + "/" + libraryName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,13 @@ public class EngineTest extends TestCase
|
||||
{
|
||||
String resJson;
|
||||
try (Engine engine = new Engine()) {
|
||||
engine.pubAddPolicy(
|
||||
engine.addPolicy(
|
||||
"hello.rego",
|
||||
"package test\nmessage = concat(\", \", [input.message, data.message])"
|
||||
);
|
||||
engine.pubAddDataJson("{\"message\":\"World!\"}");
|
||||
engine.pubSetInputJson("{\"message\":\"Hello\"}");
|
||||
resJson = engine.pubEvalQuery("data.test.message");
|
||||
engine.addDataJson("{\"message\":\"World!\"}");
|
||||
engine.setInputJson("{\"message\":\"Hello\"}");
|
||||
resJson = engine.evalQuery("data.test.message");
|
||||
}
|
||||
|
||||
Gson gson = new Gson();
|
||||
|
||||
Reference in New Issue
Block a user