From 669d004d1b1939a48e453f19785ad95decaa3a97 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Thu, 14 Apr 2022 11:13:42 +0200 Subject: [PATCH] Fix: Setting file with same content should not make bean dirty (cherry picked from commit 62c4acd36a19c78becb9cbd446a8fc4f96e2279b) --- .../io/ebean/bean/EntityBeanIntercept.java | 34 +++++++++++++++++++ .../java/org/tests/types/TestFileType.java | 26 ++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java index 86de48bd8..888f8f23e 100644 --- a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java +++ b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java @@ -6,6 +6,11 @@ import io.ebean.ValuePair; import javax.persistence.EntityNotFoundException; import javax.persistence.PersistenceException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; import java.io.Serializable; import java.math.BigDecimal; import java.net.URL; @@ -910,9 +915,38 @@ public final class EntityBeanIntercept implements Serializable { // use the string format to determine if dirty return !obj1.toString().equals(obj2.toString()); } + if (obj1 instanceof File && obj2 instanceof File) { + File file1 = (File) obj1; + File file2 = (File) obj2; + if (file1.exists() && file2.exists() && file1.length() == file2.length()) { + return notEqualContent(file1, file2); + } + } return !obj1.equals(obj2); } + private static boolean notEqualContent(File file1, File file2) { + try (InputStream is1 = new FileInputStream(file1); InputStream is2 = new FileInputStream(file2)) { + byte[] buf1 = new byte[16384]; + byte[] buf2 = new byte[16384]; + int len1; + int len2; + while ((len1 = is1.read(buf1)) != -1 && (len2 = is2.read(buf2)) != -1) { + if (len1 != len2) { + return true; + } + if (!Arrays.equals(buf1, buf2)) { + // it does not matter, if we compare more than len1/len2 as the remainig + // bytes in the buffers are either 0 or equals from the prev. loop. + return true; + } + } + return false; + } catch (IOException e) { + return true; // handle them as "not equals" + } + } + /** * Called when a BeanCollection is initialised automatically. */ diff --git a/ebean-test/src/test/java/org/tests/types/TestFileType.java b/ebean-test/src/test/java/org/tests/types/TestFileType.java index 28e1e02a5..6ea0445e4 100644 --- a/ebean-test/src/test/java/org/tests/types/TestFileType.java +++ b/ebean-test/src/test/java/org/tests/types/TestFileType.java @@ -12,6 +12,7 @@ import java.io.PrintStream; import java.net.URL; import static org.junit.jupiter.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; public class TestFileType extends BaseTestCase { @@ -184,6 +185,31 @@ public class TestFileType extends BaseTestCase { DB.delete(bean3); } + @Test + void test_FileDirty() throws Exception { + SomeFileBean bean0 = new SomeFileBean(); + bean0.setName("afile"); + bean0.setContent(file); + + DB.save(bean0); + + SomeFileBean bean1 = DB.find(SomeFileBean.class) + .select("name, file") + .setId(bean0.getId()) + .findOne(); + + assertThat(bean1.getContent()) + .isNotEqualTo(file) // not same file object, but same content + .hasSameBinaryContentAs(file); + + assertFalse(DB.beanState(bean1).isDirty()); + bean1.setContent(file); + assertFalse(DB.beanState(bean1).isDirty()); + bean1.setContent(file2); + assertTrue(DB.beanState(bean1).isDirty()); + + } + private File getFile(String resource) { URL url = getClass().getResource(resource); return new File(url.getFile());