【问题标题】:Can't call public method of non-public class: public (Google gcloud library)无法调用非公共类的公共方法:public(Google gcloud 库)
【发布时间】:2016-06-27 17:36:41
【问题描述】:

我正在尝试使用 gcloud 库。

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

上面的clojure代码翻译自following code snippet(省略,点击“在其他地方运行”)。

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

当我从 Clojure REPL 调用此代码时,我收到以下错误。

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 ...

com.google.cloud.datastore.DatastoreOptions 代码can be found here

2016 年 6 月 29 日更新: 根据下面Schlomi 的建议,我认为如果我 AOT 围绕DatastoreOptions 编译自己的包装器,它可能会起作用。

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

我修改了我的 boot development 任务以包含以下内容:

(deftask development
  "Launch Immediate Feedback Development Environment"
  []
  (comp
   (aot :namespace '#{firengine.datastore})
   (repl :port 6800)
   (reload)
   (watch)
   (cljs)
   (target :dir #{"target"})))

我尝试像这样构造对象:

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

很遗憾,我仍然遇到同样的错误?

    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)

我真的不会编译firengine.datastore吗?

【问题讨论】:

    标签: clojure gcloud


    【解决方案1】:

    是的....那个问题。你不会相信,但它实际上是来自 ... 1999 年的 open bug in the jdk

    您可以在clojure jiragoogle groups 上了解更多信息。

    您可能必须创建自己的 java 包装器来避免这种情况,或者 ask the library author 来考虑这个已知的旧 java 错误。

    如果您不想编写自己的 java 包装器,并且作者坚持认为“这是最好的设计,就像,永远!”,那么您总是可以通过使用 (.setAccessible method true) 等设置方法可访问性来强制它自定义反射代码..

    祝你好运!

    【讨论】:

      【解决方案2】:

      正如 Shlomi 所说,这是一个长期存在的错误。根据 Noam Ben Ari 在 clj-jira 上的建议,我通过编写一个包装客户端创建的小型 Java 类来解决这个问题。然后我可以直接从我的 clj 代码中使用它。

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

      有关将 Java 编译添加到项目的指导:

      https://github.com/technomancy/leiningen/blob/master/doc/MIXED_PROJECTS.md

      【讨论】:

        【解决方案3】:

        所以,我是一个完整的 Clojure 菜鸟,在使用 Caffeine Cache 时遇到了类似的错误:“IllegalArgumentException 无法调用非公共类的公共方法:公共默认无效 com.github.benmanes.caffeine.cache.LocalManualCache。 put(java.lang.Object,java.lang.Object) clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:88)"

        我原来的函数签名是这样的:

        (defn get
          [cache key]
          (.getIfPresent cache key)
          )
        

        我认为问题在于 Clojure 无法确定在哪里应用 Cache 的 .getIfPresent 函数。添加类型修复它:

        (defn get
          [^Cache cache key]
          (.getIfPresent cache key)
          )
        

        尽管这不是一个直接的答案,而且你的问题让我无法理解,但我希望这会有所帮助。

        【讨论】:

          【解决方案4】:

          完全受到hironroy's answer 的启发,我通过一个孤立的例子来实现这个工作。在the following github project 中的src/gcloud/GcloduDatastore.java 中创建了以下文件。

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

          【讨论】:

            猜你喜欢
            • 2011-01-30
            • 2018-09-27
            • 1970-01-01
            • 2011-08-16
            • 2015-08-26
            • 1970-01-01
            • 2011-08-24
            • 2015-12-17
            • 2020-09-22
            相关资源
            最近更新 更多