j4rs/api/
instance.rs

1// Copyright 2022 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
15use crate::logger::debug;
16use crate::{cache, errors, jni_utils, InvocationArg, Jvm};
17use jni_sys::jobject;
18use serde::de::DeserializeOwned;
19use serde::Serialize;
20use std::any::Any;
21use std::convert::TryFrom;
22use std::sync::mpsc::{Receiver, Sender};
23
24/// A Java instance
25#[derive(Serialize)]
26pub struct Instance {
27    /// The name of the class of this instance
28    pub(crate) class_name: String,
29    /// The JNI jobject that manipulates this instance.
30    ///
31    /// This object is an instance of `org/astonbitecode/j4rs/api/Instance`
32    #[serde(skip)]
33    pub(crate) jinstance: jobject,
34    #[serde(skip)]
35    pub(crate) skip_deleting_jobject: bool,
36}
37
38impl Instance {
39    /// Creates a new Instance, leaving the passed jobject as is.
40    /// In most cases, the jobject is already transformed to a global reference.
41    pub(crate) fn new(obj: jobject, classname: &str) -> errors::Result<Instance> {
42        Ok(Instance {
43            jinstance: obj,
44            class_name: classname.to_string(),
45            skip_deleting_jobject: false,
46        })
47    }
48
49    /// Returns the class name of this instance
50    pub fn class_name(&self) -> &str {
51        self.class_name.as_ref()
52    }
53
54    /// Consumes the Instance and returns its jobject
55    pub fn java_object(mut self) -> jobject {
56        self.skip_deleting_jobject = true;
57        self.jinstance
58    }
59
60    #[deprecated(
61        since = "0.12.0",
62        note = "Please use Instance::from_jobject or Instance::from_jobject_with_global_ref instead"
63    )]
64    pub fn from(obj: jobject) -> errors::Result<Instance> {
65        let _jvm = cache::get_thread_local_env().map_err(|_| Jvm::attach_thread());
66
67        let global =
68            jni_utils::create_global_ref_from_local_ref(obj, cache::get_thread_local_env()?)?;
69        Ok(Instance {
70            jinstance: global,
71            class_name: cache::UNKNOWN_FOR_RUST.to_string(),
72            skip_deleting_jobject: false,
73        })
74    }
75
76    pub fn from_jobject(obj: jobject) -> errors::Result<Instance> {
77        let _jvm = cache::get_thread_local_env().map_err(|_| Jvm::attach_thread());
78
79        Ok(Instance {
80            jinstance: obj,
81            class_name: cache::UNKNOWN_FOR_RUST.to_string(),
82            skip_deleting_jobject: false,
83        })
84    }
85
86    pub fn from_jobject_with_global_ref(obj: jobject) -> errors::Result<Instance> {
87        let _jvm = cache::get_thread_local_env().map_err(|_| Jvm::attach_thread());
88
89        let global =
90            jni_utils::create_global_ref_from_local_ref(obj, cache::get_thread_local_env()?)?;
91        Ok(Instance {
92            jinstance: global,
93            class_name: cache::UNKNOWN_FOR_RUST.to_string(),
94            skip_deleting_jobject: false,
95        })
96    }
97
98    /// Creates a weak reference of this Instance.
99    fn _weak_ref(&self) -> errors::Result<Instance> {
100        Ok(Instance {
101            class_name: self.class_name.clone(),
102            jinstance: jni_utils::_create_weak_global_ref_from_global_ref(
103                self.jinstance.clone(),
104                cache::get_thread_local_env()?,
105            )?,
106            skip_deleting_jobject: false,
107        })
108    }
109}
110
111impl TryFrom<InvocationArg> for Instance {
112    type Error = errors::J4RsError;
113    fn try_from(invocation_arg: InvocationArg) -> errors::Result<Instance> {
114        let obj = invocation_arg.as_java_ptr_with_local_ref(cache::get_thread_local_env()?)?;
115        Instance::new(obj, invocation_arg.class_name())
116    }
117}
118
119impl Drop for Instance {
120    fn drop(&mut self) {
121        debug(&format!("Dropping an instance of {}", self.class_name));
122        if !self.skip_deleting_jobject {
123            if let Some(j_env) = cache::get_thread_local_env_opt() {
124                jni_utils::delete_java_ref(j_env, self.jinstance);
125            }
126        }
127    }
128}
129
130unsafe impl Send for Instance {}
131
132/// A receiver for Java Instances.
133///
134/// It keeps a channel Receiver to get callback Instances from the Java world
135/// and the address of a Box<Sender<Instance>> Box in the heap. This Box is used by Java to communicate
136/// asynchronously Instances to Rust.
137///
138/// On Drop, the InstanceReceiver removes the Box from the heap.
139pub struct InstanceReceiver {
140    pub(crate) rx: Box<Receiver<Instance>>,
141    tx_address: u64,
142}
143
144impl InstanceReceiver {
145    pub(crate) fn new(rx: Receiver<Instance>, tx_address: u64) -> InstanceReceiver {
146        InstanceReceiver {
147            rx: Box::new(rx),
148            tx_address,
149        }
150    }
151
152    pub fn rx(&self) -> &Receiver<Instance> {
153        &self.rx
154    }
155}
156
157impl Drop for InstanceReceiver {
158    fn drop(&mut self) {
159        if self.tx_address > 0 {
160            debug("Dropping an InstanceReceiver");
161            let p = self.tx_address as *mut Sender<Instance>;
162            unsafe {
163                let tx = Box::from_raw(p);
164                drop(tx);
165            }
166        }
167    }
168}
169
170/// Allows chained Jvm calls to created Instances
171pub struct ChainableInstance<'a> {
172    instance: Instance,
173    jvm: &'a Jvm,
174}
175
176impl<'a> ChainableInstance<'a> {
177    pub(crate) fn new(instance: Instance, jvm: &'a Jvm) -> ChainableInstance {
178        ChainableInstance { instance, jvm }
179    }
180
181    pub(crate) fn new_with_instance_ref(
182        instance: &Instance,
183        jvm: &'a Jvm,
184    ) -> errors::Result<ChainableInstance<'a>> {
185        let cloned = jvm.clone_instance(&instance)?;
186        Ok(ChainableInstance {
187            instance: cloned,
188            jvm,
189        })
190    }
191
192    pub fn collect(self) -> Instance {
193        self.instance
194    }
195
196    /// Invokes the method `method_name` of a this `Instance`, passing an array of `InvocationArg`s. It returns an `Instance` as the result of the invocation.
197    pub fn invoke(
198        &self,
199        method_name: &str,
200        inv_args: &[InvocationArg],
201    ) -> errors::Result<ChainableInstance> {
202        let instance = self.jvm.invoke(&self.instance, method_name, inv_args)?;
203        Ok(ChainableInstance::new(instance, self.jvm))
204    }
205
206    /// Creates a clone of the Instance
207    pub fn clone_instance(&self) -> errors::Result<ChainableInstance> {
208        let instance = self.jvm.clone_instance(&self.instance)?;
209        Ok(ChainableInstance::new(instance, self.jvm))
210    }
211
212    /// Invokes the static method `method_name` of the class `class_name`, passing an array of `InvocationArg`s. It returns an `Instance` as the result of the invocation.
213    pub fn cast(&self, to_class: &str) -> errors::Result<ChainableInstance> {
214        let instance = self.jvm.cast(&self.instance, to_class)?;
215        Ok(ChainableInstance::new(instance, self.jvm))
216    }
217
218    /// Retrieves the field `field_name` of the `Instance`.
219    pub fn field(&self, field_name: &str) -> errors::Result<ChainableInstance> {
220        let instance = self.jvm.field(&self.instance, field_name)?;
221        Ok(ChainableInstance::new(instance, self.jvm))
222    }
223
224    /// Returns the Rust representation of the provided instance
225    pub fn to_rust<T: Any>(self) -> errors::Result<T>
226    where
227        T: DeserializeOwned,
228    {
229        self.jvm.to_rust(self.instance)
230    }
231
232    /// Returns the Rust representation of the provided instance, boxed
233    pub fn to_rust_boxed<T: Any>(self) -> errors::Result<Box<T>>
234    where
235        T: DeserializeOwned,
236    {
237        self.jvm.to_rust_boxed(self.instance)
238    }
239}