Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(bindings/java): implement Operator#delete #2345

Merged
merged 5 commits into from
May 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bindings/java/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl Error {
&self,
env: &mut JNIEnv<'local>,
) -> jni::errors::Result<JThrowable<'local>> {
let class = env.find_class("org/apache/opendal/exception/OpenDALException")?;
let class = env.find_class("org/apache/opendal/OpenDALException")?;
let code = env.new_string(match self.inner.kind() {
ErrorKind::Unexpected => "Unexpected",
ErrorKind::Unsupported => "Unsupported",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
import java.util.Map;

/**
* A blocking operator represents an underneath OpenDAL operator that
* BlockingOperator represents an underneath OpenDAL operator that
* accesses data synchronously.
*/
public class BlockingOperator extends NativeObject {
/**
* Construct a blocking operator:
* Construct an OpenDAL blocking operator:
*
* <p>
* You can find all possible schemes <a href="https://docs.rs/opendal/latest/opendal/enum.Scheme.html">here</a>
Expand Down Expand Up @@ -67,5 +67,5 @@ public Metadata stat(String path) {

private static native void delete(long nativeHandle, String path);

private static native long stat(long nativeHandle, String file);
private static native long stat(long nativeHandle, String path);
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
import java.io.UncheckedIOException;
import java.util.Properties;

/**
* Environment resolves environment-specific project metadata.
*/
public enum Environment {
INSTANCE;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@

package org.apache.opendal;

/**
* Metadata carries all metadata associated with a path.
*/
public class Metadata extends NativeObject {
protected Metadata(long nativeHandle) {
super(nativeHandle);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,36 @@
import io.questdb.jar.jni.JarJniLoader;
import java.util.concurrent.atomic.AtomicReference;

/**
* NativeObject is the base-class of all OpenDAL classes that have
* a pointer to a native object.
*
* <p>
* NativeObject has the {@link NativeObject#close()} method, which frees its associated
* native object.
*
* <p>
* This function should be called manually, or even better, called implicitly using a
* <a href="https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html">try-with-resources</a>
* statement, when you are finished with the object. It is no longer called automatically
* during the regular Java GC process via {@link NativeObject#finalize()}.
*
* <p>
* <b>Explanatory note</b>
*
* <p>
* When or if the Garbage Collector calls {@link Object#finalize()}
* depends on the JVM implementation and system conditions, which the programmer
* cannot control. In addition, the GC cannot see through the native reference
* long member variable (which is the pointer value to the native object),
* and cannot know what other resources depend on it.
*
* <p>
* Finalization is deprecated and subject to removal in a future release.
* The use of finalization can lead to problems with security, performance,
* and reliability. See <a href="https://openjdk.org/jeps/421">JEP 421</a>
* for discussion and alternatives.
*/
public abstract class NativeObject implements AutoCloseable {

private enum LibraryState {
Expand Down Expand Up @@ -55,6 +85,10 @@ public static void loadLibrary() {
}
}

/**
* An immutable reference to the value of the underneath pointer pointing
* to some underlying native OpenDAL object.
*/
protected final long nativeHandle;

protected NativeObject(long nativeHandle) {
Expand All @@ -66,5 +100,10 @@ public void close() {
disposeInternal(nativeHandle);
}

/**
* Deletes underlying native object pointer.
*
* @param handle to the native object pointer
*/
protected abstract void disposeInternal(long handle);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@
* under the License.
*/

package org.apache.opendal.exception;
package org.apache.opendal;

/**
* A OpenDALException encapsulates the error of an operation. This exception
* An OpenDALException encapsulates the error of an operation. This exception
* type is used to describe an internal error from the native opendal library.
*/
public class OpenDALException extends RuntimeException {
private final Code code;

/**
* Construct an OpenDALException. This is called from JNI bindings code.
* Construct an OpenDALException. This constructor is called from native code.
*
* @param code string representation of the error code
* @param message error message
Expand Down
61 changes: 59 additions & 2 deletions bindings/java/src/main/java/org/apache/opendal/Operator.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,42 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

/**
* Operator represents an underneath OpenDAL operator that
* accesses data asynchronously.
*/
public class Operator extends NativeObject {
private static AsyncRegistry registry() {
return AsyncRegistry.INSTANCE;
}

/**
* Singleton to hold all outstanding futures.
*
* <p>
* This is a trick to avoid using global references to pass {@link CompletableFuture}
* among language boundary and between multiple native threads.
*
* @see <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#global_references">Global References</a>
* @see <a href="https://docs.rs/jni/latest/jni/objects/struct.GlobalRef.html">jni::objects::GlobalRef</a>
*/
private enum AsyncRegistry {
INSTANCE;

private final Map<Long, CompletableFuture<?>> registry = new ConcurrentHashMap<>();

@SuppressWarnings("unused") // called by jni-rs
/**
* Request a new {@link CompletableFuture} that is associated with a unique ID.
*
* <p>
* This method is called from native code. The return ID is used by:
*
* <li>Rust side: {@link #get(long)} the future when the native async op completed</li>
* <li>Java side: {@link #take(long)} the future to compose with more actions</li>
*
* @return the request ID associated to the obtained future
*/
@SuppressWarnings("unused")
private long requestId() {
final CompletableFuture<?> f = new CompletableFuture<>();
while (true) {
Expand All @@ -46,10 +71,25 @@ private long requestId() {
}
}

/**
* Get the future associated with the request ID.
*
* <p>
* This method is called from native code.
*
* @param requestId to identify the future
* @return the future associated with the request ID
*/
private CompletableFuture<?> get(long requestId) {
return registry.get(requestId);
}

/**
* Take the future associated with the request ID.
*
* @param requestId to identify the future
* @return the future associated with the request ID
*/
@SuppressWarnings("unchecked")
private <T> CompletableFuture<T> take(long requestId) {
final CompletableFuture<?> f = get(requestId);
Expand All @@ -60,6 +100,16 @@ private <T> CompletableFuture<T> take(long requestId) {
}
}

/**
* Construct an OpenDAL operator:
*
* <p>
* You can find all possible schemes <a href="https://docs.rs/opendal/latest/opendal/enum.Scheme.html">here</a>
* and see what config options each service supports.
*
* @param schema the name of the underneath service to access data from.
* @param map a map of properties to construct the underneath operator.
*/
public Operator(String schema, Map<String, String> map) {
super(constructor(schema, map));
}
Expand All @@ -80,6 +130,11 @@ public CompletableFuture<String> read(String path) {
return registry().take(requestId);
}

public CompletableFuture<Void> delete(String path) {
final long requestId = delete(nativeHandle, path);
return registry().take(requestId);
}

@Override
protected native void disposeInternal(long handle);

Expand All @@ -89,5 +144,7 @@ public CompletableFuture<String> read(String path) {

private static native long write(long nativeHandle, String path, String content);

private static native long stat(long nativeHandle, String file);
private static native long delete(long nativeHandle, String path);

private static native long stat(long nativeHandle, String path);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.opendal;
35 changes: 35 additions & 0 deletions bindings/java/src/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,41 @@ async fn do_read<'local>(op: &mut Operator, path: String) -> Result<JObject<'loc
Ok(result.into())
}

/// # Safety
///
/// This function should not be called before the Operator are ready.
#[no_mangle]
pub unsafe extern "system" fn Java_org_apache_opendal_Operator_delete(
mut env: JNIEnv,
_: JClass,
op: *mut Operator,
path: JString,
) -> jlong {
intern_delete(&mut env, op, path).unwrap_or_else(|e| {
e.throw(&mut env);
0
})
}

fn intern_delete(env: &mut JNIEnv, op: *mut Operator, path: JString) -> Result<jlong> {
let op = unsafe { &mut *op };
let id = request_id(env)?;

let path = env.get_string(&path)?.to_str()?.to_string();

let runtime = unsafe { RUNTIME.get_unchecked() };
runtime.spawn(async move {
let result = do_delete(op, path).await;
complete_future(id, result.map(|_| JValueOwned::Void))
});

Ok(id)
}

async fn do_delete(op: &mut Operator, path: String) -> Result<()> {
Ok(op.delete(&path).await?)
}

fn request_id(env: &mut JNIEnv) -> Result<jlong> {
let registry = env
.call_static_method(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import java.util.HashMap;
import java.util.Map;
import org.apache.opendal.exception.OpenDALException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.opendal;

import static org.assertj.core.api.Assertions.assertThat;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletionException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class OperatorTest {
private Operator op;

@BeforeEach
public void init() {
Map<String, String> params = new HashMap<>();
params.put("root", "/tmp");
this.op = new Operator("Memory", params);
}

@AfterEach
public void clean() {
this.op.close();
}

@Test
public void testCreateAndDelete() {
op.write("testCreateAndDelete", "Odin").join();
assertThat(op.read("testCreateAndDelete").join()).isEqualTo("Odin");
op.delete("testCreateAndDelete").join();
op.stat("testCreateAndDelete")
.handle((r, e) -> {
assertThat(r).isNull();
assertThat(e).isInstanceOf(CompletionException.class).hasCauseInstanceOf(OpenDALException.class);
OpenDALException.Code code = ((OpenDALException) e.getCause()).getCode();
assertThat(code).isEqualTo(OpenDALException.Code.NotFound);
return null;
})
.join();
}
}
2 changes: 1 addition & 1 deletion core/src/types/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use flagset::FlagSet;
use crate::raw::*;
use crate::*;

/// Metadata carries all metadata associated with an path.
/// Metadata carries all metadata associated with a path.
///
/// # Notes
///
Expand Down