how to use the bean function in clojure - clojure

I don't really understand how the bean function works when I am using it on beans. This code here throws an exception:
(import java.lang.management.ManagementFactory)
(def runtime (bean (ManagementFactory/getRuntimeMXBean))
(:name runtime)
;; =>
Class clojure.core$bean$fn__5177$fn__5178 can not access a member of class sun.management.RuntimeImpl with modifiers "public"
[Thrown class java.lang.IllegalAccessException]
but the class does have a method called getName(). http://docs.oracle.com/javase/6/docs/api/java/lang/management/RuntimeMXBean.html

You must have missed it, it's right there.
getName() Returns the name representing the runn
(import java.lang.management.ManagementFactory)
(def runtime (bean (ManagementFactory/getRuntimeMXBean))
(:name runtime)
;; =>
Class clojure.core$bean$fn_5177$fn_5178 can not access a member of class sun.management.RuntimeImpl with modifiers "public"
[Thrown class java.lang.IllegalAccessException]
but the class does have a method called getName(). http://docs.oracle.com/javase/6/docs/api/java/lang/management/RuntimeMXBean.html
ing Java virtual machine.
Edit1:
This issue is very similar to one here and response to it is here.
Class RuntimeImpl returned by ManagementFactory.getRuntimeMXBean is only package visible but it implements public visible interface RuntimeMXBean. So what happens is that function bean tries to call a method on the class RuntimImpl but it can't as class is only package visible. I think if it tried to call getMethod on interface RuntimeMXBean it would have worked.
Yeap this works:
(def mx-bean (ManagementFactory/getRuntimeMXBean))
(def interface-method
(.getMethod RuntimeMXBean "getName" (into-array java.lang.Class [])))
(.invoke interface-method mx-bean (into-array []))
I'm not sure if it's a bug or feature. I would recommend asking on Clojure mailing list.

I think this is captured by a bug right here: http://dev.clojure.org/jira/browse/CLJ-978?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
Upvote it!
You can roll-your-own bean function using the available patch.

Related

Creating new Java object instantiation error

I am trying to create a new SimpleLinkResolver in Clojure. This is the JavaDoc:
http://prismicio.github.io/java-kit/io/prismic/SimpleLinkResolver.html
My clojure code is:
(def lr (new io.prismic.SimpleLinkResolver))
but at the repl I get the following error:
CompilerException java.lang.InstantiationException, compiling:(form-init460449823042827832.clj:1:1)
I have no problem creating a java.util.Date:
(def d (new java.util.Date))
=> #'prismic-clojure.core/d
d
=> #inst"2018-03-17T10:30:36.016-00:00"
The above JavaDoc does say that SimpleLinkResolver is deprecated because the interface LinkResolver (http://prismicio.github.io/java-kit/io/prismic/LinkResolver.html) has default methods and so can be implemented directly. So I gave this a go to:
(def lr (new io.prismic.LinkResolver))
CompilerException java.lang.IllegalArgumentException: No matching ctor found for interface io.prismic.LinkResolver,
And I get this "no ctor" error - which I am guessing means the compiler can't find a constructor?
Questions:
Why does the first effort produce an InstantiationException?
Not being familiar with the Java-8 default methods, how would I create a new LinkResolver using its default methods?
Thanks
Why does the first effort produce an InstantiationException?
You can't instantiate an abstract class:
public abstract class SimpleLinkResolver
Not being familiar with the Java-8 default methods, how would I create a new LinkResolver using its default methods?
You'll need to implement LinkResolver interface, which can be done using Clojure's reify:
(def resolver
(reify LinkResolver
(^String resolve [this ^Fragment$DocumentLink link]
"a string"))) ;; put actual impl. here
(.resolve resolver nil)
;; => "a string"
Note you need to type-hint the return value (and arguments) because .resolve() is an overloaded method.
Also, you typically see (Class.) dot-syntax rather than (new Class) to instantiate Java classes.

Clojure: Is it possible to call package protected methods of java?

I am not able to find any documentation (or blogs) regarding this. Is it possible to call package protected methods from clojure? We have a huge java code base and I was thinking if its possible to start developing some clojure libraries around them.
I tried making the namespace same as that of the package in java but the method (static) was not found.
Here's a quick code example:
(def a (ref 0))
(def klass (class a))
(def m (.getDeclaredMethod klass "currentVal" (into-array Class [])))
(.setAccessible m true)
(.invoke m a (into-array []))
You should probably have a look at the reflect API
Although, I would recommend only using the publicly declared fields and methods from your old java code base.

Clojure reify a Java interface with overloaded methods

I'm trying to implement the following Java interface in Clojure:
package quickfix;
public interface MessageFactory {
Message create(String beginString, String msgType);
Group create(String beginString, String msgType, int correspondingFieldID);
}
The following Clojure code is my attempt at doing this:
(defn -create-message-factory
[]
(reify quickfix.MessageFactory
(create [beginString msgType]
nil)
(create [beginString msgType correspondingFieldID]
nil)))
This fails to compile with the error:
java.lang.IllegalArgumentException: Can't define method not in interfaces: create
The documentation suggests overloaded interface methods are ok, so long as the arity is different as it is in this case:
If a method is overloaded in a protocol/interface, multiple
independent method definitions must be supplied. If overloaded with
same arity in an interface you must specify complete hints to
disambiguate - a missing hint implies Object.
How can I get this working?
You're missing a parameter. The first parameter of every method implemented by reify is the object itself (as is the case with defrecord/deftype). So, try this:
(defn -create-message-factory
[]
(reify quickfix.MessageFactory
(create [this beginString msgType]
nil)
(create [this beginString msgType correspondingFieldID]
nil)))

How to add annotations to the reified class in Clojure?

I need to create a class which implements an interface and also has an annotation, and later pass an instance of this class to some API implemented in Java.
With Clojure, I could reify the interface and get an instance like this:
(reify MyInterface (method1 [this] ...))
However, reify doesn't seem to accept annotations. How may I work around this?
First of all reify returns an object not a class. If you need to generate a class with annotations, you will need to use gen-class as shown below (the example shows adding a Deprecated annotation to the class):
(gen-class :name ^{java.lang.Deprecated true} MyClass
...)
Also, you can use deftype:
(deftype ^{java.lang.Deprecated true} MyClass
...)

clojure: Can't access non-public fields in the same package

In my clojure program I cannot access package scoped fields of the java class com.foo.Foo although I am in the namespace "com.foo" (via "(ns com.foo)" at the top of my clojure program). However, public fields in com.foo.Foo are accessible.
Why?
Two problems here:
First, the namespace com.foo is compiled to a class foo_whatever in package com; it's not compiled to a class in package com.foo.
See:
user> (ns com.foo)
nil
com.foo>
nil
com.foo> (defn hello[] "hello !")
#'com.foo/hello
com.foo> (class hello)
com.foo$hello
com.foo> (ns com.foo.hello)
nil
com.foo.hello> (defn hi[] "hi !")
#'com.foo.hello/hi
com.foo.hello> (class hi)
com.foo.hello$hi
Second, when looking for constructors or fields, the Clojure compiler uses methods getConstructor and getFields from java.lang.Class, which, by spec, only return the public constructors and public fields.
So, bad luck here. It seems you won't be able to access package-protected fields.
Edit, answering comments. The best approach for accessing package-level fields in legacy Java code would be to write a class in Java that wraps the existing class and which exposes the package protected methods and fields from that class with public methods. This way, you have more control over the name and package of the generated class.
This wrapper is a small amount of Java code, and from there you can access the fields from Clojure code.