2012-06-07 10 views
5

私は、バージョン管理システムのクラス構造に柔軟性を持たせるために、さまざまなオプションを検討しています。私に言う必要がある)。readClassDescriptor()とresolveClass()を使用してシリアル化のバージョン管理を有効にする

たとえば、下位互換性のみが必要な場合は、デフォルトのシリアル化メカニズムでフィールドの追加と削除の両方を処理できます。

クラスの名前を変更したり、別のパッケージに移動したりするのはずっと難しくなっています。私はObjectInputStreamのとオーバーライドreadClassDescriptor()をサブクラス化することで、私はパッケージを単純なクラス名の変更を行うこと、および/または移動することができましたthis questionで見つかった:簡単なリネームの罰金です

if (resultClassDescriptor.getName().equals("package.OldClass")) 
     resultClassDescriptor = ObjectStreamClass.lookup(newpackage.NewClass.class); 

。しかし、フィールドを追加または削除しようとすると、java.io.StreamCorruptedExceptionが発生します。さらに悪いことに、フィールドが追加または削除されたとしても、という名前を変更すると、複数の開発者や複数のチェックインで問題が発生する可能性があります。

私が行ったいくつかの読解に基づいて、私は新しいクラスに正しく名前を再命名したが、古いクラス自体を読み込んでフィールドに爆破しないという考えで、resolveClass()をオーバーライドして少し実験しました変更。しかし、これはシリアライゼーションの仕組みの詳細をあいまいに理解していることから来ており、私が正しいツリーを吠えているかどうかはわかりません。

SO 2の正確な質問:

    なぜreadClassDescriptorを使用して、クラス名() 直列化復元は、通常、互換性のあるクラスの変更に失敗する原因をrepointingさ
  1. を回避するためにresolveClass()または別のメカニズムを使用する方法がありますか?また、クラスの進化(フィールドの追加と削除)と の名前の変更/

私は覗き込んでいて、それに相当する質問は見つかりませんでした。是非、私にそのような質問がある場合はそれを指摘してください。他の質問が私の正確な質問に実際に答えなければ、あなたは私を閉じないでください。

+0

あなたは解決策を見つけましたか? – gaponov

+0

@orbfishあなたが見つけたら解決策を教えてください – Tenacious

+0

@ enthu-manどういうわけか、これを閉じるのを忘れてしまいました。それは長い時間がかかり、問題のコードはもうありません。ここで3つのすっきりしたソリューションがあります。私はそれらを試してみましょう。作品が見つかったら、それを受け入れます; – orbfish

答えて

7

私はあなたのような柔軟性と同じ問題を抱えていました。私は方法を見つけました。 readClassDescriptor()の私のバージョン

static class HackedObjectInputStream extends ObjectInputStream 
{ 

    /** 
    * Migration table. Holds old to new classes representation. 
    */ 
    private static final Map<String, Class<?>> MIGRATION_MAP = new HashMap<String, Class<?>>(); 

    static 
    { 
     MIGRATION_MAP.put("DBOBHandler", com.foo.valueobjects.BoardHandler.class); 
     MIGRATION_MAP.put("DBEndHandler", com.foo.valueobjects.EndHandler.class); 
     MIGRATION_MAP.put("DBStartHandler", com.foo.valueobjects.StartHandler.class); 
    } 

    /** 
    * Constructor. 
    * @param stream input stream 
    * @throws IOException if io error 
    */ 
    public HackedObjectInputStream(final InputStream stream) throws IOException 
    { 
     super(stream); 
    } 

    @Override 
    protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException 
    { 
     ObjectStreamClass resultClassDescriptor = super.readClassDescriptor(); 

     for (final String oldName : MIGRATION_MAP.keySet()) 
     { 
      if (resultClassDescriptor.getName().equals(oldName)) 
      { 
       String replacement = MIGRATION_MAP.get(oldName).getName(); 

       try 
       { 
        Field f = resultClassDescriptor.getClass().getDeclaredField("name"); 
        f.setAccessible(true); 
        f.set(resultClassDescriptor, replacement); 
       } 
       catch (Exception e) 
       { 
        LOGGER.severe("Error while replacing class name." + e.getMessage()); 
       } 

      } 
     } 

     return resultClassDescriptor; 
    } 
1

問題は、readClassDescriptorが、現在読んでいるストリームのであるのデータをObjectInputStreamに読み取る方法を伝えることになっているということです。シリアライズされたデータストリームを見ると、データだけでなく、どのフィールドが存在するかについての多くのメタデータが格納されていることがわかります。これはシリアライゼーションが単純なフィールドの追加/削除を処理できるようにするものです。ただし、そのメソッドをオーバーライドしてストリームから返された情報を破棄すると、のシリアル化されたデータに含まれるフィールドに関する情報が破棄されます。

私は問題を解決するには、super.readClassDescriptor(によって返された値を取る)して、新しいクラス名を返します新しいクラス記述子を作成することですと思いますが、そうでない場合は、古い記述子からの情報を返す。 (しかし、ObjectStreamFieldを見ると、それはもっと複雑になるかもしれませんが、それは一般的な考えです)。

0

私はクラス記述子で十分に触れていませんが、問題の名前を変更して再パッケージしただけの場合は、はるかに簡単な解決策があります。シリアライズされたデータファイルをテキストエディタで編集して、古い名前を新しいものに置き換えるだけで済みます。それは人間が読める形式でそこにあります。私たちは、このクラスのインスタンスをシリアル化し、このような何かを得るとき今

package oldpackage; 

import java.io.Serializable; 

public class OldClass implements Serializable 
{ 
    int oldField; 
} 

:私たちならば今

¬í sr oldpackage.OldClasstqŽÇ§Üï I oldFieldxp  

を例えば、私たちはこのようなoldpackageoldFieldを含む内部に配置されたこのOldClassを、持っていると仮定クラスの名前をNewClassに変更し、newpackageを入れてフィールドの名前をnewFieldに変更するには、次のようにファイルの名前を変更します。

¬í sr newpackage.NewClasstqŽÇ§Üï I newFieldxp  

とし、新しいクラスに適切なserialVersionUIDを定義します。

それだけです。拡張と上書きは必要ありません。

+0

シリアル化されたデータはバイナリです。あなたはテキストエディタでそれを傷つけることになります。実用的な答えではありません。 – EJP

+0

私はそれをテストしました。あなたはそれを試すことができます。 – Untitled

+0

あなたは何を正確にテストしましたか?どのテキストエディタでですか?どんなシステムで?シリアル化されたデータは何ですか?バイナリデータを含んでいますか? – EJP

1

これは、writeReplace()とreadResolve()が対象です。あなたは本当にそれをはるかに複雑にしています。これらのメソッドは、関係する2つのオブジェクトまたはオブジェクトストリームクラスのサブクラスで定義できます。

+0

これはそうは思いませんが、上記のように、私はもはやテストするコードを持っていません。私はこの問題をもう一度見たら、これを試してみるよ、ありがとう! – orbfish