changeset 918:f6336bc81da8

Added MD5SumWatcher utility class Added MD5SumWatcher utility class to detect when a file's contents have been changed on disk. * netx/net/sourceforge/jnlp/util/FileUtils.java: (getFileMD5Sum) new function * netx/net/sourceforge/jnlp/util/MD5SumWatcher.java: new class * tests/netx/unit/net/sourceforge/jnlp/util/MD5SumWatcherTest.java: new tests for MD5SumWatcher
author Andrew Azores <aazores@redhat.com>
date Mon, 10 Mar 2014 15:39:56 -0400
parents 483ab446ea4c
children bd7ce0fce548
files ChangeLog netx/net/sourceforge/jnlp/util/FileUtils.java netx/net/sourceforge/jnlp/util/MD5SumWatcher.java tests/netx/unit/net/sourceforge/jnlp/util/MD5SumWatcherTest.java
diffstat 4 files changed, 254 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Mar 10 12:29:47 2014 -0400
+++ b/ChangeLog	Mon Mar 10 15:39:56 2014 -0400
@@ -1,3 +1,13 @@
+2014-03-10  Andrew Azores  <aazores@redhat.com>
+
+	Added MD5SumWatcher utility class to detect when a file's contents have been
+	changed on disk.
+	* netx/net/sourceforge/jnlp/util/FileUtils.java: (getFileMD5Sum) new
+	function
+	* netx/net/sourceforge/jnlp/util/MD5SumWatcher.java: new class
+	* tests/netx/unit/net/sourceforge/jnlp/util/MD5SumWatcherTest.java: new
+	tests for MD5SumWatcher
+
 2014-03-10  Andrew Azores  <aazores@redhat.com>
 
 	* tests/netx/unit/net/sourceforge/jnlp/security/policyeditor/PolicyEditorPermissionsTest.java:
--- a/netx/net/sourceforge/jnlp/util/FileUtils.java	Mon Mar 10 12:29:47 2014 -0400
+++ b/netx/net/sourceforge/jnlp/util/FileUtils.java	Mon Mar 10 15:39:56 2014 -0400
@@ -30,8 +30,12 @@
 import java.io.OutputStreamWriter;
 import java.io.RandomAccessFile;
 import java.io.Writer;
+import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -587,4 +591,27 @@
     public static String loadFileAsString(File f, String encoding) throws IOException {
         return getContentOfStream(new FileInputStream(f), encoding);
     }
+
+    public static byte[] getFileMD5Sum(final File file, final String algorithm) throws NoSuchAlgorithmException,
+            FileNotFoundException, IOException {
+        final MessageDigest md5;
+        InputStream is = null;
+        DigestInputStream dis = null;
+        try {
+            md5 = MessageDigest.getInstance(algorithm);
+            is = new FileInputStream(file);
+            dis = new DigestInputStream(is, md5);
+
+            md5.update(getContentOfStream(dis).getBytes());
+        } finally {
+            if (is != null) {
+                is.close();
+            }
+            if (dis != null) {
+                dis.close();
+            }
+        }
+
+        return md5.digest();
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/netx/net/sourceforge/jnlp/util/MD5SumWatcher.java	Mon Mar 10 15:39:56 2014 -0400
@@ -0,0 +1,104 @@
+/*Copyright (C) 2014 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version.
+ */
+
+package net.sourceforge.jnlp.util;
+
+import static net.sourceforge.jnlp.runtime.Translator.R;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+
+import net.sourceforge.jnlp.util.logging.OutputController;
+
+public class MD5SumWatcher {
+
+    private final File watchedFile;
+    private byte[] md5sum;
+
+    /**
+     * Create a new MD5SumWatcher instance
+     * @param watchedFile the file to watch
+     */
+    public MD5SumWatcher(final File watchedFile) {
+        this.watchedFile = watchedFile;
+        try {
+            this.md5sum = getSum();
+        } catch (final IOException ioe) {
+            OutputController.getLogger().log(ioe);
+            this.md5sum = null;
+        }
+    }
+
+    /**
+     * Get the current MD5 sum of the watched file
+     * @return a byte array of the MD5 sum
+     * @throws FileNotFoundException if the watched file does not exist
+     * @throws IOException if the file cannot be read
+     */
+    public byte[] getSum() throws FileNotFoundException, IOException {
+        update();
+        return md5sum;
+    }
+
+    /**
+     * Detect if the file's MD5 has changed and track its new sum if so
+     * @return if the file's MD5 has changed since the last update
+     * @throws FileNotFoundException if the watched file does not exist
+     * @throws IOException if the file cannot be read
+     */
+    public boolean update() throws FileNotFoundException, IOException {
+        byte[] newSum;
+        try {
+            newSum = FileUtils.getFileMD5Sum(watchedFile, "MD5");
+        } catch (final NoSuchAlgorithmException e) {
+            // There definitely should be an MD5 algorithm, but if not, all we can do is fail.
+            // This really, really is not expected to happen, so rethrow as RuntimeException
+            // to avoid having to check for NoSuchAlgorithmExceptions all the time
+            OutputController.getLogger().log(e);
+            throw new RuntimeException(e);
+        }
+        final boolean changed = !Arrays.equals(newSum, md5sum);
+        md5sum = newSum;
+        return changed;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/netx/unit/net/sourceforge/jnlp/util/MD5SumWatcherTest.java	Mon Mar 10 15:39:56 2014 -0400
@@ -0,0 +1,113 @@
+/*Copyright (C) 2014 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING.  If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version.
+ */
+
+package net.sourceforge.jnlp.util;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Arrays;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MD5SumWatcherTest {
+
+    private File file;
+    private MD5SumWatcher watcher;
+
+    @Before
+    public void createNewFile() throws Exception {
+        file = File.createTempFile("md5sumwatchertest", "tmp");
+        file.deleteOnExit();
+        watcher = new MD5SumWatcher(file);
+    }
+
+    @After
+    public void deleteTempFile() throws Exception {
+        if (file.exists()) {
+            file.delete();
+        }
+    }
+
+    @Test
+    public void testNonExistentFile() {
+        file.delete();
+        file.mkdirs();
+        watcher = new MD5SumWatcher(file);
+        boolean gotException = false;
+        try {
+            watcher.update();
+        } catch (final Exception e) {
+            gotException = true;
+            assertTrue("Should have received FileNotFoundException", e instanceof FileNotFoundException);
+        }
+        assertTrue("Should have received FileNotFoundException", gotException);
+    }
+
+    @Test
+    public void testNoFileChangeGivesSameMd5() throws Exception {
+        byte[] sum = watcher.getSum();
+        byte[] sum2 = watcher.getSum();
+        assertTrue("MD5 sums should be the same. first: " + Arrays.toString(sum) + ", second: " + Arrays.toString(sum2),
+                Arrays.equals(sum, sum2));
+    }
+
+    @Test
+    public void testSavingToFileChangesMd5() throws Exception {
+        byte[] original = watcher.getSum();
+        FileUtils.saveFile("some test content\n", file);
+        byte[] changed = watcher.getSum();
+        assertFalse("MD5 sum should have changed, but was constant as " + Arrays.toString(original),
+                Arrays.equals(original, changed));
+    }
+
+    @Test
+    public void testUnchangedContentUpdate() throws Exception {
+        assertFalse("update() should return false", watcher.update());
+    }
+
+    @Test
+    public void testChangedContentUpdate() throws Exception {
+        FileUtils.saveFile("some test content\n", file);
+        final boolean changed = watcher.update();
+        assertTrue("update() should return true", changed);
+    }
+
+}