├── .github └── workflows │ └── ci-workflow.yml ├── .gitignore ├── LICENSE ├── LICENSE-MIT ├── README.md ├── charity ├── README_CHARITY.md └── smile_of_the_child_logo.png ├── java-fx ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── astonbitecode │ │ │ └── j4rs │ │ │ └── api │ │ │ ├── invocation │ │ │ ├── JavaFxInstanceGeneratorDelegate.java │ │ │ └── JavaFxInvocation.java │ │ │ └── jfx │ │ │ ├── FxApplication.java │ │ │ ├── FxApplicationStartCallback.java │ │ │ ├── J4rsFxmlLoader.java │ │ │ ├── controllers │ │ │ ├── FxController.java │ │ │ └── FxControllerImpl.java │ │ │ ├── errors │ │ │ ├── ComponentNotFoundException.java │ │ │ └── FxException.java │ │ │ └── handlers │ │ │ └── J4rsEventHandler.java │ └── resources │ │ ├── MANIFEST.MF │ │ └── META-INF │ │ └── services │ │ └── org.astonbitecode.j4rs.api.services.delegates.InstanceGeneratorDelegate │ └── test │ └── java │ └── org │ └── astonbitecode │ └── j4rs │ └── api │ └── invocation │ └── JavaFxInstanceGeneratorDelegateTest.java ├── java ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── astonbitecode │ │ │ └── j4rs │ │ │ ├── api │ │ │ ├── Instance.java │ │ │ ├── JsonValue.java │ │ │ ├── NativeInstantiation.java │ │ │ ├── ObjectValue.java │ │ │ ├── async │ │ │ │ ├── J4rsAsyncContext.java │ │ │ │ └── J4rsPolledFuture.java │ │ │ ├── deploy │ │ │ │ ├── DeployUtils.java │ │ │ │ ├── FileSystemDeployer.java │ │ │ │ ├── J4rsClassLoader.java │ │ │ │ └── SimpleMavenDeployer.java │ │ │ ├── dtos │ │ │ │ ├── GeneratedArg.java │ │ │ │ ├── InvocationArg.java │ │ │ │ └── InvocationArgGenerator.java │ │ │ ├── instantiation │ │ │ │ └── NativeInstantiationImpl.java │ │ │ ├── invocation │ │ │ │ ├── InstanceGenerator.java │ │ │ │ ├── JsonInvocationImpl.java │ │ │ │ ├── NativeCallbackToRustChannelSupport.java │ │ │ │ └── NativeCallbackToRustFutureSupport.java │ │ │ ├── java2rust │ │ │ │ └── Java2RustUtils.java │ │ │ ├── services │ │ │ │ ├── delegates │ │ │ │ │ └── InstanceGeneratorDelegate.java │ │ │ │ └── json │ │ │ │ │ ├── Codec.java │ │ │ │ │ └── exceptions │ │ │ │ │ └── JsonCodecException.java │ │ │ └── value │ │ │ │ ├── JsonValueFactory.java │ │ │ │ ├── JsonValueImpl.java │ │ │ │ ├── NullJsonValueImpl.java │ │ │ │ └── NullObject.java │ │ │ ├── errors │ │ │ ├── InstantiationException.java │ │ │ ├── InvalidArgumentException.java │ │ │ └── InvocationException.java │ │ │ ├── json │ │ │ ├── JacksonCodec.java │ │ │ └── JsonCodecService.java │ │ │ ├── rust │ │ │ ├── FunctionPointer.java │ │ │ └── RustPointer.java │ │ │ └── utils │ │ │ └── Utils.java │ └── resources │ │ └── MANIFEST.MF │ └── test │ └── java │ └── org │ └── astonbitecode │ └── j4rs │ ├── api │ ├── deploy │ │ ├── FileSystemDeployerTest.java │ │ └── SimpleMavenDeployerTest.java │ ├── dtos │ │ └── InvocationArgTest.java │ ├── instantiation │ │ └── NativeInstantiationImplTest.java │ └── invocation │ │ ├── InstanceGeneratorTest.java │ │ ├── JsonInvocationImplTest.java │ │ └── NativeCallbackToRustChannelSupportTest.java │ ├── json │ └── JacksonCodecTest.java │ ├── tests │ └── MyTestTest.java │ ├── utils │ ├── ChildDummy.java │ ├── ChildOfDummyWithFields.java │ ├── ClassWithDummyAtConstructor.java │ ├── Dummy.java │ ├── DummyInterface.java │ ├── DummyInterfaceFather.java │ ├── DummyInterfaceMother.java │ ├── DummyMapImpl.java │ ├── DummyMapInterface.java │ ├── DummyWithFields.java │ ├── DummyWithStatic.java │ ├── FailingDummy.java │ ├── GrandchildDummy.java │ └── OtherDummy.java │ └── value │ └── JsonValueImplTest.java ├── rust ├── .gitignore ├── Cargo.toml ├── Cross.toml ├── README.md ├── benches │ └── j4rs_benchmark.rs ├── build.rs ├── j4rs_derive │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── lib.rs ├── jassets │ └── j4rs-0.23.0-SNAPSHOT-jar-with-dependencies.jar └── src │ ├── api │ ├── instance.rs │ ├── invocation_arg.rs │ └── mod.rs │ ├── api_tweaks │ ├── android.rs │ ├── generic.rs │ ├── mod.rs │ └── no_runtime_lib_loading.rs │ ├── async_api │ └── mod.rs │ ├── cache.rs │ ├── errors.rs │ ├── jfx.rs │ ├── jni_utils.rs │ ├── lib.rs │ ├── logger.rs │ ├── prelude.rs │ ├── provisioning.rs │ └── utils.rs ├── static └── j4rs-small.png └── test-resources └── java ├── pom.xml └── src └── main ├── java └── org │ └── astonbitecode │ └── j4rs │ └── tests │ ├── DummyMapImpl.java │ ├── DummyMapInterface.java │ ├── MyBean.java │ ├── MySecondTest.java │ └── MyTest.java └── resources └── MANIFEST.MF /.github/workflows/ci-workflow.yml: -------------------------------------------------------------------------------- 1 | name: j4rs CI 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | runs-on: ${{ matrix.os }} 15 | 16 | strategy: 17 | matrix: 18 | os: [ ubuntu-latest, macos-latest, windows-latest ] 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | - name: Set up JDK 17 23 | uses: actions/setup-java@v3 24 | with: 25 | java-version: '17' 26 | distribution: 'adopt' 27 | - name: Build and Test Java with Maven 28 | run: mvn -f ./java/pom.xml --batch-mode --update-snapshots install 29 | - name: Build and Test the JavaFX module with Maven 30 | run: mvn -f ./java/pom.xml --batch-mode --update-snapshots install 31 | - name: Build and install Java testing resources with Maven 32 | run: mvn -f ./test-resources/java/pom.xml --batch-mode --update-snapshots install 33 | - name: Build Rust with Cargo 34 | run: cargo build --manifest-path ./rust/Cargo.toml --verbose 35 | - name: Test Rust with Cargo 36 | run: cargo test --manifest-path ./rust/Cargo.toml --verbose -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.project 2 | **/target/ 3 | **/*.rs.bk 4 | **/.idea/ 5 | **/*.iml 6 | **/Cargo.lock 7 | **/settings/ 8 | **/primer/ 9 | **/.settings 10 | **/.classpath 11 | *.code-workspace 12 | **/.vscode/ 13 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 astonbitecode 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /charity/README_CHARITY.md: -------------------------------------------------------------------------------- 1 | # The Smile of the Child 2 | 3 | [](https://www.hamogelo.gr/gr/en/poioi-eimaste/) 4 | 5 | "[The Smile of the Child](https://www.hamogelo.gr/gr/en/poioi-eimaste/)" was created in 1995 by 10-year-old Andreas Yannopoulos, who, shortly before leaving from life, expressed in his diary his wish to found an organization that will ensure for all children what he had so generously enjoyed: love, affection, care and respect. 6 | 7 | The financial data of the Organization is publicized on an annual basis and is audited by International Body of Auditors of Ernst and Young. 8 | 9 | --- 10 | 11 | ### Please consider donating; there are [many ways](https://www.hamogelo.gr/gr/en/stirikste-mas/) to do so. 12 | 13 | _Note: `j4rs` is not affiliated with "The Smile of the Child", we just want to help and contribute to the longevity of the organization._ -------------------------------------------------------------------------------- /charity/smile_of_the_child_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astonbitecode/j4rs/40614a8dbe9a32ca51c5cc63c3ec3b3ebd75b766/charity/smile_of_the_child_logo.png -------------------------------------------------------------------------------- /java-fx/src/main/java/org/astonbitecode/j4rs/api/invocation/JavaFxInstanceGeneratorDelegate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.invocation; 16 | 17 | import org.astonbitecode.j4rs.api.Instance; 18 | import org.astonbitecode.j4rs.api.services.delegates.InstanceGeneratorDelegate; 19 | 20 | public class JavaFxInstanceGeneratorDelegate implements InstanceGeneratorDelegate { 21 | @Override 22 | public Instance proxy(Instance instance) { 23 | return new JavaFxInvocation(instance); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /java-fx/src/main/java/org/astonbitecode/j4rs/api/invocation/JavaFxInvocation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.invocation; 16 | 17 | import javafx.application.Platform; 18 | import org.astonbitecode.j4rs.api.Instance; 19 | import org.astonbitecode.j4rs.api.dtos.InvocationArg; 20 | import org.astonbitecode.j4rs.errors.InvocationException; 21 | 22 | import java.util.concurrent.CompletableFuture; 23 | import java.util.concurrent.ExecutionException; 24 | 25 | public class JavaFxInvocation implements Instance { 26 | private Instance jsonInvocation; 27 | 28 | public JavaFxInvocation(Instance jsonInvocation) { 29 | this.jsonInvocation = jsonInvocation; 30 | } 31 | 32 | @Override 33 | public Instance invoke(String methodName, InvocationArg... args) { 34 | CompletableFuture> f = new CompletableFuture(); 35 | Platform.runLater(() -> { 36 | Instance i = jsonInvocation.invoke(methodName, args); 37 | f.complete(i); 38 | }); 39 | 40 | try { 41 | return f.get(); 42 | } catch (InterruptedException | ExecutionException error) { 43 | throw new InvocationException("While invoking method " + methodName + " of Class " 44 | + this.jsonInvocation.getObjectClass().getName(), error); 45 | } 46 | } 47 | 48 | @Override 49 | public Instance invokeStatic(String methodName, InvocationArg... args) { 50 | CompletableFuture> f = new CompletableFuture(); 51 | Platform.runLater(() -> { 52 | Instance i = jsonInvocation.invokeStatic(methodName, args); 53 | f.complete(i); 54 | }); 55 | 56 | try { 57 | return f.get(); 58 | } catch (InterruptedException | ExecutionException error) { 59 | throw new InvocationException("While invoking method " + methodName + " of Class " 60 | + this.jsonInvocation.getObjectClass().getName(), error); 61 | } 62 | } 63 | 64 | @Override 65 | public void invokeAsyncToChannel(long channelAddress, String methodName, InvocationArg... args) { 66 | Platform.runLater(() -> jsonInvocation.invokeAsyncToChannel(channelAddress, methodName, args)); 67 | } 68 | 69 | @Override 70 | public void invokeToChannel(long channelAddress, String methodName, InvocationArg... args) { 71 | System.out.println("Invoking to channel " + methodName + " with " + args.length + " args"); 72 | Platform.runLater(() -> { 73 | jsonInvocation.invokeToChannel(channelAddress, methodName, args); 74 | }); 75 | } 76 | 77 | @Override 78 | public void initializeCallbackChannel(long channelAddress) { 79 | jsonInvocation.initializeCallbackChannel(channelAddress); 80 | } 81 | 82 | @Override 83 | public Instance field(String fieldName) { 84 | return jsonInvocation.field(fieldName); 85 | } 86 | 87 | @Override 88 | public String getJson() { 89 | return jsonInvocation.getJson(); 90 | } 91 | 92 | @Override 93 | public Object getObject() { 94 | return jsonInvocation.getObject(); 95 | } 96 | 97 | @Override 98 | public Class getObjectClass() { 99 | return jsonInvocation.getObjectClass(); 100 | } 101 | 102 | @Override 103 | public String getObjectClassName() { 104 | return jsonInvocation.getObjectClassName(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /java-fx/src/main/java/org/astonbitecode/j4rs/api/jfx/FxApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.jfx; 16 | 17 | import javafx.application.Application; 18 | import javafx.stage.Stage; 19 | import org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport; 20 | 21 | import java.util.concurrent.atomic.AtomicReference; 22 | 23 | /** 24 | * Manipulates a JavaFX application 25 | */ 26 | public class FxApplication extends Application { 27 | private static final AtomicReference callback = new AtomicReference<>(null); 28 | 29 | @Override 30 | public void start(Stage fxStage) { 31 | callback.get().doCallback(fxStage); 32 | } 33 | 34 | /** 35 | * Sets a channel callback for j4rs 36 | * @param nativeCallbackToRustChannelSupport A {@link NativeCallbackToRustChannelSupport} 37 | */ 38 | static void setCallback(NativeCallbackToRustChannelSupport nativeCallbackToRustChannelSupport) { 39 | callback.getAndSet(nativeCallbackToRustChannelSupport); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /java-fx/src/main/java/org/astonbitecode/j4rs/api/jfx/FxApplicationStartCallback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.jfx; 16 | 17 | import javafx.application.Application; 18 | import org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport; 19 | 20 | public class FxApplicationStartCallback extends NativeCallbackToRustChannelSupport { 21 | public void setCallbackToApplicationAndLaunch() { 22 | FxApplication.setCallback(this); 23 | new Thread(() -> Application.launch(FxApplication.class)).start(); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /java-fx/src/main/java/org/astonbitecode/j4rs/api/jfx/J4rsFxmlLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.jfx; 16 | 17 | import javafx.fxml.FXMLLoader; 18 | import javafx.scene.Parent; 19 | import javafx.scene.Scene; 20 | import javafx.stage.Stage; 21 | import org.astonbitecode.j4rs.api.jfx.controllers.FxController; 22 | import org.astonbitecode.j4rs.api.jfx.controllers.FxControllerImpl; 23 | import org.astonbitecode.j4rs.api.jfx.errors.FxException; 24 | 25 | import java.io.File; 26 | import java.io.IOException; 27 | import java.net.URL; 28 | 29 | @SuppressWarnings("unused") 30 | public class J4rsFxmlLoader { 31 | /** 32 | * Loads a FXML and returns an {@link FxController} for it. 33 | * 34 | * @param stage The {@link Stage} to load the FXML on. 35 | * @param fxmlPath The location of the FXML file. 36 | * @return A {@link FxController} instance. 37 | * @throws IOException In case that the FXML cannot be loaded. 38 | * @throws FxException In case the fxml cannot be loaded 39 | */ 40 | @SuppressWarnings("unused") 41 | public static FxController loadFxml(Stage stage, String fxmlPath) throws IOException, FxException { 42 | FXMLLoader loader = new FXMLLoader(); 43 | URL resurl = new File(fxmlPath).toURI().toURL(); 44 | loader.setControllerFactory(clazz -> new FxControllerImpl()); 45 | loader.setLocation(resurl); 46 | Parent root = loader.load(); 47 | 48 | Scene scene = new Scene(root); 49 | stage.setScene(scene); 50 | 51 | FxController controller = loader.getController(); 52 | if (controller == null) { 53 | throw new FxException(String.format( 54 | "Could not load the fxml %s. Please make sure that its root element contains fx:controller=\"org.astonbitecode.j4rs.api.jfx.controllers.FxController\"", 55 | fxmlPath)); 56 | } 57 | controller.setScene(scene); 58 | 59 | return controller; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /java-fx/src/main/java/org/astonbitecode/j4rs/api/jfx/controllers/FxController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.jfx.controllers; 16 | 17 | import javafx.event.Event; 18 | import javafx.event.EventHandler; 19 | import javafx.event.EventType; 20 | import javafx.fxml.Initializable; 21 | import javafx.scene.Node; 22 | import javafx.scene.Scene; 23 | import org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport; 24 | import org.astonbitecode.j4rs.api.jfx.errors.ComponentNotFoundException; 25 | 26 | public interface FxController extends Initializable { 27 | /** 28 | * This will be called when the initialize method of {@link Initializable} is 29 | * called. 30 | * 31 | * @param callback The callback to add. 32 | */ 33 | @SuppressWarnings("unused") 34 | void addControllerInitializedCallback(NativeCallbackToRustChannelSupport callback); 35 | 36 | /** 37 | * Add a handler for an {@link javafx.event.ActionEvent} that comes from a 38 | * component with a specific id. 39 | * 40 | * @param id The id of the callback. 41 | * @param handler The handler to add. 42 | * @param eventType The EventType for Event to handle. 43 | * @throws ComponentNotFoundException in case the handler is not found 44 | */ 45 | @SuppressWarnings("unused") 46 | void addEventHandler(String id, EventHandler handler, EventType eventType) 47 | throws ComponentNotFoundException; 48 | 49 | /** 50 | * Retrieves a node given its ID. 51 | * 52 | * @param id The id of the node to retrieve. 53 | * @return The {@link Node} found. 54 | * @throws ComponentNotFoundException In case that the node is not found. 55 | */ 56 | Node getNodeById(String id) throws ComponentNotFoundException; 57 | 58 | /** 59 | * Sets a scene for this controller. 60 | * 61 | * @param scene The scene to set. 62 | */ 63 | void setScene(Scene scene); 64 | } 65 | -------------------------------------------------------------------------------- /java-fx/src/main/java/org/astonbitecode/j4rs/api/jfx/controllers/FxControllerImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.jfx.controllers; 16 | 17 | import javafx.event.Event; 18 | import javafx.event.EventHandler; 19 | import javafx.event.EventType; 20 | import javafx.scene.Node; 21 | import javafx.scene.Scene; 22 | import org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport; 23 | import org.astonbitecode.j4rs.api.jfx.errors.ComponentNotFoundException; 24 | 25 | import java.net.URL; 26 | import java.util.ResourceBundle; 27 | import java.util.concurrent.atomic.AtomicBoolean; 28 | import java.util.concurrent.atomic.AtomicReference; 29 | 30 | public class FxControllerImpl implements FxController { 31 | private Scene scene; 32 | private AtomicReference initializeCb = new AtomicReference<>(null); 33 | private AtomicBoolean controllerLoaded = new AtomicBoolean(false); 34 | 35 | @Override 36 | public void initialize(URL url, ResourceBundle resourceBundle) { 37 | controllerLoaded.getAndSet(true); 38 | if (initializeCb.get() != null) { 39 | initializeCb.get().doCallback(new Object()); 40 | } 41 | } 42 | 43 | @Override 44 | public void addControllerInitializedCallback(NativeCallbackToRustChannelSupport callback) { 45 | initializeCb.getAndSet(callback); 46 | if (controllerLoaded.get()) { 47 | callback.doCallback(new Object()); 48 | } 49 | } 50 | 51 | @Override 52 | public void addEventHandler(String id, EventHandler handler, EventType eventType) 53 | throws ComponentNotFoundException { 54 | Node node = getNodeById(id); 55 | node.addEventHandler(eventType, handler); 56 | } 57 | 58 | @Override 59 | public Node getNodeById(String id) throws ComponentNotFoundException { 60 | if (scene != null) { 61 | Node node = scene.lookup("#" + id); 62 | if (node != null) { 63 | return node; 64 | } 65 | } 66 | throw new ComponentNotFoundException(String.format("Node with id %s was not found.", id)); 67 | } 68 | 69 | @Override 70 | public void setScene(Scene scene) { 71 | this.scene = scene; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /java-fx/src/main/java/org/astonbitecode/j4rs/api/jfx/errors/ComponentNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.jfx.errors; 16 | 17 | /** 18 | * Exception thrown if a JavaFX component is not found 19 | */ 20 | public class ComponentNotFoundException extends Exception { 21 | /** 22 | * Instantiate with a message 23 | * @param message A message 24 | */ 25 | public ComponentNotFoundException(String message) { 26 | super(message); 27 | } 28 | 29 | /** 30 | * Instantiate with a {@link Throwable} 31 | * @param throwable The Throwable 32 | */ 33 | @SuppressWarnings("unused") 34 | public ComponentNotFoundException(Throwable throwable) { 35 | super(throwable); 36 | } 37 | 38 | /** 39 | * Instantiate with a message and a {@link Throwable} 40 | * @param message The message 41 | * @param throwable The throwable 42 | */ 43 | @SuppressWarnings("unused") 44 | public ComponentNotFoundException(String message, Throwable throwable) { 45 | super(message, throwable); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /java-fx/src/main/java/org/astonbitecode/j4rs/api/jfx/errors/FxException.java: -------------------------------------------------------------------------------- 1 | package org.astonbitecode.j4rs.api.jfx.errors; 2 | 3 | public class FxException extends Exception { 4 | public FxException(String message) { 5 | super(message); 6 | } 7 | 8 | public FxException(Throwable throwable) { 9 | super(throwable); 10 | } 11 | 12 | public FxException(String message, Throwable throwable) { 13 | super(message, throwable); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /java-fx/src/main/java/org/astonbitecode/j4rs/api/jfx/handlers/J4rsEventHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.jfx.handlers; 16 | 17 | import javafx.event.Event; 18 | import javafx.event.EventHandler; 19 | import org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport; 20 | 21 | public class J4rsEventHandler extends NativeCallbackToRustChannelSupport implements EventHandler { 22 | @Override 23 | public void handle(T event) { 24 | doCallback(event); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /java-fx/src/main/resources/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Built-By: astonbitecode 2 | url: https://github.com/astonbitecode/j4rs 3 | -------------------------------------------------------------------------------- /java-fx/src/main/resources/META-INF/services/org.astonbitecode.j4rs.api.services.delegates.InstanceGeneratorDelegate: -------------------------------------------------------------------------------- 1 | org.astonbitecode.j4rs.api.invocation.JavaFxInstanceGeneratorDelegate -------------------------------------------------------------------------------- /java-fx/src/test/java/org/astonbitecode/j4rs/api/invocation/JavaFxInstanceGeneratorDelegateTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.invocation; 16 | 17 | import org.astonbitecode.j4rs.api.Instance; 18 | import org.junit.Ignore; 19 | 20 | import javafx.scene.layout.StackPane; 21 | 22 | public class JavaFxInstanceGeneratorDelegateTest { 23 | @Ignore 24 | public void generateCorrectImpl() { 25 | Instance javaFxInstance = InstanceGenerator.create(new StackPane(), StackPane.class); 26 | assert (javaFxInstance.getClass().equals(JavaFxInvocation.class)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/Instance.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api; 16 | 17 | import org.astonbitecode.j4rs.api.dtos.InvocationArg; 18 | import org.astonbitecode.j4rs.api.invocation.JsonInvocationImpl; 19 | import org.astonbitecode.j4rs.api.value.JsonValueFactory; 20 | import org.astonbitecode.j4rs.errors.InvocationException; 21 | import org.astonbitecode.j4rs.utils.Utils; 22 | 23 | public interface Instance extends ObjectValue, JsonValue { 24 | /** 25 | * Invokes a method of the instance of the class that is set for this 26 | * {@link Instance} 27 | * 28 | * @param methodName The method name 29 | * @param args The arguments to use for invoking the method 30 | * @return A {@link Instance} instance containing the result of the invocation 31 | */ 32 | Instance invoke(String methodName, InvocationArg... args); 33 | 34 | /** 35 | * Invokes a static method of the class that is set for this {@link Instance} 36 | * 37 | * @param methodName The static method name 38 | * @param args The arguments to use for invoking the static method 39 | * @return A {@link Instance} instance containing the result of the invocation 40 | */ 41 | Instance invokeStatic(String methodName, InvocationArg... args); 42 | 43 | /** 44 | * Invokes asynchronously a method of the instance of the class that is set for 45 | * this {@link Instance}. The result of the invocation must be a 46 | * {@link java.util.concurrent.Future}. When the Future returned from the 47 | * invocation completes, j4rs will invoke native Rust code to either send a 48 | * success value or a failure. 49 | *

50 | * Please note that it is best that this function returns a 51 | * {@link java.util.concurrent.CompletableFuture}, as this improves performance. 52 | * j4rs handles simple {@link java.util.concurrent.Future}s with polling using 53 | * an internal {@link java.util.concurrent.ScheduledExecutorService} with one 54 | * thread and this has apparent performance issues. You may have a look at 55 | * {@link org.astonbitecode.j4rs.api.async.J4rsPolledFuture} for more details. 56 | * 57 | * @param functionPointerAddress The address of the function pointer that will 58 | * be used when the 59 | * {@link java.util.concurrent.Future} completes, 60 | * in the native side, in order to actually 61 | * perform the callback and complete a Future that 62 | * is created in Rust and awaits for the Java 63 | * Future to complete. 64 | * @param methodName The method name 65 | * @param args The arguments to use when invoking the callback 66 | * method (the functionPointer) 67 | */ 68 | void invokeAsyncToChannel(long functionPointerAddress, String methodName, InvocationArg... args); 69 | 70 | /** 71 | * Invokes a method of the instance of the class that is set for this 72 | * {@link Instance}. The result of the invocation should be provided later using 73 | * the doCallback method of a 74 | * {@link org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport} 75 | * class. Any possible returned objects from the actual synchronous invocation 76 | * of the defined method will be dropped. 77 | * 78 | * @param channelAddress The memory address of the channel 79 | * @param methodName The method name 80 | * @param args The arguments 81 | */ 82 | void invokeToChannel(long channelAddress, String methodName, InvocationArg... args); 83 | 84 | /** 85 | * Initialize a callback channel for this {@link Instance}. The channel can be 86 | * used by Java to send values to Rust using the doCallback method of a 87 | * {@link org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport} 88 | * class. 89 | * 90 | * @param channelAddress The memory address of the channel 91 | */ 92 | void initializeCallbackChannel(long channelAddress); 93 | 94 | /** 95 | * Retrieves the instance held under the Field fieldName 96 | * 97 | * @param fieldName The name of the field to retrieve 98 | * @return A {@link Instance} instance containing the defined field. 99 | */ 100 | Instance field(String fieldName); 101 | 102 | /** 103 | * Casts a the object that is contained in a Instance to an object of class 104 | * clazz. 105 | * 106 | * @param Generically defined return type 107 | * @param from The {@link Instance} to cast. 108 | * @param toClass The class that the provided {@link Instance} should be casted 109 | * to 110 | * @return A {@link Instance} instance containing the result of the cast. 111 | */ 112 | static Instance cast(Instance from, String toClass) { 113 | try { 114 | Class clazz = (Class) Utils.forNameEnhanced(toClass); 115 | return new JsonInvocationImpl(clazz.cast(from.getObject()), clazz); 116 | } catch (Exception error) { 117 | throw new InvocationException( 118 | "Cannot cast instance of " + from.getObject().getClass().getName() + " to " + toClass, error); 119 | } 120 | } 121 | 122 | /** 123 | * Clones a Instance 124 | * 125 | * @param from The object to clone. 126 | * @param Generically defined return type 127 | * @return a {@link Instance} instance. 128 | */ 129 | static Instance cloneInstance(Instance from) { 130 | return new JsonInvocationImpl(from.getObject(), from.getObjectClass()); 131 | } 132 | 133 | default T getOrDeserializeJavaObject() { 134 | boolean isSerialized = false; 135 | if (InvocationArg.class.isAssignableFrom(this.getClass())) { 136 | isSerialized = ((InvocationArg) this).isSerialized(); 137 | } 138 | if (!isSerialized) { 139 | return (T) this.getObject(); 140 | } else { 141 | ObjectValue objValue = JsonValueFactory.create(this.getJson(), this.getObjectClassName()); 142 | return (T) objValue.getObject(); 143 | } 144 | } 145 | 146 | default boolean checkEquals(Instance other) { 147 | T a = this.getOrDeserializeJavaObject(); 148 | U b = other.getOrDeserializeJavaObject(); 149 | if (a != null && b != null) { 150 | return a.equals(b); 151 | } else if (a == null && b == null) { 152 | return true; 153 | } else { 154 | return false; 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/JsonValue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api; 16 | 17 | /** 18 | * Representation for a json value 19 | */ 20 | public interface JsonValue extends ObjectValue { 21 | /** 22 | * Returns the json representation 23 | * @return The json representation 24 | */ 25 | String getJson(); 26 | } 27 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/NativeInstantiation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api; 16 | 17 | import org.astonbitecode.j4rs.api.dtos.InvocationArg; 18 | 19 | public interface NativeInstantiation { 20 | Instance instantiate(String className, InvocationArg... arg); 21 | 22 | Instance createForStatic(String className); 23 | 24 | Instance createJavaArray(String className, InvocationArg... arg); 25 | } 26 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/ObjectValue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api; 16 | 17 | /** 18 | * Represents an object for j4rs needs 19 | */ 20 | public interface ObjectValue { 21 | /** 22 | * Returns the Object 23 | * @return An object instance 24 | */ 25 | Object getObject(); 26 | 27 | /** 28 | * Returns the {@link Class} of the Object 29 | * @return A {@link Class} 30 | */ 31 | Class getObjectClass(); 32 | 33 | /** 34 | * Returns the name of the Object Class 35 | * @return A String representing the name of the Object Class 36 | */ 37 | String getObjectClassName(); 38 | } 39 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/async/J4rsAsyncContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.async; 16 | 17 | import java.util.concurrent.Executors; 18 | import java.util.concurrent.ScheduledExecutorService; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | class J4rsAsyncContext { 22 | private static final ScheduledExecutorService SERVICE = Executors.newSingleThreadScheduledExecutor(); 23 | 24 | static void schedule(Runnable r) { 25 | SERVICE.schedule(r, 10, TimeUnit.NANOSECONDS); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/async/J4rsPolledFuture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.async; 16 | 17 | import java.util.concurrent.CompletableFuture; 18 | import java.util.concurrent.ExecutionException; 19 | import java.util.concurrent.Future; 20 | 21 | /** 22 | * A {@link CompletableFuture} that completes by polling a {@link Future}. 23 | * 24 | * @param A type 25 | */ 26 | public class J4rsPolledFuture extends CompletableFuture { 27 | private final Future future; 28 | 29 | public J4rsPolledFuture(Future future) { 30 | this.future = future; 31 | J4rsAsyncContext.schedule(this::tryToComplete); 32 | } 33 | 34 | private void tryToComplete() { 35 | if (future.isDone()) { 36 | try { 37 | complete(future.get()); 38 | } catch (InterruptedException error) { 39 | completeExceptionally(error); 40 | } catch (ExecutionException error) { 41 | completeExceptionally(error.getCause()); 42 | } 43 | return; 44 | } 45 | 46 | if (future.isCancelled()) { 47 | cancel(true); 48 | return; 49 | } 50 | 51 | J4rsAsyncContext.schedule(this::tryToComplete); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/deploy/DeployUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.deploy; 16 | 17 | import java.io.File; 18 | import java.net.MalformedURLException; 19 | 20 | public class DeployUtils { 21 | /** 22 | * Adds a jar to the classpath 23 | * @param fullJarPath The full path of the jar to add 24 | * @throws MalformedURLException In case the path is not valid 25 | */ 26 | static void addToClasspath(String fullJarPath) throws MalformedURLException { 27 | if (ClassLoader.getSystemClassLoader().getClass().isAssignableFrom(J4rsClassLoader.class)) { 28 | J4rsClassLoader classLoader = (J4rsClassLoader) ClassLoader.getSystemClassLoader(); 29 | File jar = new File(fullJarPath); 30 | classLoader.add(jar.toURI().toURL()); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/deploy/FileSystemDeployer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.deploy; 16 | 17 | import java.io.File; 18 | import java.io.FileOutputStream; 19 | import java.io.IOException; 20 | import java.nio.channels.Channels; 21 | import java.nio.channels.ReadableByteChannel; 22 | 23 | /** 24 | * Deploys a resource to be used by j4rs. 25 | */ 26 | public class FileSystemDeployer { 27 | private final String deployTarget; 28 | 29 | /** 30 | * Generates a default {@link FileSystemDeployer} with target the working directory 31 | */ 32 | public FileSystemDeployer() { 33 | this("."); 34 | } 35 | 36 | /** 37 | * Generates a {@link FileSystemDeployer} defining the deploy target 38 | * @param deployTarget The target path for the deployments 39 | */ 40 | public FileSystemDeployer(String deployTarget) { 41 | this.deployTarget = deployTarget; 42 | new File(deployTarget).mkdirs(); 43 | } 44 | 45 | /** 46 | * Deploys a resource. The resource may be automatically added to the classpath for j4rs 47 | * @param path The path of the resource 48 | * @throws IOException In case the path is invalid or the resource cannot be accessed 49 | */ 50 | public void deploy(String path) throws IOException { 51 | File jarFile = new File(path); 52 | ReadableByteChannel readableByteChannel = Channels.newChannel(jarFile.toURI().toURL().openStream()); 53 | String fullJarDeployPath = deployTarget + File.separator + jarFile.getName(); 54 | try (FileOutputStream fileOutputStream = new FileOutputStream(fullJarDeployPath)) { 55 | fileOutputStream.getChannel().transferFrom(readableByteChannel, 0, Long.MAX_VALUE); 56 | } 57 | DeployUtils.addToClasspath(fullJarDeployPath); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/deploy/J4rsClassLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.deploy; 16 | 17 | import java.net.URL; 18 | import java.net.URLClassLoader; 19 | 20 | public final class J4rsClassLoader extends URLClassLoader { 21 | public J4rsClassLoader(ClassLoader offeredClassLoader) { 22 | super("j4rsClassLoader", new URL[0], offeredClassLoader); 23 | } 24 | 25 | void add(URL url) { 26 | addURL(url); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/deploy/SimpleMavenDeployer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.deploy; 16 | 17 | import org.w3c.dom.Document; 18 | import org.w3c.dom.Node; 19 | import org.w3c.dom.NodeList; 20 | import org.xml.sax.SAXException; 21 | 22 | import javax.xml.parsers.DocumentBuilder; 23 | import javax.xml.parsers.DocumentBuilderFactory; 24 | import javax.xml.parsers.ParserConfigurationException; 25 | import javax.xml.xpath.XPath; 26 | import javax.xml.xpath.XPathConstants; 27 | import javax.xml.xpath.XPathExpressionException; 28 | import javax.xml.xpath.XPathFactory; 29 | import java.io.*; 30 | import java.net.MalformedURLException; 31 | import java.net.URL; 32 | import java.nio.channels.Channels; 33 | import java.nio.channels.ReadableByteChannel; 34 | 35 | public class SimpleMavenDeployer { 36 | private static final String MAVEN_CENTRAL = "https://repo.maven.apache.org/maven2"; 37 | private final String M2_CACHE = System.getProperty("user.home") + File.separator + ".m2" + File.separator 38 | + "repository"; 39 | 40 | private final String repoBase; 41 | private final boolean checkLocalCache; 42 | private final String deployTarget; 43 | 44 | public SimpleMavenDeployer() { 45 | this(MAVEN_CENTRAL, true, "."); 46 | } 47 | 48 | public SimpleMavenDeployer(String deployTarget) { 49 | this(MAVEN_CENTRAL, true, deployTarget); 50 | } 51 | 52 | public SimpleMavenDeployer(String repoBase, String deployTarget) { 53 | this(repoBase, true, deployTarget); 54 | } 55 | 56 | public SimpleMavenDeployer(String repoBase, boolean checkLocalCache, String deployTarget) { 57 | this.repoBase = repoBase; 58 | this.checkLocalCache = checkLocalCache; 59 | this.deployTarget = deployTarget; 60 | new File(deployTarget).mkdirs(); 61 | } 62 | 63 | public void deploy(String groupId, String artifactId, String version, String qualifier) throws IOException { 64 | String jarName = generateArtifactName(artifactId, version, qualifier); 65 | boolean searchRemoteRepo = true; 66 | 67 | if (!artifactExists(groupId, artifactId, version, qualifier)) { 68 | String fullJarDeployPath = deployTarget + File.separator + jarName; 69 | if (checkLocalCache) { 70 | try { 71 | deployFromLocalCache(groupId, artifactId, version, qualifier); 72 | searchRemoteRepo = false; 73 | } catch (Exception error) { 74 | /* ignore */ 75 | } 76 | } 77 | if (searchRemoteRepo) { 78 | String urlString = generateUrlTagret(groupId, artifactId, version, jarName); 79 | ReadableByteChannel readableByteChannel = Channels.newChannel(new URL(urlString).openStream()); 80 | try (FileOutputStream fileOutputStream = new FileOutputStream(fullJarDeployPath)) { 81 | fileOutputStream.getChannel().transferFrom(readableByteChannel, 0, Long.MAX_VALUE); 82 | } 83 | } 84 | 85 | DeployUtils.addToClasspath(fullJarDeployPath); 86 | } 87 | } 88 | 89 | private boolean artifactExists(String groupId, String artifactId, String version, String qualifier) { 90 | String jarName = generateArtifactName(artifactId, version, qualifier); 91 | String pathString = deployTarget + File.separator + jarName; 92 | return new File(pathString).exists(); 93 | } 94 | 95 | void deployFromLocalCache(String groupId, String artifactId, String version, String qualifier) 96 | throws MalformedURLException, IOException { 97 | String jarName = generateArtifactName(artifactId, version, qualifier); 98 | String pathString = generatePathTagret(M2_CACHE, groupId, artifactId, version, jarName); 99 | 100 | ReadableByteChannel readableByteChannel = Channels 101 | .newChannel(new File(pathString).toURI().toURL().openStream()); 102 | FileOutputStream fileOutputStream = new FileOutputStream(deployTarget + File.separator + jarName); 103 | fileOutputStream.getChannel().transferFrom(readableByteChannel, 0, Long.MAX_VALUE); 104 | } 105 | 106 | String generateArtifactName(String artifactId, String version, String qualifier) { 107 | StringBuilder jarName = new StringBuilder(String.format("%s-%s", artifactId, version)); 108 | if (qualifier != null && !qualifier.isEmpty()) { 109 | jarName.append("-").append(qualifier); 110 | } 111 | jarName.append(".jar"); 112 | return jarName.toString(); 113 | } 114 | 115 | String generateUrlTagret(String groupId, String artifactId, String version, String jarName) throws IOException { 116 | if (version.endsWith("-SNAPSHOT")) { 117 | String latestSnapshotJarName = getLatestSnapshotName(groupId, artifactId, version); 118 | return String.format("%s/%s/%s/%s/%s", repoBase, groupId.replace(".", "/"), artifactId, version, latestSnapshotJarName); 119 | } else { 120 | return String.format("%s/%s/%s/%s/%s", repoBase, groupId.replace(".", "/"), artifactId, version, jarName); 121 | } 122 | } 123 | 124 | private String getLatestSnapshotName(String groupId, String artifactId, String version) throws IOException { 125 | String metadataXmlUrl = String.format("%s/%s/%s/%s/%s", repoBase, groupId.replace(".", "/"), artifactId, version, "maven-metadata.xml"); 126 | ReadableByteChannel readableByteChannel = Channels.newChannel(new URL(metadataXmlUrl).openStream()); 127 | try (InputStream inputStream = Channels.newInputStream(readableByteChannel)) { 128 | DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); 129 | DocumentBuilder builder = builderFactory.newDocumentBuilder(); 130 | Document xmlDocument = builder.parse(inputStream); 131 | XPath xPath = XPathFactory.newInstance().newXPath(); 132 | String timestamp = xPath.evaluate("/metadata/versioning/snapshot/timestamp", xmlDocument); 133 | String buildNumber = xPath.evaluate("/metadata/versioning/snapshot/buildNumber", xmlDocument); 134 | String snapshotVersion = version.replace("SNAPSHOT", (timestamp + "-" + buildNumber)); 135 | return String.format("%s-%s.jar", artifactId, snapshotVersion); 136 | } catch (XPathExpressionException | ParserConfigurationException | SAXException e) { 137 | throw new RuntimeException(e); 138 | } 139 | } 140 | 141 | String generatePathTagret(String base, String groupId, String artifactId, String version, String jarName) { 142 | return String.format("%s%s%s%s%s%s%s%s%s", base, File.separator, groupId.replace(".", File.separator), 143 | File.separator, artifactId, File.separator, version, File.separator, jarName); 144 | } 145 | 146 | public String getRepoBase() { 147 | return repoBase; 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/dtos/GeneratedArg.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.dtos; 16 | 17 | public class GeneratedArg { 18 | private Class clazz; 19 | private Object object; 20 | 21 | public GeneratedArg(Class clazz, Object object) { 22 | this.clazz = clazz; 23 | this.object = object; 24 | } 25 | 26 | public Class getClazz() { 27 | return clazz; 28 | } 29 | 30 | public Object getObject() { 31 | return object; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/dtos/InvocationArg.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.dtos; 16 | 17 | import org.astonbitecode.j4rs.api.Instance; 18 | import org.astonbitecode.j4rs.api.invocation.JsonInvocationImpl; 19 | import org.astonbitecode.j4rs.errors.InvalidArgumentException; 20 | import org.astonbitecode.j4rs.utils.Utils; 21 | 22 | public class InvocationArg implements Instance { 23 | /** 24 | * The array contents should map to a List. This is in order to allow calls of 25 | * type Arrays.asList(arg1, arg2, arg3, ...) 26 | */ 27 | public static final String CONTENTS_ARRAY = "org.astonbitecode.j4rs.api.dtos.Array"; 28 | private final Instance instance; 29 | private final String json; 30 | /** 31 | * If not serialized, the argument is taken straight by the Java code as Object. 32 | * Otherwise, the argument is a json document that needs to be deserialized to 33 | * an Object. 34 | */ 35 | private boolean serialized; 36 | /** 37 | * The type of this argument. This is used when json objects come from Rust, in 38 | * order to be mapped to proper Java Objects. 39 | */ 40 | private String className; 41 | 42 | public InvocationArg(String className, Instance instance) { 43 | this.json = null; 44 | this.className = className; 45 | this.instance = instance; 46 | this.serialized = false; 47 | } 48 | 49 | public InvocationArg(Instance instance) { 50 | this.json = null; 51 | this.className = instance.getClass().getName(); 52 | this.instance = instance; 53 | this.serialized = false; 54 | } 55 | 56 | public InvocationArg(String className, String json) { 57 | this.instance = null; 58 | this.className = className; 59 | this.json = json; 60 | this.serialized = true; 61 | } 62 | 63 | public InvocationArg(String className, Object object) throws ClassNotFoundException { 64 | this.instance = new JsonInvocationImpl(object, Utils.forNameEnhanced(className)); 65 | this.className = className; 66 | this.json = null; 67 | this.serialized = false; 68 | } 69 | 70 | /** 71 | * If true, the argument is taken straight by the Java code as Object. If false, 72 | * the argument is a json document that need to be deserialized to an Object. 73 | * 74 | * @return The The argFrom 75 | */ 76 | public boolean isSerialized() { 77 | return serialized; 78 | } 79 | 80 | /** 81 | * The type of this argument. This is used when json objects come from Rust, in 82 | * order to be mapped to proper Java Objects. 83 | * 84 | * @return The classname 85 | */ 86 | @Override 87 | public String getObjectClassName() { 88 | return className; 89 | } 90 | 91 | public Instance getInstance() { 92 | if (isSerialized()) { 93 | throw new InvalidArgumentException( 94 | "This InvocationArg of class " + className + " is created by Rust code."); 95 | } 96 | return instance; 97 | } 98 | 99 | public String getJson() { 100 | if (!isSerialized()) { 101 | throw new InvalidArgumentException( 102 | "This InvocationArg of class " + className + " is created by Java code."); 103 | } 104 | return json; 105 | } 106 | 107 | @Override 108 | public String toString() { 109 | return "classname:" + this.className + ", serialized:" + this.serialized + ", json:" + this.json + ", instance:" 110 | + this.instance; 111 | } 112 | 113 | @Override 114 | public Object getObject() { 115 | return getInstance() != null ? getInstance().getObject() : null; 116 | } 117 | 118 | @Override 119 | public Class getObjectClass() { 120 | return getInstance() != null ? getInstance().getObjectClass() : null; 121 | } 122 | 123 | @Override 124 | public Instance invoke(String methodName, InvocationArg... args) { 125 | return getInstance() != null ? getInstance().invoke(methodName, args) : null; 126 | } 127 | 128 | @Override 129 | public Instance invokeStatic(String methodName, InvocationArg... args) { 130 | return getInstance() != null ? getInstance().invokeStatic(methodName, args) : null; 131 | } 132 | 133 | @Override 134 | public void invokeAsyncToChannel(long channelAddress, String methodName, InvocationArg... args) { 135 | if (getInstance() != null) { 136 | getInstance().invokeAsyncToChannel(channelAddress, methodName, args); 137 | } 138 | } 139 | 140 | @Override 141 | public void invokeToChannel(long channelAddress, String methodName, InvocationArg... args) { 142 | if (getInstance() != null) { 143 | getInstance().invokeToChannel(channelAddress, methodName, args); 144 | } 145 | } 146 | 147 | @Override 148 | public void initializeCallbackChannel(long channelAddress) { 149 | if (getInstance() != null) { 150 | getInstance().initializeCallbackChannel(channelAddress); 151 | } 152 | } 153 | 154 | @Override 155 | public Instance field(String fieldName) { 156 | return getInstance() != null ? getInstance().field(fieldName) : null; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/dtos/InvocationArgGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.dtos; 16 | 17 | import org.astonbitecode.j4rs.api.Instance; 18 | import org.astonbitecode.j4rs.api.ObjectValue; 19 | import org.astonbitecode.j4rs.api.value.JsonValueFactory; 20 | import org.astonbitecode.j4rs.errors.InvalidArgumentException; 21 | import org.astonbitecode.j4rs.utils.Utils; 22 | 23 | import java.util.Arrays; 24 | 25 | public class InvocationArgGenerator { 26 | public GeneratedArg[] generateArgObjects(InvocationArg[] args) { 27 | GeneratedArg[] generatedArgArr = Arrays.stream(args).map(invArg -> { 28 | GeneratedArg generatedArg; 29 | if (invArg.isSerialized()) { 30 | ObjectValue objValue = JsonValueFactory.create(invArg.getJson(), invArg.getObjectClassName()); 31 | try { 32 | // If the invArg is an array, use its type class. In other cases, use the 33 | // forNameEnhanced to retrieve its class. 34 | generatedArg = invArg.getObjectClassName().equals(InvocationArg.CONTENTS_ARRAY) 35 | ? new GeneratedArg(objValue.getObjectClass(), objValue.getObject()) 36 | : new GeneratedArg(Utils.forNameEnhanced(invArg.getObjectClassName()), 37 | objValue.getObject()); 38 | } catch (ClassNotFoundException cnfe) { 39 | throw new InvalidArgumentException("Cannot parse InvocationArgument ", cnfe); 40 | } 41 | } else { 42 | Instance inv = invArg.getInstance(); 43 | try { 44 | generatedArg = new GeneratedArg( 45 | inv != null ? inv.getObjectClass() : Utils.forNameEnhanced(invArg.getObjectClassName()), 46 | inv != null ? inv.getObject() : null); 47 | } catch (ClassNotFoundException cnfe) { 48 | System.out.println("j4rs Warning! ClassNotFoundException for " + invArg.getObjectClassName() 49 | + " Using java.lang.Object instead..."); 50 | generatedArg = new GeneratedArg(Object.class, null); 51 | } 52 | } 53 | return generatedArg; 54 | }).toArray(i -> new GeneratedArg[i]); 55 | 56 | return generatedArgArr; 57 | } 58 | 59 | public static GeneratedArg argOf(Class clazz, Object object) { 60 | return new GeneratedArg(clazz, object); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/invocation/InstanceGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.invocation; 16 | 17 | import java.lang.reflect.Type; 18 | import java.util.HashMap; 19 | import java.util.Iterator; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.ServiceLoader; 23 | 24 | import org.astonbitecode.j4rs.api.Instance; 25 | import org.astonbitecode.j4rs.api.services.delegates.InstanceGeneratorDelegate; 26 | import org.astonbitecode.j4rs.errors.InvocationException; 27 | 28 | public class InstanceGenerator { 29 | private static Map delegates = new HashMap<>(); 30 | static { 31 | ServiceLoader loader = ServiceLoader.load(InstanceGeneratorDelegate.class); 32 | Iterator discoveredDelegates = loader.iterator(); 33 | while (discoveredDelegates.hasNext()) { 34 | InstanceGeneratorDelegate d = discoveredDelegates.next(); 35 | delegates.put(d.getClass().getCanonicalName(), d); 36 | } 37 | } 38 | 39 | public static Instance create(T instance, Class clazz, List classGenTypes) { 40 | JsonInvocationImpl jsonInvocation = new JsonInvocationImpl(instance, clazz, classGenTypes); 41 | if (shouldRunInFxThread(jsonInvocation.getObjectClass())) { 42 | return getProxiedForJavaFx(jsonInvocation); 43 | } else { 44 | return jsonInvocation; 45 | } 46 | } 47 | 48 | public static Instance create(T instance, Class clazz) { 49 | JsonInvocationImpl jsonInvocation = new JsonInvocationImpl(instance, clazz); 50 | if (shouldRunInFxThread(jsonInvocation.getObjectClass())) { 51 | return getProxiedForJavaFx(jsonInvocation); 52 | } else { 53 | return jsonInvocation; 54 | } 55 | } 56 | 57 | public static Instance create(Class clazz) { 58 | JsonInvocationImpl jsonInvocation = new JsonInvocationImpl(clazz); 59 | if (shouldRunInFxThread(jsonInvocation.getObjectClass())) { 60 | return getProxiedForJavaFx(jsonInvocation); 61 | } else { 62 | return jsonInvocation; 63 | } 64 | } 65 | 66 | private static Instance getProxiedForJavaFx(Instance instance) { 67 | InstanceGeneratorDelegate delegate = delegates 68 | .get("org.astonbitecode.j4rs.api.invocation.JavaFxInstanceGeneratorDelegate"); 69 | if (delegate == null) { 70 | throw new InvocationException( 71 | "Attempted to proxy Instance in order to be executed in FX thread, but delegate is not configured. Please make sure you have j4rs-javafx in the classpath"); 72 | } else { 73 | return delegate.proxy(instance); 74 | } 75 | } 76 | 77 | private static boolean shouldRunInFxThread(Class clazz) { 78 | String className = clazz.getName(); 79 | return className.startsWith("javafx") || (className.startsWith("org.astonbitecode.j4rs.api.jfx") 80 | && !className.startsWith("org.astonbitecode.j4rs.api.jfx.FxApplication")); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/invocation/NativeCallbackToRustChannelSupport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.invocation; 16 | 17 | import org.astonbitecode.j4rs.api.Instance; 18 | import org.astonbitecode.j4rs.errors.InvocationException; 19 | import org.astonbitecode.j4rs.rust.RustPointer; 20 | 21 | import java.util.Optional; 22 | 23 | /** 24 | * Performs native callbacks to Rust channels 25 | */ 26 | public class NativeCallbackToRustChannelSupport { 27 | private static native int docallbacktochannel(long channelPointerAddress, Instance inv); 28 | 29 | private Optional channelPointerOpt = Optional.empty(); 30 | 31 | static void initialize(String libname) { 32 | try { 33 | System.loadLibrary(libname); 34 | } catch (UnsatisfiedLinkError error) { 35 | System.err.println( 36 | "The Callbacks are not initialized because the j4rs lib was not found. You may ignore this error if you don't use callbacks."); 37 | error.printStackTrace(); 38 | } 39 | } 40 | 41 | /** 42 | * Perform a callback 43 | * 44 | * @param obj The {@link Object} to pass in the callback. 45 | */ 46 | public void doCallback(Object obj) { 47 | if (channelPointerOpt.isPresent() && obj != null) { 48 | docallbacktochannel(channelPointerOpt.get().getAddress(), InstanceGenerator.create(obj, obj.getClass())); 49 | } else { 50 | throw new InvocationException( 51 | "Cannot do callback. Please make sure that you don't try to access this method while being in the constructor of your class (that extends NativeCallbackSupport)"); 52 | } 53 | } 54 | 55 | final void initPointer(RustPointer p) { 56 | this.channelPointerOpt = Optional.of(p); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/invocation/NativeCallbackToRustFutureSupport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.invocation; 16 | 17 | import org.astonbitecode.j4rs.api.Instance; 18 | import org.astonbitecode.j4rs.api.value.NullObject; 19 | import org.astonbitecode.j4rs.errors.InvocationException; 20 | import org.astonbitecode.j4rs.rust.RustPointer; 21 | 22 | import java.io.PrintWriter; 23 | import java.io.StringWriter; 24 | import java.util.Optional; 25 | 26 | /** 27 | * Performs native callbacks to Rust channels that are transformed to Rust 28 | * Futures 29 | */ 30 | class NativeCallbackToRustFutureSupport { 31 | private static native int docallbacktochannel(long channelPointerAddress, Instance inv); 32 | 33 | private static native int failcallbacktochannel(long channelPointerAddress, String stacktrace); 34 | 35 | private Optional channelPointerOpt = Optional.empty(); 36 | 37 | static void initialize(String libname) { 38 | try { 39 | System.loadLibrary(libname); 40 | } catch (UnsatisfiedLinkError error) { 41 | System.err.println( 42 | "The Callbacks are not initialized because the j4rs lib was not found. You may ignore this error if you don't use callbacks."); 43 | error.printStackTrace(); 44 | } 45 | } 46 | 47 | /** 48 | * Perform a callback to signal successful operation 49 | * 50 | * @param obj The {@link Object} to pass in the callback. 51 | */ 52 | public void doCallbackSuccess(Object obj) { 53 | if (channelPointerOpt.isPresent()) { 54 | if (obj != null) { 55 | docallbacktochannel(channelPointerOpt.get().getAddress(), 56 | InstanceGenerator.create(obj, obj.getClass())); 57 | } else { 58 | docallbacktochannel(channelPointerOpt.get().getAddress(), 59 | InstanceGenerator.create(null, NullObject.class)); 60 | } 61 | } else { 62 | throw new InvocationException( 63 | "Cannot do callback. Please make sure that you don't try to access this method while being in the constructor of your class (that extends NativeCallbackToRustFutureSupport)"); 64 | } 65 | } 66 | 67 | /** 68 | * Perform a callback to signal failure 69 | * 70 | * @param error The error 71 | */ 72 | public void doCallbackFailure(Throwable error) { 73 | if (channelPointerOpt.isPresent() && error != null) { 74 | StringWriter sw = new StringWriter(); 75 | PrintWriter pw = new PrintWriter(sw); 76 | error.printStackTrace(pw); 77 | String stringStackTrace = sw.toString(); 78 | failcallbacktochannel(channelPointerOpt.get().getAddress(), stringStackTrace); 79 | } else { 80 | throw new InvocationException( 81 | "Cannot do callback for failure. Please make sure that you don't try to access this method while being in the constructor of your class (that extends NativeCallbackSupport). The failure was: ", 82 | error); 83 | } 84 | } 85 | 86 | final void initPointer(RustPointer p) { 87 | this.channelPointerOpt = Optional.of(p); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/java2rust/Java2RustUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.java2rust; 16 | 17 | import org.astonbitecode.j4rs.api.Instance; 18 | import org.astonbitecode.j4rs.api.invocation.JsonInvocationImpl; 19 | 20 | public class Java2RustUtils { 21 | @SuppressWarnings("unchecked") 22 | public static Instance createInstance(T obj) { 23 | return new JsonInvocationImpl(obj, (Class) obj.getClass()); 24 | } 25 | 26 | @SuppressWarnings("unchecked") 27 | public static T getObjectCasted(Instance instance) { 28 | return (T) instance.getOrDeserializeJavaObject(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/services/delegates/InstanceGeneratorDelegate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.services.delegates; 16 | 17 | import org.astonbitecode.j4rs.api.Instance; 18 | 19 | /** 20 | * Delegates the generation of Intsances, by creating Instances proxies. 21 | * This is needed for example to execute code in the JavaFX thread. 22 | */ 23 | public interface InstanceGeneratorDelegate { 24 | /** 25 | * Creates a proxied instance 26 | * @param The type of the Object the Instance contains 27 | * @param instance The Instance 28 | * @return A proxied Instance 29 | */ 30 | Instance proxy(Instance instance); 31 | } -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/services/json/Codec.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package org.astonbitecode.j4rs.api.services.json; 17 | 18 | import org.astonbitecode.j4rs.api.services.json.exceptions.JsonCodecException; 19 | 20 | /** 21 | * Codec to perform JSON encoding and decoding 22 | */ 23 | public interface Codec { 24 | 25 | /** 26 | * Deocde a json String to an Object 27 | * @param json The json 28 | * @param className A class name 29 | * @return An Object 30 | * @param The type of the object 31 | * @throws JsonCodecException In case something goes wrong 32 | */ 33 | T decode(String json, String className) throws JsonCodecException; 34 | 35 | /** 36 | * Encode an object to json String 37 | * @param obj The Object to encode 38 | * @return A json String 39 | * @param The type of the Object 40 | * @throws JsonCodecException In case something goes wrong 41 | */ 42 | String encode(T obj) throws JsonCodecException; 43 | 44 | /** 45 | * Decodes a json String to an array of Objects 46 | * @param json The json String to decode 47 | * @return An array of Objects 48 | * @throws JsonCodecException In case something goes wrong 49 | */ 50 | Object[] decodeArrayContents(String json) throws JsonCodecException; 51 | } 52 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/services/json/exceptions/JsonCodecException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.services.json.exceptions; 16 | 17 | public class JsonCodecException extends RuntimeException { 18 | public JsonCodecException(String message) { 19 | super(message); 20 | } 21 | 22 | public JsonCodecException(String message, Exception nestedException) { 23 | super(message, nestedException); 24 | } 25 | 26 | public JsonCodecException(Exception nestedException) { 27 | super(nestedException); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/value/JsonValueFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.value; 16 | 17 | import org.astonbitecode.j4rs.api.JsonValue; 18 | 19 | public class JsonValueFactory { 20 | public static JsonValue create(T obj) { 21 | return obj != null ? new JsonValueImpl(obj) : new NullJsonValueImpl(); 22 | } 23 | 24 | public static JsonValue create(String json, String className) { 25 | return new JsonValueImpl(json, className); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/value/JsonValueImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.value; 16 | 17 | import org.astonbitecode.j4rs.api.JsonValue; 18 | import org.astonbitecode.j4rs.api.dtos.InvocationArg; 19 | import org.astonbitecode.j4rs.api.services.json.Codec; 20 | import org.astonbitecode.j4rs.api.services.json.exceptions.JsonCodecException; 21 | import org.astonbitecode.j4rs.json.JsonCodecService; 22 | 23 | public class JsonValueImpl implements JsonValue { 24 | private final Codec jsonCodec; 25 | private Object obj; 26 | private String json; 27 | @SuppressWarnings("unused") 28 | private String className; 29 | 30 | JsonValueImpl(T obj) { 31 | this.jsonCodec = JsonCodecService.getJsonCodec(); 32 | this.obj = obj; 33 | try { 34 | this.json = jsonCodec.encode(obj); 35 | } catch (JsonCodecException error) { 36 | throw new JsonCodecException( 37 | "While creating JsonValueCallbackImpl for instance of " + obj.getClass().getName(), error); 38 | } 39 | this.className = obj.getClass().getName(); 40 | } 41 | 42 | JsonValueImpl(String json, String className) { 43 | this.jsonCodec = JsonCodecService.getJsonCodec(); 44 | try { 45 | if (className.equals(InvocationArg.CONTENTS_ARRAY)) { 46 | this.obj = jsonCodec.decodeArrayContents(json); 47 | } else { 48 | this.obj = jsonCodec.decode(json, className); 49 | } 50 | } catch (JsonCodecException error) { 51 | throw new JsonCodecException("While creating JsonValueCallbackImpl: Could not decode " + json, error); 52 | } 53 | this.json = json; 54 | this.className = className; 55 | } 56 | 57 | @Override 58 | public String getJson() { 59 | return this.json; 60 | } 61 | 62 | @Override 63 | public Object getObject() { 64 | return this.obj; 65 | } 66 | 67 | @Override 68 | public Class getObjectClass() { 69 | return this.obj.getClass(); 70 | } 71 | 72 | @Override 73 | public String getObjectClassName() { 74 | return this.obj.getClass().getName(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/value/NullJsonValueImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.value; 16 | 17 | import org.astonbitecode.j4rs.api.JsonValue; 18 | import org.astonbitecode.j4rs.api.ObjectValue; 19 | 20 | public class NullJsonValueImpl implements JsonValue, ObjectValue { 21 | private Object obj; 22 | private String json; 23 | @SuppressWarnings("unused") 24 | private String className; 25 | 26 | NullJsonValueImpl() { 27 | this.obj = null; 28 | this.json = "null"; 29 | this.className = NullObject.class.getName(); 30 | } 31 | 32 | @Override 33 | public String getJson() { 34 | return this.json; 35 | } 36 | 37 | @Override 38 | public Object getObject() { 39 | return this.obj; 40 | } 41 | 42 | @Override 43 | public Class getObjectClass() { 44 | return NullObject.class; 45 | } 46 | 47 | @Override 48 | public String getObjectClassName() { 49 | return NullObject.class.getName(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/api/value/NullObject.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.value; 16 | 17 | public class NullObject { 18 | } 19 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/errors/InstantiationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.errors; 16 | 17 | public class InstantiationException extends RuntimeException { 18 | public InstantiationException(String message, Throwable nestedException) { 19 | super(message, nestedException); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/errors/InvalidArgumentException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.errors; 16 | 17 | public class InvalidArgumentException extends RuntimeException { 18 | public InvalidArgumentException(String message) { 19 | super(message); 20 | } 21 | 22 | public InvalidArgumentException(String message, Throwable inner) { 23 | super(message, inner); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/errors/InvocationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.errors; 16 | 17 | public class InvocationException extends RuntimeException { 18 | public InvocationException(String message, Throwable nestedException) { 19 | super(message, nestedException); 20 | } 21 | 22 | public InvocationException(String message) { 23 | super(message); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/json/JacksonCodec.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.json; 16 | 17 | import com.fasterxml.jackson.core.JsonProcessingException; 18 | import com.fasterxml.jackson.core.type.TypeReference; 19 | import com.fasterxml.jackson.databind.ObjectMapper; 20 | import org.astonbitecode.j4rs.api.services.json.Codec; 21 | import org.astonbitecode.j4rs.api.services.json.exceptions.JsonCodecException; 22 | import org.astonbitecode.j4rs.utils.Utils; 23 | 24 | import java.io.IOException; 25 | import java.util.Arrays; 26 | import java.util.Map; 27 | 28 | public class JacksonCodec implements Codec { 29 | private static final String RUST_FIELD = "Rust"; 30 | private static final String JSON_FIELD = "json"; 31 | private static final String CLASS_NAME_FIELD = "class_name"; 32 | private ObjectMapper mapper = new ObjectMapper(); 33 | TypeReference[]> typeRef = new TypeReference[]>() { 34 | }; 35 | 36 | @Override 37 | @SuppressWarnings("unchecked") 38 | public T decode(String json, String className) throws JsonCodecException { 39 | try { 40 | Class clazz = null; 41 | clazz = (Class) Utils.forNameEnhanced(className); 42 | T obj = mapper.readValue(json, clazz); 43 | return obj; 44 | } catch (ClassNotFoundException | JsonProcessingException error) { 45 | throw new JsonCodecException(error); 46 | } 47 | } 48 | 49 | @Override 50 | public String encode(Object obj) throws JsonCodecException { 51 | try { 52 | return mapper.writeValueAsString(obj); 53 | } catch (JsonProcessingException error) { 54 | throw new JsonCodecException(error); 55 | } 56 | } 57 | 58 | @Override 59 | public Object[] decodeArrayContents(String json) throws JsonCodecException { 60 | try { 61 | Map[] array = mapper.readValue(json, typeRef); 62 | 63 | return Arrays.stream(array).map(elem -> { 64 | try { 65 | return retrieveFromMap(elem); 66 | } catch (Exception error) { 67 | throw new JsonCodecException("Error while retrieving Array", error); 68 | } 69 | }).toArray(); 70 | } catch (JsonProcessingException error) { 71 | throw new JsonCodecException(error); 72 | } 73 | } 74 | 75 | /** 76 | * [ 77 | * {"Rust":{"json":"\"arg1\"","class_name":"java.lang.String","arg_from":"rust"}}, 78 | * {"Rust":{"json":"\"arg2\"","class_name":"java.lang.String","arg_from":"rust"}}, 79 | * {"Rust":{"json":"\"arg3\"","class_name":"java.lang.String","arg_from":"rust"}} 80 | * ] 81 | */ 82 | private U retrieveFromMap(Map map) throws ClassNotFoundException, IOException { 83 | Map innerMap = (Map) map.get(RUST_FIELD); 84 | if (innerMap == null) { 85 | throw new JsonCodecException("Cannot create InvocationArg object form Map '" + map + "'"); 86 | } 87 | String retrievedClassName = innerMap.get(CLASS_NAME_FIELD); 88 | String retrievedJson = innerMap.get(JSON_FIELD); 89 | if (retrievedClassName == null || retrievedJson == null) { 90 | throw new JsonCodecException("Cannot create InvocationArg object form the JSON '" + retrievedJson + "'"); 91 | } 92 | return decode(retrievedJson, retrievedClassName); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/json/JsonCodecService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.json; 16 | 17 | import org.astonbitecode.j4rs.api.services.json.Codec; 18 | 19 | import java.util.Iterator; 20 | import java.util.ServiceLoader; 21 | 22 | public class JsonCodecService { 23 | private static JsonCodecService instance; 24 | private final Codec jsonCodec; 25 | 26 | static synchronized JsonCodecService getInstance() { 27 | if (instance == null) { 28 | instance = new JsonCodecService(); 29 | } 30 | return instance; 31 | } 32 | 33 | private JsonCodecService() { 34 | Codec jsonCodec = null; 35 | ServiceLoader loader = ServiceLoader.load(Codec.class); 36 | Iterator jsonCodecs = loader.iterator(); 37 | while (jsonCodec == null && jsonCodecs.hasNext()) { 38 | jsonCodec = jsonCodecs.next(); 39 | } 40 | if (jsonCodec == null) { 41 | this.jsonCodec = new JacksonCodec(); 42 | } else { 43 | this.jsonCodec = jsonCodec; 44 | } 45 | } 46 | 47 | public static Codec getJsonCodec() { 48 | return getInstance().jsonCodec; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/rust/FunctionPointer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.rust; 16 | 17 | @Deprecated 18 | public class FunctionPointer { 19 | private long address; 20 | 21 | public FunctionPointer(long address) { 22 | this.address = address; 23 | } 24 | 25 | public long getAddress() { 26 | return address; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/rust/RustPointer.java: -------------------------------------------------------------------------------- 1 | package org.astonbitecode.j4rs.rust; 2 | 3 | public class RustPointer { 4 | private Long address; 5 | 6 | public RustPointer(long address) { 7 | this.address = address; 8 | } 9 | 10 | public Long getAddress() { 11 | return address; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /java/src/main/java/org/astonbitecode/j4rs/utils/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.utils; 16 | 17 | import java.io.PrintWriter; 18 | import java.io.StringWriter; 19 | import java.lang.reflect.InvocationTargetException; 20 | import java.util.Arrays; 21 | 22 | import org.astonbitecode.j4rs.api.dtos.GeneratedArg; 23 | import org.astonbitecode.j4rs.errors.InvocationException; 24 | 25 | public class Utils { 26 | 27 | private static boolean IsAndroid; 28 | 29 | static { 30 | try { 31 | Class.forName("android.os.Build"); 32 | IsAndroid = true; 33 | } catch (ClassNotFoundException e) { 34 | IsAndroid = false; 35 | } 36 | } 37 | 38 | public static Class forNameEnhanced(final String className) throws ClassNotFoundException { 39 | switch (className) { 40 | case "boolean": 41 | return boolean.class; 42 | case "byte": 43 | return byte.class; 44 | case "short": 45 | return short.class; 46 | case "int": 47 | return int.class; 48 | case "long": 49 | return long.class; 50 | case "float": 51 | return float.class; 52 | case "double": 53 | return double.class; 54 | case "char": 55 | return char.class; 56 | case "void": 57 | return void.class; 58 | default: 59 | if (!IsAndroid) { 60 | return Class.forName(className, true, ClassLoader.getSystemClassLoader()); 61 | } else { 62 | return Class.forName(className); 63 | } 64 | } 65 | } 66 | 67 | // Return one of the classes of the GeneratedArgs. 68 | // Currently there is no need to support many classes. 69 | // In the future, we may need to converge to the common parent of all the 70 | // GeneratedArgs. 71 | public static Class forNameBasedOnArgs(final GeneratedArg[] params) { 72 | return Arrays.stream(params).map(arg -> arg.getClazz()).reduce((a, b) -> a).orElse(Void.class); 73 | } 74 | 75 | public static String throwableToString(Throwable throwable) { 76 | if (throwable != null) { 77 | StringWriter sw = new StringWriter(); 78 | PrintWriter pw = new PrintWriter(sw); 79 | if (throwable != null && throwable instanceof InvocationException 80 | && throwable.getCause() != null && throwable.getCause() instanceof InvocationTargetException) { 81 | throwable.getCause().getCause().printStackTrace(pw); 82 | } else { 83 | throwable.printStackTrace(pw); 84 | } 85 | return sw.toString(); 86 | } else { 87 | return "Cannot create String out of a null Throwable"; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /java/src/main/resources/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Built-By: astonbitecode 2 | url: https://github.com/astonbitecode/j4rs 3 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/api/deploy/FileSystemDeployerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.deploy; 16 | 17 | import org.junit.Test; 18 | 19 | import java.io.File; 20 | import java.io.FileNotFoundException; 21 | 22 | import static org.mockito.Mockito.*; 23 | 24 | public class FileSystemDeployerTest { 25 | @Test 26 | public void deploySuccess() throws Exception { 27 | // Download a jar file first 28 | SimpleMavenDeployer md = new SimpleMavenDeployer(); 29 | 30 | md.deploy("io.github.astonbitecode", "j4rs", "0.5.1", ""); 31 | 32 | FileSystemDeployer fsd = new FileSystemDeployer("./fsdTarget"); 33 | 34 | fsd.deploy("./j4rs-0.5.1.jar"); 35 | 36 | File f1 = new File("./j4rs-0.5.1.jar"); 37 | File f2 = new File("./fsdTarget/j4rs-0.5.1.jar"); 38 | File f3 = new File("./fsdTarget"); 39 | f1.delete(); 40 | f2.delete(); 41 | f3.delete(); 42 | } 43 | 44 | @Test(expected = FileNotFoundException.class) 45 | public void deployFailure() throws Exception { 46 | FileSystemDeployer fsd = new FileSystemDeployer(); 47 | 48 | fsd.deploy("./NonExistingJar.jar"); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/api/deploy/SimpleMavenDeployerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.deploy; 16 | 17 | import org.junit.Test; 18 | 19 | import java.io.File; 20 | import java.io.IOException; 21 | 22 | import static org.mockito.Mockito.*; 23 | 24 | public class SimpleMavenDeployerTest { 25 | @Test 26 | public void repoBase() { 27 | assert (new SimpleMavenDeployer("my", true, "depltarget").getRepoBase().equals("my")); 28 | File f = new File("depltarget"); 29 | f.delete(); 30 | } 31 | 32 | @Test 33 | public void generateArtifactName() { 34 | assert (new SimpleMavenDeployer().generateArtifactName("j4rs", "0.5.1", "").equals("j4rs-0.5.1.jar")); 35 | } 36 | 37 | @Test 38 | public void generateUrlTagret() throws IOException { 39 | assert (new SimpleMavenDeployer("https://my.artifactory.com", true, "depltarget") 40 | .generateUrlTagret("io.github.astonbitecode", "j4rs", "0.5.1", "j4rs-0.5.1.jar") 41 | .equals("https://my.artifactory.com/io/github/astonbitecode/j4rs/0.5.1/j4rs-0.5.1.jar")); 42 | 43 | File f = new File("depltarget"); 44 | f.delete(); 45 | } 46 | 47 | @Test 48 | public void deploySuccess() throws Exception { 49 | SimpleMavenDeployer md = new SimpleMavenDeployer(); 50 | 51 | md.deploy("io.github.astonbitecode", "j4rs", "0.5.1", ""); 52 | 53 | File f = new File("./j4rs-0.5.1.jar"); 54 | f.delete(); 55 | } 56 | 57 | @Test(expected = IOException.class) 58 | public void deployFailure() throws Exception { 59 | SimpleMavenDeployer md = new SimpleMavenDeployer(); 60 | 61 | md.deploy("io.github.astonbitecode", "j4rs", "non-existing", ""); 62 | } 63 | 64 | @Test() 65 | public void doNotDownloadArtifactIfAlreadyDeployed() throws Exception { 66 | new SimpleMavenDeployer().deploy("io.github.astonbitecode", "j4rs", "0.5.1", ""); 67 | 68 | SimpleMavenDeployer mdmock = mock(SimpleMavenDeployer.class); 69 | mdmock.deploy("io.github.astonbitecode", "j4rs", "0.5.1", ""); 70 | 71 | verify(mdmock, times(0)).deployFromLocalCache(any(), any(), any(), any()); 72 | 73 | File f = new File("./j4rs-0.5.1.jar"); 74 | f.delete(); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/api/dtos/InvocationArgTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.dtos; 16 | 17 | import org.astonbitecode.j4rs.api.invocation.JsonInvocationImpl; 18 | import org.astonbitecode.j4rs.errors.InvalidArgumentException; 19 | import org.astonbitecode.j4rs.utils.Dummy; 20 | import org.junit.Test; 21 | 22 | public class InvocationArgTest { 23 | private static String CLASS_NAME = "a.class.Name"; 24 | 25 | @Test(expected = InvalidArgumentException.class) 26 | public void getNativeInvocationOnAnArgCreatedByRust() { 27 | InvocationArg ia = new InvocationArg(CLASS_NAME, "{\"a\":\"b\"}"); 28 | ia.getInstance(); 29 | } 30 | 31 | @Test 32 | public void getSerializedNativeInvocationOnAnArgCreatedByRust() { 33 | InvocationArg ia = new InvocationArg("org.astonbitecode.j4rs.utils.Dummy", "{\"i\":3}"); 34 | Object dummyObj = ia.getOrDeserializeJavaObject(); 35 | assert (dummyObj instanceof Dummy); 36 | } 37 | 38 | @Test(expected = InvalidArgumentException.class) 39 | public void getNativeInvocationOnAnArgCreatedByJava() { 40 | InvocationArg ia = new InvocationArg(CLASS_NAME, new JsonInvocationImpl(new Dummy(), Dummy.class)); 41 | ia.getJson(); 42 | } 43 | 44 | @Test 45 | public void getArgFrom() { 46 | InvocationArg ia1 = new InvocationArg(CLASS_NAME, "{\"a\":\"b\"}"); 47 | assert ia1.isSerialized(); 48 | assert ia1.getObjectClassName().equals(CLASS_NAME); 49 | 50 | InvocationArg ia2 = new InvocationArg(CLASS_NAME, new JsonInvocationImpl(new Dummy(), Dummy.class)); 51 | assert !ia2.isSerialized(); 52 | assert ia2.getObjectClassName().equals(CLASS_NAME); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/api/instantiation/NativeInstantiationImplTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.instantiation; 16 | 17 | import org.astonbitecode.j4rs.api.dtos.GeneratedArg; 18 | import org.astonbitecode.j4rs.api.dtos.InvocationArg; 19 | import org.astonbitecode.j4rs.api.invocation.JsonInvocationImpl; 20 | import org.astonbitecode.j4rs.utils.ChildDummy; 21 | import org.astonbitecode.j4rs.utils.ClassWithDummyAtConstructor; 22 | import org.astonbitecode.j4rs.utils.Dummy; 23 | import org.junit.Test; 24 | 25 | import java.util.List; 26 | 27 | public class NativeInstantiationImplTest { 28 | 29 | @Test 30 | public void constructorMatches() throws Exception { 31 | String className = Dummy.class.getName(); 32 | 33 | GeneratedArg[] generatedArgs1 = { new GeneratedArg(Integer.class, Integer.parseInt("11")) }; 34 | NativeInstantiationImpl.CreatedInstance createdInstance1 = NativeInstantiationImpl.createInstance(className, 35 | generatedArgs1); 36 | assert (createdInstance1.getObject() instanceof Dummy); 37 | 38 | GeneratedArg[] generatedArgs2 = { new GeneratedArg(int.class, 11) }; 39 | NativeInstantiationImpl.CreatedInstance createdInstance2 = NativeInstantiationImpl.createInstance(className, 40 | generatedArgs2); 41 | assert (createdInstance2.getObject() instanceof Dummy); 42 | 43 | GeneratedArg[] noGeneratedArgs = {}; 44 | NativeInstantiationImpl.CreatedInstance createdInstanceNoArgs = NativeInstantiationImpl 45 | .createInstance(className, noGeneratedArgs); 46 | assert (createdInstanceNoArgs.getObject() instanceof Dummy); 47 | } 48 | 49 | @Test(expected = Exception.class) 50 | public void noConstructorFound() throws Exception { 51 | String className = Dummy.class.getName(); 52 | 53 | GeneratedArg[] generatedArgs = { new GeneratedArg(Long.class, Long.parseLong("11")) }; 54 | NativeInstantiationImpl.createInstance(className, generatedArgs); 55 | } 56 | 57 | @Test 58 | public void generateArgObjectsFromJava() throws Exception { 59 | @SuppressWarnings("rawtypes") 60 | InvocationArg arg = new InvocationArg(Dummy.class.getName(), new JsonInvocationImpl(new Dummy(), Dummy.class)); 61 | InvocationArg[] args = { arg }; 62 | 63 | GeneratedArg[] generated = NativeInstantiationImpl.generateArgObjects(args); 64 | assert (generated.length == 1); 65 | assert (generated[0].getClazz().equals(Dummy.class)); 66 | } 67 | 68 | @Test 69 | public void generateArgObjectsFromRust() throws Exception { 70 | String json = "{\"i\":0}"; 71 | InvocationArg arg = new InvocationArg(Dummy.class.getName(), json); 72 | InvocationArg[] args = { arg }; 73 | 74 | GeneratedArg[] generated = NativeInstantiationImpl.generateArgObjects(args); 75 | assert (generated.length == 1); 76 | assert (generated[0].getClazz().equals(Dummy.class)); 77 | } 78 | 79 | @Test 80 | public void generatePrimitiveArgObjectsFromRust() throws Exception { 81 | String json = "0"; 82 | InvocationArg arg = new InvocationArg("int", json); 83 | InvocationArg[] args = { arg }; 84 | 85 | GeneratedArg[] generated = NativeInstantiationImpl.generateArgObjects(args); 86 | assert (generated.length == 1); 87 | assert (generated[0].getClazz().equals(int.class)); 88 | } 89 | 90 | @Test 91 | public void createJavaArraySuccess() throws Exception { 92 | String className = Integer.class.getName(); 93 | GeneratedArg[] generatedArgs = { new GeneratedArg(Integer.class, Integer.parseInt("11")) }; 94 | NativeInstantiationImpl.CreatedInstance createdInstance = NativeInstantiationImpl.createCollection(className, 95 | generatedArgs, NativeInstantiationImpl.J4rsCollectionType.Array); 96 | assert (createdInstance.getClazz().getName().equals("[Ljava.lang.Integer;")); 97 | } 98 | 99 | @Test 100 | public void createJavaListSuccess() throws Exception { 101 | String className = Integer.class.getName(); 102 | GeneratedArg[] generatedArgs = { new GeneratedArg(Integer.class, Integer.parseInt("11")) }; 103 | NativeInstantiationImpl.CreatedInstance createdInstance = NativeInstantiationImpl.createCollection(className, 104 | generatedArgs, NativeInstantiationImpl.J4rsCollectionType.List); 105 | assert (List.class.isAssignableFrom(createdInstance.getClazz())); 106 | } 107 | 108 | @Test(expected = IllegalArgumentException.class) 109 | public void createJavaArrayFailure() throws Exception { 110 | String className = Integer.class.getName(); 111 | GeneratedArg[] generatedArgs = { new GeneratedArg(Integer.class, Integer.parseInt("11")), 112 | new GeneratedArg(String.class, "this is a string") }; 113 | NativeInstantiationImpl.createCollection(className, generatedArgs, 114 | NativeInstantiationImpl.J4rsCollectionType.Array); 115 | } 116 | 117 | @Test 118 | public void createClassWithNeededArgsInHierarchy() throws Exception { 119 | GeneratedArg[] generatedArgs = { new GeneratedArg(ChildDummy.class, new ChildDummy()) }; 120 | NativeInstantiationImpl.CreatedInstance instance = NativeInstantiationImpl 121 | .createInstance(ClassWithDummyAtConstructor.class.getName(), generatedArgs); 122 | assert (instance.getClazz().equals(ClassWithDummyAtConstructor.class)); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/api/invocation/InstanceGeneratorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.invocation; 16 | 17 | import org.astonbitecode.j4rs.api.Instance; 18 | import org.astonbitecode.j4rs.utils.ChildDummy; 19 | import org.junit.Test; 20 | 21 | public class InstanceGeneratorTest { 22 | @Test 23 | public void generateCorrectImpl() { 24 | Instance nonJavaFxInstance = InstanceGenerator.create(new ChildDummy(), ChildDummy.class); 25 | assert (nonJavaFxInstance.getClass().equals(JsonInvocationImpl.class)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/api/invocation/NativeCallbackToRustChannelSupportTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.api.invocation; 16 | 17 | import org.astonbitecode.j4rs.errors.InvocationException; 18 | import org.astonbitecode.j4rs.rust.RustPointer; 19 | import org.junit.Test; 20 | import org.mockito.Mockito; 21 | 22 | import static org.mockito.Mockito.mock; 23 | 24 | public class NativeCallbackToRustChannelSupportTest { 25 | 26 | @Test(expected = InvocationException.class) 27 | public void invokeBeforeInitialized() { 28 | class Dummy extends NativeCallbackToRustChannelSupport { 29 | public Dummy() { 30 | super.doCallback(""); 31 | } 32 | } 33 | 34 | new Dummy(); 35 | } 36 | 37 | @Test(expected = UnsatisfiedLinkError.class) 38 | public void invokeSuccess() { 39 | RustPointer fp = mock(RustPointer.class); 40 | class Dummy extends NativeCallbackToRustChannelSupport { 41 | } 42 | 43 | Dummy d = new Dummy(); 44 | Dummy spied = Mockito.spy(d); 45 | 46 | spied.initPointer(fp); 47 | // Here we will get an UnsatisfiedLinkError since the native libs are not 48 | // initialized in the tests 49 | spied.doCallback(""); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/json/JacksonCodecTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.json; 16 | 17 | import org.astonbitecode.j4rs.api.services.json.exceptions.JsonCodecException; 18 | import org.astonbitecode.j4rs.utils.Dummy; 19 | import org.astonbitecode.j4rs.utils.OtherDummy; 20 | import org.junit.Test; 21 | 22 | public class JacksonCodecTest { 23 | private JacksonCodec jacksonCodec = new JacksonCodec(); 24 | 25 | @Test 26 | public void decodeSuccess() { 27 | String json = "{\"i\":3}"; 28 | Dummy dummy = jacksonCodec.decode(json, "org.astonbitecode.j4rs.utils.Dummy"); 29 | assert (dummy.getI() == 3); 30 | } 31 | 32 | @Test(expected = JsonCodecException.class) 33 | public void decodeFailureWrongClassName() { 34 | String json = "{\"i\":3}"; 35 | jacksonCodec.decode(json, "org.astonbitecode.j4rs.utils.Nothing"); 36 | } 37 | 38 | @Test(expected = JsonCodecException.class) 39 | public void decodeFailureInvalidJson() { 40 | String json = "{mb}"; 41 | jacksonCodec.decode(json, "org.astonbitecode.j4rs.utils.Dummy"); 42 | } 43 | 44 | @Test(expected = JsonCodecException.class) 45 | public void decodeFailureInvalidMapping() { 46 | String json = "{\"i\":3}"; 47 | jacksonCodec.decode(json, "org.astonbitecode.j4rs.utils.OtherDummy"); 48 | } 49 | 50 | @Test 51 | public void encodeSuccess() { 52 | String json = "{\"i\":3,\"j\":33}"; 53 | OtherDummy person = new OtherDummy(3, 33); 54 | assert (jacksonCodec.encode(person).equals(json)); 55 | } 56 | 57 | @Test 58 | public void encodeJ4rsArray() { 59 | String json = "[\n" 60 | + " {\"Rust\":{\"json\":\"\\\"arg1\\\"\",\"class_name\":\"java.lang.String\",\"arg_from\":\"rust\"}},\n" 61 | + " {\"Rust\":{\"json\":\"\\\"arg2\\\"\",\"class_name\":\"java.lang.String\",\"arg_from\":\"rust\"}},\n" 62 | + " {\"Rust\":{\"json\":\"\\\"arg3\\\"\",\"class_name\":\"java.lang.String\",\"arg_from\":\"rust\"}}\n" 63 | + " ]"; 64 | Object[] ret = jacksonCodec.decodeArrayContents(json); 65 | } 66 | 67 | @Test(expected = JsonCodecException.class) 68 | public void encodeJ4rsArrayError() { 69 | String json = "[{\"i\":3,\"j\":33}, {\"i\":333,\"j\":3333}]"; 70 | jacksonCodec.decodeArrayContents(json); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/tests/MyTestTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.tests; 16 | 17 | import org.astonbitecode.j4rs.api.Instance; 18 | import org.astonbitecode.j4rs.api.dtos.InvocationArg; 19 | import org.astonbitecode.j4rs.api.instantiation.NativeInstantiationImpl; 20 | import org.astonbitecode.j4rs.errors.InvocationException; 21 | import org.junit.Ignore; 22 | 23 | import java.util.Arrays; 24 | import java.util.List; 25 | import java.util.concurrent.ExecutorService; 26 | import java.util.concurrent.Executors; 27 | import java.util.concurrent.Future; 28 | import java.util.stream.IntStream; 29 | 30 | public class MyTestTest { 31 | private static ExecutorService executor = Executors.newSingleThreadExecutor(); 32 | 33 | public Future getStringWithFuture(String string) { 34 | return executor.submit(() -> string); 35 | } 36 | 37 | public Future getErrorWithFuture(String message) { 38 | return executor.submit(() -> { 39 | throw new InvocationException(message); 40 | }); 41 | } 42 | 43 | public void dummy() { 44 | Instance instance = NativeInstantiationImpl.instantiate("org.astonbitecode.j4rs.tests.MyTest"); 45 | IntStream.range(0, 1000000000).forEach(i -> { 46 | if (i % 100000 == 0) { 47 | System.out.println(i); 48 | } 49 | 50 | InvocationArg ia = new InvocationArg("java.lang.String", "\"astring\""); 51 | instance.invoke("getMyWithArgs", ia); 52 | }); 53 | } 54 | 55 | public Integer addInts(Integer... args) { 56 | int result = Arrays.stream(args).reduce(0, (a, b) -> { 57 | return a + b; 58 | }); 59 | return result; 60 | } 61 | 62 | public Integer addInts(List args) { 63 | int result = args.stream().reduce(0, (a, b) -> { 64 | return a + b; 65 | }); 66 | return result; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/utils/ChildDummy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.utils; 16 | 17 | public class ChildDummy extends Dummy implements DummyInterface { 18 | public ChildDummy() { 19 | super(); 20 | } 21 | 22 | @Override 23 | public void doSomething() { 24 | System.out.println("I am doing something..."); 25 | } 26 | 27 | public DummyMapInterface getMap() { 28 | return new DummyMapImpl(); 29 | } 30 | 31 | public Class invokeGeneric(T check) { 32 | return (Class) check.getClass(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/utils/ChildOfDummyWithFields.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.utils; 16 | 17 | public class ChildOfDummyWithFields extends DummyWithFields { 18 | } 19 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/utils/ClassWithDummyAtConstructor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.utils; 16 | 17 | public class ClassWithDummyAtConstructor { 18 | private Dummy d; 19 | 20 | public ClassWithDummyAtConstructor(Dummy d) { 21 | this.d = d; 22 | } 23 | 24 | public void replaceDummy(Dummy d) { 25 | this.d = d; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/utils/Dummy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.utils; 16 | 17 | public class Dummy { 18 | private Integer i = 0; 19 | 20 | public Dummy() { 21 | this.setI(33); 22 | } 23 | 24 | public Dummy(Integer ig) { 25 | this.setI(ig); 26 | } 27 | 28 | public Dummy(int ig) { 29 | this.setI(ig); 30 | } 31 | 32 | public Integer getI() { 33 | return i; 34 | } 35 | 36 | public void setI(Integer i) { 37 | this.i = i; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/utils/DummyInterface.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.utils; 16 | 17 | public interface DummyInterface extends DummyInterfaceFather, DummyInterfaceMother { 18 | void doSomething(); 19 | } 20 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/utils/DummyInterfaceFather.java: -------------------------------------------------------------------------------- 1 | package org.astonbitecode.j4rs.utils; 2 | 3 | public interface DummyInterfaceFather { 4 | default public void doSomethingMoreDad() { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/utils/DummyInterfaceMother.java: -------------------------------------------------------------------------------- 1 | package org.astonbitecode.j4rs.utils; 2 | 3 | public interface DummyInterfaceMother { 4 | default public void doSomethingMoreMom() { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/utils/DummyMapImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.utils; 16 | 17 | import java.util.HashMap; 18 | import java.util.stream.Collectors; 19 | 20 | public class DummyMapImpl extends HashMap implements DummyMapInterface { 21 | private static final long serialVersionUID = 1L; 22 | 23 | public DummyMapImpl() { 24 | put("one", 1); 25 | put("two", 2); 26 | } 27 | 28 | public long keysLength() { 29 | return keySet().stream().map(String::length).collect(Collectors.summingInt(Integer::intValue)); 30 | } 31 | } -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/utils/DummyMapInterface.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.utils; 16 | 17 | import java.util.Map; 18 | 19 | public interface DummyMapInterface extends Map { 20 | public long keysLength(); 21 | } -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/utils/DummyWithFields.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.utils; 16 | 17 | public class DummyWithFields { 18 | public Integer pubInt = 11; 19 | Integer packageInt = 111; 20 | protected Integer protectedInt = 1111; 21 | private Integer privateInt = 1111; 22 | } 23 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/utils/DummyWithStatic.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.utils; 16 | 17 | public class DummyWithStatic { 18 | public static String method() { 19 | return "method product"; 20 | } 21 | 22 | public static String methodWithArg(Integer i) { 23 | return i.toString(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/utils/FailingDummy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.utils; 16 | 17 | public class FailingDummy { 18 | public void throwException() { 19 | throw new RuntimeException(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/utils/GrandchildDummy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.utils; 16 | 17 | public class GrandchildDummy extends ChildDummy { 18 | public GrandchildDummy() { 19 | super(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/utils/OtherDummy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.utils; 16 | 17 | public class OtherDummy { 18 | private int i = 0; 19 | private int j = 0; 20 | 21 | public OtherDummy(int i, int j) { 22 | this.i = i; 23 | this.j = j; 24 | } 25 | 26 | public int getI() { 27 | return i; 28 | } 29 | 30 | public void setI(int i) { 31 | this.i = i; 32 | } 33 | 34 | public int getJ() { 35 | return j; 36 | } 37 | 38 | public void setJ(int j) { 39 | this.j = j; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /java/src/test/java/org/astonbitecode/j4rs/value/JsonValueImplTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.value; 16 | 17 | import org.astonbitecode.j4rs.api.JsonValue; 18 | import org.astonbitecode.j4rs.api.value.JsonValueFactory; 19 | import org.astonbitecode.j4rs.utils.Dummy; 20 | import org.junit.Test; 21 | 22 | public class JsonValueImplTest { 23 | 24 | @Test 25 | public void fromString() { 26 | JsonValue jvi = JsonValueFactory.create("This is a String"); 27 | String json = jvi.getJson(); 28 | String obj = (String) jvi.getObject(); 29 | assert json.equals("\"This is a String\""); 30 | assert obj.equals("This is a String"); 31 | } 32 | 33 | @Test 34 | public void fromNumber() { 35 | JsonValue jvi = JsonValueFactory.create(3.33); 36 | String json = jvi.getJson(); 37 | double obj = (double) jvi.getObject(); 38 | assert json.equals("3.33"); 39 | assert obj == 3.33; 40 | } 41 | 42 | @Test 43 | public void fromObject() { 44 | JsonValue jvi = JsonValueFactory.create(new Dummy(3)); 45 | String json = jvi.getJson(); 46 | Dummy obj = (Dummy) jvi.getObject(); 47 | assert json.equals("{\"i\":3}"); 48 | assert obj.getI() == 3; 49 | } 50 | 51 | @Test 52 | public void nullableObject() { 53 | JsonValue jvi = JsonValueFactory.create(null); 54 | String json = jvi.getJson(); 55 | Object obj = jvi.getObject(); 56 | assert json.equals("null"); 57 | assert obj == null; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /rust/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml -------------------------------------------------------------------------------- /rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "j4rs" 3 | version = "0.23.0-SNAPSHOT" 4 | authors = ["aston "] 5 | description = "j4rs stands for 'Java for Rust' and allows effortless calls to Java code, from Rust" 6 | keywords = ["java", "jni"] 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/astonbitecode/j4rs" 9 | readme = "README.md" 10 | categories = ["api-bindings"] 11 | build = "build.rs" 12 | edition = "2021" 13 | 14 | [badges] 15 | travis-ci = { repository = "astonbitecode/j4rs", branch = "master" } 16 | 17 | [lib] 18 | name = "j4rs" 19 | crate-type = ["rlib", "cdylib"] 20 | path = "src/lib.rs" 21 | 22 | [dependencies] 23 | log = "0.4" 24 | libc = "0.2" 25 | jni-sys = "0.4" 26 | serde = { version = "1.0", features = ["derive"] } 27 | serde_json = "1.0" 28 | lazy_static = "1.4" 29 | java-locator = { version = "0.1", optional = true } 30 | fs_extra = "1.3" 31 | libloading = { version = "0.8", optional = true } 32 | cesu8 = "1.1.0" 33 | dunce = "1.0" 34 | futures = "0.3" 35 | 36 | [build-dependencies] 37 | fs_extra = "1.3" 38 | 39 | [dev-dependencies] 40 | criterion = { version = "0.5", features = ["html_reports"] } 41 | tokio = { version = "1.36", features = ["full"] } 42 | async-std = {version = "1.12", features = ["attributes"]} 43 | 44 | [features] 45 | default = ["dep:libloading", "dep:java-locator"] 46 | # Use a predefined linker setup to load the jvm dynamic library instead of libloading 47 | no-runtime-libloading = [] 48 | 49 | [[bench]] 50 | name = "j4rs_benchmark" 51 | harness = false -------------------------------------------------------------------------------- /rust/Cross.toml: -------------------------------------------------------------------------------- 1 | [target.x86_64-pc-windows-gnu] 2 | image = "astonbitecode/x86_64-pc-windows-gnu-java:0.1.1" -------------------------------------------------------------------------------- /rust/benches/j4rs_benchmark.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | 4 | use std::convert::TryFrom; 5 | 6 | use criterion::Criterion; 7 | use criterion::{black_box, BenchmarkId}; 8 | 9 | use j4rs::{self, Instance, InvocationArg, Jvm}; 10 | 11 | fn do_instance_creation(jvm: &Jvm) -> Instance { 12 | jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty()) 13 | .unwrap() 14 | } 15 | 16 | fn do_invocation_w_no_args(jvm: &Jvm, instance: &Instance) -> Instance { 17 | jvm.invoke(instance, "getMyString", InvocationArg::empty()).unwrap() 18 | } 19 | 20 | fn do_invocation_w_string_args(jvm: &Jvm, instance: &Instance) -> Instance { 21 | jvm.invoke( 22 | instance, 23 | "echo", 24 | &[InvocationArg::try_from("a").unwrap()], 25 | ) 26 | .unwrap() 27 | } 28 | 29 | fn do_invocation_w_integer_args(jvm: &Jvm, instance: &Instance) -> Instance { 30 | jvm.invoke( 31 | instance, 32 | "echo", 33 | &[InvocationArg::try_from(33_i32).unwrap()], 34 | ) 35 | .unwrap() 36 | } 37 | 38 | fn do_invocation_w_string_args_and_to_rust(jvm: &Jvm, instance: &Instance) { 39 | let s_instance = jvm 40 | .invoke( 41 | instance, 42 | "getMyWithArgs", 43 | &[InvocationArg::try_from("a").unwrap()], 44 | ) 45 | .unwrap(); 46 | let _: String = jvm.to_rust(s_instance).unwrap(); 47 | } 48 | 49 | fn use_to_rust_deserialized(jvm: &Jvm, instance: &Instance) { 50 | let i_instance = jvm 51 | .invoke( 52 | instance, 53 | "addInts", 54 | &[InvocationArg::try_from(30_i32) 55 | .unwrap() 56 | .into_primitive() 57 | .unwrap(), 58 | InvocationArg::try_from(3_i32) 59 | .unwrap() 60 | .into_primitive() 61 | .unwrap()], 62 | ) 63 | .unwrap(); 64 | let _: i32 = jvm.to_rust_deserialized(i_instance).unwrap(); 65 | } 66 | 67 | fn use_to_rust_boxed(jvm: &Jvm, instance: &Instance) { 68 | let i_instance = jvm 69 | .invoke( 70 | instance, 71 | "addInts", 72 | &[InvocationArg::try_from(30_i32) 73 | .unwrap() 74 | .into_primitive() 75 | .unwrap(), 76 | InvocationArg::try_from(3_i32) 77 | .unwrap() 78 | .into_primitive() 79 | .unwrap()], 80 | ) 81 | .unwrap(); 82 | let _: Box = jvm.to_rust_boxed(i_instance).unwrap(); 83 | } 84 | 85 | fn j4rs_benchmark(c: &mut Criterion) { 86 | let jvm: Jvm = j4rs::new_jvm(Vec::new(), Vec::new()).unwrap(); 87 | c.bench_function("instances creation", move |b| { 88 | b.iter(|| do_instance_creation(black_box(&jvm))) 89 | }); 90 | 91 | let jvm: Jvm = j4rs::new_jvm(Vec::new(), Vec::new()).unwrap(); 92 | let instance = jvm 93 | .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty()) 94 | .unwrap(); 95 | c.bench_function("invocations with no args and String result", move |b| { 96 | b.iter(|| do_invocation_w_no_args(black_box(&jvm), black_box(&instance))) 97 | }); 98 | 99 | let jvm: Jvm = j4rs::new_jvm(Vec::new(), Vec::new()).unwrap(); 100 | let instance = jvm 101 | .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty()) 102 | .unwrap(); 103 | c.bench_function("invocations with String arg and String result", move |b| { 104 | b.iter(|| do_invocation_w_string_args(black_box(&jvm), black_box(&instance))) 105 | }); 106 | 107 | let jvm: Jvm = j4rs::new_jvm(Vec::new(), Vec::new()).unwrap(); 108 | let instance = jvm 109 | .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty()) 110 | .unwrap(); 111 | c.bench_function( 112 | "invocations with Integer arg and Integer result", 113 | move |b| b.iter(|| do_invocation_w_integer_args(black_box(&jvm), black_box(&instance))), 114 | ); 115 | 116 | let jvm: Jvm = j4rs::new_jvm(Vec::new(), Vec::new()).unwrap(); 117 | let instance = jvm 118 | .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty()) 119 | .unwrap(); 120 | c.bench_function( 121 | "invocations with String arg and String result transformed to Rust", 122 | move |b| { 123 | b.iter(|| { 124 | do_invocation_w_string_args_and_to_rust(black_box(&jvm), black_box(&instance)) 125 | }) 126 | }, 127 | ); 128 | 129 | let jvm: Jvm = j4rs::new_jvm(Vec::new(), Vec::new()).unwrap(); 130 | let instance = jvm 131 | .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty()) 132 | .unwrap(); 133 | c.bench_function("to_rust_unboxed", move |b| { 134 | b.iter(|| use_to_rust_deserialized(black_box(&jvm), black_box(&instance))) 135 | }); 136 | } 137 | 138 | fn bench_create_java_objects_and_to_rust(c: &mut Criterion) { 139 | let mut group = c.benchmark_group("create_java_objects_and_to_rust"); 140 | 141 | let jvm: Jvm = j4rs::new_jvm(Vec::new(), Vec::new()).unwrap(); 142 | let instance = jvm 143 | .create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty()) 144 | .unwrap(); 145 | 146 | for i in 0..2 { 147 | group.bench_function(BenchmarkId::new("to_rust_boxed", i), |b| { 148 | b.iter(|| use_to_rust_boxed(black_box(&jvm), black_box(&instance))) 149 | }); 150 | group.bench_function(BenchmarkId::new("to_rust_deserialized", i), |b| { 151 | b.iter(|| use_to_rust_deserialized(black_box(&jvm), black_box(&instance))) 152 | }); 153 | } 154 | group.finish(); 155 | } 156 | 157 | criterion_group!( 158 | benches, 159 | /*j4rs_benchmark,*/ bench_create_java_objects_and_to_rust 160 | ); 161 | criterion_main!(benches); 162 | -------------------------------------------------------------------------------- /rust/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 astonbitecode 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::error::Error; 16 | use std::fmt; 17 | use std::path::{Path, PathBuf}; 18 | use std::{env, fs}; 19 | 20 | use fs_extra::dir::CopyOptions; 21 | 22 | // This is the version of the jar that should be used 23 | const VERSION: &str = "0.23.0-SNAPSHOT"; 24 | const JAVA_FX_VERSION: &str = "21.0.2"; 25 | 26 | fn main() -> Result<(), J4rsBuildError> { 27 | // ensure build.rs is not rerun when there are no `rerun-if-*` printed 28 | println!("cargo:rerun-if-changed=build.rs"); 29 | 30 | let out_dir = env::var("OUT_DIR")?; 31 | let source_jar_location = PathBuf::from(format!( 32 | "../java/target/j4rs-{VERSION}-jar-with-dependencies.jar" 33 | )); 34 | if Path::new(&source_jar_location).exists() { 35 | println!( 36 | "cargo:rerun-if-changed={}", 37 | source_jar_location.to_string_lossy() 38 | ); 39 | } 40 | 41 | let target_os_res = env::var("CARGO_CFG_TARGET_OS"); 42 | let target_os = target_os_res.as_ref().map(|x| &**x).unwrap_or("unknown"); 43 | if target_os == "android" { 44 | generate_src(&out_dir)?; 45 | return Ok(()); 46 | } 47 | 48 | // Copy the needed jar files if they are available 49 | // (that is, if the build is done with the full source-code - not in crates.io) 50 | copy_jars_from_java(&source_jar_location)?; 51 | copy_jars_to_exec_directory(&out_dir)?; 52 | generate_src(&out_dir)?; 53 | 54 | Ok(()) 55 | } 56 | 57 | fn generate_src(out_dir: &str) -> Result<(), J4rsBuildError> { 58 | let dest_path = Path::new(&out_dir).join("j4rs_init.rs"); 59 | let contents = format!( 60 | " 61 | pub(crate) fn j4rs_version() -> &'static str {{ 62 | \"{VERSION}\" 63 | }} 64 | 65 | pub(crate) fn java_fx_version() -> &'static str {{ 66 | \"{JAVA_FX_VERSION}\" 67 | }} 68 | " 69 | ); 70 | std::fs::write(dest_path, contents)?; 71 | Ok(()) 72 | } 73 | 74 | // Copies the jars from the `java` directory to the source directory of rust. 75 | fn copy_jars_from_java(jar_source_path: &Path) -> Result<(), J4rsBuildError> { 76 | if jar_source_path.exists() { 77 | // Find the destination file 78 | let home = env::var("CARGO_MANIFEST_DIR")?; 79 | let jassets_path_buf = Path::new(&home).join("jassets"); 80 | let jassets_path = jassets_path_buf.to_str().unwrap().to_owned(); 81 | 82 | let destination_jar_file = 83 | Path::new(&jassets_path).join(format!("j4rs-{VERSION}-jar-with-dependencies.jar")); 84 | 85 | // Copy only if the files are not the same 86 | let do_copy = if destination_jar_file.exists() { 87 | !are_same_files(jar_source_path, &destination_jar_file).unwrap_or(true) 88 | } else { 89 | true 90 | }; 91 | 92 | if do_copy { 93 | fs_extra::remove_items(&[&jassets_path])?; 94 | 95 | let _ = fs::create_dir_all(&jassets_path_buf) 96 | .map_err(|error| panic!("Cannot create dir '{jassets_path_buf:?}': {error:?}")); 97 | 98 | fs_extra::copy_items(&[jar_source_path], jassets_path, &CopyOptions::new())?; 99 | } 100 | } 101 | Ok(()) 102 | } 103 | 104 | fn are_same_files(path1: &Path, path2: &Path) -> Result { 105 | Ok(std::fs::read(path1)? == std::fs::read(path2)?) 106 | } 107 | 108 | // Copies the jars to the exec directory. 109 | fn copy_jars_to_exec_directory(out_dir: &str) -> Result<(), J4rsBuildError> { 110 | let mut exec_dir_path_buf = PathBuf::from(out_dir); 111 | exec_dir_path_buf.pop(); 112 | exec_dir_path_buf.pop(); 113 | exec_dir_path_buf.pop(); 114 | 115 | let jassets_output_dir = exec_dir_path_buf.to_str().unwrap(); 116 | 117 | let home = env::var("CARGO_MANIFEST_DIR")?; 118 | let jassets_path_buf = Path::new(&home).join("jassets"); 119 | let jassets_path = jassets_path_buf.to_str().unwrap().to_owned(); 120 | 121 | let jassets_jar_file = 122 | Path::new(&jassets_path).join(format!("j4rs-{VERSION}-jar-with-dependencies.jar")); 123 | let jassets_output_file = Path::new(&jassets_output_dir) 124 | .join("jassets") 125 | .join(format!("j4rs-{VERSION}-jar-with-dependencies.jar")); 126 | 127 | // Delete the target jassets and copy only if the files are not the same 128 | let do_copy = if jassets_jar_file.exists() && jassets_output_file.exists() { 129 | !are_same_files(&jassets_jar_file, &jassets_output_file).unwrap_or(true) 130 | } else { 131 | true 132 | }; 133 | 134 | if do_copy { 135 | fs_extra::remove_items(&[format!("{jassets_output_dir}/jassets")])?; 136 | fs_extra::copy_items(&[jassets_path], jassets_output_dir, &CopyOptions::new())?; 137 | } 138 | 139 | Ok(()) 140 | } 141 | 142 | #[derive(Debug)] 143 | struct J4rsBuildError { 144 | description: String, 145 | } 146 | 147 | impl fmt::Display for J4rsBuildError { 148 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 149 | write!(f, "{}", self.description) 150 | } 151 | } 152 | 153 | impl Error for J4rsBuildError { 154 | fn description(&self) -> &str { 155 | self.description.as_str() 156 | } 157 | } 158 | 159 | impl From for J4rsBuildError { 160 | fn from(err: std::env::VarError) -> J4rsBuildError { 161 | J4rsBuildError { 162 | description: format!("{:?}", err), 163 | } 164 | } 165 | } 166 | 167 | impl From for J4rsBuildError { 168 | fn from(err: std::io::Error) -> J4rsBuildError { 169 | J4rsBuildError { 170 | description: format!("{:?}", err), 171 | } 172 | } 173 | } 174 | 175 | impl From for J4rsBuildError { 176 | fn from(err: fs_extra::error::Error) -> J4rsBuildError { 177 | J4rsBuildError { 178 | description: format!("{:?}", err), 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /rust/j4rs_derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "j4rs_derive" 3 | version = "0.1.1" 4 | authors = ["aston "] 5 | description = "The proc macro crate for `j4rs` that allows jni calls to Rust libraries." 6 | keywords = ["java", "jni"] 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/astonbitecode/j4rs" 9 | readme = "README.md" 10 | categories = ["api-bindings"] 11 | edition = "2018" 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [dependencies] 17 | proc-macro2 = "1.0" 18 | syn = { version = "2.0", features = ["full"] } 19 | quote = "1.0" -------------------------------------------------------------------------------- /rust/j4rs_derive/README.md: -------------------------------------------------------------------------------- 1 | # j4rs_derive 2 | 3 | The proc macro crate for `j4rs` that allows jni calls to Rust libraries. 4 | 5 | It provides the `call_from_java` attribute. The functions annotated with it, can be called from Java code using JNI. 6 | 7 | For more information and examples please see [here](https://github.com/astonbitecode/j4rs#Java-to-Rust-support) and [here](https://github.com/astonbitecode/j4rs-java-call-rust). 8 | -------------------------------------------------------------------------------- /rust/j4rs_derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 astonbitecode 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | extern crate proc_macro; 15 | extern crate proc_macro2; 16 | 17 | use proc_macro::TokenStream; 18 | 19 | use proc_macro2::{Ident, Span}; 20 | use syn::{parse_macro_input, Expr, FnArg, ItemFn, ReturnType, LitStr}; 21 | 22 | use quote::quote; 23 | 24 | #[proc_macro_attribute] 25 | pub fn call_from_java(macro_args: TokenStream, user_function: TokenStream) -> TokenStream { 26 | let cloned_user_function = user_function.clone(); 27 | let macro_arg = parse_macro_input!(macro_args as LitStr); 28 | let user_function = parse_macro_input!(user_function as ItemFn); 29 | let mut generated = impl_call_from_java_macro(&user_function, macro_arg); 30 | 31 | generated.extend(cloned_user_function.into_iter()); 32 | generated 33 | } 34 | 35 | fn impl_call_from_java_macro(user_function: &ItemFn, macro_arg: LitStr) -> TokenStream { 36 | // Retrieve the Ident for the jni function 37 | let jni_ident_string = format!("Java_{}", macro_arg.value().replace(".", "_")); 38 | let ref jni_ident = Ident::new(jni_ident_string.as_ref(), Span::call_site()); 39 | // Retrieve the user function Ident, input arguments and return output 40 | // Ident 41 | let user_function_signature = &user_function.sig; 42 | let user_function_name = &user_function_signature.ident; 43 | // Arguments 44 | let user_function_args = &user_function_signature.inputs; 45 | // The argument names as defined by the user 46 | let user_function_arg_names: Vec = user_function_args 47 | .iter() 48 | .map(|arg| { 49 | let a = arg.clone(); 50 | let q = quote!(#a).to_string(); 51 | let v: Vec<&str> = q.split(' ').collect(); 52 | v.get(0) 53 | .expect(&format!("Could not locate the argument name for: {}", q)) 54 | .to_string() 55 | }) 56 | .collect(); 57 | // The arguments of the jni function 58 | let jni_function_args: Vec = user_function_arg_names 59 | .iter() 60 | .map(|arg| { 61 | let a: FnArg = syn::parse_str(&format!("{}: jobject", arg)).unwrap(); 62 | a 63 | }) 64 | .collect(); 65 | // The jni function return type 66 | let ref jni_function_output = match &user_function_signature.output { 67 | ReturnType::Default => ReturnType::Default, 68 | _ => { 69 | let ret_type: ReturnType = syn::parse_str("-> jobject").unwrap(); 70 | ret_type 71 | } 72 | }; 73 | // The jni return value. This may be void or jobject 74 | let return_value = match &user_function_signature.output { 75 | ReturnType::Default => { 76 | let ret_value: Expr = syn::parse_str("()").unwrap(); 77 | ret_value 78 | } 79 | _ => { 80 | let ret_value: Expr = syn::parse_str( 81 | r#"match instance_to_return { 82 | Ok(i) => { 83 | i.java_object() 84 | // i.as_java_ptr_with_local_ref(jni_env).unwrap() 85 | }, 86 | Err(error) => { 87 | let message = format!("{}", error); 88 | let _ = jvm.throw_invocation_exception(&message); 89 | ptr::null_mut() 90 | }, 91 | }"#, 92 | ).unwrap(); 93 | ret_value 94 | } 95 | }; 96 | 97 | let instance_args_to_pass_to_user_function: Vec = user_function_arg_names.iter() 98 | .map(|jobj_arg_name| { 99 | let expression: Expr = syn::parse_str(&format!("Instance::from_jobject_with_global_ref({}).expect(\"Could not create Instance from jobject\")", jobj_arg_name)).unwrap(); 100 | expression 101 | }) 102 | .collect(); 103 | 104 | let gen = quote! { 105 | #[no_mangle] 106 | pub fn #jni_ident(jni_env: *mut JNIEnv, _class: *const c_void, #(#jni_function_args),*) #jni_function_output { 107 | match unsafe {Jvm::try_from(jni_env)} { 108 | Ok(mut jvm) => { 109 | jvm.detach_thread_on_drop(false); 110 | // println!("Called {}. Calling now {}", stringify!(#jni_ident), stringify!(#user_function_name)); 111 | let instance_to_return = #user_function_name(#(#instance_args_to_pass_to_user_function),*); 112 | #return_value 113 | }, 114 | Err(error) => { 115 | let message = format!("Could not attach to the JVM thread: {}", error); 116 | println!("{}", message); 117 | panic!("{}", message); 118 | }, 119 | } 120 | } 121 | }; 122 | gen.into() 123 | } 124 | -------------------------------------------------------------------------------- /rust/jassets/j4rs-0.23.0-SNAPSHOT-jar-with-dependencies.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astonbitecode/j4rs/40614a8dbe9a32ca51c5cc63c3ec3b3ebd75b766/rust/jassets/j4rs-0.23.0-SNAPSHOT-jar-with-dependencies.jar -------------------------------------------------------------------------------- /rust/src/api_tweaks/android.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 astonbitecode 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | use std::collections::HashMap; 15 | use std::os::raw::c_void; 16 | use std::sync::Mutex; 17 | 18 | use jni_sys::{jclass, jint, jobject, jsize, JNIEnv, JavaVM, JNI_OK, JNI_TRUE}; 19 | 20 | use crate::errors::opt_to_res; 21 | use crate::jni_utils::create_global_ref_from_local_ref; 22 | use crate::{cache, errors, jni_utils, utils}; 23 | 24 | lazy_static! { 25 | static ref MUTEX: Mutex> = Mutex::new(None); 26 | // Cache the classes in order to avoid classloading issues when spawning threads 27 | static ref CLASSES: Mutex> = Mutex::new(HashMap::new()); 28 | static ref CLASSLOADER: Mutex> = Mutex::new(None); 29 | } 30 | 31 | pub fn get_created_java_vms( 32 | vm_buf: &mut Vec<*mut JavaVM>, 33 | _buf_len: jsize, 34 | n_vms: *mut jsize, 35 | ) -> jint { 36 | unsafe { 37 | match MUTEX.lock() { 38 | Ok(g) => { 39 | if g.is_some() { 40 | *n_vms = 1; 41 | *vm_buf = vec![g.as_ref().unwrap().java_vm.clone()]; 42 | } else { 43 | *n_vms = 0; 44 | *vm_buf = Vec::new(); 45 | }; 46 | } 47 | Err(error) => { 48 | error!("Could not get the lock for J4rsAndroidJavaVM: {:?}", error) 49 | } 50 | } 51 | } 52 | JNI_OK 53 | } 54 | 55 | pub(crate) fn set_java_vm(java_vm: *mut JavaVM) { 56 | let mut g = MUTEX.lock().unwrap(); 57 | *g = Some(J4rsAndroidJavaVM { java_vm }); 58 | } 59 | 60 | pub(crate) fn cache_classloader_of(env: *mut JNIEnv, obj: jobject) -> errors::Result<()> { 61 | unsafe { 62 | let classloader_instance = (opt_to_res(cache::get_jni_call_object_method())?)( 63 | env, 64 | obj, 65 | cache::get_get_classloader_method()?, 66 | ); 67 | let mut g = CLASSLOADER.lock().unwrap(); 68 | let classloader_instance = jni_utils::create_global_ref_from_local_ref(classloader_instance, env)?; 69 | 70 | *g = Some(J4rsAndroidClassloader { 71 | class_loader: classloader_instance, 72 | }); 73 | Ok(()) 74 | } 75 | } 76 | 77 | pub(crate) fn create_java_vm( 78 | _jvm: *mut *mut JavaVM, 79 | _env: *mut *mut c_void, 80 | _args: *mut c_void, 81 | ) -> jint { 82 | panic!("Cannot create Java VM in Android.") 83 | } 84 | 85 | // Search the class in the cache first. If not found, then call the FindClass of JNI and insert the result to the cache. 86 | pub(crate) fn find_class(env: *mut JNIEnv, classname: &str) -> errors::Result { 87 | unsafe { 88 | let mut add_to_cache = false; 89 | 90 | let found_in_cache_opt = CLASSES 91 | .lock()? 92 | .get(classname) 93 | .map(|j4rs_class| j4rs_class.class.clone()); 94 | 95 | let found: errors::Result = match found_in_cache_opt { 96 | Some(class) => Ok(class), 97 | None => { 98 | add_to_cache = true; 99 | find_class_default(env, classname) 100 | .or_else(|_| find_class_using_cached_classloader(env, classname)) 101 | } 102 | }; 103 | 104 | if add_to_cache { 105 | let mut g = CLASSES.lock()?; 106 | let global = create_global_ref_from_local_ref(found?, env)?; 107 | g.insert( 108 | classname.to_string(), 109 | J4rsAndroidJclass { 110 | class: global.clone(), 111 | }, 112 | ); 113 | Ok(global as jclass) 114 | } else { 115 | Ok(found?) 116 | } 117 | } 118 | } 119 | 120 | unsafe fn find_class_default(env: *mut JNIEnv, classname: &str) -> errors::Result { 121 | let fc = (**env).v1_6.FindClass; 122 | let cstr = utils::to_c_string(classname); 123 | let found: jclass = (fc)(env, cstr); 124 | let found = do_return(env, found, classname); 125 | utils::drop_c_string(cstr); 126 | found 127 | } 128 | 129 | unsafe fn find_class_using_cached_classloader( 130 | env: *mut JNIEnv, 131 | classname: &str, 132 | ) -> errors::Result { 133 | let g = CLASSLOADER.lock()?; 134 | if g.is_some() { 135 | let cstr = jni_utils::local_jobject_from_str(classname, env).unwrap(); 136 | let classloader_instance = g.as_ref().unwrap().class_loader; 137 | let found = cache::get_jni_call_object_method().unwrap()( 138 | env, 139 | classloader_instance, 140 | cache::get_load_class_method().unwrap(), 141 | cstr, 142 | ); 143 | 144 | let found = do_return(env, found, classname); 145 | found 146 | } else { 147 | Err(errors::J4RsError::JavaError(format!( 148 | "Class not found {classname}" 149 | ))) 150 | } 151 | } 152 | 153 | fn do_return(jni_env: *mut JNIEnv, to_return: T, message: &str) -> errors::Result { 154 | unsafe { 155 | if (opt_to_res(cache::get_jni_exception_check())?)(jni_env) == JNI_TRUE { 156 | (opt_to_res(cache::get_jni_exception_clear())?)(jni_env); 157 | Err(errors::J4RsError::JavaError(format!( 158 | "Class not found {message}" 159 | ))) 160 | } else { 161 | Ok(to_return) 162 | } 163 | } 164 | } 165 | 166 | pub(crate) struct J4rsAndroidJavaVM { 167 | java_vm: *mut JavaVM, 168 | } 169 | 170 | // Implementing Send and Sync is actually safe and proposed for Android 171 | // https://developer.android.com/training/articles/perf-jni 172 | unsafe impl Send for J4rsAndroidJavaVM {} 173 | 174 | unsafe impl Sync for J4rsAndroidJavaVM {} 175 | 176 | pub(crate) struct J4rsAndroidJclass { 177 | class: jclass, 178 | } 179 | 180 | // Implementing Send and Sync is actually safe and proposed for Android to avoid classloading issues 181 | // when creating new threads. 182 | // https://developer.android.com/training/articles/perf-jni 183 | unsafe impl Send for J4rsAndroidJclass {} 184 | 185 | unsafe impl Sync for J4rsAndroidJclass {} 186 | 187 | pub(crate) struct J4rsAndroidClassloader { 188 | class_loader: jobject, 189 | } 190 | 191 | // Implementing Send and Sync is actually safe and proposed for Android to avoid classloading issues 192 | // when creating new threads. 193 | // https://developer.android.com/training/articles/perf-jni 194 | unsafe impl Send for J4rsAndroidClassloader {} 195 | 196 | unsafe impl Sync for J4rsAndroidClassloader {} 197 | -------------------------------------------------------------------------------- /rust/src/api_tweaks/generic.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 astonbitecode 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | use std::os::raw::c_void; 15 | use std::path::MAIN_SEPARATOR; 16 | 17 | use java_locator::{get_jvm_dyn_lib_file_name, locate_jvm_dyn_library}; 18 | use jni_sys::{jclass, jint, jsize, JNIEnv, JavaVM}; 19 | 20 | use crate::{errors, utils}; 21 | 22 | type JNIGetCreatedJavaVMs = 23 | unsafe extern "system" fn(vmBuf: *mut *mut JavaVM, bufLen: jsize, nVMs: *mut jsize) -> jint; 24 | 25 | type JNICreateJavaVM = unsafe extern "system" fn( 26 | pvm: *mut *mut JavaVM, 27 | penv: *mut *mut c_void, 28 | args: *mut c_void, 29 | ) -> jint; 30 | 31 | lazy_static! { 32 | static ref JVM_LIB: libloading::Library = { 33 | let full_path = format!( 34 | "{}{}{}", 35 | locate_jvm_dyn_library().expect("Could not find the jvm dynamic library"), 36 | MAIN_SEPARATOR, 37 | get_jvm_dyn_lib_file_name() 38 | ); 39 | unsafe { 40 | libloading::Library::new(full_path).expect("Could not load the jvm dynamic library") 41 | } 42 | }; 43 | static ref GET_CREATED_JVMS: libloading::Symbol<'static, JNIGetCreatedJavaVMs> = unsafe { 44 | JVM_LIB 45 | .get(b"JNI_GetCreatedJavaVMs") 46 | .expect("Could not find symbol: JNI_GetCreatedJavaVMs") 47 | }; 48 | static ref CREATE_JVM: libloading::Symbol<'static, JNICreateJavaVM> = unsafe { 49 | JVM_LIB 50 | .get(b"JNI_CreateJavaVM") 51 | .expect("Could not find symbol: JNI_CreateJavaVM") 52 | }; 53 | } 54 | 55 | pub(crate) fn get_created_java_vms( 56 | vm_buf: &mut Vec<*mut JavaVM>, 57 | buf_len: jsize, 58 | n_vms: *mut jsize, 59 | ) -> jint { 60 | unsafe { GET_CREATED_JVMS(vm_buf.as_mut_ptr(), buf_len, n_vms) } 61 | } 62 | 63 | pub(crate) fn create_java_vm( 64 | jvm: *mut *mut JavaVM, 65 | penv: *mut *mut c_void, 66 | args: *mut c_void, 67 | ) -> jint { 68 | unsafe { CREATE_JVM(jvm, penv, args) } 69 | } 70 | 71 | pub(crate) fn find_class(env: *mut JNIEnv, classname: &str) -> errors::Result { 72 | unsafe { 73 | let cstr = utils::to_c_string(classname); 74 | let fc = (**env).v1_6.FindClass; 75 | let jc = (fc)(env, cstr); 76 | utils::drop_c_string(cstr); 77 | Ok(jc) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /rust/src/api_tweaks/mod.rs: -------------------------------------------------------------------------------- 1 | use std::os::raw::c_void; 2 | 3 | // Copyright 2018 astonbitecode 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | use crate::errors; 17 | use jni_sys::{jclass, jint, jobject, jsize, JNIEnv, JavaVM}; 18 | 19 | #[cfg(all(not(feature = "no-runtime-libloading"), not(target_os = "android")))] 20 | mod generic; 21 | 22 | #[cfg(all(not(feature = "no-runtime-libloading"), not(target_os = "android")))] 23 | pub fn get_created_java_vms( 24 | vm_buf: &mut Vec<*mut JavaVM>, 25 | buf_len: jsize, 26 | n_vms: *mut jsize, 27 | ) -> jint { 28 | generic::get_created_java_vms(vm_buf, buf_len, n_vms) 29 | } 30 | 31 | #[cfg(all(not(feature = "no-runtime-libloading"), not(target_os = "android")))] 32 | pub fn set_java_vm(_: *mut JavaVM) {} 33 | 34 | #[cfg(all(not(feature = "no-runtime-libloading"), not(target_os = "android")))] 35 | pub fn create_java_vm(pvm: *mut *mut JavaVM, penv: *mut *mut c_void, args: *mut c_void) -> jint { 36 | generic::create_java_vm(pvm, penv, args) 37 | } 38 | 39 | #[cfg(all(not(feature = "no-runtime-libloading"), not(target_os = "android")))] 40 | pub fn find_class(env: *mut JNIEnv, classname: &str) -> errors::Result { 41 | generic::find_class(env, classname) 42 | } 43 | 44 | #[cfg(all(not(feature = "no-runtime-libloading"), not(target_os = "android")))] 45 | pub fn cache_classloader_of(_env: *mut JNIEnv, _obj: jobject) -> errors::Result<()> {Ok(())} 46 | // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // 47 | 48 | #[cfg(all(feature = "no-runtime-libloading", not(target_os = "android")))] 49 | mod no_runtime_lib_loading; 50 | 51 | #[cfg(all(feature = "no-runtime-libloading", not(target_os = "android")))] 52 | pub fn get_created_java_vms( 53 | vm_buf: &mut Vec<*mut JavaVM>, 54 | buf_len: jsize, 55 | n_vms: *mut jsize, 56 | ) -> jint { 57 | no_runtime_lib_loading::get_created_java_vms(vm_buf, buf_len, n_vms) 58 | } 59 | 60 | #[cfg(all(feature = "no-runtime-libloading", not(target_os = "android")))] 61 | pub fn set_java_vm(_: *mut JavaVM) {} 62 | 63 | #[cfg(all(feature = "no-runtime-libloading", not(target_os = "android")))] 64 | pub fn create_java_vm(pvm: *mut *mut JavaVM, penv: *mut *mut c_void, args: *mut c_void) -> jint { 65 | no_runtime_lib_loading::create_java_vm(pvm, penv, args) 66 | } 67 | 68 | #[cfg(all(feature = "no-runtime-libloading", not(target_os = "android")))] 69 | pub fn find_class(env: *mut JNIEnv, classname: &str) -> errors::Result { 70 | no_runtime_lib_loading::find_class(env, classname) 71 | } 72 | 73 | #[cfg(all(feature = "no-runtime-libloading", not(target_os = "android")))] 74 | pub fn cache_classloader_of(_env: *mut JNIEnv, _obj: jobject) -> errors::Result<()> {Ok(())} 75 | // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // 76 | 77 | #[cfg(target_os = "android")] 78 | mod android; 79 | 80 | #[cfg(target_os = "android")] 81 | pub fn get_created_java_vms( 82 | vm_buf: &mut Vec<*mut JavaVM>, 83 | buf_len: jsize, 84 | n_vms: *mut jsize, 85 | ) -> jint { 86 | android::get_created_java_vms(vm_buf, buf_len, n_vms) 87 | } 88 | 89 | #[cfg(target_os = "android")] 90 | pub fn set_java_vm(java_vm: *mut JavaVM) { 91 | android::set_java_vm(java_vm); 92 | } 93 | 94 | #[cfg(target_os = "android")] 95 | pub fn create_java_vm(pvm: *mut *mut JavaVM, penv: *mut *mut c_void, args: *mut c_void) -> jint { 96 | android::create_java_vm(pvm, penv, args) 97 | } 98 | 99 | #[cfg(target_os = "android")] 100 | pub fn find_class(env: *mut JNIEnv, classname: &str) -> errors::Result { 101 | android::find_class(env, classname) 102 | } 103 | 104 | #[cfg(target_os = "android")] 105 | pub fn cache_classloader_of(env: *mut JNIEnv, obj: jobject) -> errors::Result<()> { 106 | android::cache_classloader_of(env, obj) 107 | } -------------------------------------------------------------------------------- /rust/src/api_tweaks/no_runtime_lib_loading.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 astonbitecode 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | use std::os::raw::c_void; 15 | 16 | use jni_sys::{jclass, jint, jsize, JNIEnv, JNI_CreateJavaVM, JNI_GetCreatedJavaVMs, JavaVM}; 17 | 18 | use crate::{errors, utils}; 19 | 20 | #[link(name = "jvm")] 21 | extern "C" {} 22 | 23 | pub(crate) fn get_created_java_vms( 24 | vm_buf: &mut Vec<*mut JavaVM>, 25 | buf_len: jsize, 26 | n_vms: *mut jsize, 27 | ) -> jint { 28 | unsafe { JNI_GetCreatedJavaVMs(vm_buf.as_mut_ptr(), buf_len, n_vms) } 29 | } 30 | 31 | pub(crate) fn create_java_vm( 32 | jvm: *mut *mut JavaVM, 33 | penv: *mut *mut c_void, 34 | args: *mut c_void, 35 | ) -> jint { 36 | unsafe { JNI_CreateJavaVM(jvm, penv, args) } 37 | } 38 | 39 | pub(crate) fn find_class(env: *mut JNIEnv, classname: &str) -> errors::Result { 40 | unsafe { 41 | let cstr = utils::to_c_string(classname); 42 | let fc = (**env).v1_6.FindClass; 43 | let jc = (fc)(env, cstr); 44 | utils::drop_c_string(cstr); 45 | Ok(jc) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /rust/src/errors.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 astonbitecode 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::convert::Infallible; 16 | use std::env::VarError; 17 | use std::error::Error; 18 | use std::ffi::NulError; 19 | use std::io; 20 | use std::sync::mpsc::RecvError; 21 | use std::sync::{PoisonError, TryLockError}; 22 | use std::{fmt, result}; 23 | 24 | use cesu8::Cesu8DecodingError; 25 | use fs_extra; 26 | use serde_json; 27 | 28 | use futures::channel::oneshot::Canceled; 29 | 30 | pub type Result = result::Result; 31 | 32 | pub(crate) fn opt_to_res(opt: Option) -> Result { 33 | opt.ok_or(J4RsError::RustError("Option was found None while converting to result".to_string())) 34 | } 35 | 36 | #[allow(unused)] 37 | pub(crate) fn res_to_opt(res: Result) -> Option { 38 | if res.is_err() { 39 | None 40 | } else { 41 | Some(res.unwrap()) 42 | } 43 | } 44 | 45 | #[derive(Debug, PartialEq, Eq, Clone)] 46 | pub enum J4RsError { 47 | GeneralError(String), 48 | JavaError(String), 49 | JniError(String), 50 | RustError(String), 51 | ParseError(String), 52 | Timeout, 53 | } 54 | 55 | impl fmt::Display for J4RsError { 56 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 57 | match self { 58 | J4RsError::GeneralError(message) => write!(f, "{}", message), 59 | J4RsError::JavaError(message) => write!(f, "{}", message), 60 | J4RsError::JniError(message) => write!(f, "{}", message), 61 | J4RsError::RustError(message) => write!(f, "{}", message), 62 | J4RsError::ParseError(message) => write!(f, "{}", message), 63 | &J4RsError::Timeout => write!(f, "Timeout"), 64 | } 65 | } 66 | } 67 | 68 | impl Error for J4RsError { 69 | fn description(&self) -> &str { 70 | match *self { 71 | J4RsError::GeneralError(_) => "A general error occured", 72 | J4RsError::JavaError(_) => "An error coming from Java occured", 73 | J4RsError::JniError(_) => "A JNI error occured", 74 | J4RsError::RustError(_) => "An error coming from Rust occured", 75 | J4RsError::ParseError(_) => "A parsing error occured", 76 | J4RsError::Timeout => "Timeout", 77 | } 78 | } 79 | } 80 | 81 | impl From for J4RsError { 82 | fn from(err: NulError) -> J4RsError { 83 | J4RsError::JniError(format!("{:?}", err)) 84 | } 85 | } 86 | 87 | impl From for J4RsError { 88 | fn from(err: io::Error) -> J4RsError { 89 | J4RsError::GeneralError(format!("{:?}", err)) 90 | } 91 | } 92 | 93 | impl From for J4RsError { 94 | fn from(err: serde_json::Error) -> J4RsError { 95 | J4RsError::ParseError(format!("{:?}", err)) 96 | } 97 | } 98 | 99 | impl From for J4RsError { 100 | fn from(err: fs_extra::error::Error) -> J4RsError { 101 | J4RsError::GeneralError(format!("{:?}", err)) 102 | } 103 | } 104 | 105 | impl From> for J4RsError { 106 | fn from(err: TryLockError) -> J4RsError { 107 | J4RsError::GeneralError(format!("{:?}", err)) 108 | } 109 | } 110 | 111 | impl From> for J4RsError { 112 | fn from(err: PoisonError) -> J4RsError { 113 | J4RsError::GeneralError(format!("{:?}", err)) 114 | } 115 | } 116 | 117 | impl From for J4RsError { 118 | fn from(err: Infallible) -> J4RsError { 119 | J4RsError::RustError(format!("{:?}", err)) 120 | } 121 | } 122 | 123 | impl From for J4RsError { 124 | fn from(err: RecvError) -> J4RsError { 125 | J4RsError::RustError(format!("{:?}", err)) 126 | } 127 | } 128 | 129 | impl From for J4RsError { 130 | fn from(err: VarError) -> J4RsError { 131 | J4RsError::RustError(format!("{:?}", err)) 132 | } 133 | } 134 | 135 | impl From for J4RsError { 136 | fn from(err: Canceled) -> J4RsError { 137 | J4RsError::RustError(format!("{:?}", err)) 138 | } 139 | } 140 | 141 | impl From for J4RsError { 142 | fn from(err: Cesu8DecodingError) -> J4RsError { 143 | J4RsError::ParseError(format!("{:?}", err)) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /rust/src/logger.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 astonbitecode 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | use std::env; 15 | 16 | lazy_static! { 17 | static ref CONSOLE_ENABLED: i8 = { 18 | let var_level = env::var("J4RS_CONSOLE_LOG_LEVEL") 19 | .unwrap_or("warn".to_owned()) 20 | .to_lowercase(); 21 | match var_level.as_str() { 22 | "disabled" => 0, 23 | "error" => 1, 24 | "warn" => 2, 25 | "info" => 3, 26 | "debug" => 4, 27 | _ => { 28 | println!("WARN: The env variable 'J4RS_CONSOLE_LOG_LEVEL' is not correctly set. Please use one of the 'debug', 'info', 'warn', 'error', or 'disabled'. Defaulting to warning"); 29 | 2 30 | } 31 | } 32 | }; 33 | } 34 | 35 | pub fn debug(message: &str) { 36 | if CONSOLE_ENABLED.to_owned() > 3 { 37 | println!("DEBUG: {}", message); 38 | } 39 | debug!("{}", message); 40 | } 41 | 42 | pub fn info(message: &str) { 43 | if CONSOLE_ENABLED.to_owned() > 2 { 44 | println!("INFO: {}", message); 45 | } 46 | info!("{}", message); 47 | } 48 | 49 | #[allow(dead_code)] 50 | pub fn warn(message: &str) { 51 | if CONSOLE_ENABLED.to_owned() > 1 { 52 | println!("WARN: {}", message); 53 | } 54 | warn!("{}", message); 55 | } 56 | 57 | #[allow(dead_code)] 58 | pub fn error(message: &str) { 59 | if CONSOLE_ENABLED.to_owned() > 0 { 60 | println!("ERROR: {}", message); 61 | } 62 | error!("{}", message); 63 | } 64 | -------------------------------------------------------------------------------- /rust/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::api::instance::Instance; 2 | pub use crate::jni_sys::{jlong, jobject, JNIEnv}; 3 | pub use crate::Jvm; 4 | pub use core::ptr; 5 | pub use std::os::raw::c_void; 6 | -------------------------------------------------------------------------------- /rust/src/provisioning.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 astonbitecode 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::cell::RefCell; 16 | 17 | use crate::utils; 18 | 19 | const MAVEN_CENTRAL: &str = "MavenCentral::https://repo.maven.apache.org/maven2"; 20 | const OSS_SNAPSHOTS: &str = "OssSnapshots::https://oss.sonatype.org/content/repositories/snapshots"; 21 | 22 | thread_local! { 23 | static MAVEN_SETTINGS: RefCell = RefCell::new(MavenSettings::default()); 24 | } 25 | 26 | pub(crate) fn set_maven_settings(ms: &MavenSettings) { 27 | MAVEN_SETTINGS.with(|maven_settings| { 28 | *maven_settings.borrow_mut() = ms.clone(); 29 | }); 30 | } 31 | 32 | pub(crate) fn get_maven_settings() -> MavenSettings { 33 | MAVEN_SETTINGS.with(|maven_settings| { 34 | let ms = maven_settings.borrow(); 35 | ms.clone() 36 | }) 37 | } 38 | 39 | /// Marker trait to be used for deploying artifacts. 40 | pub trait JavaArtifact {} 41 | 42 | /// Represents a Jar artifact that resides in the local storage. 43 | /// It can be deployed in order to be loaded and used by j4rs by calling the `JVM::deploy_artifact` method. 44 | #[derive(Debug)] 45 | pub struct LocalJarArtifact { 46 | pub(crate) base: String, 47 | pub(crate) path: String, 48 | } 49 | 50 | impl LocalJarArtifact { 51 | /// Creates a new LocalJarArtifact. 52 | /// path is the location of the jar file in the local storage 53 | pub fn new(path: &str) -> LocalJarArtifact { 54 | LocalJarArtifact { 55 | base: utils::jassets_path() 56 | .unwrap_or_default() 57 | .to_str() 58 | .unwrap_or("") 59 | .to_string(), 60 | path: path.to_string(), 61 | } 62 | } 63 | } 64 | 65 | impl JavaArtifact for LocalJarArtifact {} 66 | 67 | impl<'a> From<&'a str> for LocalJarArtifact { 68 | fn from(string: &'a str) -> LocalJarArtifact { 69 | LocalJarArtifact::new(string) 70 | } 71 | } 72 | 73 | impl From for LocalJarArtifact { 74 | fn from(string: String) -> LocalJarArtifact { 75 | LocalJarArtifact::new(&string) 76 | } 77 | } 78 | 79 | /// Represents an Artifact that can be fetched by a remote Maven repository. 80 | /// It can loaded and used by j4rs by calling the `JVM::deploy_artifact` method. 81 | #[derive(Debug, Clone)] 82 | pub struct MavenArtifact { 83 | pub(crate) base: String, 84 | pub(crate) group: String, 85 | pub(crate) id: String, 86 | pub(crate) version: String, 87 | pub(crate) qualifier: String, 88 | } 89 | 90 | impl JavaArtifact for MavenArtifact {} 91 | 92 | impl From<&[&str]> for MavenArtifact { 93 | fn from(slice: &[&str]) -> MavenArtifact { 94 | MavenArtifact { 95 | base: utils::jassets_path() 96 | .unwrap_or_default() 97 | .to_str() 98 | .unwrap_or("") 99 | .to_string(), 100 | group: slice.first().unwrap_or(&"").to_string(), 101 | id: slice.get(1).unwrap_or(&"").to_string(), 102 | version: slice.get(2).unwrap_or(&"").to_string(), 103 | qualifier: slice.get(3).unwrap_or(&"").to_string(), 104 | } 105 | } 106 | } 107 | 108 | impl From> for MavenArtifact { 109 | fn from(v: Vec<&str>) -> MavenArtifact { 110 | MavenArtifact::from(v.as_slice()) 111 | } 112 | } 113 | 114 | impl From<&Vec<&str>> for MavenArtifact { 115 | fn from(v: &Vec<&str>) -> MavenArtifact { 116 | MavenArtifact::from(v.as_slice()) 117 | } 118 | } 119 | 120 | impl<'a> From<&'a str> for MavenArtifact { 121 | /// Convenience for creating a MavenArtifact. 122 | /// 123 | /// The `&str` should be formed like following: 124 | /// 125 | /// __group__:__id__:__version__:__qualifier__ 126 | /// 127 | /// E.g: 128 | /// _io.github.astonbitecode:j4rs:0.5.1_ 129 | fn from(string: &'a str) -> MavenArtifact { 130 | let v: Vec<&str> = string.split(':').collect(); 131 | MavenArtifact::from(v.as_slice()) 132 | } 133 | } 134 | 135 | impl From for MavenArtifact { 136 | /// Convenience for creating a MavenArtifact. 137 | /// 138 | /// The `&str` should be formed like following: 139 | /// 140 | /// __group__:__id__:__version__:__qualifier__ 141 | /// 142 | /// E.g: 143 | /// _io.github.astonbitecode:j4rs:0.5.1_ 144 | fn from(string: String) -> MavenArtifact { 145 | let v: Vec<&str> = string.split(':').collect(); 146 | MavenArtifact::from(v.as_slice()) 147 | } 148 | } 149 | 150 | /// Contains Maven settings and configuration 151 | #[derive(Debug, Clone)] 152 | pub struct MavenSettings { 153 | pub(crate) repos: Vec, 154 | } 155 | 156 | impl MavenSettings { 157 | /// Creates new Maven Settings by defining additional repositories to use. 158 | /// The [maven central](https://repo.maven.apache.org/maven2) is always being included as a repo. 159 | pub fn new(repos: Vec) -> MavenSettings { 160 | let mut repos = repos; 161 | repos.push(MavenArtifactRepo::from(MAVEN_CENTRAL)); 162 | repos.push(MavenArtifactRepo::from(OSS_SNAPSHOTS)); 163 | MavenSettings { repos } 164 | } 165 | } 166 | 167 | impl Default for MavenSettings { 168 | fn default() -> Self { 169 | MavenSettings::new(vec![]) 170 | } 171 | } 172 | 173 | /// A repository from which Java artifacts can be fetched. 174 | #[derive(Debug, Clone)] 175 | pub struct MavenArtifactRepo { 176 | pub(crate) _id: String, 177 | pub(crate) uri: String, 178 | } 179 | 180 | impl From<&[&str]> for MavenArtifactRepo { 181 | fn from(slice: &[&str]) -> MavenArtifactRepo { 182 | MavenArtifactRepo { 183 | _id: slice.first().unwrap_or(&"").to_string(), 184 | uri: slice.get(1).unwrap_or(&"").to_string(), 185 | } 186 | } 187 | } 188 | 189 | impl<'a> From<&'a str> for MavenArtifactRepo { 190 | /// Convenience for creating a MavenArtifactRepo. 191 | /// 192 | /// The `&str` should be formed like following: 193 | /// 194 | /// `id::uri` 195 | /// 196 | /// E.g: 197 | /// `MyAlterRepo::https://myalterrepo.io` 198 | fn from(string: &'a str) -> MavenArtifactRepo { 199 | let v: Vec<&str> = string.split("::").collect(); 200 | MavenArtifactRepo::from(v.as_slice()) 201 | } 202 | } 203 | 204 | impl From for MavenArtifactRepo { 205 | /// Convenience for creating a MavenArtifactRepo. 206 | /// 207 | /// The `&str` should be formed like following: 208 | /// 209 | /// `id::uri` 210 | /// 211 | /// E.g: 212 | /// `MyAlterRepo::https://myalterrepo.io` 213 | fn from(string: String) -> MavenArtifactRepo { 214 | MavenArtifactRepo::from(string.as_str()) 215 | } 216 | } 217 | 218 | #[cfg(test)] 219 | mod provisioning_unit_tests { 220 | use super::*; 221 | 222 | #[test] 223 | fn maven_artifact_from() { 224 | let ma1 = MavenArtifact::from("io.github.astonbitecode:j4rs:0.5.1"); 225 | assert_eq!(ma1.group, "io.github.astonbitecode"); 226 | assert_eq!(ma1.id, "j4rs"); 227 | assert_eq!(ma1.version, "0.5.1"); 228 | assert_eq!(ma1.qualifier, ""); 229 | 230 | let ma2 = MavenArtifact::from("io.github.astonbitecode:j4rs:0.5.1".to_string()); 231 | assert_eq!(ma2.group, "io.github.astonbitecode"); 232 | assert_eq!(ma2.id, "j4rs"); 233 | assert_eq!(ma2.version, "0.5.1"); 234 | assert_eq!(ma2.qualifier, ""); 235 | 236 | let ma3 = MavenArtifact::from(&vec!["io.github.astonbitecode", "j4rs", "0.5.1"]); 237 | assert_eq!(ma3.group, "io.github.astonbitecode"); 238 | assert_eq!(ma3.id, "j4rs"); 239 | assert_eq!(ma3.version, "0.5.1"); 240 | assert_eq!(ma3.qualifier, ""); 241 | } 242 | 243 | #[test] 244 | fn maven_artifact_repo_from() { 245 | let mar = MavenArtifactRepo::from("myrepo::https://myrepo.io"); 246 | assert_eq!(mar._id, "myrepo"); 247 | assert_eq!(mar.uri, "https://myrepo.io"); 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /rust/src/utils.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 astonbitecode 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use std::ffi::{CStr, CString}; 16 | use std::path::PathBuf; 17 | use std::{self, env, fs, str}; 18 | 19 | use cesu8::{from_java_cesu8, to_java_cesu8}; 20 | use dunce::canonicalize; 21 | use libc::{self, c_char}; 22 | 23 | use crate::api::{ 24 | PRIMITIVE_BOOLEAN, PRIMITIVE_BYTE, PRIMITIVE_CHAR, PRIMITIVE_DOUBLE, PRIMITIVE_FLOAT, 25 | PRIMITIVE_INT, PRIMITIVE_LONG, PRIMITIVE_SHORT, 26 | }; 27 | use crate::{cache, errors, InvocationArg, JavaClass}; 28 | 29 | pub(crate) unsafe fn to_rust_string(pointer: *const c_char) -> errors::Result { 30 | let slice = CStr::from_ptr(pointer).to_bytes(); 31 | Ok(from_java_cesu8(slice)?.to_string()) 32 | } 33 | 34 | pub(crate) fn to_c_string(string: &str) -> *mut c_char { 35 | let cs = CString::new(string.as_bytes()).unwrap(); 36 | cs.into_raw() 37 | } 38 | 39 | pub(crate) fn to_c_string_struct(string: &str) -> CString { 40 | let enc = to_java_cesu8(string).into_owned(); 41 | unsafe { CString::from_vec_unchecked(enc) } 42 | } 43 | 44 | pub(crate) unsafe fn drop_c_string(ptr: *mut c_char) { 45 | let _ = CString::from_raw(ptr); 46 | } 47 | 48 | #[cfg(not(target_os = "windows"))] 49 | pub(crate) fn classpath_sep() -> &'static str { 50 | ":" 51 | } 52 | 53 | #[cfg(target_os = "windows")] 54 | pub(crate) fn classpath_sep() -> &'static str { 55 | ";" 56 | } 57 | 58 | pub(crate) fn java_library_path() -> errors::Result { 59 | let default = format!("-Djava.library.path={}", deps_dir()?); 60 | if cfg!(windows) { 61 | Ok(default) 62 | } else { 63 | Ok(format!("{}:/usr/lib:/lib", default)) 64 | } 65 | } 66 | 67 | pub(crate) fn deps_dir() -> errors::Result { 68 | let mut pb = jassets_path()?; 69 | pb.pop(); 70 | pb.push("deps"); 71 | Ok(pb.to_str().unwrap_or("./deps/").to_owned()) 72 | } 73 | 74 | pub(crate) fn jassets_path() -> errors::Result { 75 | let pb_opt = { 76 | let guard = cache::JASSETS_PATH.lock()?; 77 | guard.clone() 78 | }; 79 | match pb_opt { 80 | Some(pb) => Ok(pb), 81 | None => default_jassets_path(), 82 | } 83 | } 84 | 85 | pub(crate) fn default_jassets_path() -> errors::Result { 86 | let is_build_script = env::var("OUT_DIR").is_ok(); 87 | 88 | let mut start_path = if is_build_script { 89 | PathBuf::from(env::var("OUT_DIR")?) 90 | } else { 91 | env::current_exe()? 92 | }; 93 | start_path = canonicalize(start_path)?; 94 | 95 | while start_path.pop() { 96 | for entry in std::fs::read_dir(&start_path)? { 97 | let path = entry?.path(); 98 | if path.file_name().map(|x| x == "jassets").unwrap_or(false) { 99 | return Ok(path); 100 | } 101 | } 102 | } 103 | 104 | Err(errors::J4RsError::GeneralError("Can not find jassets directory".to_owned())) 105 | } 106 | 107 | pub(crate) fn find_j4rs_dynamic_libraries_names() -> errors::Result> { 108 | let entries: Vec = find_j4rs_dynamic_libraries_dir_entries()? 109 | .iter() 110 | .map(|entry| entry.file_name().to_str().unwrap().to_owned()) 111 | .collect(); 112 | 113 | Ok(entries) 114 | } 115 | 116 | pub(crate) fn find_j4rs_dynamic_libraries_paths() -> errors::Result> { 117 | let entries: Vec = find_j4rs_dynamic_libraries_dir_entries()? 118 | .iter() 119 | .map(|entry| entry.path().to_str().unwrap().to_owned()) 120 | .collect(); 121 | 122 | Ok(entries) 123 | } 124 | 125 | fn find_j4rs_dynamic_libraries_dir_entries() -> errors::Result> { 126 | let v: Vec = fs::read_dir(deps_dir()?)? 127 | .filter(|entry| entry.is_ok()) 128 | .filter(|entry| { 129 | let entry = entry.as_ref().unwrap(); 130 | let file_name = entry.file_name(); 131 | let file_name = file_name.to_str().unwrap(); 132 | file_name.contains("j4rs") 133 | && !file_name.contains("derive") 134 | && (file_name.contains(".so") 135 | || file_name.contains(".dll") 136 | || file_name.contains(".dylib")) 137 | }) 138 | .map(|entry| entry.unwrap()) 139 | .collect(); 140 | 141 | Ok(v) 142 | } 143 | 144 | pub(crate) fn primitive_of(inv_arg: &InvocationArg) -> Option { 145 | match get_class_name(inv_arg).into() { 146 | JavaClass::Boolean => Some(PRIMITIVE_BOOLEAN.to_string()), 147 | JavaClass::Byte => Some(PRIMITIVE_BYTE.to_string()), 148 | JavaClass::Short => Some(PRIMITIVE_SHORT.to_string()), 149 | JavaClass::Integer => Some(PRIMITIVE_INT.to_string()), 150 | JavaClass::Long => Some(PRIMITIVE_LONG.to_string()), 151 | JavaClass::Float => Some(PRIMITIVE_FLOAT.to_string()), 152 | JavaClass::Double => Some(PRIMITIVE_DOUBLE.to_string()), 153 | JavaClass::Character => Some(PRIMITIVE_CHAR.to_string()), 154 | JavaClass::Void => Some("void".to_string()), 155 | _ => None, 156 | } 157 | } 158 | 159 | pub(crate) fn get_class_name(inv_arg: &InvocationArg) -> &str { 160 | let class_name = match inv_arg { 161 | InvocationArg::Java { 162 | instance: _, 163 | class_name, 164 | serialized: _, 165 | } => class_name, 166 | InvocationArg::Rust { 167 | json: _, 168 | class_name, 169 | serialized: _, 170 | } => class_name, 171 | InvocationArg::RustBasic { 172 | instance: _, 173 | class_name, 174 | serialized: _, 175 | } => class_name, 176 | }; 177 | class_name.as_ref() 178 | } 179 | 180 | #[cfg(test)] 181 | mod utils_unit_tests { 182 | use std::convert::TryFrom; 183 | 184 | use super::*; 185 | use crate::lib_unit_tests::create_tests_jvm; 186 | 187 | #[test] 188 | fn get_class_name_test() -> errors::Result<()> { 189 | let _jvm = create_tests_jvm()?; 190 | assert!(get_class_name(&InvocationArg::try_from(false)?) == "java.lang.Boolean"); 191 | 192 | Ok(()) 193 | } 194 | 195 | #[test] 196 | fn primitive_of_test() -> errors::Result<()> { 197 | let _jvm = create_tests_jvm()?; 198 | assert_eq!(primitive_of(&InvocationArg::try_from(false)?), Some("boolean".to_string())); 199 | assert_eq!(primitive_of(&InvocationArg::try_from(1_i8)?), Some("byte".to_string())); 200 | assert_eq!(primitive_of(&InvocationArg::try_from(1_i16)?), Some("short".to_string())); 201 | assert_eq!(primitive_of(&InvocationArg::try_from(1_i32)?), Some("int".to_string())); 202 | assert_eq!(primitive_of(&InvocationArg::try_from(1_i64)?), Some("long".to_string())); 203 | assert_eq!(primitive_of(&InvocationArg::try_from(0.1_f32)?), Some("float".to_string())); 204 | assert_eq!(primitive_of(&InvocationArg::try_from(0.1_f64)?), Some("double".to_string())); 205 | assert_eq!(primitive_of(&InvocationArg::try_from('c')?), Some("char".to_string())); 206 | assert_eq!(primitive_of(&InvocationArg::try_from(())?), Some("void".to_string())); 207 | 208 | Ok(()) 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /static/j4rs-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astonbitecode/j4rs/40614a8dbe9a32ca51c5cc63c3ec3b3ebd75b766/static/j4rs-small.png -------------------------------------------------------------------------------- /test-resources/java/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | io.github.astonbitecode 5 | j4rs-testing 6 | 0.23.0-SNAPSHOT 7 | j4rs-testing 8 | Testing resources for j4rs 9 | https://github.com/astonbitecode/j4rs 10 | 11 | 12 | 13 | astonbitecode 14 | 15 | 16 | 17 | 18 | 19 | Apache License, Version 2.0 20 | https://www.apache.org/licenses/LICENSE-2.0 21 | 22 | 23 | MIT 24 | https://opensource.org/licenses/MIT 25 | 26 | 27 | 28 | 29 | scm:git:git@github.com:astonbitecode/j4rs.git 30 | git@github.com:astonbitecode/j4rs.git 31 | scm:git:git@github.com:astonbitecode/j4rs.git 32 | 33 | 34 | 35 | 36 | ossrh 37 | https://oss.sonatype.org/content/repositories/snapshots 38 | 39 | 40 | ossrh 41 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 42 | 43 | 44 | 45 | 46 | 3.1.2 47 | 4.13.2 48 | 3.3.0 49 | 1.6.13 50 | 3.1.0 51 | 3.3.0 52 | 3.5.0 53 | 3.6.0 54 | 3.11.0 55 | 2.15.2 56 | 5.4.0 57 | 13.0.2 58 | 59 | 60 | 61 | io.github.astonbitecode 62 | j4rs 63 | ${project.version} 64 | provided 65 | 66 | 67 | 68 | 69 | 70 | src/resources 71 | 72 | **/* 73 | 74 | 75 | MANIFEST.MF 76 | 77 | 78 | 79 | 80 | 81 | org.apache.maven.plugins 82 | maven-compiler-plugin 83 | ${build.plugins.plugin.version} 84 | 85 | 86 | compile 87 | 88 | compile 89 | 90 | 91 | 92 | 93 | 1.8 94 | 1.8 95 | 96 | 97 | 98 | org.apache.maven.plugins 99 | maven-surefire-plugin 100 | ${maven.surefire.version} 101 | 102 | true 103 | false 104 | 105 | **/*Test.* 106 | 107 | 108 | 109 | 110 | org.apache.maven.plugins 111 | maven-jar-plugin 112 | ${maven.jar.plugin.version} 113 | 114 | 115 | true 116 | 117 | false 118 | 119 | 120 | astonbitecode 121 | ${project.url} 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | release 131 | 132 | 133 | 134 | org.apache.maven.plugins 135 | maven-jar-plugin 136 | ${maven.jar.plugin.version} 137 | 138 | 139 | true 140 | 141 | false 142 | 143 | 144 | astonbitecode 145 | ${project.url} 146 | 147 | 148 | 149 | 150 | 151 | org.sonatype.plugins 152 | nexus-staging-maven-plugin 153 | ${nexus.staging.plugin.version} 154 | true 155 | 156 | ossrh 157 | https://oss.sonatype.org/ 158 | true 159 | 160 | 161 | 162 | org.apache.maven.plugins 163 | maven-gpg-plugin 164 | ${maven.gpg.plugin.version} 165 | 166 | 167 | sign-artifacts 168 | verify 169 | 170 | sign 171 | 172 | 173 | 174 | 175 | 176 | org.apache.maven.plugins 177 | maven-source-plugin 178 | ${maven.source.plugin.version} 179 | 180 | false 181 | 182 | 183 | 184 | attach-sources 185 | 186 | jar-no-fork 187 | 188 | 189 | 190 | 191 | 192 | org.apache.maven.plugins 193 | maven-javadoc-plugin 194 | ${maven.javadoc.plugin.version} 195 | 196 | 8 197 | 198 | 199 | 200 | attach-javadocs 201 | 202 | jar 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | -------------------------------------------------------------------------------- /test-resources/java/src/main/java/org/astonbitecode/j4rs/tests/DummyMapImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.tests; 16 | 17 | import java.util.HashMap; 18 | import java.util.stream.Collectors; 19 | 20 | public class DummyMapImpl extends HashMap implements DummyMapInterface { 21 | private static final long serialVersionUID = 1L; 22 | 23 | public DummyMapImpl() { 24 | put("one", 1); 25 | put("two", 2); 26 | } 27 | 28 | public long keysLength() { 29 | return keySet().stream().map(String::length).collect(Collectors.summingInt(Integer::intValue)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test-resources/java/src/main/java/org/astonbitecode/j4rs/tests/DummyMapInterface.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.tests; 16 | 17 | import java.util.Map; 18 | 19 | public interface DummyMapInterface extends Map { 20 | public long keysLength(); 21 | } -------------------------------------------------------------------------------- /test-resources/java/src/main/java/org/astonbitecode/j4rs/tests/MyBean.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.tests; 16 | 17 | public class MyBean { 18 | private String someString; 19 | private Integer someInteger; 20 | 21 | public MyBean() { 22 | } 23 | 24 | public String getSomeString() { 25 | return someString; 26 | } 27 | 28 | public void setSomeString(String someString) { 29 | this.someString = someString; 30 | } 31 | 32 | public Integer getSomeInteger() { 33 | return someInteger; 34 | } 35 | 36 | public void setSomeInteger(Integer someInteger) { 37 | this.someInteger = someInteger; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test-resources/java/src/main/java/org/astonbitecode/j4rs/tests/MySecondTest.java: -------------------------------------------------------------------------------- 1 | package org.astonbitecode.j4rs.tests; 2 | 3 | import org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport; 4 | 5 | import java.util.stream.IntStream; 6 | 7 | public class MySecondTest extends NativeCallbackToRustChannelSupport { 8 | 9 | public static MyTest myTestFactory() { 10 | return new MyTest(); 11 | } 12 | 13 | public void performCallback() { 14 | new Thread(() -> { 15 | doCallback("THIS IS FROM CALLBACK TO A CHANNEL..."); 16 | }).start(); 17 | } 18 | 19 | public void performTenCallbacks() { 20 | new Thread(() -> { 21 | IntStream.range(0, 10).forEach(i -> doCallback("THIS IS FROM CALLBACK TO A CHANNEL..." + i)); 22 | }).start(); 23 | } 24 | 25 | public void performCallbackFromTenThreads() { 26 | IntStream.range(0, 10).forEach(i -> performCallback()); 27 | } 28 | 29 | public static void main(String[] args) { 30 | for (long i = 0; i < Long.MAX_VALUE; i++) { 31 | if (i % 100000 == 0) { 32 | System.out.println(i); 33 | } 34 | new MySecondTest(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test-resources/java/src/main/java/org/astonbitecode/j4rs/tests/MyTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 astonbitecode 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package org.astonbitecode.j4rs.tests; 16 | 17 | import org.astonbitecode.j4rs.errors.InvocationException; 18 | 19 | import java.util.ArrayList; 20 | import java.util.Arrays; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.concurrent.ExecutorService; 24 | import java.util.concurrent.Executors; 25 | import java.util.concurrent.Future; 26 | import java.util.stream.Collectors; 27 | import java.util.stream.IntStream; 28 | 29 | public class MyTest { 30 | private String string; 31 | public static String StaticString = "This is a static String from Java"; 32 | 33 | private static ExecutorService executor = Executors.newSingleThreadExecutor(); 34 | 35 | public MyTest() { 36 | this.string = "THE DEFAULT CONSTRUCTOR WAS CALLED"; 37 | } 38 | 39 | public MyTest(MyTest myTest) { 40 | this.string = myTest.string; 41 | } 42 | 43 | public MyTest(String str) { 44 | this.string = str; 45 | } 46 | 47 | public MyTest(String... args) { 48 | this.string = Arrays.stream(args).collect(Collectors.joining(", ")); 49 | } 50 | 51 | public static void useLongPrimitivesArray(long[] args) { 52 | } 53 | 54 | public String getMyString() { 55 | return string; 56 | } 57 | 58 | public String appendToMyString(String str) { 59 | this.string = this.string + str; 60 | return this.string; 61 | } 62 | 63 | public String getMyWithArgs(String arg) { 64 | return string + arg; 65 | } 66 | 67 | public String getMyWithArgsList(String... args) { 68 | String str = Arrays.stream(args).reduce("", (a, b) -> { 69 | return a + b; 70 | }); 71 | return str; 72 | } 73 | 74 | public List getNumbersUntil(Integer until) { 75 | if (until == null) { 76 | return new ArrayList<>(); 77 | } else { 78 | return IntStream.range(0, until).boxed().collect(Collectors.toList()); 79 | } 80 | } 81 | 82 | public Integer addInts(Integer... args) { 83 | int result = Arrays.stream(args).reduce(0, (a, b) -> { 84 | return a + b; 85 | }); 86 | return result; 87 | } 88 | 89 | public Integer addInts(List args) { 90 | int result = args.stream().reduce(0, (a, b) -> { 91 | return a + b; 92 | }); 93 | return result; 94 | } 95 | 96 | public Integer addInts(int a, int b) { 97 | return a + b; 98 | } 99 | 100 | public void list(List l) { 101 | String str = l.stream().reduce("The arguments passed where", (a, b) -> { 102 | return a + "\n" + b; 103 | }); 104 | } 105 | 106 | public void map(Map m) { 107 | m.entrySet().stream().map(entry -> entry.getKey().getClass().isAssignableFrom(String.class) 108 | && entry.getValue().getClass().isAssignableFrom(Integer.class)).collect(Collectors.toList()); 109 | } 110 | 111 | public void aMethod() { 112 | System.out.println("A METHOD CALLED"); 113 | } 114 | 115 | public static void StaticMethod() { 116 | System.out.println("Static"); 117 | } 118 | 119 | public T echo(T o) { 120 | return o; 121 | } 122 | 123 | public DummyMapInterface getMap() { 124 | return new DummyMapImpl(); 125 | } 126 | 127 | public Integer getNullInteger() { 128 | return null; 129 | } 130 | 131 | public String getTheString(MyBean myBean) { 132 | return myBean.getSomeString(); 133 | } 134 | 135 | public Integer getTheInteger(MyBean myBean) { 136 | return myBean.getSomeInteger(); 137 | } 138 | 139 | public Future getStringWithFuture(String string) { 140 | return executor.submit(() -> string); 141 | } 142 | 143 | public Future getErrorWithFuture(String message) { 144 | return executor.submit(() -> { 145 | throw new InvocationException(message); 146 | }); 147 | } 148 | 149 | public Future executeVoidFuture(String message) { 150 | return executor.submit(() -> { 151 | return null; 152 | }); 153 | } 154 | 155 | public static Future getErrorWithFutureStatic(String string) { 156 | return executor.submit(() -> string); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /test-resources/java/src/main/resources/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Built-By: astonbitecode 2 | url: https://github.com/astonbitecode/j4rs 3 | --------------------------------------------------------------------------------