2016-04-26 5 views
2

DoFnSecureRandomインスタンスを指しRandomフィールドを持つタイプが含まれ、そのフィールドがときデシリアライズに失敗しますDataflowPipelineRunnerを使用してデータフローサービスで実行します。 (以下のスタックトレース)「にClassNotFoundException:sun.security.provider.Sun」私たちのデータフローパイプラインでのGoogle App EngineのでGoogleクラウドデータフローパイプラインを実行している

我々はSecureRandomとしてsun.security.provider.Sunを使用してインスタンスをバック手にたまたまそのデフォルトctorのを、使用して作成してjava.security.ProviderSecureRandom#getProviderを参照してください)。 SecureRandomは、Randomを継承します。これはシリアル化可能です。

sun.security.provider.Sunを作成できないため、このクラスを逆シリアル化しようとすると、データフローサービスが異常終了します。近いスタックトレースで探し

は、私は、逆シリアル化がcom.google.apphosting.runtime.security.UserClassLoaderを通じて起こることがわかり、そして今、私の理論は、このクラスローダがsun.*クラスのロード、または少なくともこの特定のsun.*クラスを許可していないということです。

java.lang.IllegalArgumentException: unable to deserialize [email protected] 
    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) 
    [...] 

答えて

1

問題はsun.security.provider.Sunは、App EngineのJREのホワイトリストに表示されないということですので、クラスローダはそれのインスタンスをインスタンス化することはできません。

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

しかし幸いにも、あなたはまだnew SecureRandom()を言うことができます同じ環境で

この問題を回避するために、Randomフィールドを持つカスタムのde/serializationフックをクラスに追加しました。簡略化された例:

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; 
    } 
    } 
} 
関連する問題