" ClassNotFoundException:sun.security.provider.Sun"在Google App Engine中运行Google Cloud Dataflow管道时 [英] "ClassNotFoundException: sun.security.provider.Sun" when running Google Cloud Dataflow pipeline in Google App Engine

查看:131
本文介绍了" ClassNotFoundException:sun.security.provider.Sun"在Google App Engine中运行Google Cloud Dataflow管道时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我们使用默认的ctor创建 SecureRandom ,这会发生一个实例使用 sun.security.provider.Sun 作为其 java.security.Provider (请参阅 SecureRandom#getProvider )。 SecureRandom 扩展随机,这是可序列化的。



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



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

  java.lang.IllegalArgumentException:无法在com.google.cloud.dataflow.sdk.util反序列化com.example.Example@13e88d 
.SerializableUtils.deserializeFromByteArray(SerializableUtils.java:73)
,com.google.cloud.dataflow.sdk.util.SerializableUtils.clone(SerializableUtils.java:88)
,com.google.cloud.dataflow .sdk.transforms.ParDo $ Bound。< init>(ParDo.java:683)
[...]
由以下原因引起: 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)
[...]


解决方案

问题在于 sun.security.provider.Sun 未显示App Engine JRE白名单,因此类加载程序无法实例化它的实例:



https://cloud.google.com/appengine/docs/java/jrewhitelist



但幸运的是,仍然可以在同一个环境中使用新的SecureRandom()



要解决这个问题,我们添加了一个自定义使用 Random 字段将de / serialization挂钩到类。简单示例:

  class示例实现了Serializable {

//查看{@link #writeObject上的注释}为什么这是暂时的。
//应该被视为最终的,但不能这样声明。
私人暂时随机随机;

//
// [班级的勇气在这里...]
//
$ b $ / **
*序列化钩来处理瞬态随机场。
* /
private void writeObject(ObjectOutputStream out)throws IOException {
out.defaultWriteObject();
if(random instanceof SecureRandom){
//写一个null来告诉readObject()在反序列化期间创建一个新的
// SecureRandom; null可以安全地使用
//作为占位符,因为构造函数不允许为null
// Randoms。
//
//数据流云环境不会反序列化
//使用sun.security.provider.Sun
//作为Provider的SecureRandom实例,因为它是一个系统
//不在App Engine白名单上的类:
// https://cloud.google.com/appengine/docs/java/jrewhitelist
out.writeObject(null);
} else {
out.writeObject(random);
}
}

/ **
*反序列化钩子初始化瞬态随机场。
* /
private void readObject(ObjectInputStream in)
throws IOException,ClassNotFoundException {
in.defaultReadObject();
Object newRandom = in.readObject();
if(newRandom == null){
// writeObject()会在原始字段为
// SecureRandom时写入null;创建一个新的实例来替换它。请参阅writeObject()中的
//注释以了解背景。
random = new SecureRandom();
random.nextDouble(); //强制播种
} else {
random =(Random)newRandom;
}
}
}


A DoFn in our Dataflow pipeline contains a type with a Random field pointing to a SecureRandom instance, and that field fails to deserialize when running in the Dataflow service using DataflowPipelineRunner. (stack trace below)

We create the SecureRandom using its default ctor, which happens to hand back an instance that uses sun.security.provider.Sun as its java.security.Provider (see SecureRandom#getProvider). SecureRandom extends Random, which is serializable.

The Dataflow service chokes when trying to deserialize this class because it can't create sun.security.provider.Sun.

Looking closer at the stack trace, I see that deserialization happens through com.google.apphosting.runtime.security.UserClassLoader, and now my theory is that this classloader doesn't allow loading of sun.* classes, or at least this particular sun.* class.

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)
    [...]

解决方案

The problem is that sun.security.provider.Sun doesn't appear on the App Engine JRE whitelist, so the classloader can't instantiate instances of it:

https://cloud.google.com/appengine/docs/java/jrewhitelist

But luckily you can still say new SecureRandom() in the same environment.

To work around the problem, we added a custom de/serialization hook to the class with the Random field. Simplified example:

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

这篇关于&quot; ClassNotFoundException:sun.security.provider.Sun&quot;在Google App Engine中运行Google Cloud Dataflow管道时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆