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!