バックグラウンド
オブジェクトの配列に (コードの変更により) デシリアライズできないオブジェクトが含まれている場合、配列内のそれらの参照が例外をスローするのではなく null になるように、Kryo デシリアライゼーションを記述しようとしています。オブジェクトの残りを回収できるようにします。私は以前にJavaの組み込みのシリアライゼーションを使用していましたが、配列内の各アイテムの間に「既知の適切な」整数を書き込み、エラーが発生した場合はストリーム内でそれを探すことでこれを達成することができました次のオブジェクト。これは、いくつかの非直列化可能オブジェクトを含む配列を非直列化する (非直列化可能部分のサルベージ) という質問の詳細です。
効率の理由から Kryo のシリアル化に移行し、このアプローチを再作成しようとしましたが、Kryo 内ではこのエラー回復が一度は機能しているように見えますが、その後は正しく回復しません。
私が試したこと
シリアライゼーション中END_OF_APPLE_MAGIC
に s の配列内の各オブジェクト間に既知の適切な integer( ) を書き込もうとしました。逆シリアル化中に、逆シリアル化できないApple
a が見つかった場合、それは an に置き換えられ(アナロジーが弱くなっています) 、次のリンゴを探す場所を見つけるために が検索されます。これは、配列内に 1 つがあり、それが最初のエントリでない場合に機能します。しかし、配列に複数の配列がある場合、または最初の配列がBadApple
ErrorApple
END_OF_APPLE_MAGIC
BadApple
BadApple
BadApple
Apple
BadApple
public class AppleHolder implements Serializable,KryoSerializable{
static int END_OF_APPLE_MAGIC=1234467895; //if this just "turns up" in the stream we will have a bad day; however this is only the case in recovery mode, so is an acceptable risk
int numberOfApples=6;
Apple[] apples=new Apple[numberOfApples];
double otherData=15;
//these are just for debug
int dividers=0; //counts number of times END_OF_APPLE_MAGIC is found
int problems=0; //counts number of times an apple fails to load
int badIntegers=0; //counts number of times END_OF_APPLE_MAGIC is looked for and a different Integer is found (I have never seen this happen)
public AppleHolder(){
Apple goodApple=new Apple("GoodApple","tastyGood");
BadApple badApple=new BadApple("BadApple","untastyBad");
apples[0]=goodApple;
apples[1]=badApple;
apples[2]=goodApple;
apples[3]=goodApple; // multiple references to same object intentional
apples[4]=goodApple;
apples[5]=goodApple;
}
public void write (Kryo kryo, Output output) {
for(int i=0;i<apples.length;i++){
//kryo.writeObject(output, apples[i]);
kryo.writeClassAndObject(output, apples[i]);
kryo.writeClassAndObject(output, END_OF_APPLE_MAGIC);
}
kryo.writeObject(output,otherData);
}
public void read (Kryo kryo, Input input) {
try{
apples =new Apple[numberOfApples];
for(int i=0;i<apples.length;i++){
try{
Object ob=kryo.readClassAndObject(input);
apples[i]=(Apple)ob;
}catch(Exception e){
apples[i]=new ErrorApple();
problems++;
}
//Search for next Apple Boundary (except in recovery mode
//it will be the next entry)
boolean atBoundary=false;
while (atBoundary==false){ //should probably put a limit on this just in case
try{
int appleMagic =(Integer)kryo.readClassAndObject(input);
if (appleMagic == END_OF_APPLE_MAGIC){
atBoundary=true;
dividers++;
}else{
badIntegers++;
}
}catch(Exception e){
//painful byte reading mode only entered in recovery mode; under good
//situations it does not represent an efficiency problem
input.skip(1); //consume byte of bad input
//Where buffer underflow exceptions occur they occur here
}
}
}
otherData = kryo.readObject(input, Double.class);
}catch(Exception e){
//something when wrong (always a Buffer underflow so far), print what we have
for(int i=0;i<apples.length;i++){
System.out.println(apples[i]);
}
throw e;
}
}
public static void main(String[] args)
throws Exception {
/*
* (1) First run serialize()
* (2) Rename/delete badApple such that it cannot be found for deserialization
* (3) Run deSerialize(()
*/
serialize();
//deSerialize();
}
public static void serialize() throws Exception{
AppleHolder testWrite = new AppleHolder();
/*FileOutputStream fos = new FileOutputStream("testfile");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(testWrite);
oos.flush();
oos.close();
*/
Kryo kryo = new Kryo();
Output output = new Output(new FileOutputStream("testfile"));
kryo.writeObject(output, testWrite);
output.close();
}
public static void deSerialize() throws Exception{
/*AppleHolder testRead;
FileInputStream fis = new FileInputStream("testfile");
ObjectInputStream ois = new ObjectInputStream(fis);
testRead = (AppleHolder) ois.readObject();
ois.close();
*/
Kryo kryo = new Kryo();
Input input = new Input(new FileInputStream("testfile"));
AppleHolder testRead = kryo.readObject(input, AppleHolder.class);
input.close();
for(int i=0;i<testRead.apples.length;i++){
System.out.println(testRead.apples[i]);
}
System.out.println("otherData: " + testRead.otherData);
}
}
public class Apple implements Serializable {
private String propertyOne;
private String propertyTwo;
public Apple(){}
public Apple(String propertyOne, String propertyTwo) {
this.propertyOne = propertyOne;
this.propertyTwo = propertyTwo;
validate();
}
private void writeObject(ObjectOutputStream o)
throws IOException {
o.writeObject(propertyOne);
o.writeObject(propertyTwo);
}
private void readObject(ObjectInputStream o)
throws IOException, ClassNotFoundException {
propertyOne = (String) o.readObject();
propertyTwo = (String) o.readObject();
validate();
}
private void validate(){
if(propertyOne == null ||
propertyOne.length() == 0 ||
propertyTwo == null ||
propertyTwo.length() == 0){
throw new IllegalArgumentException();
}
}
public String getPropertyOne() {
return propertyOne;
}
public String getPropertyTwo() {
return propertyTwo;
}
@Override
public String toString() {
return "goodApple";
}
}
public class BadApple extends Apple {
public BadApple(){}
public BadApple(String propertyOne, String propertyTwo) {
super(propertyOne, propertyTwo);
}
@Override
public String toString() {
return "badApple";
}
}
public class ErrorApple extends Apple {
public ErrorApple(){}
public ErrorApple(String propertyOne, String propertyTwo) {
super(propertyOne, propertyTwo);
}
@Override
public String toString() {
return "errorApple";
}
}
質問
一部のオブジェクトのみがデシリアライズ可能である Kyro のシリアライズされた配列をどのように救済できますか? ErrorApple
これにより、非シリアル化可能部分のエントリを含む配列を取得します。私の配列内には、単一の配列内の同じオブジェクトへの複数の参照があります。これが逆シリアル化プロセスで保持されることが不可欠です。
だから私が持っている連載に入る
[GoodApple]
[GoodApple]
[GoodApple]
[BadApple]
[BadApple]
[GoodApple]
そして、私が望む逆シリアル化から出てきます(badAppleが変更され、逆シリアル化できないため
[GoodApple]
[GoodApple]
[GoodApple]
[ErrorApple]
[ErrorApple]
[GoodApple]
これにより、下位互換性を達成できない場合、または以前にインストールされたプログラムに対するサードパーティの変更が削除された場合にフォールバックを提供したい
詳細な分析
このセクションでは、既存のプログラムが失敗する方法について概説します。
一般に
BadApple
配列の最初の位置以外の場所にあるシングルは正しく機能します- 配列の最初の位置にあるAは、次の読み取りに正しく
BadApple
つながり、それ以降は (良いs であっても)Apple
ErrorApples
Apple
- 複数ある場合
BadApple
、最初の goodApple
は 2 番目の後にBadApple
正しく読み取られますが、配列内で前方に移動され、それErrorApples
以降 (goodApple
の場合でも) 移動される可能性があります。がありKryoException: Buffer underflow
、配列の最後に null エントリがある場合があります。
私が使用した入力と出力を以下に示します。
インアウト [グッドアップル] [グッドアップル] [グッドアップル] [グッドアップル] [悪いアップル] [悪いアップル] [グッドアップル] [グッドアップル] [グッドアップル] [グッドアップル] [グッドアップル] [グッドアップル] インアウト [悪いアップル] [エラーアップル] [グッドアップル] [グッドアップル] [良いアップル] [エラーアップル] [良いアップル] [エラーアップル] [良いアップル] [エラーアップル] [良いアップル] [エラーアップル] インアウト [グッドアップル] [グッドアップル] [悪いアップル] [エラーアップル] [グッドアップル] [グッドアップル] [悪いアップル] [エラーアップル] [グッドアップル] [グッドアップル] [良いアップル] [エラーアップル] KryoException: バッファ アンダーフロー。(input.skip(1); で発生) インアウト [グッドアップル] [グッドアップル] [グッドアップル] [グッドアップル] [悪いアップル] [エラーアップル] [悪いアップル] [エラーアップル] [グッドアップル] [グッドアップル] [良いアップル] [エラーアップル] KryoException: バッファ アンダーフロー (input.skip(1); で発生) インアウト [グッドアップル] [グッドアップル] [悪いアップル] [エラーアップル] [悪いアップル] [エラーアップル] [悪いアップル] [良いアップル] [良いアップル] [エラーアップル] [グッドアップル] [ヌル] KryoException: バッファ アンダーフロー。(input.skip(1); で発生)