无法调用非 public class 的 public 方法:public(Google gcloud 库)

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

我正在尝试使用 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(省略号,点击"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();

当我从 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吗?

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

您可以在 clojure jira and on google groups 中阅读更多信息。

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

如果您不想编写自己的 java 包装器,而作者坚持 "this is the best design, like, ever!",那么您总是可以通过使用 (.setAccessible method true) 和一些设置方法可访问性来强制它更多自定义反射代码..

祝你好运!

正如 Shlomi 所说,这是一个很长的 运行 错误。按照 Noam Ben Ari 在 clj-jira 上的建议,我通过编写一个包装客户端创建的小 java class 来解决这个问题。然后我可以直接从我的 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

完全受 , 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();
    }

}

所以,我是一个完整的 Clojure 菜鸟,运行 使用 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)"

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

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

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

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

虽然这不是一个直接的答案而且你的问题让我难以理解,但我希望这对你有所帮助。