2013-02-25 4 views
6

不変クラスFoo(IDと名前からなるPOJO)を考慮してください。これは、サーバーからクライアントにデータを送信するためにシリアル化する必要があります。 FooのオブジェクトのGWTカスタムフィールドシリアル化の問題

public final class Foo 
{ 
    private final int m_id; 
    private final String m_displayName; 

    private Foo(final int id, final String displayName) 
    { 
     m_id = id; 
     m_displayName = displayName; 
    } 

    public static Foo create(final int id, final String displayName) 
    { 
     // Some error checking occurs here. 
     . . . 

     m_id = id; 
     m_displayName = displayName; 
    } 

    // Getters etc. 
    . . . 
} 

インスタンス化は、静的なファクトリ関数を介して発生し、オブジェクトの不変ので全くゼロ引数のコンストラクタが存在しません。

データメンバFooを含み、インスタンス化のためのBuilderパターンを実装する不変クラスのBarも考慮してください(問題とは無関係にスニペットから省略されています)。

public final class Bar 
{ 
    private final Foo m_foo; 
    . . . 

    private Bar(final Builder builder) 
    { 
     . . . 
    } 

    public static Builder createBuilder() 
    { 
     return new Builder(); 
    } 
} 

私はその不変性を取り除くか、直列化のために任意のゼロ引数のコンストラクタを追加することなく、このオブジェクトをシリアル化する必要がある方法に関する私の選択肢を評価した後、私は両方のために、私はCustomFieldSerializerを実装するために必要な事実(に締結しましたクライアントとサーバー)。

私はGWTのServer Communicationの記事に書かれたガイドラインに従い、以下に示すように私自身のCustomFieldSerializerを実装しました。

// Contains the serialization logic of the class Bar. 
public final class Bar_CustomFieldSerializerBase 
{ 
    public static Bar instantiate(final SerializationStreamReader streamReader) throws SerializationException 
    { 
     return Bar.createBuilder().forFoo((Foo) streamReader.readObject()).build(); 
    } 

    public static void serialize(final SerializationStreamWriter streamWriter, final Bar instance) 
     throws SerializationException 
    { 
     // . . . 
     streamWriter.writeObject(instance.getFoo()); 
    } 

    public static void deserialize(final SerializationStreamReader streamReader, final Bar instance) 
    { 
     /* 
     * Empty as everything is handled on instantiateInstance(). 
     */ 
    } 
} 

// The CustomFieldSerializer for class Bar. 
public class Bar_CustomFieldSerializer extends CustomFieldSerializer<Bar> 
{ 
    public static void deserialize(final SerializationStreamReader streamReader, final Bar instance) throws SerializationException 
    { 
    Bar_CustomFieldSerializerBase.deserialize(streamReader, instance); 
    } 

    public static void serialize(final SerializationStreamWriter streamWriter, final Bar instance) throws SerializationException 
    { 
     Bar_CustomFieldSerializerBase.serialize(streamWriter, instance); 
    } 

    public static Bar instantiate(final SerializationStreamReader streamReader) throws SerializationException 
    { 
     return Bar_CustomFieldSerializerBase.instantiate(streamReader); 
    } 

    @Override 
    public boolean hasCustomInstantiateInstance() 
    { 
     return true; 
    } 

    @Override 
    public Bar instantiateInstance(final SerializationStreamReader streamReader) throws SerializationException 
    { 
     return instantiate(streamReader); 
    } 

    @Override 
    public void deserializeInstance(final SerializationStreamReader streamReader, final Bar instance) throws SerializationException 
    { 
     deserialize(streamReader, instance); 
    } 

    @Override 
    public void serializeInstance(final SerializationStreamWriter streamWriter, final Bar instance) throws SerializationException 
    { 
     serialize(streamWriter, instance); 
    } 

// Server side CustomFieldSerializer for class Bar. 
public class Bar_ServerCustomFieldSerializer extends ServerCustomFieldSerializer<Bar> 
{ 
    public static void deserialize(ServerSerializationStreamReader streamReader, Bar instance, 
     Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException 
    { 
    /* 
    * Empty as everything is handled on instantiateInstance(). 
    */ 
    } 

    @Override 
    public Bar instantiateInstance(ServerSerializationStreamReader streamReader) throws SerializationException 
    { 
     return Bar_CustomFieldSerializerBase.instantiate(streamReader); 
    } 

    @Override 
    public void deserializeInstance(ServerSerializationStreamReader streamReader, Bar instance, 
     Type[] expectedParameterTypes, DequeMap<TypeVariable<?>, Type> resolvedTypes) throws SerializationException 
    { 
     deserialize(streamReader, instance, expectedParameterTypes, resolvedTypes); 
    } 

    @Override 
    public void deserializeInstance(SerializationStreamReader streamReader, Bar instance) throws SerializationException 
    { 
     Bar_CustomFieldSerializerBase.deserialize(streamReader, instance); 
    } 

    @Override 
    public void serializeInstance(SerializationStreamWriter streamWriter, Bar instance) throws SerializationException 
    { 
     Bar_CustomFieldSerializerBase.serialize(streamWriter, instance); 
    } 
} 

クラスバーは私が行って、CustomFieldSerializersの別のセットを実装し、Fooクラスのために、この時間は、クライアントとサーバーの両方で、同じパターンを以下のシリアル化する必要はFooオブジェクトが含まれているため。

シリアライゼーションクラスバーのために、特にこの時点で発生したときに問題が発生:

public static void serialize(final SerializationStreamWriter streamWriter, final Bar instance) 
     throws SerializationException 
{ 
    // . . . 
    streamWriter.writeObject(instance.getFoo()); 
} 

私が取得例外メッセージは以下の通りです:

[WARN] Exception while dispatching incoming RPC call com.google.gwt.user.client.rpc.SerializationException: Type 'ui.shared.models.fooItems.Foo' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized. 

それはのwriteObject(と思われます)は、クライアントとサーバーの両方にカスタムシリアライザが提供されていても、Fooクラスがホワイトリスト項目に属していないため、Foo型のオブジェクトをシリアル化できません。

私はいつものwriteObjectを(スキップできる)呼び出しとのwriteIntを(呼び出し) & WriteStringは(うまく機能している)各フーのデータメンバーの()が、私はむしろのwriteObjectを(になるだろう)働く私が提案した解決策は、Fooのシリアライザ(明白な)とバーのシリアライザ(あまり明白ではない)の両方に反映されなければならないので、Fooクラスの変更が反映されなければならないので、メンテナンスの間違いが起こりやすい非常にです。

私は(任意の違いはありませんでしたし、それが彼らの供給私の知る限りクラスので、任意の違いを作るべきではないfooとbarの両方でisSerializableインタフェースを実装するから、私はネット上で見つけることができるほとんど何でも試してみました独自のシリアライザはこのルールを遵守する必要はありません)、プライベートゼロ-argsコンストラクタを提供することもできます(カスタムフィールドシリアライザのインス​​タンス化関数は静的ファクトリを介して処理する必要があります)。

なぜFooクラスはホワイトリストに登録されていませんか?私は何かを見逃したり、何かを誤解したことがありますか?

ありがとうございます。

答えて

6

ここでの問題は、コード内のどこにでも明示的に言及していない可能性が高いことです。つまり、Fooクラスはこれまでにサーバーに送信されています。例えば。あなたが明示的にパラメータとしてFooクラスを取る新しいサービスメソッドを追加することにより、直列化復元リストにFooクラスが含まれるように、GWTのためのヒントを確認する必要がありwriteObjectを使用するには

interface MyService { 

    Foo getFoo(Bar bar); 

} 

:あなたはこのようなサービスのみのメソッドを持っています:

interface MyService { 

    Foo getFoo(Bar bar); 

    void setFoo(Foo foo);// letting GWT know that we might send Foo object over the wire, you don't have to ever call this method in your app, or implement it in some meaningful way, just let it be there 

} 

アプリでこのメソッドを呼び出す必要はありません。しかしそれ以外の場合は動作しません。 GWTのヒントを作成する方法は他にもいくつかありますが、アイデアは同じです。GWT-RPCのFooクラスを明示的に公開する必要があります。複数のサービスでBarクラスを使用する場合は、Barクラス

を使用している各サービスにこのようなメソッドを追加する必要があることを忘れないでください。サーバー側では、GWT-RPCは2つのリストを追跡します。つまり、シリアル化できるオブジェクトと逆シリアル化できるオブジェクトです。この情報は、RPCポリシーマニフェストから取得されます。私の最初の例では、私はBarオブジェクトをサーバに送ることができるものとして言及しました。しかし、カスタムフィールドシリアライザをBarクラスに定義しているため、GWTはBarの解析を実行しないため、Fooのインスタンスがサーバに送信される可能性はないため、サーバ上でFooに必要なデシリアライザがないと判断します。側。したがって、Barのインスタンスをワイヤで送信しようとすると、サーバはデシリアライズしようとしますが、readObjectがカスタムシリアライザ内で使用されているため、Fooのデシリアライザも検索しようとしますが、失敗します。 Fooオブジェクトのみを送信する余分なサービスメソッドを追加すると、GWTはそのようなオブジェクトもサーバーに送信できることを認識します。したがって、Fooも非直列化としてマークします。

これは非常に混乱し、個人的に私はカスタムシリアル化を持つクラスはすべてのホワイトリストに自動的に追加されるべきだと考えていますが、それはそのとおりです。

EDIT

別(愚かな空のメソッドのない非ハックソリューション)、通信のための専門のDTO層を使用するようになります(既定のパブリックコンストラクタを持つたとえばPOJOのみ)、しかし、あなたはケースでは難しいかもしれません多くの相互参照を持つ複雑なオブジェクトグラフを送信する必要があります。

+0

回答にスポットがあります。私はシナリオ全体が完全に混乱していることに非常に同意します。カスタムフィールドのシリアライゼーションクラスを提供するクラスは、ホワイトリストに登録されたオブジェクトに自動的に追加する必要があります。現在の実装では、開発者が問題を突き抜けたり、複雑なシステムを追加したりすることがあります。 –

+2

真実は、この答えが示唆するよりもやや簡単だと思います。まず、「最終」フィールドは無視されます(http://code.google.com/p/google-web-toolkit/issues/detail?id=1054のように提出されます)。そのため、理論的にはすべてのコードを呼び出すことができるので(つまり、可能な型を解決するのが難しい問題です)、独自のシリアライザの機能に関係なく、所有者の型が送信されます。その結果、そのタイプを参照する新しいメソッドを追加するだけで、それをマークすることができます。 'final'を削除するか(または1054を修正する)、このメソッドは必要ありません。 –

+0

@ColinAlworth実際にあなたは100%正しいです – jusio