2016-04-26 8 views
2

Ein DoFn in unserem Datenfluss Pipelines einen Typen mit einem enthält Random Feld zu einem SecureRandom Beispiel zeigt, und das Feld nicht deserialisieren, wenn Ausführen im Dataflow-Dienst unter Verwendung von DataflowPipelineRunner. (Stack-Trace unten)„ClassNotFoundException: sun.security.provider.Sun“ beim Ausführen von Google Cloud Dataflow-Pipeline in Google App Engine

Wir schaffen die SecureRandom seinen Standard Ctor verwenden, die eine Instanz zur Hand zurück kommt vor, dass sun.security.provider.Sun als java.security.Provider (siehe SecureRandom#getProvider) verwendet. SecureRandom erweitert Random, die serialisierbar ist.

Der Dataflow-Dienst erstickt beim Versuch, diese Klasse zu deserialisieren, da er sun.security.provider.Sun nicht erstellen kann.

Bei genauerer Betrachtung des Stack-Trace, ich sehe, dass Deserialisierung durch com.google.apphosting.runtime.security.UserClassLoader passiert, und jetzt ist meine Theorie, dass diese nicht Classloader Laden von sun.* Klassen erlaubt es, oder zumindest diese besondere sun.* Klasse.

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

Antwort

1

Das Problem ist, dass sun.security.provider.Sun nicht auf der App Engine JRE Weiße Liste angezeigt wird, so dass der Klassenlader kann Instanzen nicht instanziiert:

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

Aber zum Glück kann man noch sagen new SecureRandom() in der gleichen Umgebung.

Um das Problem zu umgehen, fügten wir einen benutzerdefinierten de/Serialisierung-Hook der Klasse mit dem Feld Random hinzu. Vereinfachtes Beispiel:

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