Can't call public method of non-public class: public (Google gcloud library)

≡放荡痞女 提交于 2019-12-19 04:04:40

问题


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?


回答1:


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!




回答2:


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




回答3:


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.




回答4:


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();
    }

}


来源:https://stackoverflow.com/questions/38059977/cant-call-public-method-of-non-public-class-public-google-gcloud-library

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!