diff --git a/java/craftlabs-auth-core/pom.xml b/java/craftlabs-auth-core/pom.xml
index 37b0cd2..492ebed 100644
--- a/java/craftlabs-auth-core/pom.xml
+++ b/java/craftlabs-auth-core/pom.xml
@@ -19,6 +19,10 @@
com.fasterxml.jackson.core
jackson-databind
+
+ net.java.dev.jna
+ jna
+
org.junit.jupiter
junit-jupiter
diff --git a/java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/internal/CraftCoreLibrary.java b/java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/internal/CraftCoreLibrary.java
new file mode 100644
index 0000000..3abc769
--- /dev/null
+++ b/java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/internal/CraftCoreLibrary.java
@@ -0,0 +1,53 @@
+package cn.craftlabs.auth.internal;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+import com.sun.jna.Structure;
+import java.util.Arrays;
+import java.util.List;
+
+public interface CraftCoreLibrary extends Library {
+ CraftCoreLibrary INSTANCE = Native.load("craftlabs_auth_core", CraftCoreLibrary.class);
+
+ class CraftResult extends Structure {
+ public int success;
+ public String message;
+
+ @Override
+ protected List getFieldOrder() {
+ return Arrays.asList("success", "message");
+ }
+ }
+
+ class LicenseInfoStruct extends Structure {
+ public int isLicensed;
+ public long expirationDate;
+ public Pointer featureNames;
+ public Pointer featureValues;
+ public int featureCount;
+
+ @Override
+ protected List getFieldOrder() {
+ return Arrays.asList("isLicensed", "expirationDate", "featureNames", "featureValues", "featureCount");
+ }
+ }
+
+ Pointer craft_initialize(String configJson);
+
+ void craft_destroy(Pointer handle);
+
+ CraftResult craft_activate(Pointer handle, String licenseKey);
+
+ CraftResult craft_check_license(Pointer handle);
+
+ LicenseInfoStruct craft_get_license_info(Pointer handle);
+
+ void craft_free_license_info(LicenseInfoStruct info);
+
+ int craft_has_feature(Pointer handle, String featureName);
+
+ CraftResult craft_release(Pointer handle);
+
+ CraftResult craft_heartbeat(Pointer handle);
+}
diff --git a/java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/internal/JnaAuthProvider.java b/java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/internal/JnaAuthProvider.java
new file mode 100644
index 0000000..dade941
--- /dev/null
+++ b/java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/internal/JnaAuthProvider.java
@@ -0,0 +1,89 @@
+package cn.craftlabs.auth.internal;
+
+import cn.craftlabs.auth.AuthProvider;
+import cn.craftlabs.auth.AuthResult;
+import cn.craftlabs.auth.LicenseInfo;
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+public class JnaAuthProvider implements AuthProvider {
+ private Pointer nativeHandle;
+
+ @Override
+ public AuthResult initialize(String configJson) {
+ if (nativeHandle != null) {
+ CraftCoreLibrary.INSTANCE.craft_destroy(nativeHandle);
+ }
+ nativeHandle = CraftCoreLibrary.INSTANCE.craft_initialize(
+ configJson != null ? configJson : "{}");
+ return new AuthResult(nativeHandle != null, "Initialized");
+ }
+
+ @Override
+ public AuthResult activate(String licenseKey) {
+ CraftCoreLibrary.CraftResult r = CraftCoreLibrary.INSTANCE.craft_activate(
+ nativeHandle, licenseKey);
+ return new AuthResult(r.success != 0, r.message);
+ }
+
+ @Override
+ public AuthResult checkLicense() {
+ CraftCoreLibrary.CraftResult r = CraftCoreLibrary.INSTANCE.craft_check_license(nativeHandle);
+ return new AuthResult(r.success != 0, r.message);
+ }
+
+ @Override
+ public LicenseInfo getLicenseInfo() {
+ CraftCoreLibrary.LicenseInfoStruct s = CraftCoreLibrary.INSTANCE.craft_get_license_info(nativeHandle);
+ if (s == null) {
+ return new LicenseInfo(false, null, null);
+ }
+
+ Map features = null;
+ if (s.featureCount > 0 && s.featureNames != null) {
+ features = new HashMap<>(s.featureCount);
+ for (int i = 0; i < s.featureCount; i++) {
+ Pointer namePtr = s.featureNames.getPointer((long) i * Native.POINTER_SIZE);
+ String name = namePtr != null ? namePtr.getString(0) : null;
+ if (name != null) {
+ int val = s.featureValues != null
+ ? s.featureValues.getInt((long) i * Integer.BYTES)
+ : 0;
+ features.put(name, val != 0);
+ }
+ }
+ }
+
+ Date expirationDate = s.expirationDate > 0 ? new Date(s.expirationDate) : null;
+ CraftCoreLibrary.INSTANCE.craft_free_license_info(s);
+ return new LicenseInfo(s.isLicensed != 0, expirationDate, features);
+ }
+
+ @Override
+ public boolean hasFeature(String featureName) {
+ return CraftCoreLibrary.INSTANCE.craft_has_feature(nativeHandle, featureName) != 0;
+ }
+
+ @Override
+ public AuthResult release() {
+ CraftCoreLibrary.CraftResult r = CraftCoreLibrary.INSTANCE.craft_release(nativeHandle);
+ return new AuthResult(r.success != 0, r.message);
+ }
+
+ @Override
+ public AuthResult heartbeat() {
+ CraftCoreLibrary.CraftResult r = CraftCoreLibrary.INSTANCE.craft_heartbeat(nativeHandle);
+ return new AuthResult(r.success != 0, r.message);
+ }
+
+ @Override
+ public void close() {
+ if (nativeHandle != null) {
+ CraftCoreLibrary.INSTANCE.craft_destroy(nativeHandle);
+ nativeHandle = null;
+ }
+ }
+}
diff --git a/java/pom.xml b/java/pom.xml
index ddd2932..1e04e5b 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -51,6 +51,11 @@
${json-schema-validator.version}
test
+
+ net.java.dev.jna
+ jna
+ 5.14.0
+