4

Trying to do some java interop with https://github.com/googleapis/google-cloud-java/blob/master/google-cloud-clients/google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java#L139 - the create method with a byte array.

I have in my repl:

user=> (->> s r/reflect :members 
               (filter #(instance? clojure.reflect.Method %)) 
               (filter #(:public (:flags %))) 
               (filter #(= (str (:name %)) "create")) 
               (print-table [:name :flags :parameter-types]))

|  :name |              :flags |                                                                                           :parameter-types |
|--------+---------------------+------------------------------------------------------------------------------------------------------------|
| create | #{:varargs :public} |             [com.google.cloud.storage.BlobInfo byte<> com.google.cloud.storage.Storage$BlobTargetOption<>] |

(There are others, but this appears the most relevant.)

Also:

user=> s
#object[com.google.cloud.storage.StorageImpl 0x57fb59c8 "com.google.cloud.storage.StorageImpl@57fb59c8"]
user=> blob-info
#object[com.google.cloud.storage.BlobInfo$BuilderImpl 0x1e8ce729 "com.google.cloud.storage.BlobInfo$BuilderImpl@1e8ce729"]
user=> b
#whidbey/bin "SEVZIE1ZIEdVWQ==“

But when I go to call .create, I get:

user=> (.create s blob-info (bytes b))

java.lang.IllegalArgumentException: No matching method create found taking 2 args for class com.google.cloud.storage.StorageImpl

If I try to add nil as the 3rd argument, I get the same error with 3 args.

Am I missing something obvious here? Thanks!

Edit: How to handle java variable length arguments in clojure? was very similar, and more generic (which is good). This one wound up being a specific question about the one particular create function signature.

Hoopes
  • 3,943
  • 4
  • 44
  • 60
  • Possible duplicate of [How to handle java variable length arguments in clojure?](https://stackoverflow.com/questions/11702184/how-to-handle-java-variable-length-arguments-in-clojure) – cfrick Feb 25 '19 at 10:06
  • The function you liked there does not use two args, but three. You can call that with just two args in java, because the compiler takes care. The JVM always takes a third argument; an array of that type, even if empty – cfrick Feb 25 '19 at 10:08

2 Answers2

4

The answer wound up being (from seancorfield on the clojurians slack) that the blob-info was a BuilderImpl inner class and needed to be an actual BlobInfo. The code that works:

(defn get-storage []
  (-> (StorageOptions/getDefaultInstance)
      (.getService)))

(defn get-blob-info [bucket storage-key]
  (let [content-type "text/plain"
        blob-id (BlobId/of bucket storage-key)
        builder (doto
                  (BlobInfo/newBuilder blob-id)
                  (.setContentType content-type))]

    (.build builder)))

(defn upload-str [bucket storage-key str-to-store]
  (let [storage (get-storage)
        blob-info (get-blob-info bucket storage-key)
        byte-arr (.getBytes str-to-store)]
    (.create storage
             blob-info
             byte-arr
             (into-array Storage$BlobTargetOption []))))

No type hinting wound up being required - just needed the types to line up correctly.

Hoopes
  • 3,943
  • 4
  • 44
  • 60
1

I'm not sure (bytes b) is the right syntax (what is #whidbey/bin ???).

Maybe try

(byte-array [1 2 3]) 

or similar. You could also try type-hinting the param:

(.create s 
    blob-info
    ^"[B" (byte-array [1 2 3])    ; type-hinted param
)

Update

Here is an example of the type hinting that I think you need:

(let [byte-array-obj  (byte-array [1 2 3])
      sss             (java.util.Arrays/toString  ^"[B"  byte-array-obj) ]
    (spyxx byte-array-obj)
    (spyx (type byte-array-obj))
    (spyx (aget byte-array-obj 2))
    (spyx sss))

with result:

byte-array-obj => <#[B #object["[B" 0x26071f95 "[B@26071f95"]>
(type byte-array-obj) => [B
(aget byte-array-obj 2) => 3
sss => "[1, 2, 3]"

Note that Clojure has an easy way to type hint without resorting to the java-native "[B" string syntax:

(java.util.Arrays/toString ^"[B"  byte-array-obj)  ; type hint using a string
(java.util.Arrays/toString ^bytes byte-array-obj)  ; Clojure "build-in" type hint 

both of which are equivalent.

Alan Thompson
  • 29,276
  • 6
  • 41
  • 48
  • https://github.com/greglook/whidbey - it came with the repl i'm using from a docker container. Casting to byte-array did not do the trick. This seems to have something to do with clojure being able to find the function on the class by its signature, and I'm having trouble with the correct incantation to nail it down. – Hoopes Feb 25 '19 at 03:29