X

A blog about Oracle Technology Network Japan

クイズに挑戦:安全なシリアライズとデシリアライズ(上級者向け)

Guest Author

※本記事は、Simon RobertsとMikalai Zaikinによる"Quiz yourself: Secure serialization and deserialization (advanced)"を翻訳したものです。


頻繁に変更されるコードをメンテナンスしやすくする方法に関する知識を確認する

著者:Simon Roberts、Mikalai Zaikin 
2020年6月22日 | 本記事をPDFでダウンロード
その他の設問はこちらから

過去にこのクイズの設問に挑戦したことがある方なら、どれ1つとして簡単な問題はないことをご存じでしょう。クイズの設問は、認定試験の中でも難しいものに合わせています。「中級者向け」「上級者向け」という指定は、設問の難易度ではなく、対応する試験による分類です。しかし、ほとんどすべての場合において、「上級者向け」の方が難しくなります。設問は認定試験対策として作成しており、認定試験と同じルールの適用を意図しています。文章は文字どおりに解釈してください。回答者を引っかけようとする設問ではなく、率直に言語の詳細な知識を試すものだと考えてください。

シリアライズとデシリアライズを安全にしつつ、頻繁に変更されるコードをメンテナンスしやすくしたいと思います。

今回の上級レベルのJava SE 11クイズでは、ビジネス・オブジェクトのシリアライズを多用するアプリケーションに関する作業を行っていることを想定します。ビジネス・オブジェクトのクラスには、時間とともにフィールドの追加や削除が行われます。これにより、新しいコードが古いシリアライズ表現と一致しなくなるという問題が発生します。

変更点の管理を容易にし、ビジネス・オブジェクトが円滑に動作し続けるようにするために行う、もっとも適切な変更はどれですか。1つ選んでください。

A. A.    java.io.Externalizableを実装し、シリアライズする変数を選択する
B. 必要ないインスタンス変数をprivateにする
C. 必要ないインスタンス変数をtransientにする
D. serialPersistentFields配列を追加し、必要な変数を読み書きする際にwriteObject/readObjectから参照する

 

解答:シリアライズの仕組みにおけるデフォルトの動作は、オブジェクト全体に含まれるすべてのインスタンスの状態を保存およびリストアするというものです。フィールドの追加や削除が行われることで、アプリケーションが破損することがあり、最悪の場合はセキュリティの問題が紛れ込む可能性もあります。デフォルトのシリアライズを使用する場合、static final long serialVersionUID定数を含め、互換性を伴わない形でクラス構造を変更するたびにこの定数を更新する必要があります。

それでは、この問題を軽減する方法として提案されている選択肢を確認します。

選択肢Aには、java.io.Externalizableインタフェースを実装し、オブジェクトのどの部分を保存およびリストアするかを制御できるようにすることで、シリアライズする変数を選択できるようにすると述べられています。確かに、これは非常に強力な仕組みです。ただし、このモデルで、シリアライズとデシリアライズをすべて低レベルで処理しなければならないのはプログラマーです。そのため、プログラマーはシリアライズとデシリアライズの順番を厳密に管理し、クラスの進化にも完全に対応しなければなりません。これらすべての作業はエラーが起こりやすく、メンテナンスはかなり難しくなります。そのため、このアプローチは大変強力で、役立つこともあるかもしれませんが、当面の問題に対しては作業の負担が大きすぎると考えられます。

問題を軽減できることから、選択肢Aは誤りとは言えません。しかし、設問ではもっとも適切な選択肢が問われているため、他の選択肢との比較が終わるまで判断を保留する必要があります。

選択肢Bでは、privateアクセス修飾子を使ってシリアライズを制御するとしています。しかし、privateによってフィールドのシリアライズ動作が変わることはありません。この選択肢で状況は何も変わらないことを考えれば、この時点ですぐに、選択肢Bは誤りだと判断できます。

選択肢Cでは、一部のフィールドをtransientとマークすることが提案されています。このキーワードを使用した場合、シリアライズ形式に変換される際に、その変数が書き込まれなくなります。そのため、当面の問題の対処策となる可能性があります。しかし、transientにも欠点があります。その1つは、頻繁に変更されるクラスでは、「許可リスト」によるアプローチの方が、「ブロックリスト」によるアプローチよりも一般的にコードのメンテナンスがしやすいという点です。許可リストによるアプローチとは、シリアライズに含めない変数ではなく、含める変数をプログラマーが指定するというものです。

2つ目は、transientでは、シリアライズされる変数の型が変わった場合に互換性が失われる問題の対処策にはならないという点です。この選択肢に多少の利点があることは間違いありませんが、選択肢Aほど表現力は高くありません。

選択肢Dでは、serialPersistentFieldsという予約された名前のprivate static final配列を作るとしています。この配列には、次のようにして、シリアライズするフィールドの名前と型を指定するObjectStreamFieldオブジェクトを設定します。

class BusinessObject implements Serializable {
    List list;
    private static final ObjectStreamField[] serialPersistentFields = 
      { new ObjectStreamField("list", List.class) };
}

この仕組みを使用して、シリアライズするフィールドをプログラマーが許可リストに明示的に追加することができます。さらに、いくつかの制約のもとで、フィールドの追加や削除もサポートされます。コードが進化した場合でも、writeObjectおよびreadObjectというprivateメソッドを使って、シリアライズされたコピーからカスタムのマッピングを行うことができます。次の例をご覧ください。

class BusinessObject implements Serializable {
    MyList list; // 変更されたデータタイプ(変更前はList)

    private void readObject(ObjectInputStream ois)  throws IOException, ClassNotFoundException {
        // オリジナルのシリアライズ形式からすべてのデータを取得する
        ObjectInputStream.GetField gf = ois.readFields();
        // そのデータからオリジナルのリストを抽出し、
        // そこに含まれる要素から新しいデータタイプMyListを作成する
        this.list = MyList.createFromList(gf.get("list", list)));
    }
}

4つの選択肢のうち3つが、当面の問題に対する何らかの対処策となる可能性があることがわかりました。そこで、どれが最適な選択肢かについて考えてみます。3つの有効な選択肢とそれぞれの特徴を表形式で整理すれば、一番わかりやすいでしょう。

この表から、serialPersistentFieldsを使う選択肢Dがバランスの点でもっとも優れていることがわかります。この方法は、実装や変更が非常に簡単でありながら、実際のプロジェクトで起こり得る変更の大部分に対処できるほど強力で表現力が高いものです。

一方、Externalizableを使う選択肢Aは、表現力がもっとも高いものの、非常に慎重に実装する必要があります。データ構造に細かな変更が行われるたびにコードを正しく変更するのは厄介な作業です。そのため、選択肢Aは誤りです。

transientを使う選択肢Cはとてもシンプルですが、データタイプの変更には対処できません。さらに、transientを使用する場合、シリアライズしてはいけないフィールドが新しく追加されたときに注意が必要です。そのため、許可リストのアプローチよりもエラーが起こりやすくなる可能性があります。よって、選択肢Cは誤りです。

以上の理由から、serialPersistentFieldsの仕組みを使う方法が最適で汎用的なアプローチであると考えられます。そのため、選択肢Dが正解となります。

正解は選択肢Dです。
 


Simon Roberts

Simon Roberts:Sun Microsystemsがイギリスで初めてJavaの研修を行う少し前にSunに入社し、Sun認定Javaプログラマー試験とSun認定Java開発者試験の作成に携わる。複数のJava認定ガイドを執筆し、現在はフリーランスでPearson InformITにおいて録画やライブによるビデオ・トレーニングを行っている(直接またはO'Reilly Safari Books Onlineサービス経由で視聴可能)。OracleのJava認定プロジェクトにも継続的に関わっている。

 

Mikalai Zaikin

Mikalai Zaikin:ベラルーシのミンスクを拠点とするIBA IT ParkのリードJava開発者。OracleによるJava認定試験の作成に携わるとともに、複数のJava認定教科書のテクニカル・レビューを行っている。Kathy Sierra氏とBert Bates氏による有名な学習ガイド『Sun Certified Programmer for Java』では、3版にわたってテクニカル・レビューを務めた。

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.