Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/345.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java &引用;ClassNotFoundException:sun.security.provider.sun“;在Google App Engine中运行Google云数据流管道时_Java_Google App Engine_Google Cloud Dataflow - Fatal编程技术网

Java &引用;ClassNotFoundException:sun.security.provider.sun“;在Google App Engine中运行Google云数据流管道时

Java &引用;ClassNotFoundException:sun.security.provider.sun“;在Google App Engine中运行Google云数据流管道时,java,google-app-engine,google-cloud-dataflow,Java,Google App Engine,Google Cloud Dataflow,数据流管道中的DoFn包含一个类型,其Random字段指向SecureRandom实例,并且该字段在使用DataflowPipelineRunner在数据流服务中运行时无法反序列化。(下面是堆栈跟踪) 我们使用默认的ctor创建SecureRandom,该ctor恰好将sun.security.provider.sun用作java.security.provider(请参阅)SecureRandom扩展了可序列化的Random 数据流服务在尝试反序列化此类时阻塞,因为它无法创建sun.secur

数据流管道中的
DoFn
包含一个类型,其
Random
字段指向
SecureRandom
实例,并且该字段在使用
DataflowPipelineRunner
在数据流服务中运行时无法反序列化。(下面是堆栈跟踪)

我们使用默认的ctor创建
SecureRandom
,该ctor恰好将
sun.security.provider.sun
用作
java.security.provider
(请参阅)
SecureRandom
扩展了可序列化的
Random

数据流服务在尝试反序列化此类时阻塞,因为它无法创建
sun.security.provider.sun

仔细查看堆栈跟踪,我发现反序列化是通过
com.google.apphosting.runtime.security.UserClassLoader
实现的,现在我的理论是这个类加载器不允许加载
sun.*
类,或者至少是这个特定的
sun.

java.lang.IllegalArgumentException: unable to deserialize com.example.Example@13e88d
    at com.google.cloud.dataflow.sdk.util.SerializableUtils.deserializeFromByteArray(SerializableUtils.java:73)
    at com.google.cloud.dataflow.sdk.util.SerializableUtils.clone(SerializableUtils.java:88)
    at com.google.cloud.dataflow.sdk.transforms.ParDo$Bound.<init>(ParDo.java:683)
    [...]
    Caused by: java.lang.ClassNotFoundException: sun.security.provider.Sun
    at com.google.apphosting.runtime.security.UserClassLoader.loadClass(UserClassLoader.java:442)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:375)
    at java.lang.Class.forName0(Native Method)
    [...]
java.lang.IllegalArgumentException:无法反序列化com.example。Example@13e88d
位于com.google.cloud.dataflow.sdk.util.SerializableUtils.deserializeFromByteArray(SerializableUtils.java:73)
位于com.google.cloud.dataflow.sdk.util.SerializableUtils.clone(SerializableUtils.java:88)
位于com.google.cloud.dataflow.sdk.transforms.ParDo$Bound.(ParDo.java:683)
[...]
原因:java.lang.ClassNotFoundException:sun.security.provider.sun
位于com.google.apphosting.runtime.security.UserClassLoader.loadClass(UserClassLoader.java:442)
位于java.lang.ClassLoader.loadClass(ClassLoader.java:375)
位于java.lang.Class.forName0(本机方法)
[...]

问题在于
sun.security.provider.sun
没有出现在App Engine JRE白名单上,因此类加载器无法实例化它的实例:

但幸运的是,您仍然可以在相同的环境中说
newsecurerandom()

为了解决这个问题,我们在类的
Random
字段中添加了一个自定义的反序列化钩子。简化示例:

class Example implements Serializable {

  // See comments on {@link #writeObject} for why this is transient.
  // Should be treated as final, but can't be declared as such.
  private transient Random random;

  //
  // [Guts of the class go here...]
  //

  /**
   * Serialization hook to handle the transient Random field.
   */
  private void writeObject(ObjectOutputStream out) throws IOException {
    out.defaultWriteObject();
    if (random instanceof SecureRandom) {
      // Write a null to tell readObject() to create a new
      // SecureRandom during deserialization; null is safe to use
      // as a placeholder because the constructor disallows null
      // Randoms.
      //
      // The dataflow cloud environment won't deserialize
      // SecureRandom instances that use sun.security.provider.Sun
      // as their Provider, because it's a system
      // class that's not on the App Engine whitelist:
      // https://cloud.google.com/appengine/docs/java/jrewhitelist
      out.writeObject(null);
    } else {
      out.writeObject(random);
    }
  }

  /**
   * Deserialization hook to initialize the transient Random field.
   */
  private void readObject(ObjectInputStream in)
      throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    Object newRandom = in.readObject();
    if (newRandom == null) {
      // writeObject() will write a null if the original field was
      // SecureRandom; create a new instance to replace it. See
      // comments in writeObject() for background.
      random = new SecureRandom();
      random.nextDouble(); // force seeding
    } else {
      random = (Random) newRandom;
    }
  }
}