Strange behaviour for proxy when two abstract classes are passed in - clojure

I know this is a silly example but I am curious to know what is happening with the proxy method.
I have set up two calls to proxy:
1.
(def cp
(proxy [java.util.AbstractMap clojure.asm.ClassVisitor] []))
2.
(def cp
(proxy [clojure.asm.ClassVisitor java.util.AbstractMap] []))
The first call is fine.... and it return cp. The second call gives me an exception.
clojure.lang.Compiler$CompilerException: java.lang.IncompatibleClassChangeError: Implementing class, compiling:(/private/var/folders/dd/qfdy6sbn3mlgk20vcxc3j0ljnpxsqr/T/form-init4780219965491827451.clj:2:5)
java.lang.IncompatibleClassChangeError: Implementing class
java.lang.ClassLoader.defineClass1 ClassLoader.java
java.lang.ClassLoader.defineClass ClassLoader.java: 800
java.lang.ClassLoader.defineClass ClassLoader.java: 643
clojure.lang.DynamicClassLoader.defineClass DynamicClassLoader.java: 46
clojure.core/get-proxy-class core_proxy.clj: 262
What is happening underneath?

Neither will work in Clojure 1.6.0. In 1.5, clojure.asm.ClassVisitor was an interface instead of an abstract class. Proxy expects at most one class followed by optional interfaces. As java.util.AbstractMap is an abstract class, it cannot appear second in the list of class-and-interfaces.

Related

How can I create a new object from import class

I have imported the firestore snapshot and trying to create it's object
(:import [com.google.cloud.firestore
QueryDocumentSnapshot])
(def snapshot1 (QueryDocumentSnapshot.toObject. [:reference "user1" :type "Promotion" :included-scans 100]))
But it compile failed and error is :
Exception in thread "main" java.lang.ClassNotFoundException: QueryDocumentSnapshot.toObject,
Can you please help me to create a new object for that class QueryDocumentSnapshot ?
You can call a constructor like
(QueryDocumentSnapshot. whatever-arguments)
But QueryDocumentSnapshot has no public constructor and looking at the source it can only be instantiated using a static factory method like such:
(QueryDocumentSnapshot/fromDocument firestore timestamp document)
I'm not sure what you're actually trying to achieve here but it doesn't look like you can do what you think you can do to that class.
Okay, after taking your and Joost's answers into consideration, I discovered something rather funny:
Your error message says, that the class QueryDocumentSnapshot.toObject cannot be found. There you have it.
If you want to call a static method, you have to write (class/method args).
For more information on java interop I highly recommend the official documentation: https://clojure.org/reference/java_interop
Also, take Joost's comments on the class itself into consideration.

Clojure om next multiple print-method exception

I am trying to build server-side rendering for om.next (1.0.0-alpha47). At some point I have to create a reconciler from Clojure:
(om/reconciler {})
(om/reconciler
{:state (atom {})
:normalize true
:parser (om/parser {})})
However evaluating any of these in my REPL gives:
Unhandled java.lang.IllegalArgumentException Multiple methods in
multimethod 'print-method' match dispatch value: class
om.next.Reconciler -> interface clojure.lang.IDeref and interface
clojure.lang.IRecord, and neither is preferred
How do I fix that?
I came to this solution via trial and error:
(prefer-method print-method clojure.lang.IPersistentMap clojure.lang.IDeref)
This seems to solve the conflict, sorry that I can't explain any details.

Errors extending a Java Interface in Clojure

I have a basic Java interface defined as follows:
public interface Action {
void execute(Metadata var1, Parameter var2);
}
I'm trying to extend it in Clojure but keep getting errors. After importing the class into my namespace, I've tried using reify as follows:
(defn action [action-fn]
(reify Action
(execute [metadata parameter] (action-fn metadata parameter))))
but that throws a compiler illegal argument exception:
CompilerException java.lang.IllegalArgumentException: Can't define method not in interfaces: execute
Next I tried using proxy
(defn action [action-fn]
(proxy [Action] []
(execute [metadata parameter] (action-fn metadata parameter))))
That compiles successfully, and my editor (IntelliJ + Cursive) navigates to the interface definition via a border decoration, but trying to invoke execute on a generate proxy fails:
(.execute (action (fn [_ _] "Test action")))
throws the following:
IllegalArgumentException No matching field found: execute for class
Finally I tried using deftype as follows:
(deftype cljAction [action-fn]
Action
(execute [metadata parameter] (action-fn metadata parameter)))
which throws the same compiler error as for reify, e.g:
CompilerException java.lang.IllegalArgumentException: Can't define method not in interfaces: execute
Trawling through various blog posts and SO answers seems to suggest it's a problem with the arity of arguments, but I'm not sure how to resolve it. What am I doing wrong??
You are missing the this reference from the function. So what you want is this:
(defn action [action-fn]
(reify Action
(execute [this metadata parameter] (action-fn metadata parameter))))
Obviously because you are not using it you can just call it _ or whatever makes the most sense in your opinion. When you are calling the function you want this:
(.execute (action action-fn) metadata parameter)
This differs slightly from when you are implementing a protocol. See https://clojuredocs.org/clojure.core/definterface for more information.
ponzao's answer is correct. But note that Cursive can actually fill in the stubs for you: you can write (reify Action) and then (with the cursor in that form somewhere) choose Code->Generate... and choose to implement methods. Cursive will then fill in the stubs with the correct form. This currently only works when implementing interfaces, not protocols.

Clojure not recognizing parent classes

I'm writing using wsimport to generate a client for a web service, however, when I try to initiate the binding with an Addressing feature, I get the following error:
Exception in thread "main" java.lang.ClassCastException: javax.xml.ws.soap.AddressingFeature cannot be cast to [Ljavax.xml.ws.WebServiceFeature;
I know for a fact that javax.xml.ws.soap.AddressingFeature extends javax.xml.ws.WebServiceFeature, so I'm not sure what's happening. I know you cannot downcast in Clojure, but casting to a parent should work.
From my understanding, objects should be autocast, and if they are not, clojure.core/cast should work, however, both throw an exception.
My code looks something like this:
(-> (com.test.TestAPISOAP.)
(.getTestWSHttpBinding
(javax.xml.ws.soap.AddressingFeature. true true)))
It looks like .getTestWSHttpBinding accepts an array (the [L in [Ljavax.xml.ws.WebServiceFeature), not a single element.
Try creating an array with AddressingFeature as a single element using clojure.core/to-array:
(-> (com.test.TestAPISOAP.)
(.getTestWSHttpBinding
(to-array [(javax.xml.ws.soap.AddressingFeature. true true)])))

How do I call a non-public method of a public class in Clojure?

I'm calling the twitter4j library using Clojure like so:
(def twitter (. (TwitterFactory.) getInstance))
This works fine when I call it as a script. But when I use gen-class, I get:
java.lang.IllegalArgumentException: Can't call public method of non-public class: public java.lang.Object twitter4j.TwitterFactoryBase.getInstance()
Is there a workaround for this?
I have no experience with it myself, but Meikel Brandmeyer did a nice writeup on gen-class once, maybe that will help you:
http://kotka.de/blog/2010/02/gen-class_how_it_works_and_how_to_use_it.html
Try:
(def twitter (.getInstance (new TwitterFactory)))