Fork me on GitHub
#clojure
<
2023-02-18
>
grzm03:02:21

hey y'all. I'm trying to call a Java private static method and getting an "argument type mismatch" error. Here's the source code of the function: https://github.com/aws/aws-sdk-java-v2/blob/584ccb59e770177aeaa4c3b6bda4e24015b8ece9/services/sqs/src/main/java/software/amazon/awssdk/services/sqs/internal/MessageMD5ChecksumInterceptor.java#L223 And my investigation:

(ns com.grzm.x.sqs.md5
  (:import
   (com.amazonaws.services.sqs MessageMD5ChecksumHandler)
   (com.amazonaws.services.sqs.model MessageAttributeValue)))

(def msg-attr-val (doto (MessageAttributeValue.)
                    (.setStringValue "some-val")))

@(def msg-attrs {"some-key" msg-attr-val})
;; => {"some-key"
;;     #object[com.amazonaws.services.sqs.model.MessageAttributeValue 0x844951e "{StringValue: some-val,StringListValues: [],BinaryListValues: [],}"]}

@(def juh-msg-attrs (java.util.HashMap. msg-attrs))
;; => #'com.grzm.x.sqs.md5/juh-msg-attrs

(def meths (->> (.getDeclaredMethods MessageMD5ChecksumHandler)
                (filter (fn [m] (= (.getName m) "calculateMessageAttributesMd5")))))

# confirm there's only one, so I can't be accidentally choosing the wrong one
(count meths)
;; => 1

@(def calc-md5 (first meths))
;; => #object[java.lang.reflect.Method 0x55338250 "private static java.lang.String com.amazonaws.services.sqs.MessageMD5ChecksumHandler.calculateMessageAttributesMd5(java.util.Map)"]

(.setAccessible calc-md5 true)

(.invoke calc-md5 MessageMD5ChecksumHandler (into-array Object juh-msg-attrs))
;; => Execution error (IllegalArgumentException) at jdk.internal.reflect.NativeMethodAccessorImpl/invoke0 (NativeMethodAccessorImpl.java:-2).
;;    argument type mismatch

grzm03:02:37

What am I likely doing wrong?

grzm03:02:10

I did try it first with just a normal Clojure map (not casting it to a java.util.HashMap), but with the same result, which is why I ended up trying the cast version, too:

(.invoke calc-md5 MessageMD5ChecksumHandler (into-array Object msg-attrs))
;; => Execution error (IllegalArgumentException) at jdk.internal.reflect.NativeMethodAccessorImpl/invoke0 (NativeMethodAccessorImpl.java:-2).
;;    argument type mismatch

hiredman03:02:21

There is no casting

hiredman03:02:21

I would suggest a closer reading of into-array's docstring

hiredman03:02:15

I'd also suggest looking at the entire stacktrace

grzm03:02:42

I haven't found the stack trace very enlightening.

NativeMethodAccessorImpl.java:   -2  jdk.internal.reflect.NativeMethodAccessorImpl/invoke0
NativeMethodAccessorImpl.java:   77  jdk.internal.reflect.NativeMethodAccessorImpl/invoke
DelegatingMethodAccessorImpl.java:   43  jdk.internal.reflect.DelegatingMethodAccessorImpl/invoke
               Method.java:  568  java.lang.reflect.Method/invoke
NativeMethodAccessorImpl.java:   -2  jdk.internal.reflect.NativeMethodAccessorImpl/invoke0
NativeMethodAccessorImpl.java:   77  jdk.internal.reflect.NativeMethodAccessorImpl/invoke
DelegatingMethodAccessorImpl.java:   43  jdk.internal.reflect.DelegatingMethodAccessorImpl/invoke
               Method.java:  568  java.lang.reflect.Method/invoke
            Reflector.java:  167  clojure.lang.Reflector/invokeMatchingMethod
            Reflector.java:  102  clojure.lang.Reflector/invokeInstanceMethod
                      REPL:   35  com.grzm.x.sqs.md5/eval24704

grzm03:02:32

Are you suggesting that my use of Object with into-array rather than a more specific type is the issue?

hiredman03:02:12

user=> (seq (into-array Object (doto (java.util.HashMap.) (.put 1 1))))
(#object[java.util.HashMap$Node 0x78c1a023 "1=1"])
user=>

grzm03:02:54

I'll straight up admit my ignorance of more than straightforward java-interop. My understanding is that .invoke is a varargs function, so I should be using an array to pass the argument list to in in Clojure, thus the use of into-array. Is this wrong?

grzm04:02:47

Looking at the docs a bit more closely, looks like I should be using a sequence as the second argument to into-array. This is looking promising: at least it's a different error:

(.invoke calc-md5 MessageMD5ChecksumHandler (into-array Object [msg-attrs]))
;; => Execution error (NullPointerException) at com.amazonaws.services.sqs.MessageMD5ChecksumHandler/updateLengthAndBytes (MessageMD5ChecksumHandler.java:270).
;;    Cannot invoke "String.getBytes(java.nio.charset.Charset)" because "str" is null

Victor Vollbrecht11:02:21

Hello, if you had to select a Clojure GUI library to create a touchscreen + raspberry pi + Zulu Embedded OpenJDK to allow neighbors to interact with it, which one would you choose? basic GUI stuff, big buttons, some data input but nothing crazy

phronmophobic18:02:33

Which touch screen are you using? Do you have an example of what it looks like to draw to the touch screen? In theory, this use case should be a good fit for my library, https://github.com/phronmophobic/membrane. You would have to write a "graphics backend" (ie. some glue code for graphics and events), but after that, you should be able to reuse everything else. Creating a graphics backend isn't really documented, but I can help you get setup if that's something you're interested in.

thom22:02:55

Does Zulu Embedded preclude JavaFX? I was impressed with my one project on top of #CGHHJNENB but it might seem alien if you’ve not done much React (or really re-frame) stuff.

Rupert (All Street)11:02:13

If you need something simple to work with but not too fancy then maybe use: https://github.com/clj-commons/seesaw. It uses Swing so the compatibility with most java versions should be very high. Otherwise an option is to start a webserver (ring + hiccup + compojure) and show an HTML form in a web browser.