Call custom Clojure function with dependencies from Java

128 views
Skip to first unread message

Pablo J. Villacorta

unread,
Dec 24, 2017, 11:31:23 AM12/24/17
to Clojure
Hi all,

I am totally new to Clojure. From Java I am trying to call a Clojure function which belongs to a wider project and has dependencies. I have compiled the Clojure project to an uber jar running lein uberjar, the imported the jar into my Java project, and then did

IFn require = Clojure.var("clojure.core", "require");
require.invoke(Clojure.read("xapi-schema.core"));

and then

IFn myfunction = Clojure.var("xapi-schema.core", "validate-statement-data");
myfunction.invoke("some string");

but since myfile.core has dependencies (I see some "require" sentences in the Clojure code of that file), I wonder if I have to manually read all the dependent clojure files in the project (which are a lot), or there's a better way to do this from Java. The error I am getting is:

java.lang.Exception: schema.utils.NamedError@cec1ce2

at xapi_schema.core$validate_statement.invokeStatic(core.cljc:27)
at xapi_schema.core$validate_statement.invoke(core.cljc:24)
at xapi_schema.core$validate_statement_data_STAR_.invokeStatic(core.cljc:40)
at xapi_schema.core$validate_statement_data_STAR_.invoke(core.cljc:38)
at xapi_schema.core$validate_statement_data.invokeStatic(core.cljc:44)
at xapi_schema.core$validate_statement_data.invoke(core.cljc:43)
at clojure.lang.Var.invoke(Var.java:379)
at StatementValidatorXAPITest.testClojureValidator(StatementValidatorXAPITest.java:133)

where schema.utils is one of the files that I did not read explicitly from Java. 

Thank you so much in advance and Merry Christmas to everyone!




Justin Smith

unread,
Dec 24, 2017, 11:36:26 AM12/24/17
to clo...@googlegroups.com
If you require a namespace that requires another namespace, this will all be resolved and loaded automatically as long as all the namespace files can be found on the classpath.

I suspect that what you showed here is not the full error output, it seems to be missing the information we would need to know what actually went wrong here.

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Gary Verhaegen

unread,
Dec 24, 2017, 9:27:05 PM12/24/17
to clo...@googlegroups.com
schema.utils.NamedError does not look like a loading error; is there any chance that could be an error thrown by the library? Are you positive "some string" is a valid argument to validate-statement-data?

A cursory look at the README of xapi-schema seems to indicate that function will throw an exception if the input is not valid. I suspect it's working as intended.

Pablo J. Villacorta

unread,
Dec 26, 2017, 8:54:11 AM12/26/17
to Clojure
Thank you so much guys.

Yes, it is an error launched by the library, but with a statement that should be valid since I copied it from the github page of the project. It seems that the "@cec1ce2" means something but I don't know...

@Justin Smith: I just copied and pasted the last lines, which involve the Clojure library. From bottom line (StatementValidatorXAPITest.testClojureValidator (StatementValidatorXAPITest.java:133)) there is a stack of Java errors that are not related to the Clojure part but to microservices stuff, that's why they are not relevant so I did not paste them. 

Thank you again and Merry Xmas!

Gary Verhaegen

unread,
Dec 26, 2017, 11:37:54 AM12/26/17
to clo...@googlegroups.com
On 26 December 2017 at 14:54, Pablo J. Villacorta <olba...@gmail.com> wrote:
Thank you so much guys.

Yes, it is an error launched by the library, but with a statement that should be valid since I copied it from the github page of the project. It seems that the "@cec1ce2" means something but I don't know...

The code you showed in your email explicitly calls

myfunction.invoke("some string");

which is the equivalent of the Clojure code

(xs/validate-statement-data "some string")

which is documented to throw an Exception by the README, in the Usage section:

(let [bad-statement (dissoc statement "actor")]
  (xs/validate-statement-data bad-statement)) ;; => throws Exception or js/Error

If you look at the code for xs/validate-satement-data, you can see that it pretty much reduces to calling xs/validate-statement, which itself looks like:

(defn validate-statement [s]
  (if-let [error (statement-checker s)]
    (throw #?(:clj (Exception. (str error))
              :cljs (js/Error. (str error))))
    s))

See https://github.com/yetanalytics/xapi-schema/blob/master/src/xapi_schema/core.cljc#L24-L29 for more context.

So what I think has happened is that you have given an invalid string to the function ("some string"), so it is throwing a java.lang.Exception with, as its message, the result of calling clojure.lang/str on the error returned by the schema library. That error is a Java object of a class that clojure.lang/str doesn't know about, so it falls back to calling the Java method toString. As the schema.utils.NamedError class does not implement (override) toString, this falls back to java.lang.Object#toString(), which prints the name of the class followed by some random-looking hex value that is supposed to be somehow linked to the place of the object in memory (except that number doesn't change when the GC moves the object, so it's more some sort of identity than an address).

If you also get a stack trace when you do pass expectedly valid data to the function, can you prepare a minimal Github project with the code that fails? I can't see anything wrong in your descriptions so far so I'd need to look at the exact code you're trying to run to help you further.

Pablo J. Villacorta

unread,
Dec 29, 2017, 12:19:52 PM12/29/17
to Clojure
Thanks! You are right, I have tried another minimal statement and does not throw any exception... looks like the statement on that repo is not correct either (I don't know what's wrong with it), but don't know how to output proper error messages. According to the repo, 

(xs/validate-statement-data statement-str) ;; => returns statement edn

but I do not know what edn stands for. Moreover, I always have the argument as a plain string, not as an object as in this example. I have read also this:

(xs/errors->data (xs/statement-checker bad-statement :en)))) ;; ltag is optional, defaults to :en
;; => {"actor" "Missing required key", "id" "Not a string: 123"}

but I am unclear of the meaning of -> and how to use that from Java. Looks like statement-checker is for objects, not for strings?

I have a minimal working example here: https://github.com/olbapjose/xapi-clojure

Regards and happy new year!

Gary Verhaegen

unread,
Dec 30, 2017, 12:58:38 PM12/30/17
to clo...@googlegroups.com
On 29 December 2017 at 18:19, Pablo J. Villacorta <olba...@gmail.com> wrote:
Thanks! You are right, I have tried another minimal statement and does not throw any exception... looks like the statement on that repo is not correct either (I don't know what's wrong with it), but don't know how to output proper error messages. According to the repo, 

(xs/validate-statement-data statement-str) ;; => returns statement edn

but I do not know what edn stands for. Moreover, I always have the argument as a plain string, not as an object as in this example. I have read also this:

(xs/errors->data (xs/statement-checker bad-statement :en)))) ;; ltag is optional, defaults to :en
;; => {"actor" "Missing required key", "id" "Not a string: 123"}

but I am unclear of the meaning of -> and how to use that from Java. Looks like statement-checker is for objects, not for strings?

I have a minimal working example here: https://github.com/olbapjose/xapi-clojure

Regards and happy new year!

Disclaimer: I am not familiar with this library at all, so there's a little bit of guesswork on my part. 

From the README, it looks like validate-statement-data accepts either Clojure data (a.k.a. "edn", a combination of nested maps and vectors) or a string that can be parsed as JSON. It will throw an exception if the data given is not valid, and otherwise return the data, i.e. it will return its argument unchanged if you pass in Clojure data, but if you pass it a string, it will parse that to Clojure data and then (assuming no validation error) it will return the (parsed) data. In Java terms this would be the kind of signature you'd expect in so-called "fluent" APIs.

statement-checker seems to have essentially the same underlying semantics, but be designed for a more imperative API: it returns null if the given data is valid, and an error otherwise. This seems to be the same java.lang.Exception that would be thrown by validate-statement-checker, but it is simply returned instead of being thrown. So this is designed for a usage pattern where you want to have an explicit if statement rather than a try/catch.

Because Exception objects are not that great to work with, the library supplies a errors->data function that extracts some additional descriptive data from the exception. The -> is just part of the normal, valid characters for identifiers in Clojure, so this is just the name of the function with no special (language-level) semantics; the intention here is that this function transforms an error into data, so the -> is meant to represent an arrow. You can read it as "error to data". (I don't really see a reason why statement-checker doesn't just return the error data, rather than wrapping it in an exception, though I'm sure the author had some reason for designing it this way.)

EDN stands for "extensible data notation"; more details here: https://github.com/edn-format/edn

However, in this context I think the author simply means "Clojure data" when he writes "edn". Clojure data is composed of nested maps and vectors, that respectively implement java.lang.Map and java.lang.List.

Reply all
Reply to author
Forward
0 new messages