I want to take use of the reflection in clojure.
Suppose there is a part of java code
import com.example.SampleClass;
import java.lang.reflect.Field;
public class Sample {
public static void main(String[] args) {
Field field = SampleClass.class.getDeclaredField("someFiledName");
}
}
I can not find how to express the meaning of SampleClass.class where there is not any instance of SampleClass. It is just imported so I can not use (.class SampleClass) or (.getClass SampleClass)
The class itself is the class - only Java, the language, needs .class
to get to that.
So you import the class and then you can call your function on it.
user=> (import 'java.lang.Math) ; that's not really needed
java.lang.Math
user=> (.getDeclaredField java.lang.Math "PI")
#object[java.lang.reflect.Field 0x74ad8d05 "public static final double java.lang.Math.PI"]
Related
I can do the following and it works...
=> (. java.awt.event.KeyEvent getKeyText 10)
"Enter"
But, I have an instance of java.awt.event.KeyEvent called ev. For example,
=> (class ev)
java.awt.event.KeyEvent
I want to call the method like this instead (but, this produces an error):
=> (. (class ev) getKeyText 10)
No matching method getKeyText found taking 1 args for class java.lang.Class
Is it possible to call a static method from an instance?
I have read the docs and searched stack overflow. The question here is not the same.
Just like in Java, you can only call static methods directly if you know at compile time what method of what class you want to call. Otherwise you need to use reflection on the Class object to find method handles.
Here is an attempt using the MethodHandle API.
(ns playground.static-method-handle
(:import [java.lang.invoke MethodType MethodHandles]))
;; Get a MethodType instance that encodes the shape of the method (return type and arguments)
(def getKeyText-method-type (MethodType/methodType String Integer/TYPE))
(defn call-getKeyText-on-class [cl key-code]
(let [lu (MethodHandles/lookup)
h (.findStatic lu cl "getKeyText" getKeyText-method-type)]
(.invokeWithArguments h [(int key-code)])))
and we use it like this:
(call-getKeyText-on-class KeyEvent 10)
;; => "Enter"
I am attempting to use the gcloud library.
(ns firengine.state
(:import
[com.google.cloud AuthCredentials]
[com.google.cloud.datastore DatastoreOptions]))
(-> (DatastoreOptions/builder)
(.projectId "<project_id>")
(.authCredentials
(AuthCredentials/createForJson
(clojure.java.io/input-stream service-account-path)))
.build)
The above clojure code is translated from the following code snippet (ellided, click on "Run elsewhere").
import com.google.cloud.AuthCredentials;
import com.google.cloud.datastore.DatastoreOptions;
DatastoreOptions options = DatastoreOptions.builder()
.projectId(PROJECT_ID)
.authCredentials(AuthCredentials.createForJson(
new FileInputStream(PATH_TO_JSON_KEY))).build();
When I call this code from the Clojure REPL, I get the following error.
Unhandled java.lang.IllegalArgumentException
Can't call public method of non-public class: public
com.google.cloud.ServiceOptions$Builder
com.google.cloud.ServiceOptions$Builder.projectId(java.lang.String)
Reflector.java: 88 clojure.lang.Reflector/invokeMatchingMethod
Reflector.java: 28 clojure.lang.Reflector/invokeInstanceMethod
boot.user4590132375374459695.clj: 168 firengine.state/eval17529
boot.user4590132375374459695.clj: 167 firengine.state/eval17529
Compiler.java: 6927 clojure.lang.Compiler/eval
... elided ...
The com.google.cloud.datastore.DatastoreOptions code can be found here.
Updated June 29, 2016:
Pursuant to Schlomi's advice below, I thought that maybe if I AOT compiled my own wrapper around DatastoreOptions that it would work.
(ns firengine.datastore
(:import
[com.google.cloud AuthCredentials]
[com.google.cloud.datastore Datastore DatastoreOptions Entity Key KeyFactory])
(:gen-class
:state state
:init init
:constructors {[String String] []}))
(defn -init
[^String project-id ^String service-account-path]
(let [service-account (clojure.java.io/input-stream service-account-path)
credentials (AuthCredentials/createForJson service-account)
dsoptions (-> (DatastoreOptions/builder)
(.projectId project-id)
(.authCredentials credentials)
.build)]
[[] {:project-id project-id
:service-account-path service-account-path
:datastore-options dsoptions}]))
I modified my boot development task to include the following:
(deftask development
"Launch Immediate Feedback Development Environment"
[]
(comp
(aot :namespace '#{firengine.datastore})
(repl :port 6800)
(reload)
(watch)
(cljs)
(target :dir #{"target"})))
And I attempted to construct the object like so:
(def service-account-path (System/getenv "FIRENGINE_SERVICE_ACCOUNT_PATH"))
(def project-id (System/getenv "PROJECT_ID"))
(def datastore-options (firengine.datastore. project-id service-account-path))
Unfortunately, I still get the same error?
clojure.lang.Compiler$CompilerException: java.lang.reflect.InvocationTargetException, compiling:(state.clj:15:1)
java.lang.reflect.InvocationTargetException:
java.lang.IllegalArgumentException: Can't call public method of non-public class: public com.google.cloud.ServiceOptions$Builder com.google.cloud.ServiceOptions$Builder.projectId(java.lang.String)
Am I not really aot compiling firengine.datastore?
Yeah.... that problem. You wouldnt believe it, but its actually an open bug in the jdk from ... wait for it ... 1999!
You can read about it more in clojure jira and on google groups.
You might have to make your own java wrapper to avoid this, or ask the library author to take this old known java bug into consideration.
If you dont want to write your own java wrapper, and the author insists that "this is the best design, like, ever!", then you could always force it by setting the method accessibility with (.setAccessible method true) and some more custom reflection code..
Good luck!
As Shlomi said, that's a long running bug. Following the advice of Noam Ben Ari on the clj-jira, I've managed the circumvent the issue by writing a small java class that wraps the client creation. I can then use that directly from my clj code.
package pubsub_echo.pubsub;
import com.google.cloud.AuthCredentials;
import com.google.cloud.pubsub.PubSub;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class GCloudPubSub {
public PubSub getClient() throws FileNotFoundException, IOException {
PubSubOptions.Builder optionsBuilder = PubSubOptions.builder();
ClassLoader classLoader = getClass().getClassLoader();
FileInputStream authStream = new FileInputStream(classLoader.getResource("SERVICE_ACCOUNT.json").getPath());
AuthCredentials creds = AuthCredentials.createForJson(authStream);
return optionsBuilder
.authCredentials(creds)
.projectId("PROJECT_ID")
.build()
.service();
}
}
For guidance on adding Java compilation to your project:
https://github.com/technomancy/leiningen/blob/master/doc/MIXED_PROJECTS.md
so, i am a complete Clojure noob and ran into a similar error using Caffeine Cache: "IllegalArgumentException Can't call public method of non-public class: public default void com.github.benmanes.caffeine.cache.LocalManualCache.put(java.lang.Object,java.lang.Object) clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:88)"
my original function signature was this:
(defn get
[cache key]
(.getIfPresent cache key)
)
and i think the issue is that Clojure could not figure out where to apply Cache's .getIfPresent function. Adding the type fixed it:
(defn get
[^Cache cache key]
(.getIfPresent cache key)
)
Even though it's not a direct answer and your question went over my head, I hope this helps.
Completely inspired by hironroy's answer, I worked through an isolated example of getting this working. Created the file below in src/gcloud/GcloduDatastore.java in the following github project.
package gcloud;
import com.google.cloud.AuthCredentials;
import com.google.cloud.datastore.Datastore;
import com.google.cloud.datastore.DatastoreOptions;
import com.google.cloud.datastore.DatastoreOptions.Builder;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class GCloudDatastore {
public static Datastore getDatastore() throws FileNotFoundException, IOException {
DatastoreOptions.Builder optionsBuilder = DatastoreOptions.builder();
FileInputStream authStream = new FileInputStream(System.getenv("SERVICE_ACCOUNT_DOT_JSON_PATH"));
AuthCredentials creds = AuthCredentials.createForJson(authStream);
return optionsBuilder
.authCredentials(creds)
.projectId(System.getenv("PROJECT_ID"))
.build()
.service();
}
}
I know I can declare a reusable pure Scala block like this in a template:
#title(text: String) = #{
text.split(' ').map(_.capitalize).mkString(" ")
}
I can now call #title("someString") in the template but this code block is not accessible from outside this template.
How can I declare such a block that is accessible from other templates as well?
I've tried to create a new template title.scala.html like this:
#(text : String)
#{
text.split(' ').map(_.capitalize).mkString(" ")
}
I can now call #title("someString") from any template I want, but this doesn't give me the exact same result as the first block, inside the template (I assume in the first case it returns a String whereas it returns Html in the second case).
I'm using Play framework 2.0.4 and I'm coding in Java (hence my limited Scala knowledge).
Using tags is targeted for building reusable blocks of HTML code, therefore it returns Html
To work easily with common types of data you can easily add a custom Java class (for an example in freshly created utils package (in app directory), and prepare in it all required formatters as a static methods:
utils.MyFormats.java:
package utils;
import org.apache.commons.lang3.text.WordUtils;
public class MyFormats {
public static String capitalize(String str) {
return WordUtils.capitalize(str);
}
public static int sumElements(int a, int b) {
return a + b;
}
}
In template:
<h2>Capitalized each word: #(utils.MyFormats.capitalize("foo bar"))</h2>
<h3>Sum of two integers, 2+3 = #(utils.MyFormats.sumElements(2, 3))</h3>
How can I start a Clojure REPL from my java application and provide some "predefined variables" (I´m a Clojure newbie, I guess there´s a better term for that)? In fact I already tried to make it work by coincidence... I started with clojure.main and added an additional call to RT.var(). Is this the correct way to do it?
import clojure.lang.Symbol;
import clojure.lang.Var;
import clojure.lang.RT;
public class MyClojure {
final static private Symbol CLOJURE_MAIN = Symbol.intern("clojure.main");
final static private Var REQUIRE = RT.var("clojure.core", "require");
final static private Var MAIN = RT.var("clojure.main", "main");
// Provide some context, this is my custimisation...
#SuppressWarnings("unused")
final static private Var Test = RT.var("testme", "myvar", "This is the initial value");
public static void main(String[] args) throws Exception {
REQUIRE.invoke(CLOJURE_MAIN);
MAIN.applyTo(RT.seq(args));
}
}
EDIT: My application is not a web application. It´s a standalone desktop application that basically displays/edits a pojo data model. I now want to add support to expose the data model from the running application to a clojure repl. I would then be able to modify/inspect the data model thrugh both, the original application and the repl.
you're looking for the clojure.contrib/server-socket's create-repl-server function:
here is an excerpt from the examples:
(use '[clojure.contrib.server-socket :only [create-repl-server]])
(gen-class
:name "org.project.ReplServer"
:implements [javax.servlet.ServletContextListener])
(defn -contextInitialized [this e]
(.start (Thread. (partial create-repl-server 11111))))
there are also a lot of ideas on this SO question
I want to write the following in Clojure, but I can't figure it out for the life of me:
new TouchCommand() {
#Override
public void itemTouched(TouchMenuItem selectedItem) {
}
I tried:
(reify com.vaadin.touchkit.TouchMenu$TouchCommand
(itemTouched [^com.vaadin.touchkit.TouchMenu$TouchMenuItem item]))
but it returns:
java.lang.IllegalArgumentException: Can't define method not in interfaces: itemTouched
even though "itemTouched" does exist in the interface. Can anyone help?
I haven't done extensive Java interop with Clojure so this might be wrong, but how about
(proxy [TouchCommand] []
(itemTouched [selectedItem]
(.. (getParent)
(navigateTo
(UiBasics.)))))