[dna-commits] DNA SVN: r461 - in trunk: dna-spi/src/main/java/org/jboss/dna/spi and 4 other directories.

dna-commits at lists.jboss.org dna-commits at lists.jboss.org
Fri Aug 22 15:32:13 EDT 2008


Author: rhauch
Date: 2008-08-22 15:32:12 -0400 (Fri, 22 Aug 2008)
New Revision: 461

Modified:
   trunk/dna-common/src/main/java/org/jboss/dna/common/util/StringUtil.java
   trunk/dna-spi/src/main/java/org/jboss/dna/spi/SpiI18n.java
   trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/Binary.java
   trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/ValueComparators.java
   trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/InMemoryBinary.java
   trunk/dna-spi/src/main/resources/org/jboss/dna/spi/SpiI18n.properties
   trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/impl/InMemoryBinaryTest.java
Log:
Added to the Binary interface the ability to obtain the SHA-1 hash of the content.  This can be used to determine whether two Binary instances contain the same content, as the SHA-1 digests will be the same given the same content.

Modified: trunk/dna-common/src/main/java/org/jboss/dna/common/util/StringUtil.java
===================================================================
--- trunk/dna-common/src/main/java/org/jboss/dna/common/util/StringUtil.java	2008-08-20 19:16:50 UTC (rev 460)
+++ trunk/dna-common/src/main/java/org/jboss/dna/common/util/StringUtil.java	2008-08-22 19:32:12 UTC (rev 461)
@@ -336,8 +336,8 @@
     }
 
     /**
-     * Read and return the entire contents of the supplied {@link InputStream}. This method always closes the stream when
-     * finished reading.
+     * Read and return the entire contents of the supplied {@link InputStream}. This method always closes the stream when finished
+     * reading.
      * 
      * @param stream the streamed contents; may be null
      * @return the contents, or an empty string if the supplied stream is null
@@ -383,8 +383,8 @@
      * <li>A string is written wrapped by double quotes.</li>
      * <li>A boolean is written using {@link Boolean#toString()}.</li>
      * <li>A {@link Number number} is written using the standard {@link Number#toString() toString()} method.</li>
-     * <li>A {@link java.util.Date date} is written using the the {@link DateUtil#getDateAsStandardString(java.util.Date)}
-     * utility method.</li>
+     * <li>A {@link java.util.Date date} is written using the the {@link DateUtil#getDateAsStandardString(java.util.Date)} utility
+     * method.</li>
      * <li>A {@link java.sql.Date SQL date} is written using the the {@link DateUtil#getDateAsStandardString(java.util.Date)}
      * utility method.</li>
      * <li>A {@link Calendar Calendar instance} is written using the the {@link DateUtil#getDateAsStandardString(Calendar)}
@@ -392,10 +392,10 @@
      * <li>An array of bytes is written with a leading "[ " and trailing " ]" surrounding the bytes written as UTF-8.
      * <li>An array of objects is written with a leading "[ " and trailing " ]", and with all objects sent through
      * {@link #readableString(Object)} and separated by ", ".</li>
-     * <li>A collection of objects (e.g, <code>Collection<?></code>) is written with a leading "[ " and trailing " ]", and
-     * with all objects sent through {@link #readableString(Object)} and separated by ", ".</li>
-     * <li>A map of objects (e.g, <code>Map<?></code>) is written with a leading "{ " and trailing " }", and with all map
-     * entries written in the form "key => value" and separated by ", ". All key and value objects are sent through the
+     * <li>A collection of objects (e.g, <code>Collection<?></code>) is written with a leading "[ " and trailing " ]", and with
+     * all objects sent through {@link #readableString(Object)} and separated by ", ".</li>
+     * <li>A map of objects (e.g, <code>Map<?></code>) is written with a leading "{ " and trailing " }", and with all map entries
+     * written in the form "key => value" and separated by ", ". All key and value objects are sent through the
      * {@link #readableString(Object)} method.</li>
      * <li>Any other object is written using the object's {@link Object#toString() toString()} method.</li>
      * </ul>
@@ -649,6 +649,28 @@
         return NORMALIZE_PATTERN.matcher(text).replaceAll(" ").trim();
     }
 
+    private static final byte[] HEX_CHAR_TABLE = {(byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6',
+        (byte)'7', (byte)'8', (byte)'9', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f'};
+
+    /**
+     * Get the hexadecimal string representation of the supplied byte array.
+     * 
+     * @param bytes the byte array
+     * @return the hex string representation of the byte array; never null
+     * @throws UnsupportedEncodingException
+     */
+    public static String getHexString( byte[] bytes ) throws UnsupportedEncodingException {
+        byte[] hex = new byte[2 * bytes.length];
+        int index = 0;
+
+        for (byte b : bytes) {
+            int v = b & 0xFF;
+            hex[index++] = HEX_CHAR_TABLE[v >>> 4];
+            hex[index++] = HEX_CHAR_TABLE[v & 0xF];
+        }
+        return new String(hex, "ASCII");
+    }
+
     private StringUtil() {
         // Prevent construction
     }

Modified: trunk/dna-spi/src/main/java/org/jboss/dna/spi/SpiI18n.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/SpiI18n.java	2008-08-20 19:16:50 UTC (rev 460)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/SpiI18n.java	2008-08-22 19:32:12 UTC (rev 461)
@@ -58,6 +58,7 @@
     public static I18n pathExpressionIsInvalid;
     public static I18n pathExpressionHasInvalidSelect;
     public static I18n pathExpressionHasInvalidMatch;
+    public static I18n messageDigestNotFound;
 
     public static I18n executingGraphCommand;
     public static I18n executedGraphCommand;

Modified: trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/Binary.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/Binary.java	2008-08-20 19:16:50 UTC (rev 460)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/Binary.java	2008-08-22 19:32:12 UTC (rev 461)
@@ -23,10 +23,12 @@
 
 import java.io.InputStream;
 import java.io.Serializable;
+import java.security.MessageDigest;
 import net.jcip.annotations.Immutable;
 
 /**
  * Value holder for binary data. Binary instances are not mutable.
+ * 
  * @author Randall Hauch
  */
 @Immutable
@@ -34,29 +36,53 @@
 
     /**
      * Get the length of this binary data.
+     * 
      * @return the number of bytes in this binary data
+     * @see #acquire()
      */
     public long getSize();
 
     /**
+     * Get the SHA-1 hash of the contents. This hash can be used to determine whether two Binary instances contain the same
+     * content.
+     * <p>
+     * Repeatedly calling this method should generally be efficient, as it most implementations will compute the hash only once.
+     * </p>
+     * 
+     * @return the hash of the contents as a byte array, or an empty array if the hash could not be computed.
+     * @see #acquire()
+     * @see MessageDigest#digest(byte[])
+     * @see MessageDigest#getInstance(String)
+     */
+    public byte[] getHash();
+
+    /**
      * Get the contents of this data as a stream.
+     * 
      * @return the stream to this data's contents
+     * @see #acquire()
      */
     public InputStream getStream();
 
     /**
      * Get the contents of this data as a byte array.
+     * 
      * @return the data as an array
+     * @see #acquire()
      */
     public byte[] getBytes();
 
     /**
      * Acquire any resources for this data. This method must be called before any other method on this object.
+     * 
+     * @see #release()
      */
     public void acquire();
 
     /**
      * Release any acquired resources. This method must be called after a client is finished with this value.
+     * 
+     * @see #acquire()
      */
     public void release();
 

Modified: trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/ValueComparators.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/ValueComparators.java	2008-08-20 19:16:50 UTC (rev 460)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/ValueComparators.java	2008-08-22 19:32:12 UTC (rev 461)
@@ -109,6 +109,19 @@
                     final long len2 = o2.getSize();
                     if (len1 < len2) return -1;
                     if (len1 > len2) return 1;
+
+                    // Compare using the hashes, if available
+                    byte[] hash1 = o1.getHash();
+                    byte[] hash2 = o2.getHash();
+                    if (hash1.length != 0 || hash2.length != 0) {
+                        assert hash1.length == hash2.length;
+                        for (int i = 0; i != hash1.length; ++i) {
+                            int diff = hash1[i] - hash2[i];
+                            if (diff != 0) return diff;
+                        }
+                        return 0;
+                    }
+
                     // Otherwise they are the same length ...
                     InputStream stream1 = null;
                     InputStream stream2 = null;

Modified: trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/InMemoryBinary.java
===================================================================
--- trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/InMemoryBinary.java	2008-08-20 19:16:50 UTC (rev 460)
+++ trunk/dna-spi/src/main/java/org/jboss/dna/spi/graph/impl/InMemoryBinary.java	2008-08-22 19:32:12 UTC (rev 461)
@@ -23,18 +23,29 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
 import net.jcip.annotations.Immutable;
 import org.jboss.dna.common.util.ArgCheck;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.spi.SpiI18n;
 import org.jboss.dna.spi.graph.Binary;
 import org.jboss.dna.spi.graph.ValueComparators;
 
 /**
  * An implementation of {@link Binary} that keeps the binary data in-memory.
+ * 
  * @author Randall Hauch
  */
 @Immutable
 public class InMemoryBinary implements Binary {
 
+    protected static final Set<String> ALGORITHMS_NOT_FOUND_AND_LOGGED = new CopyOnWriteArraySet<String>();
+    private static final String SHA1DIGEST_NAME = "SHA-1";
+    private static final byte[] NO_HASH = new byte[] {};
+
     /**
      */
     private static final long serialVersionUID = 8792863149767123559L;
@@ -42,6 +53,7 @@
     protected static final byte[] EMPTY_CONTENT = new byte[0];
 
     private final byte[] bytes;
+    private byte[] sha1hash;
 
     public InMemoryBinary( byte[] bytes ) {
         ArgCheck.isNotNull(bytes, "bytes");
@@ -57,7 +69,40 @@
 
     /**
      * {@inheritDoc}
+     * 
+     * @see org.jboss.dna.spi.graph.Binary#getHash()
      */
+    public byte[] getHash() {
+        if (sha1hash == null) {
+            // Omnipotent, so doesn't matter if we recompute in concurrent threads ...
+            try {
+                sha1hash = getHash(SHA1DIGEST_NAME);
+            } catch (NoSuchAlgorithmException e) {
+                if (ALGORITHMS_NOT_FOUND_AND_LOGGED.add(SHA1DIGEST_NAME)) {
+                    Logger.getLogger(getClass()).error(e, SpiI18n.messageDigestNotFound, SHA1DIGEST_NAME);
+                }
+                sha1hash = NO_HASH;
+            }
+        }
+        return sha1hash;
+    }
+
+    /**
+     * Get the hash of the contents, using the digest identified by the supplied name.
+     * 
+     * @param digestName the name of the hashing function (or {@link MessageDigest message digest}) that should be used
+     * @return the hash of the contents as a byte array
+     * @throws NoSuchAlgorithmException if the supplied algorithm could not be found
+     */
+    protected byte[] getHash( String digestName ) throws NoSuchAlgorithmException {
+        MessageDigest digest = MessageDigest.getInstance(digestName);
+        assert digest != null;
+        return digest.digest(bytes);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
     public byte[] getBytes() {
         return this.bytes;
     }

Modified: trunk/dna-spi/src/main/resources/org/jboss/dna/spi/SpiI18n.properties
===================================================================
--- trunk/dna-spi/src/main/resources/org/jboss/dna/spi/SpiI18n.properties	2008-08-20 19:16:50 UTC (rev 460)
+++ trunk/dna-spi/src/main/resources/org/jboss/dna/spi/SpiI18n.properties	2008-08-22 19:32:12 UTC (rev 461)
@@ -45,6 +45,7 @@
 pathExpressionIsInvalid = The path expression {0} is not valid
 pathExpressionHasInvalidSelect = Invalid select expression "{0}" in the path expression "{1}"
 pathExpressionHasInvalidMatch = Invalid match expression "{0}" in the path expression "{1}"
+messageDigestNotFound = The "{0}" message digest algorithm could not be found
 
 executingGraphCommand = Executing {0}
 executedGraphCommand = Executed {0}

Modified: trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/impl/InMemoryBinaryTest.java
===================================================================
--- trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/impl/InMemoryBinaryTest.java	2008-08-20 19:16:50 UTC (rev 460)
+++ trunk/dna-spi/src/test/java/org/jboss/dna/spi/graph/impl/InMemoryBinaryTest.java	2008-08-22 19:32:12 UTC (rev 461)
@@ -28,6 +28,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import org.jboss.dna.common.util.IoUtil;
+import org.jboss.dna.common.util.StringUtil;
 import org.jboss.dna.spi.graph.Binary;
 import org.junit.Before;
 import org.junit.Test;
@@ -109,4 +110,24 @@
         assertThat(another, hasContent(shorterContent));
     }
 
+    @Test
+    public void shouldComputeSha1HashOfEmptyContent() throws Exception {
+        validByteArrayContent = new byte[0];
+        binary = new InMemoryBinary(validByteArrayContent);
+        assertThat(binary.getSize(), is(0l));
+        assertThat(binary, hasNoContent());
+        byte[] hash = binary.getHash();
+        assertThat(hash.length, is(20));
+        assertThat(StringUtil.getHexString(hash), is("da39a3ee5e6b4b0d3255bfef95601890afd80709"));
+    }
+
+    @Test
+    public void shouldComputeSha1HashOfNonEmptyContent() throws Exception {
+        binary = new InMemoryBinary(validByteArrayContent);
+        assertThat(binary.getSize(), is((long)validByteArrayContent.length));
+        byte[] hash = binary.getHash();
+        assertThat(hash.length, is(20));
+        assertThat(StringUtil.getHexString(hash), is("14abe696257e85ba18b7c784d6c7855f46ce50ea"));
+    }
+
 }




More information about the dna-commits mailing list