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:
87
.github/workflows/publish-java.yml
vendored
Normal file
87
.github/workflows/publish-java.yml
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
name: publish-java
|
||||
|
||||
on: workflow_dispatch
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build for ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
os: ubuntu-latest
|
||||
extension: so
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
os: ubuntu-latest
|
||||
extension: so
|
||||
- target: x86_64-apple-darwin
|
||||
os: macos-latest
|
||||
extension: dylib
|
||||
- target: aarch64-apple-darwin
|
||||
os: macos-latest
|
||||
extension: dylib
|
||||
- target: x86_64-pc-windows-msvc
|
||||
os: windows-latest
|
||||
extension: dll
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 8
|
||||
distribution: "corretto"
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
|
||||
# Setup for cargo
|
||||
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV
|
||||
|
||||
- run: cargo build --release --target ${{ matrix.target }} --manifest-path ./bindings/java/Cargo.toml
|
||||
- run: mkdir -p native/${{ matrix.target }}
|
||||
- run: mv target/${{ matrix.target }}/release/*.${{ matrix.extension }} ./native/${{ matrix.target }}/
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: native-libraries-${{ matrix.target }}
|
||||
path: native/
|
||||
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 8
|
||||
distribution: "corretto"
|
||||
server-id: ossrh
|
||||
server-username: MAVEN_USERNAME
|
||||
server-password: MAVEN_PASSWORD
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: native-libraries-*
|
||||
merge-multiple: true
|
||||
path: ./bindings/java/native/
|
||||
- run: mvn package
|
||||
working-directory: ./bindings/java
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: built-jars
|
||||
path: ./bindings/java/target/regorus-java-*.jar
|
||||
- run: mvn deploy
|
||||
working-directory: ./bindings/java
|
||||
env:
|
||||
MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}
|
||||
MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }}
|
||||
@@ -8,19 +8,38 @@
|
||||
|
||||
See main [Regorus page](https://github.com/microsoft/regorus) for more details about the project.
|
||||
|
||||
Regorus can be used in Java via `com.microsoft.regorus` package. (It is not yet available in Maven Central, but can be manually built.)
|
||||
## Usage
|
||||
|
||||
## Building
|
||||
Regorus Java is published to Maven Central with native libraries for the following:
|
||||
|
||||
You can build this binding using [Maven](https://maven.apache.org/):
|
||||
```shell
|
||||
$ mvn package
|
||||
$ file target/regorus-java-0.0.1*
|
||||
target/regorus-java-0.0.1-osx-aarch_64.jar: Zip archive data, at least v1.0 to extract, compression method=deflate
|
||||
target/regorus-java-0.0.1.jar: Zip archive data, at least v1.0 to extract, compression method=deflate
|
||||
- 64-bit Linux (kernel 3.2+, glibc 2.17+)
|
||||
- ARM64 Linux (kernel 4.1, glibc 2.17+)
|
||||
- 64-bit macOS (10.12+, Sierra+)
|
||||
- ARM64 macOS (11.0+, Big Sur+)
|
||||
- 64-bit MSVC (Windows 7+)
|
||||
|
||||
If you need to run it in a different OS or an architecture you need to manually [build it](#Building).
|
||||
|
||||
If you're on one of the supported platforms, you can just pull prebuilt JAR from Maven Central by declaring a dependency on `com.microsoft.regorus:regorus-java`.
|
||||
|
||||
With [Maven](https://maven.apache.org/):
|
||||
```xml
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.microsoft.regorus</groupId>
|
||||
<artifactId>regorus-java</artifactId>
|
||||
<version>0.0.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
## Usage
|
||||
With [Gradle](https://gradle.org/):
|
||||
```kotlin
|
||||
// build.gradle.kts
|
||||
implementation("com.microsoft.regorus:regorus-java:0.0.1")
|
||||
```
|
||||
|
||||
Afterwards you can use it as follows:
|
||||
|
||||
```java
|
||||
import com.microsoft.regorus.Engine;
|
||||
@@ -28,13 +47,13 @@ import com.microsoft.regorus.Engine;
|
||||
public class Test {
|
||||
public static void main(String[] args) {
|
||||
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\"}");
|
||||
String resJson = engine.pubEvalQuery("data.test.message");
|
||||
engine.addDataJson("{\"message\":\"World!\"}");
|
||||
engine.setInputJson("{\"message\":\"Hello\"}");
|
||||
String resJson = engine.evalQuery("data.test.message");
|
||||
|
||||
System.out.println(resJson);
|
||||
}
|
||||
@@ -42,8 +61,37 @@ public class Test {
|
||||
}
|
||||
```
|
||||
|
||||
and run it with:
|
||||
And you can see the following output once you run it:
|
||||
```shell
|
||||
$ java -cp target/regorus-java-0.0.1.jar:target/regorus-java-0.0.1-osx-aarch_64.jar Test.java
|
||||
{"result":[{"expressions":[{"value":"Hello, World!","text":"data.test.message","location":{"row":1,"col":1}}]}]}
|
||||
```
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
In order to build Regorus Java for a target platform, you need to install Rust target
|
||||
for that target platform first:
|
||||
|
||||
```bash
|
||||
$ rustup target add aarch64-apple-darwin
|
||||
```
|
||||
|
||||
Afterwards, you can build native library for that target using:
|
||||
```bash
|
||||
$ cargo build --release --target aarch64-apple-darwin
|
||||
```
|
||||
|
||||
You will then have a native library at `../../target/aarch64-apple-darwin/release/libregorus_java.dylib` depending on your target.
|
||||
|
||||
You can then build a JAR from source using:
|
||||
```bash
|
||||
$ mvn package
|
||||
```
|
||||
|
||||
And you will have a JAR at `./target/regorus-java-0.0.1.jar`.
|
||||
|
||||
You need to make sure both of the artifacts in Java's classpath.
|
||||
For example with `java` CLI:
|
||||
```bash
|
||||
$ java -Djava.library.path=../../target/aarch64-apple-darwin/release/ -cp target/regorus-java-0.0.1.jar Test.java
|
||||
```
|
||||
|
||||
|
||||
@@ -9,74 +9,82 @@ extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: com_microsoft_regorus_Engine
|
||||
* Method: newEngine
|
||||
* Method: nativeNewEngine
|
||||
* Signature: ()J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_com_microsoft_regorus_Engine_newEngine
|
||||
JNIEXPORT jlong JNICALL Java_com_microsoft_regorus_Engine_nativeNewEngine
|
||||
(JNIEnv *, jclass);
|
||||
|
||||
/*
|
||||
* Class: com_microsoft_regorus_Engine
|
||||
* Method: addPolicy
|
||||
* Method: nativeAddPolicy
|
||||
* Signature: (JLjava/lang/String;Ljava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_addPolicy
|
||||
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeAddPolicy
|
||||
(JNIEnv *, jclass, jlong, jstring, jstring);
|
||||
|
||||
/*
|
||||
* Class: com_microsoft_regorus_Engine
|
||||
* Method: addPolicyFromFile
|
||||
* Method: nativeAddPolicyFromFile
|
||||
* Signature: (JLjava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_addPolicyFromFile
|
||||
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeAddPolicyFromFile
|
||||
(JNIEnv *, jclass, jlong, jstring);
|
||||
|
||||
/*
|
||||
* Class: com_microsoft_regorus_Engine
|
||||
* Method: addDataJson
|
||||
* Signature: (JLjava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_addDataJson
|
||||
(JNIEnv *, jclass, jlong, jstring);
|
||||
|
||||
/*
|
||||
* Class: com_microsoft_regorus_Engine
|
||||
* Method: addDataJsonFromFile
|
||||
* Signature: (JLjava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_addDataJsonFromFile
|
||||
(JNIEnv *, jclass, jlong, jstring);
|
||||
|
||||
/*
|
||||
* Class: com_microsoft_regorus_Engine
|
||||
* Method: setInputJson
|
||||
* Signature: (JLjava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_setInputJson
|
||||
(JNIEnv *, jclass, jlong, jstring);
|
||||
|
||||
/*
|
||||
* Class: com_microsoft_regorus_Engine
|
||||
* Method: setInputJsonFromFile
|
||||
* Signature: (JLjava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_setInputJsonFromFile
|
||||
(JNIEnv *, jclass, jlong, jstring);
|
||||
|
||||
/*
|
||||
* Class: com_microsoft_regorus_Engine
|
||||
* Method: evalQuery
|
||||
* Signature: (JLjava/lang/String;)Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jstring JNICALL Java_com_microsoft_regorus_Engine_evalQuery
|
||||
(JNIEnv *, jclass, jlong, jstring);
|
||||
|
||||
/*
|
||||
* Class: com_microsoft_regorus_Engine
|
||||
* Method: destroyEngine
|
||||
* Method: nativeClearData
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_destroyEngine
|
||||
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeClearData
|
||||
(JNIEnv *, jclass, jlong);
|
||||
|
||||
/*
|
||||
* Class: com_microsoft_regorus_Engine
|
||||
* Method: nativeAddDataJson
|
||||
* Signature: (JLjava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeAddDataJson
|
||||
(JNIEnv *, jclass, jlong, jstring);
|
||||
|
||||
/*
|
||||
* Class: com_microsoft_regorus_Engine
|
||||
* Method: nativeAddDataJsonFromFile
|
||||
* Signature: (JLjava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeAddDataJsonFromFile
|
||||
(JNIEnv *, jclass, jlong, jstring);
|
||||
|
||||
/*
|
||||
* Class: com_microsoft_regorus_Engine
|
||||
* Method: nativeSetInputJson
|
||||
* Signature: (JLjava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeSetInputJson
|
||||
(JNIEnv *, jclass, jlong, jstring);
|
||||
|
||||
/*
|
||||
* Class: com_microsoft_regorus_Engine
|
||||
* Method: nativeSetInputJsonFromFile
|
||||
* Signature: (JLjava/lang/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeSetInputJsonFromFile
|
||||
(JNIEnv *, jclass, jlong, jstring);
|
||||
|
||||
/*
|
||||
* Class: com_microsoft_regorus_Engine
|
||||
* Method: nativeEvalQuery
|
||||
* Signature: (JLjava/lang/String;)Ljava/lang/String;
|
||||
*/
|
||||
JNIEXPORT jstring JNICALL Java_com_microsoft_regorus_Engine_nativeEvalQuery
|
||||
(JNIEnv *, jclass, jlong, jstring);
|
||||
|
||||
/*
|
||||
* Class: com_microsoft_regorus_Engine
|
||||
* Method: nativeDestroyEngine
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_microsoft_regorus_Engine_nativeDestroyEngine
|
||||
(JNIEnv *, jclass, jlong);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
Licensed under the MIT License.
|
||||
-->
|
||||
|
||||
<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">
|
||||
<project>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.microsoft.regorus</groupId>
|
||||
@@ -16,12 +15,27 @@
|
||||
<description>Java bindings for Regorus - a fast, lightweight Rego interpreter written in Rust</description>
|
||||
<url>https://github.com/microsoft/regorus/bindings/java</url>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>MIT License</name>
|
||||
<url>https://opensource.org/blog/license/mit</url>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>ossrh</id>
|
||||
<name>Central Repository OSSRH</name>
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
@@ -38,114 +52,80 @@
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-site-plugin</artifactId>
|
||||
<version>3.7.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</plugin>
|
||||
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<version>2.5.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.8.2</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
<resources>
|
||||
<resource>
|
||||
<!--
|
||||
Include native/ folder in built JAR.
|
||||
During CI build we build native libraries for various platforms
|
||||
and put them into native/ folder.
|
||||
See `.github/publish-java.yml`.
|
||||
-->
|
||||
<directory>${project.basedir}/native</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<extensions>
|
||||
<extension>
|
||||
<groupId>kr.motd.maven</groupId>
|
||||
<artifactId>os-maven-plugin</artifactId>
|
||||
<version>1.7.0</version>
|
||||
</extension>
|
||||
</extensions>
|
||||
|
||||
<!-- Adapted from https://github.com/apache/opendal/blob/93e5f65bbf30df2fed4bdd95bb0685c73c6418c2/bindings/java/pom.xml -->
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile-native-code</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<executable>python3</executable>
|
||||
<arguments>
|
||||
<argument>${project.basedir}/tools/build.py</argument>
|
||||
<argument>--classifier</argument>
|
||||
<argument>${os.detected.classifier}</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<!-- Build a debug release for tests -->
|
||||
<id>build-native-lib-for-test</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<executable>python3</executable>
|
||||
<arguments>
|
||||
<argument>${project.basedir}/tools/testbuild.py</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.1.2</version>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.2.5</version>
|
||||
<configuration>
|
||||
<!-- Add debug build to Java path, so it's discoverable by JVM. This is only for tests. -->
|
||||
<argLine>-Djava.library.path=${project.basedir}/target/debug:${java.library.path}</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Build javadoc JAR, this is required by Maven Central. -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<!-- Generate the fallback JAR that does not contain the native library. -->
|
||||
<execution>
|
||||
<id>default-jar</id>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>native/**</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</execution>
|
||||
<!-- Generate the JAR that contains the native library in it. -->
|
||||
<execution>
|
||||
<id>native-jar</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<classifier>${os.detected.classifier}</classifier>
|
||||
<includes>
|
||||
<include>native/**</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.6.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadoc</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
<!-- Build sources JAR, this is required by Maven Central. -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<reporting>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
#
|
||||
# This script adapted from:
|
||||
# https://github.com/apache/opendal/blob/93e5f65bbf30df2fed4bdd95bb0685c73c6418c2/bindings/java/tools/build.py
|
||||
|
||||
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
def classifier_to_target(classifier: str) -> str:
|
||||
if classifier == 'osx-aarch_64':
|
||||
return 'aarch64-apple-darwin'
|
||||
if classifier == 'osx-x86_64':
|
||||
return 'x86_64-apple-darwin'
|
||||
if classifier == 'linux-aarch_64':
|
||||
return 'aarch64-unknown-linux-gnu'
|
||||
if classifier == 'linux-x86_64':
|
||||
return 'x86_64-unknown-linux-gnu'
|
||||
if classifier == 'windows-x86_64':
|
||||
return 'x86_64-pc-windows-msvc'
|
||||
raise Exception(f'Unsupported classifier: {classifier}')
|
||||
|
||||
def get_cargo_artifact_name(classifier: str) -> str:
|
||||
if classifier.startswith('osx'):
|
||||
return 'libregorus_java.dylib'
|
||||
if classifier.startswith('linux'):
|
||||
return 'libregorus_java.so'
|
||||
if classifier.startswith('windows'):
|
||||
return 'regorus_java.dll'
|
||||
raise Exception(f'Unsupported classifier: {classifier}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
basedir = Path(__file__).parent.parent
|
||||
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('--classifier', type=str, required=True)
|
||||
args = parser.parse_args()
|
||||
|
||||
target = classifier_to_target(args.classifier)
|
||||
|
||||
# Setup target.
|
||||
command = ['rustup', 'target', 'add', target]
|
||||
print('$ ' + subprocess.list2cmdline(command))
|
||||
subprocess.run(command, cwd=basedir, check=True)
|
||||
|
||||
cmd = ['cargo', 'build', '--target', target]
|
||||
|
||||
output = basedir / 'target' / 'bindings'
|
||||
Path(output).mkdir(exist_ok=True, parents=True)
|
||||
cmd += ['--target-dir', str(output)]
|
||||
|
||||
print('$ ' + subprocess.list2cmdline(cmd))
|
||||
subprocess.run(cmd, cwd=basedir, check=True)
|
||||
|
||||
# History reason of cargo profiles.
|
||||
profile = 'debug'
|
||||
artifact = get_cargo_artifact_name(args.classifier)
|
||||
src = output / target / profile / artifact
|
||||
dst = basedir / 'target' / 'classes' / 'native' / args.classifier / artifact
|
||||
dst.parent.mkdir(exist_ok=True, parents=True)
|
||||
shutil.copy2(src, dst)
|
||||
17
bindings/java/tools/testbuild.py
Normal file
17
bindings/java/tools/testbuild.py
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
#
|
||||
# Builds Regorus Java to use in Java tests. See `pom.xml`.
|
||||
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
if __name__ == "__main__":
|
||||
basedir = Path(__file__).parent.parent
|
||||
|
||||
output = basedir / "target"
|
||||
Path(output).mkdir(exist_ok=True, parents=True)
|
||||
cmd = ["cargo", "build", "--target-dir", str(output)]
|
||||
print("$ " + subprocess.list2cmdline(cmd))
|
||||
subprocess.run(cmd, cwd=basedir, check=True)
|
||||
Reference in New Issue
Block a user