Here is another example of code that works for Java but fails on Clojure. The example class comes from the [org.opensearch.client/opensearch-java "2.1.0"]
library.
This Java code works without problems:
package example;
import org.opensearch.client.opensearch.core.bulk.IndexOperation;
import java.io.IOException;
import java.util.Map;
public class Example {
public static void main(String[] args) throws IOException {
new IndexOperation.Builder<Map<?, ?>>()
.index("test-index")
.id("test-id")
.document(Map.of("test-document", 123))
.build();
}
}
But the Clojure version cannot be written using Java interop. The workaround is to manually use reflection to call those methods:
(ns example
(:import (org.opensearch.client.opensearch.core.bulk IndexOperation$Builder)))
(defn bug []
(-> (IndexOperation$Builder.)
(.index "test-index")
(.id "test-id")
(.document {"test-document" 123})
(.build)))
(defn workaround []
(let [bulk-builder-index (-> (Class/forName "org.opensearch.client.opensearch.core.bulk.BulkOperationBase$AbstractBuilder")
(.getDeclaredMethod "index" (into-array Class [String])))
bulk-builder-id (-> (Class/forName "org.opensearch.client.opensearch.core.bulk.BulkOperationBase$AbstractBuilder")
(.getDeclaredMethod "id" (into-array Class [String])))]
(-> (IndexOperation$Builder.)
^IndexOperation$Builder ((fn [builder index]
(.invoke bulk-builder-index builder (into-array Object [index])))
"test-index")
^IndexOperation$Builder ((fn [builder id]
(.invoke bulk-builder-id builder (into-array Object [id])))
"test-id")
(.document {"test-document" 123})
(.build))))
(comment
(bug)
(workaround))
When this namespace is loaded with Clojure 1.11.1, it gives the following reflection warnings:
Reflection warning, .../example.clj:6:7 - call to method index on org.opensearch.client.opensearch.core.bulk.IndexOperation$Builder can't be resolved (argument types: java.lang.String).
Reflection warning, .../example.clj:7:7 - call to method id can't be resolved (target class is unknown).
Reflection warning, .../example.clj:8:7 - call to method document can't be resolved (target class is unknown).
Reflection warning, .../example.clj:9:7 - reference to field build can't be resolved.
And when the bug function is executed, it fails with the exception:
Execution error (IllegalArgumentException) at example/bug (example.clj:6).
Can't call public method of non-public class: public final org.opensearch.client.opensearch.core.bulk.BulkOperationBase$AbstractBuilder org.opensearch.client.opensearch.core.bulk.BulkOperationBase$AbstractBuilder.index(java.lang.String)
IndexOperation$Builder
is a public class, which extends the protected abstract class WriteOperation$AbstractBuilder
, which extends the protected abstract class BulkOperationBase$AbstractBuilder
. The index
and id
methods' return type is a class-level type parameter, but the method arguments are non-generic.
Here are the relevant places in the library code: