{"id":13822365,"url":"https://github.com/astonbitecode/j4rs","last_synced_at":"2025-05-16T15:34:15.300Z","repository":{"id":47265648,"uuid":"126169735","full_name":"astonbitecode/j4rs","owner":"astonbitecode","description":"Java for Rust","archived":false,"fork":false,"pushed_at":"2024-05-22T10:47:25.000Z","size":42325,"stargazers_count":515,"open_issues_count":9,"forks_count":34,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-05-22T11:52:29.171Z","etag":null,"topics":["java","jni","rust"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/astonbitecode.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-21T11:38:25.000Z","updated_at":"2024-05-27T13:53:42.265Z","dependencies_parsed_at":"2023-02-12T12:40:12.753Z","dependency_job_id":"fd069698-6aca-4a43-a504-4bc77b518014","html_url":"https://github.com/astonbitecode/j4rs","commit_stats":{"total_commits":333,"total_committers":8,"mean_commits":41.625,"dds":0.03003003003003002,"last_synced_commit":"d74c8fb92071d36229d8404022a662c724fdeb8c"},"previous_names":[],"tags_count":48,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astonbitecode%2Fj4rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astonbitecode%2Fj4rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astonbitecode%2Fj4rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/astonbitecode%2Fj4rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/astonbitecode","download_url":"https://codeload.github.com/astonbitecode/j4rs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225436676,"owners_count":17474194,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","jni","rust"],"created_at":"2024-08-04T08:01:56.980Z","updated_at":"2025-05-16T15:34:15.293Z","avatar_url":"https://github.com/astonbitecode.png","language":"Rust","readme":"# \u003cimg src=\"./static/j4rs-small.png\" alt=\"j4rs\"\u003e\n\n[![crates.io](https://img.shields.io/crates/v/j4rs.svg)](https://crates.io/crates/j4rs)\n[![Maven Central](https://img.shields.io/badge/Maven%20Central-0.22.0-blue.svg)](https://central.sonatype.com/artifact/io.github.astonbitecode/j4rs/)\n![Build](https://github.com/astonbitecode/j4rs/actions/workflows/ci-workflow.yml/badge.svg)\n\nj4rs stands for __'Java for Rust'__ and allows effortless calls to Java code from Rust and vice-versa.\n\n## Features\n\n* **Rust to Java direction support (call Java from Rust).**\n  * No special configuration needed (no need to tweak LD_LIBRARY_PATH, PATH etc).\n  * [Easily instantiate and invoke Java classes.](#Basics)\n  * [Support custom types via serialization.](#Passing-custom-arguments-from-Rust-to-Java)\n  * [.async/.await support](#Async-support)\n  * [Casting support.](#Casting)\n  * [Java arrays / variadic support.](#Java-arrays-and-variadics)\n  * [Java generics support.](#Java-Generics)\n  * [Java primitives support.](#Java-primitives)\n  * [Java instances invocations chaining.](#Java-instances-chaining)\n  * [Java -\u003e Rust callbacks support.](#Callback-support)\n  * [Simple Maven artifacts download and deployment.](#Using-Maven-artifacts)\n* **[Java -\u003e Rust support](#Java-to-Rust-support) (Call Rust from Java).**\n* **[JavaFX support](#JavaFX-support) (including FXML support).**\n* **Tested on Linux, Windows and [Android](#j4rs-in-android).**\n\n## Instead of donating to the dev\n\nWith the hope that you find this project useful, please consider donating to [charity](./charity/README_CHARITY.md). \n\n## Usage\n\n### Basics\n\n```rust\nuse j4rs::{Instance, InvocationArg, Jvm, JvmBuilder};\n\n// Create a JVM\nlet jvm = JvmBuilder::new().build()?;\n\n// Create a java.lang.String instance\nlet string_instance = jvm.create_instance(\n    \"java.lang.String\",     // The Java class to create an instance for\n    InvocationArg::empty(), // An array of `InvocationArg`s to use for the constructor call - empty for this example\n)?;\n\n// The instances returned from invocations and instantiations can be viewed as pointers to Java Objects.\n// They can be used for further Java calls.\n// For example, the following invokes the `isEmpty` method of the created java.lang.String instance\nlet boolean_instance = jvm.invoke(\n  \u0026string_instance,       // The String instance created above\n  \"isEmpty\",              // The method of the String instance to invoke\n  InvocationArg::empty(), // The `InvocationArg`s to use for the invocation - empty for this example\n)?;\n\n// If we need to transform an `Instance` to some Rust value, the `to_rust` should be called\nlet rust_boolean: bool = jvm.to_rust(boolean_instance)?;\nprintln!(\"The isEmpty() method of the java.lang.String instance returned {}\", rust_boolean);\n// The above prints:\n// The isEmpty() method of the java.lang.String instance returned true\n\n// Static invocation\nlet _static_invocation_result = jvm.invoke_static(\n  \"java.lang.System\",     // The Java class to invoke\n  \"currentTimeMillis\",    // The static method of the Java class to invoke\n  InvocationArg::empty(), // The `InvocationArg`s to use for the invocation - empty for this example\n)?;\n\n// Access a field of a class\nlet system_class = jvm.static_class(\"java.lang.System\")?;\nlet system_out_field = jvm.field(\u0026system_class, \"out\");\n\n// Retrieve an enum constant using the field\nlet access_mode_enum = jvm.static_class(\"java.nio.file.AccessMode\")?;\nlet access_mode_write = jvm.field(\u0026access_mode_enum, \"WRITE\")?;\n\n// Retrieve a nested class (note the use of `$` instead of `.`)\nlet state = jvm.static_class(\"java.lang.Thread$State\")?;\n```\n\n`Instances`s of Java `List`s and `Map`s can be created with the `java_list` and `java_map` functions:\n\n```rust\nlet rust_vec = vec![\"arg1\", \"arg2\", \"arg3\", \"arg33\"];\n\n// Generate a Java List\u003cString\u003e. The Java List implementation is the one that is returned by java.util.Arrays#asList\nlet java_list_instance = jvm.java_list(\n    JavaClass::String,\n    rust_vec)?;\n\nlet rust_map = HashMap::from([\n    (\"Potatoes\", 3),\n    (\"Tomatoes\", 33),\n    (\"Carrotoes\", 333),\n]);\n\n// Generate a java.util.HashMap.\nlet java_map_instance = jvm.java_map(\n    JavaClass::String,\n    JavaClass::Integer,\n    rust_map)?;\n```\n\n### Passing arguments from Rust to Java\n\nj4rs uses the `InvocationArg` enum to pass arguments to the Java world.\n\nUsers can benefit of the existing `TryFrom` implementations for several basic types:\n\n```rust\nlet i1 = InvocationArg::try_from(\"a str\")?;      // Creates an arg of java.lang.String\nlet my_string = \"a string\".to_owned();\nlet i2 = InvocationArg::try_from(my_string)?;    // Creates an arg of java.lang.String\nlet i3 = InvocationArg::try_from(true)?;         // Creates an arg of java.lang.Boolean\nlet i4 = InvocationArg::try_from(1_i8)?;         // Creates an arg of java.lang.Byte\nlet i5 = InvocationArg::try_from('c')?;          // Creates an arg of java.lang.Character\nlet i6 = InvocationArg::try_from(1_i16)?;        // Creates an arg of java.lang.Short\nlet i7 = InvocationArg::try_from(1_i64)?;        // Creates an arg of java.lang.Long\nlet i8 = InvocationArg::try_from(0.1_f32)?;      // Creates an arg of java.lang.Float\nlet i9 = InvocationArg::try_from(0.1_f64)?;      // Creates an arg of java.lang.Double\n```\n\nAnd for `Vec`s:\n\n```rust\nlet my_vec: Vec\u003cString\u003e = vec![\n    \"abc\".to_owned(),\n    \"def\".to_owned(),\n    \"ghi\".to_owned()];\n\n// Creates List\u003cString\u003e\nlet i10 = InvocationArg::try_from(my_vec.as_slice())?;\n\nlet another_vec: Vec\u003cf64\u003e = vec![0.0, 1.0, 3.6];\n\n// Creates List\u003cFloat\u003e\nlet i11 = InvocationArg::try_from(another_vec.as_slice())?;\n```\n\nThe `j4rs` apis accept `InvocationArg`s either as references, or values:\n\n```rust\nlet inv_args = InvocationArg::try_from(\"arg from Rust\")?;\nlet _ = jvm.create_instance(\"java.lang.String\", \u0026[\u0026inv_args])?; // Pass a reference\nlet _ = jvm.create_instance(\"java.lang.String\", \u0026[inv_args])?;  // Move\n```\n\nThe `Instance`s returned by j4rs can be transformed to `InvocationArg`s and be further used for invoking methods as well:\n\n```rust\nlet one_more_string_instance = jvm.create_instance(\n  \"java.lang.String\",     // The Java class to create an instance for\n  InvocationArg::empty(), // The `InvocationArg`s to use for the constructor call - empty for this example\n)?;\n\nlet i11 = InvocationArg::try_from(one_more_string_instance)?;\n```\n\nTo create an `InvocationArg` that represents a `null` Java value, use the `From` implementation with the `Null` struct:\n\n```rust\nlet null_string = InvocationArg::from(Null::String);                // A null String\nlet null_integer = InvocationArg::from(Null::Integer);              // A null Integer\nlet null_obj = InvocationArg::from(Null::Of(\"java.util.List\"));     // A null object of any other class. E.g. List\n```\n\n### Passing custom arguments from Rust to Java\n\nCustom types, for which there is no `TryFrom` implementation, are also supported via serialization.\n\nTo use a custom struct `MyBean` as an `InvocationArg` it needs to be serializable:\n\n```rust\n#[derive(Serialize, Deserialize, Debug)]\n#[allow(non_snake_case)]\nstruct MyBean {\n    someString: String,\n    someInteger: isize,\n}\n```\n\nThen, an `InvocationArg` can be created like:\n\n```rust\nlet my_bean = MyBean {\n    someString: \"My String In A Bean\".to_string(),\n    someInteger: 33,\n};\nlet ia = InvocationArg::new(\u0026my_bean, \"org.astonbitecode.j4rs.tests.MyBean\");\n```\n\nAnd it can be used as an argument to a Java method that accepts `org.astonbitecode.j4rs.tests.MyBean` instances.\n\nOf course, there should exist a respective Java class in the classpath for the deserialization to work and the custom Java Object to be created:\n\n```java\npackage org.astonbitecode.j4rs.tests;\n\npublic class MyBean {\n    private String someString;\n    private Integer someInteger;\n\n    public MyBean() {\n    }\n\n    public String getSomeString() {\n        return someString;\n    }\n\n    public void setSomeString(String someString) {\n        this.someString = someString;\n    }\n\n    public Integer getSomeInteger() {\n        return someInteger;\n    }\n\n    public void setSomeInteger(Integer someInteger) {\n        this.someInteger = someInteger;\n    }\n}\n```\n\n### Async support\n(v0.16.0 onwards)\n\n`j4rs` supports `.async/.await` via`Jvm::invoke_async` function.\nThe function returns a [Future](https://docs.rs/futures/latest/futures/future/trait.Future.html), which is completed via the `Receiver` of a [oneshot channel](https://docs.rs/futures/latest/futures/channel/oneshot/fn.channel.html).\n\nIn Java side, the methods that can be invoked by `invoke_async`, __must__ return a Java [Future](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/Future.html).\nWhen the Java Future completes, the Java side of `j4rs` invokes native Rust code that completes the pending Rust `Future`\nwith either success or failure, using the `Sender` of the oneshot channel that was created when the `invoke_async` was called.\n\nFor example, assuming we have a Java method that returns a Future:\n\n```java\npackage org.astonbitecode.j4rs.tests;\n\npublic class MyTest {\n  private static ExecutorService executor = Executors.newSingleThreadExecutor();\n\n  // Just return the passed String in a Future\n  public Future\u003cString\u003e getStringWithFuture(String string) {\n    CompletableFuture\u003cString\u003e completableFuture = new CompletableFuture\u003c\u003e();\n    executor.submit(() -\u003e {\n      completableFuture.complete(string);\n      return null;\n    });\n    return completableFuture;\n  }\n}\n```\n\nWe can invoke it like following:\n\n```rust\nlet s_test = \"j4rs_rust\";\nlet my_test = jvm.create_instance(\"org.astonbitecode.j4rs.tests.MyTest\", InvocationArg::empty())?;\nlet instance = jvm.invoke_async(\u0026my_test, \"getStringWithFuture\", \u0026[InvocationArg::try_from(s_test)?]).await?;\nlet string: String = jvm.to_rust(instance)?;\nassert_eq!(s_test, string);\n```\n\nPlease note that it is better for the Java methods that are invoked by the `invoke_async` function\nto return a [CompletableFuture](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/CompletableFuture.html),\nas this improves performance.\n\n`j4rs` handles simple Java Futures that are not `CompletableFuture`s with [polling](https://github.com/astonbitecode/j4rs/blob/86a2cb7bee10e5941fd0cada00afc355ea9e3ebb/java/src/main/java/org/astonbitecode/j4rs/api/async/J4rsPolledFuture.java#L25),\nusing an internal one-threaded `ScheduledExecutorService`.\n\nThis has apparent performance issues.\n\n#### `invoke_async` and `Send`\n\n`Instance`s  are `Send` and can be safely sent to other threads. However, because of [Send Approximation](https://rust-lang.github.io/async-book/07_workarounds/03_send_approximation.html), the `Future` returned by `invoke_async` is _not_ `Send`, even if it just contains an `Instance`. This is because the `Jvm` is being captured by the `async` call as well and the `Jvm` is __not__ `Send`.\n\nIn order to have a `Future\u003cInstance\u003e` that __is__ `Send`, the `Jvm::invoke_into_sendable_async` can be used. This function does not get a `Jvm` as argument; it creates one internally when needed and applies some scoping workarounds in order to achieve returning a `Future\u003cInstance\u003e` which is also `Send`.\n\nDiscussion [here](https://github.com/astonbitecode/j4rs/issues/103).\n\n### Casting\n\nAn `Instance` may be casted to some other Class:\n\n```rust\nlet instantiation_args = vec![InvocationArg::try_from(\"Hi\")?];\nlet instance = jvm.create_instance(\"java.lang.String\", instantiation_args.as_ref())?;\njvm.cast(\u0026instance, \"java.lang.Object\")?;\n```\n\n### Java arrays and variadics\n\n```rust\n// Create a Java array of Strings `String []`\nlet s1 = InvocationArg::try_from(\"string1\")?;\nlet s2 = InvocationArg::try_from(\"string2\")?;\nlet s3 = InvocationArg::try_from(\"string3\")?;\n\nlet arr_instance = jvm.create_java_array(\"java.lang.String\", \u0026[s1, s2, s3])?;\n// Invoke the Arrays.asList(...) and retrieve a java.util.List\u003cString\u003e\nlet list_instance = jvm.invoke_static(\"java.util.Arrays\", \"asList\", \u0026[InvocationArg::from(arr_instance)])?;\n```\n\nWhen creating an array of primitives, each `InvocationArg` item needs to be converted to one containing primitive value.\n\n```rust\nlet doubles = vec![0.1, 466.5, 21.37];\n\n// Creates Java `double` primitives within a Rust `Vec`\nlet double_args: Vec\u003cInvocationArg\u003e = doubles.iter()\n    .map(|value| Ok::\u003c_, J4RsError\u003e(\n        InvocationArg::try_from(value)?.into_primitive()?\n    ))\n    .collect::\u003cResult\u003c_, J4RsError\u003e\u003e()?\n\n// Creates an instance of `double []`\nlet doubles_array = InvocationArg::try_from(\n    jvm.create_java_array(\"double\", \u0026double_args)?\n)?;\n```\n\n### Java Generics\n\n```rust\n// Assuming the following map_instance is a Map\u003cString, Integer\u003e\n// we may invoke its put method\njvm.invoke(\u0026map_instance, \"put\", \u0026[InvocationArg::try_from(\"one\")?, InvocationArg::try_from(1)?])?;\n```\n\n### Java primitives\n\nEven if auto boxing and unboxing is in place, `j4rs` cannot invoke methods with _primitive_ int arguments using _Integer_ instances.\n\nFor example, the following code does not work:\n\n```rust\nlet ia = InvocationArg::try_from(1_i32)?;\njvm.create_instance(\"java.lang.Integer\", \u0026[ia])?;\n``` \n\nIt throws an _InstantiationException_ because the constructor of `Integer` takes a primitive `int` as an argument:\n\n\u003eException in thread \"main\" org.astonbitecode.j4rs.errors.InstantiationException: Cannot create instance of java.lang.Integer\nat org.astonbitecode.j4rs.api.instantiation.NativeInstantiationImpl.instantiate(NativeInstantiationImpl.java:37)\nCaused by: java.lang.NoSuchMethodException: java.lang.Integer.\u003cinit\u003e(java.lang.Integer)\nat java.base/java.lang.Class.getConstructor0(Class.java:3349)\nat java.base/java.lang.Class.getConstructor(Class.java:2151)\nat org.astonbitecode.j4rs.api.instantiation.NativeInstantiationImpl.createInstance(NativeInstantiationImpl.java:69)\nat org.astonbitecode.j4rs.api.instantiation.NativeInstantiationImpl.instantiate(NativeInstantiationImpl.java:34)\n\nIn situations like this, the `java.lang.Integer` instance should be transformed to a primitive `int` first:\n\n```rust\nlet ia = InvocationArg::try_from(1_i32)?.into_primitive()?;\njvm.create_instance(\"java.lang.Integer\", \u0026[ia]);\n```\n\n### Java instances chaining\n```rust\nuse j4rs::{Instance, InvocationArg, Jvm, JvmBuilder};\n\n// Create a JVM\nlet jvm = JvmBuilder::new().build()?;\n\n// Create an instance\nlet string_instance = jvm.create_instance(\n  \"java.lang.String\",\n  \u0026[InvocationArg::try_from(\" a string \")?],\n)?;\n\n// Perform chained operations on the instance\nlet string_size: isize = jvm.chain(string_instance)\n    .invoke(\"trim\", InvocationArg::empty())?\n    .invoke(\"length\", InvocationArg::empty())?\n    .to_rust()?;\n\n// Assert that the string was trimmed\nassert!(string_size == 8);\n```\n\n### Callback support\n\n`j4rs` provides support for _Java to Rust callbacks_.\n\nThese callbacks come to the Rust world via Rust [Channels](https://doc.rust-lang.org/std/sync/mpsc/fn.channel.html).\n\nIn order to initialize a channel that will provide Java callback values, the `Jvm::invoke_to_channel` should be called. It returns a result of `InstanceReceiver` struct, which contains a Channel [Receiver](https://doc.rust-lang.org/std/sync/mpsc/struct.Receiver.html):\n\n```rust\n// Invoke of a method of a Java instance and get the returned value in a Rust Channel.\n\n// Create an Instance of a class that supports Native Callbacks\n// (the class just needs to extend the \n// `org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport`)\nlet i = jvm.create_instance(\n  \"org.astonbitecode.j4rs.tests.MyTest\",\n  InvocationArg::empty())?;\n\n// Invoke the method\nlet instance_receiver_res = jvm.invoke_to_channel(\n  \u0026i,                         // The instance to invoke asynchronously\n  \"performCallback\",          // The method to invoke asynchronoysly\n  InvocationArg::empty()      // The `InvocationArg`s to use for the invocation - empty for this example\n);\n\n// Wait for the response to come\nlet instance_receiver = instance_receiver_res?;\nlet _ = instance_receiver.rx().recv();\n```\n\nIn the Java world, a Class that can do __Native Callbacks__ must extend the\n`org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport`\n\nFor example, consider the following Java class.\n\nThe `performCallback` method spawns a new Thread and invokes the `doCallback` method in this Thread. The `doCallback` method is inherited by the `NativeCallbackToRustChannelSupport` class.\n\n```java\npackage org.astonbitecode.j4rs.tests;\n\nimport org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport;\n\npublic class MyTest extends NativeCallbackToRustChannelSupport {\n\n    public void performCallback() {\n        new Thread(() -\u003e {\n            doCallback(\"THIS IS FROM CALLBACK!\");\n        }).start();\n    }\n\n}\n```\n\n### Using Maven artifacts\n\nSince release 0.6.0 there is the possibility to download Java artifacts from the Maven repositories.\nWhile it is possible to define more repos, the [maven central](https://search.maven.org/) is by default and always available.\n\nFor example, here is how the dropbox dependency can be downloaded and get deployed to be used by the rust code:\n\n```rust\nlet dbx_artifact = MavenArtifact::from(\"com.dropbox.core:dropbox-core-sdk:3.0.11\");\njvm.deploy_artifact(dbx_artifact)?;\n```\n\nAdditional artifactories can be used as well:\n\n```rust\nlet jvm: Jvm = JvmBuilder::new()\n.with_maven_settings(MavenSettings::new(vec![\n    MavenArtifactRepo::from(\"myrepo1::https://my.repo.io/artifacts\"),\n    MavenArtifactRepo::from(\"myrepo2::https://my.other.repo.io/artifacts\")])\n)\n.build()\n?;\n\njvm.deploy_artifact(\u0026MavenArtifact::from(\"io.my:library:1.2.3\"))?;\n```\n\nMaven artifacts are added automatically to the classpath and do not need to be explicitly added.\n\nA good practice is that the deployment of maven artifacts is done by build scripts, during the crate's compilation. This ensures the classpath is properly populated during the actual Rust code execution.\n\n_Note: the deployment does not take care the transitive dependencies yet._\n\n### Adding jars to the classpath\n\nIf we have one jar that needs to be accessed using `j4rs`, we need to add it in the classpath during the JVM creation:\n\n```rust\nlet entry = ClasspathEntry::new(\"/home/myuser/dev/myjar-1.0.0.jar\");\nlet jvm: Jvm = JvmBuilder::new()\n    .classpath_entry(entry)\n    .build()?;\n```\n\n## j4rs Java library\n\nThe jar for `j4rs` is available in the Maven Central. It may be used by adding the following dependency in a pom:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.astonbitecode\u003c/groupId\u003e\n    \u003cartifactId\u003ej4rs\u003c/artifactId\u003e\n    \u003cversion\u003e0.22.0\u003c/version\u003e\n    \u003cscope\u003eprovided\u003c/scope\u003e\n\u003c/dependency\u003e\n```\n\nNote that the `scope` is `provided`. This is because the `j4rs` Java resources are always available with the `j4rs` crate.\n\nUse like this in order to avoid possible classloading errors.\n\n## j4rs in android\n\n`j4rs` can be used in Android either to call a Rust native library (Java -\u003e Rust direction), or with a native-only approach ([Android NativeActivity](https://developer.android.com/ndk/reference/group/native-activity)) (Rust -\u003e Java direction, using [android-activity](https://crates.io/crates/android-activity) crate, [ndk-context](https://crates.io/crates/ndk-context) crate, or similar).\n\n### Android Java -\u003e Rust direction\n\nHere is what is needed when you have a Java/Kotlin app and you need to call a function from a Rust native library.\n\n#### Rust side\n\n1. Define your crate as cdylib in the `Cargo.toml`:\n\n```toml\n[lib]\nname = \"myandroidapp\"\ncrate-type = [\"cdylib\"]\n```\n\n2. Implement a `jni_onload` function and apply the provided `JavaVM`\n   to the `j4rs` like following:\n\n```rust\nconst JNI_VERSION_1_6: jint = 0x00010006;\n\n#[allow(non_snake_case)]\n#[no_mangle]\npub extern fn jni_onload(env: *mut JavaVM, _reserved: jobject) -\u003e jint {\n    j4rs::set_java_vm(env);\n    jni_version_1_6\n}\n```\n\n#### Java side\n\nCreate an `Activity` and define your native methods normally, as described [here](#java-to-rust-support).\n\nNote:\nIf you encounter any issues when using j4rs in older Android versions, this may be caused by Java 8 compatibility problems.\nThis is why there is a `Java 7` version of `j4rs`:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.astonbitecode\u003c/groupId\u003e\n    \u003cartifactId\u003ej4rs\u003c/artifactId\u003e\n    \u003cversion\u003e0.13.1-java7\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nUpdate: Java 7 is no more supported. `j4rs` 0.13.1 is the last version supporting Java 7.\n\n### Full Example\n\nPlease see [here](https://github.com/astonbitecode/j4rs-android-test).\n\n### Android Rust -\u003e Java direction\n\nWhen you need to call Java from Rust, using crates like [android-activity](https://crates.io/crates/android-activity), you will need to initialize the `Jvm`, providing the `JavaVM` and `Activity` from the JNI:\n\n```rust\nuse android_activity::AndroidApp;\nuse j4rs::{InvocationArg, JvmBuilder};\nuse j4rs::jni_sys::{JavaVM, jobject};\n\n#[no_mangle]\nfn android_main(app: AndroidApp) {\n    let java_vm: *mut JavaVM = app.vm_as_ptr().cast();\n    let activity_obj: jobject = app.activity_as_ptr().cast();\n    let jvm = JvmBuilder::new()\n        .with_java_vm(java_vm.clone())\n        .with_classloader_of_activity(activity_obj.clone())\n        .build()\n        .unwrap();\n}\n```\n\n### Full Example\n\nPlease see [here](https://github.com/astonbitecode/j4rs-android-activity).\n\n## JavaFX support\n(v0.13.0 onwards)\n\n### Steps to build a JavaFX UI\n\n#### 1. Have Rust, cargo and JDK 11 (or above) installed\n\n#### 2. Retrieve the JavaFX dependencies for j4rs:\n\nA good idea is that this happens during build time, in order the dependencies to be available when the actual Rust application starts and the JVM is initialized.\nThis can happen by adding the following in a [build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html?highlight=build,scrpit#build-scripts):\n\n```rust\n    use j4rs::JvmBuilder;\nuse j4rs::jfx::JavaFxSupport;\n\nfn main() {\n    let jvm = JvmBuilder::new().build().unwrap();\n    jvm.deploy_javafx_dependencies().unwrap();\n}\n\n```\n\n#### 3. Implement the UI:\n\nThere are two choices here; either build the UI using FXML, or, build it traditionally, using Java code.\nIn the code snippets below, you may find comments with a short description for each line.\n\n##### 3.a Implement the UI with Java calls to the JavaFX API\n\n```rust\n// Create a Jvm with JavaFX support\nlet jvm = JvmBuilder::new().with_javafx_support().build()?;\n\n// Start the JavaFX application.\n// When the JavaFX application starts, the `InstanceReceiver` channel that is returned from the `start_javafx_app` invocation\n// will receive an Instance of `javafx.stage.Stage`.\n// The UI may start being built using the provided `Stage`.\nlet stage = jvm.start_javafx_app()?.rx().recv()?;\n\n// Create a StackPane. Java code: StackPane root = new StackPane();\nlet root = jvm.create_instance(\"javafx.scene.layout.StackPane\", InvocationArg::empty())?;\n\n// Create the button. Java code: Button btn = new Button();\nlet btn = jvm.create_instance(\"javafx.scene.control.Button\", InvocationArg::empty())?;\n// Get the action channel for this button\nlet btn_action_channel = jvm.get_javafx_event_receiver(\u0026btn, FxEventType::ActionEvent_Action)?;\n// Set the text of the button. Java code: btn.setText(\"Say Hello World to Rust\");\njvm.invoke(\u0026btn, \"setText\", \u0026[\"A button that sends events to Rust\".try_into()?])?;\n// Add the button to the GUI. Java code: root.getChildren().add(btn);\njvm.chain(\u0026root)?\n  .invoke(\"getChildren\", InvocationArg::empty())?\n  .invoke(\"add\", \u0026[btn.try_into()?])?\n  .collect();\n\n// Create a new Scene. Java code: Scene scene = new Scene(root, 300, 250);\nlet scene = jvm.create_instance(\"javafx.scene.Scene\", \u0026[\n  root.try_into()?,\n  InvocationArg::try_from(300_f64)?.into_primitive()?,\n  InvocationArg::try_from(250_f64)?.into_primitive()?])?;\n// Set the title for the scene. Java code: stage.setTitle(\"Hello Rust world!\");\njvm.invoke(\u0026stage, \"setTitle\", \u0026[\"Hello Rust world!\".try_into()?])?;\n// Set the scene in the stage. Java code: stage.setScene(scene);\njvm.invoke(\u0026stage, \"setScene\", \u0026[scene.try_into()?])?;\n// Show the stage. Java code: stage.show();\njvm.invoke(\u0026stage, \"show\", InvocationArg::empty())?;\n\n```\n\n##### 3.b Implement the UI with [FXML](https://openjfx.io/javadoc/12/javafx.fxml/javafx/fxml/doc-files/introduction_to_fxml.html#overview)\n\nI personally prefer building the UI with FXMLs, using for example the [Scene Builder](https://gluonhq.com/products/scene-builder/).\n\nThe thing to keep in mind is that the controller class should be defined in the root FXML element and it should be `fx:controller=\"org.astonbitecode.j4rs.api.jfx.controllers.FxController\"`\n\nHere is an FXML example; it creates a window with a label and a button:\n\n```xml\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\n\u003c?import javafx.scene.control.Button?\u003e\n\u003c?import javafx.scene.control.Label?\u003e\n\u003c?import javafx.scene.layout.HBox?\u003e\n\u003c?import javafx.scene.layout.VBox?\u003e\n\u003c?import javafx.scene.text.Font?\u003e\n\n\u003cVBox alignment=\"TOP_CENTER\" maxHeight=\"-Infinity\" maxWidth=\"-Infinity\" minHeight=\"-Infinity\" minWidth=\"-Infinity\" prefHeight=\"400.0\" prefWidth=\"725.0\" spacing=\"33.0\" xmlns=\"http://javafx.com/javafx/11.0.1\" xmlns:fx=\"http://javafx.com/fxml/1\" fx:controller=\"org.astonbitecode.j4rs.api.jfx.controllers.FxController\"\u003e\n    \u003cchildren\u003e\n        \u003cLabel text=\"JavaFX in Rust\"\u003e\n            \u003cfont\u003e\n                \u003cFont size=\"65.0\" /\u003e\n            \u003c/font\u003e\n        \u003c/Label\u003e\n        \u003cLabel text=\"This UI is loaded with a FXML file\" /\u003e\n        \u003cHBox alignment=\"CENTER\" prefHeight=\"100.0\" prefWidth=\"200.0\" spacing=\"10.0\"\u003e\n            \u003cchildren\u003e\n                \u003cButton id=\"helloButton\" mnemonicParsing=\"false\" text=\"Say Hello\" /\u003e\n            \u003c/children\u003e\n        \u003c/HBox\u003e\n    \u003c/children\u003e\n\u003c/VBox\u003e\n\n```\n\nThe `id` of the elements can be used to retrieve the respective [Nodes](https://openjfx.io/javadoc/13/javafx.graphics/javafx/scene/Node.html) in Rust and act upon them (eg. adding Event Listeners, changing the texts or effects on them etc).\n\n```rust\n// Create a Jvm with JavaFX support\nlet jvm = JvmBuilder::new().with_javafx_support().build()?;\n\n// Start the JavaFX application.\n// When the JavaFX application starts, the `InstanceReceiver` channel that is returned from the `start_javafx_app` invocation\n// will receive an Instance of `javafx.stage.Stage`.\n// The UI may start being built using the provided `Stage`.\nlet stage = jvm.start_javafx_app()?.rx().recv()?;\n\n// Set the title for the scene. Java code: stage.setTitle(\"Hello Rust world!\");\njvm.invoke(\u0026stage, \"setTitle\", \u0026[\"Hello JavaFX from Rust!\".try_into()?])?;\n// Show the stage. Java code: stage.show();\njvm.invoke(\u0026stage, \"show\", InvocationArg::empty())?;\n\n// Load a fxml. This returns an `FxController` which can be used in order to find Nodes by their id,\n// add Event Listeners and more.\nlet controller = jvm.load_fxml(\u0026PathBuf::from(\"./fxml/jfx_in_rust.fxml\"), \u0026stage)?;\n\n// Wait for the controller to be initialized. This is not mandatory, it is here to shoe that the functionality exists.\nlet _ = controller.on_initialized_callback(\u0026jvm)?.rx().recv()?;\nprintln!(\"The controller is initialized!\");\n\n// Get the InstanceReceiver to retrieve callbacks from the JavaFX button with id helloButton\nlet hello_button_action_channel = controller.get_event_receiver_for_node(\"helloButton\", FxEventType::ActionEvent_Action, \u0026jvm)?;\n\n```\n\nFor a complete example, please have a look [here](https://github.com/astonbitecode/j4rs-showcase).\n\n## Java to Rust support\n\n(v0.12.0 onwards)\n\n* Add the two needed dependencies (`j4rs` and `j4rs_derive`) in the `Cargo.toml`\n  and mark the project as a `cdylib`, in order to have a shared library as output.\n  This library will be loaded and used by the Java code to achieve JNI calls.\n\n* Annotate the functions that will be accessible from the Java code with the `call_from_java` attribute:\n\n```rust\n#[call_from_java(\"io.github.astonbitecode.j4rs.example.RustSimpleFunctionCall.fnnoargs\")]\nfn my_function_with_no_args() {\n    println!(\"Hello from the Rust world!\");\n    // If you need to have a Jvm here, you need to attach the thread\n    let jvm = Jvm::attach_thread().unwrap();\n    // Now you may further call Java classes and methods as usual!\n}\n```\n\nFor a complete example, please have a look [here](https://github.com/astonbitecode/j4rs-java-call-rust).\n\n*Note: JNI is used behind the scenes, so, any [conventions in naming](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/design.html#wp133) that hold for JNI, should hold for `j4rs` too.\nFor example, underscores (`_`) should be escaped and become `_1` in the `call_from_java` definition.*\n\n## Portability assumptions after Rust build (shipping a j4rs application)\n\nDuring build, `j4rs` creates a `jassets` directory which contains the \"java world\" that is needed for the crate to work.\nIt is always automatically populated with Java libraries and can be considered something like a default classpath container that should always be available.\n\nBy default, `jassets` lies in the same directory with the crate-generated artifacts (under [CARGO_TARGET_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html)), so there should not be any issues during development.\n\nBut how can the application be shipped after the implementation is done?\n\nSomeone may specify a different [base_path](https://docs.rs/j4rs/0.13.0/j4rs/struct.JvmBuilder.html#method.with_base_path) for j4rs during the Jvm initialization, issuing something like:\n\n```rust\nlet jvm_res = j4rs::JvmBuilder::new()\n  .with_base_path(\"/opt/myapp\")\n  .build();\n```\n\nThe `base_path` defines the location of two directories that are needed for j4rs to work;\nnamely `jassets` and `deps`.\n\n1. __jassets__ contains the j4rs jar and other jars that may be deployed using [Maven](https://github.com/astonbitecode/j4rs#Using-Maven-artifacts).\n2. __deps__ should contain the j4rs dynamic library. This is needed to achieve  [callbacks](https://github.com/astonbitecode/j4rs#Callback-support) from java to rust.\n   The `deps` dir is not needed if the application does not execute Java-\u003eRust callbacks.\n\nSo, someone may have their application binary under eg. `/usr/bin`, and the `jassets` and `deps` directories under `/opt/myapp/`, or `$HOME/.myapp`, or anywhere else.\n\nAn example directory tree could be:\n\n```\n/ \n+ --- usr\n|      + --- bin\n|             + --- myapp\n| \n+ --- opt\n       + --- myapp \n              + --- jassets\n              + --- deps\n```\n\nMoreover, there is also a [utility function](https://docs.rs/j4rs/0.13.0/j4rs/struct.Jvm.html#method.copy_j4rs_libs_under) that automatically performs copying of the two directories under a specific path.\nThe `Jvm::copy_j4rs_libs_under` function can be called by the build script of the crate that is being shipped:\n\n```rust\nJvm::copy_j4rs_libs_under(\"/opt/myapp\")?;\n```\n\nAfter that, `/opt/myapp` will contain everything that is needed in order `j4rs` to work,\nas long as the Jvm creation is done using the `with_base_path` method:\n\n```rust\nlet jvm_res = j4rs::JvmBuilder::new()\n  .with_base_path(\"/opt/myapp\")\n  .build();\n```\n\n## FAQ\n\n### I get `java.lang.NoSuchMethodError: java.net.URLClassLoader.\u003cinit\u003e(Ljava/lang/String;[Ljava/net/URL;Ljava/lang/ClassLoader;)V`\n\n`j4rs` uses a custom ClassLoader, that needs minimum Java version 9. In order to use the default classloader that supports\nolder Java versions, invoke the `JvmBuilder::with_default_classloader` when building the `Jvm`.\n\n### How can I enable debug logging?\n\n`j4rs` uses the [log crate](https://docs.rs/log/latest/log/), so, logging may be configured accordingly, depending on the chosen implementation.\n\nHowever, it also supports console logging, which is configured with setting the env var `J4RS_CONSOLE_LOG_LEVEL`.\n\nAccepted values are `debug`, `info`, `warn`, `error` and `disabled`.\n\n## Licence\n\nAt your option, under:\n\n* Apache License, Version 2.0, (http://www.apache.org/licenses/LICENSE-2.0)\n* MIT license (http://opensource.org/licenses/MIT)\n","funding_links":[],"categories":["Rust","Development tools"],"sub_categories":["FFI"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fastonbitecode%2Fj4rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fastonbitecode%2Fj4rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fastonbitecode%2Fj4rs/lists"}