From 408164c8dc2153b60df92b6c0e66471026b4648d Mon Sep 17 00:00:00 2001 From: pilgr Date: Thu, 21 Sep 2017 11:52:46 +0300 Subject: [PATCH] Fix crash and data corruption during 1.5 => 2.0 transition --- .../java/io/paperdb/VersionUpgradeTest.java | 6 +- .../java/io/paperdb/DbStoragePlainFile.java | 55 ++++++++++--------- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/paperdb/src/androidTest/java/io/paperdb/VersionUpgradeTest.java b/paperdb/src/androidTest/java/io/paperdb/VersionUpgradeTest.java index 241fb4d..5d59338 100644 --- a/paperdb/src/androidTest/java/io/paperdb/VersionUpgradeTest.java +++ b/paperdb/src/androidTest/java/io/paperdb/VersionUpgradeTest.java @@ -28,7 +28,7 @@ public void setUp() throws Exception { public void testWrite() { Recipe recipe = new Recipe(); recipe.name = "chocolate cake"; - recipe.ingredients = new HashMap(); + recipe.ingredients = new HashMap<>(); recipe.ingredients.put("flour", 300); recipe.ingredients.put("eggs", 4); recipe.ingredients.put("chocolate", 200); @@ -41,6 +41,7 @@ public void testWrite() { public void testRead() throws IOException { File filesDir = new File(getTargetContext().getFilesDir(), "io.paperdb"); if (!filesDir.exists()) { + //noinspection ResultOfMethodCallIgnored filesDir.mkdirs(); } @@ -51,13 +52,12 @@ public void testRead() throws IOException { Recipe recipe = Paper.book().read("recipe"); assertThat(recipe.name).isEqualTo("chocolate cake"); - } private void copyFile(InputStream inputStream, OutputStream outputStream) throws IOException { byte buffer[] = new byte[2048]; - while(true) { + while (true) { int r = inputStream.read(buffer); if (r < 0) { break; diff --git a/paperdb/src/main/java/io/paperdb/DbStoragePlainFile.java b/paperdb/src/main/java/io/paperdb/DbStoragePlainFile.java index e77f619..0167e54 100644 --- a/paperdb/src/main/java/io/paperdb/DbStoragePlainFile.java +++ b/paperdb/src/main/java/io/paperdb/DbStoragePlainFile.java @@ -47,13 +47,17 @@ private Kryo getKryo() { private final ThreadLocal mKryo = new ThreadLocal() { @Override protected Kryo initialValue() { - return createKryoInstance(); + return createKryoInstance(false); } }; - private Kryo createKryoInstance() { + private Kryo createKryoInstance(boolean compatibilityMode) { Kryo kryo = new Kryo(); + if (compatibilityMode) { + kryo.getFieldSerializerConfig().setOptimizedGenerics(true); + } + kryo.register(PaperTable.class); kryo.setDefaultSerializer(CompatibleFieldSerializer.class); kryo.setReferences(false); @@ -245,40 +249,41 @@ private void writeTableFile(String key, PaperTable paperTable, } private E readTableFile(String key, File originalFile) { - return readTableFile(key, originalFile, false); - } - - private E readTableFile(String key, File originalFile, - boolean v1CompatibilityMode) { try { - final Input i = new Input(new FileInputStream(originalFile)); - final Kryo kryo = getKryo(); - if (v1CompatibilityMode) { - // Set temporary generic optimization to support Kryo 3.x format - kryo.getFieldSerializerConfig().setOptimizedGenerics(true); - } - //noinspection unchecked - final PaperTable paperTable = kryo.readObject(i, PaperTable.class); - i.close(); - if (v1CompatibilityMode) { - kryo.getFieldSerializerConfig().setOptimizedGenerics(false); - } - return paperTable.mContent; + return readContent(originalFile, getKryo()); } catch (FileNotFoundException | KryoException | ClassCastException e) { - // Give one more chance, reread data in compatibility mode - if (!v1CompatibilityMode) { - return readTableFile(key, originalFile, true); + Throwable exception = e; + // Give one more chance, read data in paper 1.x compatibility mode + if (e instanceof KryoException) { + try { + return readContent(originalFile, createKryoInstance(true)); + } catch (FileNotFoundException | KryoException | ClassCastException compatibleReadException) { + exception = compatibleReadException; + } } + // TODO Delete this // Clean up an unsuccessfully written file if (originalFile.exists()) { if (!originalFile.delete()) { throw new PaperDbException("Couldn't clean up broken/unserializable file " - + originalFile, e); + + originalFile, exception); } } String errorMessage = "Couldn't read/deserialize file " + originalFile + " for table " + key; - throw new PaperDbException(errorMessage, e); + throw new PaperDbException(errorMessage, exception); + } + } + + private E readContent(File originalFile, Kryo kryo) throws FileNotFoundException, KryoException { + final Input i = new Input(new FileInputStream(originalFile)); + //noinspection TryFinallyCanBeTryWithResources + try { + //noinspection unchecked + final PaperTable paperTable = kryo.readObject(i, PaperTable.class); + return paperTable.mContent; + } finally { + i.close(); } }