I'm having an issue where I can see the fields of a Java class/object, but I can't actually access them. I can see the fields in two ways. Using this following code.
=>(require '[clojure.reflect :as r])
=>(use '[clojure.pprint :only [print-table]])
=>(print-table (sort-by :name (:members (r/reflect myClass))))
And also, by creating an instance of the object. Let's say the fields are an int denoted a, a string denoted word , and a String ArrayList denoted mylist.
=>myObj
#<myClass 1 hello [world]>
In both cases, I can see that these fields exist. However, when I run the following code, I get the following error.
=>(. myObj mylist)
IllegalArgumentException No matching field found: mylist for class myClass clojure.lang.Reflector.getInstanceField (Reflector.java:271)
Does anyone have any idea what is going on?
In response to Nicolas Modrzyk's answer, I run (.-myFieild myObject) and get
IllegalArgumentException No matching field found: myField for class myClass clojure.lang.Reflector.invokeNoArgInstanceMember (Reflector.java:308)
Additionally, these fields are not private. I have the Java source code in front of me.
The correct notation for accessing java fields is slightly different:
(.-fieldName instance)
Here is a full example with the java class File and its private field path:
(require '[clojure.reflect :as r])
(use '[clojure.pprint :only [print-table]])
(import '[java.io File])
(def f (File. "test.txt"))
; access a public static field
(. File separator)
; "/"
(print-table
(sort-by :name (:members (r/reflect File))))
(.-path f)
; java.lang.IllegalArgumentException:
; No matching field found: path for class java.io.File
(def field (.getDeclaredField File "path"))
; you need the below if the field is private
(.setAccessible field true)
; get the value
(.get field f)
; "test.txt"
Thumbnail's comment was correct. I wasn't aware of Java's default access being package. I have since accessed the fields through reflection. Thank you everyone for your help.
Related
I'm hitting an API that returns some json that I am trying to parse. I look a the keys of the returned parsed values and it says that it has a key "id", but when I try to get the id from the map, I get nothing back.
E.g.:
(require '[clj-http.client :as client]
'[cheshire.core :refer :all])
(def app-url "http://myapp.com/api/endpoint")
(defn get-application-ids [] (parse-string (:body (client/get app-url))))
(defn -main [] (println (map :id (get-application-ids))))
This returns (nil nil nil). Which, AFAIKT, it shouldn't - instead it should return the value of the :id field, which is not null.
Helpful facts:
When I run (map keys (get-application-ids)) to find the keys of the returned structure, I get ((id name attempts) (id name attempts) (id name attempts)).
(type (get-application-ids)) returns clojure.lang.LazySeq
(map type (get-application-ids)) returns (clojure.lang.PersistentArrayMap clojure.lang.PersistentArrayMap clojure.lang.PersistentArrayMap)
(println (get-application-ids)) returns (one example of the three returned):
({id application_1595586907236_1211, name myname, attempts [{appSparkVersion 2.4.0-cdh6.1.0, lastUpdated 2020-07-26T20:18:47.088GMT, completed true, lastUpdatedEpoch 1595794727088, sparkUser super, endTimeEpoch 1595794726804, startTime 2020-07-26T20:04:05.998GMT, attemptId 1, duration 880806, endTime 2020-07-26T20:18:46.804GMT, startTimeEpoch 1595793845998}]})
Everything about this tells me that (map :id (get-application-ids)) should return the value of the id field, but it doesn't. What am I missing?
It seems you are using cheshire.core/parse-string. This will return string keys, not keywords. See this example.
So, it appears that your key is the string "id", not the keyword :id. To verify this theory, try putting in the debugging statement:
(prn (:body (client/get app-url)))
To ask Cheshire to convert map keys from strings to keywords, use the form
(parse-string <json-src> true) ; `true` => output keyword keys
See also this list of documentation sources. Especially study the Clojure CheatSheet daily.
I'm new to Clojure and I've been messed up with ^ in Clojure
I'm currently reading clojure code of Jepsen which is used to test the consistency of distributed database.
You can find the code here.
In row 50 there is a ^MongoDatabase. Or:
(defn ^MongoCollection collection
"Gets a Mongo collection from a DB."
[^MongoDatabase db collection-name]
(.getCollection db collection-name))
I have no idea what it is because ^MongoDatabase or MongoCollection is never used in this function.
Can anyone give me some help. Thanks a lot.
In this context, that's a type hint saying collection should return a MongoCollection instance and db arg should be a MongoDatabase instance. This is useful for performance reasons, to avoid unnecessary reflection.
See this guide for more.
Another use of ^ is for type hints. These are used to tell the compiler what type the value will be and allow it to perform type specific optimizations thus potentially making resultant code faster:
The cap symbol ^ is used in Clojure for two purposes.
The first one is for type hints. When declaring a function, you may mark arguments' types or the result value as follows:
(defn ^String concat-strings
[^String a ^String b]
(str a b))
Type hints help the compiler to perform some optimizations.
The second option of using cap is when declaring metadata. The metadata might be either a boolean flag or a map. For example:
(def ^:private secret "test")
Now the variable above is marked as private so it won't be available from other namespaces.
Here is a meta-map usage example:
(def ^{:private true
:doc "My super secret password"
:added "product-version"}
secret
"test")
Let's try to read the metadata for that variable:
(meta #'secret)
returns
{:private true,
:doc "My super secret password",
:added "product-version",
:line 70,
:column 7,
:file "*cider-repl localhost*",
:name secret,
:ns #namespace[user]}
Another point to beware of is that type hints can be deceptive (i.e. they have no "enforcement" or "warning" utility):
(defn foo [x]
(type x))
(defn bar [^String x]
(type x))
(foo "abc") => java.lang.String
(foo 123) => java.lang.Long
(bar "abc") => java.lang.String
(bar 123) => java.lang.Long
In general, I would avoid type hints as they are rarely necessary (unless dealing with low-level Java code).
According to Om Next's documentation:
query->ast
(om.next/query->ast '[(:foo {:bar 1})])
Given a query expression return the AST.
ast->query
(om.next/ast->query ast)
Given a query expression AST, unparse it into a query expression.
Question: Why would one need these functions? That is, why would one need to directly manipulate a query abstract syntax tree (which I'm assuming are clojure maps that represent a query tree, along with some meta data) in om next?
There are some scenarios where you need to manipulate the query ast directly. In remote parsing mode, the parser expects your read functions to return either {:remote-name true } or a (possibly modified) {:remote-name AST-node} (which comes in as :ast in env). Most often you'll have to modify the AST to restructure it or add some data.
Example 1:
You have a query: [{:widget {:list [:name :created]}}]
The :widget part is pure UI related, your server doesn't need to know it exists, it only cares/knows about the :list.
Basically you'll have to modify the AST in the parser:
(defmethod read :list
[{:keys [ast query state]} key _ ]
(let [st #state]
{:value (om/db->tree query (get st key) st)
:remote (assoc ast :query-root true)}))
If you use om/process-rootsin your send function, it'll pick up the :query-root out of the ast and rewrite the query from [{:widget {:list [:name :created]}}] to [{:list [:name :created]}].
Example 2:
Another example would be when you want to mutate something at a remote:
(defmethod mutate 'item/update
[{:keys [state ast]} key {:keys [id title]}]
{:remote (assoc ast :params {:data {:id id :title title })})
Here you need to explicitly tell Om to include the data you want to send in the AST. At your remote you then pick apart :data to update the title at the given id
Most of the time you won't use the functions you described in your questions directly. The env available in every method of the parser has the ast in it.
Something I stumbled on, while trying to use Compassus:
Let's say you have a complex union/join query that includes parametric sub-queries. Something like this:
`[({:foo/info
{:foo/header [:foo-id :name]
:foo/details [:id :description :title]}} {:foo-id ~'?foo-id
:foo-desc ~'?foo-desc})]
Now let's say you want to set parameters so on the server you can parse it with om/parser and see those params as 3rd argument of read dispatch. Of course it's possible to write a function that would find all necessary parameters in the query and set the values. That's not easy though, and as I said - imagine your queries can be quite complex.
So what you can do - is to modify ast, ast includes :children :params key. So let's say the actual values for :foo-id and :foo-desc are in the state atom under :route-params key:
(defn set-ast-params [children params]
"traverses given vector of `children' in an AST and sets `params`"
(mapv
(fn [c]
(let [ks (clojure.set/intersection (-> params keys set)
(-> c :params keys set))]
(update-in c [:params] #(merge % (select-keys params (vec ks))))))
children))
(defmethod readf :foo/info
[{:keys [state query ast] :as env} k params]
(let [{:keys [route-params] :as st} #state
ast' (-> ast
(update :children #(set-ast-params % route-params))
om/ast->query
om.next.impl.parser/expr->ast)]
{:value (get st k)
:remote ast'}))
So basically you are:
- grabbing ast
- modifying it with actual values
you think maybe you can send it to server right then. Alas, no! Not yet. Thing is - when you do {:remote ast}, Om takes :query part of the ast, composes ast out of it and then sends it to the server. So you actually need to: turn your modified ast into query and then convert it back to ast again.
Notes:
set-ast-params function in this example would only work for the first level (if you have nested parametrized queries - it won't work),
make it recursive - it's not difficult
there are two different ways to turn ast to query and vice-versa:
(om/ast->query) ;; retrieves query from ast and sets the params based
;; of `:params` key of the ast, BUT. it modifies the query,
;; if you have a join query it takes only the first item in it. e.g. :
[({:foo/foo [:id]
:bar/bar [:id]} {:id ~'?id})]
;; will lose its `:bar` part
(om.next.impl.parser/ast->expr) ;; retrieves query from an ast,
;; but doesn't set query params based on `:params` keys of the ast.
;; there are also
(om/query->ast) ;; and
(om.next.impl.parser/expr->ast)
I am trying to rewrite the neo4j sample code located here in clojure. But when I try to create a node, I get the following error
ClassCastException Cannot cast org.neo4j.graphdb.DynamicLabel to [Lorg.neo4j.graphdb.Label; java.lang.Class.cast (Class.java:3094)
Here is my code:
(ns neotest.handler
(:import (org.neo4j.graphdb
DynamicLabel
GraphDatabaseService
Label
Node
ResourceIterator
Transaction
factory.GraphDatabaseFactory
schema.IndexDefinition
schema.Schema)))
(def db
(let [path "C:\\Users\\xxx\\code\\neotest\\resources\\db1"]
(. (new GraphDatabaseFactory) (newEmbeddedDatabase path))))
(defn create-node []
(try (let [tx (. db beginTx)
l (. DynamicLabel (label "User"))]
(. db (createNode l))
(. tx success))))
I have tried type-hinting of all kinds and in all places, and I still get the same error.
It's because of the varargs Label... parameter. This was a bit of Clojure/Java interop I didn't know about: you have to pass the parameter in as an array (even if there's only one), so you need to do something like:
(. db (createNode (into-array Label [l])))
to make it work. There's another afternoon I won't be getting back!
the calls to dynamicLabel in the example java code look like:
DynamicLabel.label( "User" )
which would translate to:
(DynamicLabel/label "user")
because label is a static method of the class org.neo4j.graphdb.DynamicLabel which has the signature:
static Label label(String labelName)
I'm learning how to extend Java classes in Clojure, but I don't see a way declare new member variables; I only see a way for methods.
(ns test.aclass
(:gen-class
:methods [[foo [] String]]))
Is there a :members keyword or another way of declaring member variables?
:state name
If supplied, a public final instance field with the given name will be created. You must supply an :init function in order to provide a value for the state. Note that, though final, the state can be a ref or agent, supporting the creation of Java objects with transactional or asynchronous mutation semantics.
There is an example on the website of how it can be used.
I was having some trouble with this too. The example below is not elegant but it's pretty simple for writing dumb little glue classes in Clojure rather than Java. Note all I did for thread safety is to ensure that the field updates are atomic -- I didn't do any other concurrency stuff, and that may make a real difference.
The init method creates the instance variables for the object. The setfield and getfield macros abbreviate the bookkeeping of the atomic update.
(ns #^{:doc "A simple class with instance vars"
:author "David G. Durand"}
com.tizra.example )
(gen-class
:name com.tizra.example.Demo
:state state
:init init
:prefix "-"
:main false
:methods [[setLocation [String] void]
[getLocation [] String]]
)
(defn -init []
"store our fields as a hash"
[[] (atom {:location "default"})])
(defmacro setfield
[this key value]
`(swap! (.state ~this) into {~key ~value}))
(defmacro getfield
[this key]
`(#(.state ~this) ~key))
(defn -setLocation [this ^java.lang.String loc]
(setfield this :location loc))
(defn ^String -getLocation
[this]
(getfield this :location))
You have to compile this, and make sure the stub class produced is on your classpath, and then you can make instances, etc. just like any other java class.
=> (com.tizra.example.Demo.)
#<Demo com.tizra.example.Demo#673a95af>
=> (def ex (com.tizra.example.Demo.))
#'user/ex
=> (.getLocation ex)
"default"
=> (.setLocation ex "time")
nil
=> (.getLocation ex)
"time"
I found the longer summary at this blog quite helpful: http://kotka.de/blog/2010/02/gen-class_how_it_works_and_how_to_use_it.html
The body of a proxy is a lexical closure, so you can just close around whatever variables you need. If, God forbid, you need to mutate them, then close around an atom:
(defn lying-list [init-size]
(let [the-size (atom init-size)]
(proxy [java.util.ArrayList] []
(size [] #the-size)
(remove [idx] (reset! the-size idx), true))))
There's really no need for actual Java fields here.