Repository: metron
Updated Branches:
  refs/heads/master 174964832 -> a2bae0bce


METRON-539 Added functions to hash values. (jjmeyer0) closes apache/metron#641


Project: http://git-wip-us.apache.org/repos/asf/metron/repo
Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/a2bae0bc
Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/a2bae0bc
Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/a2bae0bc

Branch: refs/heads/master
Commit: a2bae0bce4d0739c5b1b7e5d803ee1c44ea529ca
Parents: 1749648
Author: jjmeyer0 <[email protected]>
Authored: Fri Aug 25 11:25:17 2017 -0500
Committer: jmeyer <[email protected]>
Committed: Fri Aug 25 11:25:17 2017 -0500

----------------------------------------------------------------------
 metron-stellar/stellar-common/README.md         | 118 ++++++-----
 .../common/utils/hashing/DefaultHasher.java     |  97 +++++++++
 .../stellar/common/utils/hashing/Hasher.java    |  35 ++++
 .../stellar/dsl/functions/HashFunctions.java    |  86 ++++++++
 .../common/utils/hashing/DefaultHasherTest.java |  59 ++++++
 .../dsl/functions/HashFunctionsTest.java        | 205 +++++++++++++++++++
 6 files changed, 548 insertions(+), 52 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metron/blob/a2bae0bc/metron-stellar/stellar-common/README.md
----------------------------------------------------------------------
diff --git a/metron-stellar/stellar-common/README.md 
b/metron-stellar/stellar-common/README.md
index f12c77d..a25c831 100644
--- a/metron-stellar/stellar-common/README.md
+++ b/metron-stellar/stellar-common/README.md
@@ -101,62 +101,63 @@ In the core language functions, we support basic 
functional programming primitiv
 
 ## Stellar Core Functions
 
-|                                                                              
    |
-| ----------                                                                   
    |
-| [ `ABS`](../../metron-analytics/metron-statistics#abs)                       
    |
-| [ `APPEND_IF_MISSING`](#append_if_missing)                                   
    |
-| [ `BIN`](../../metron-analytics/metron-statistics#bin)                       
    |
-| [ `BLOOM_ADD`](#bloom_add)                                                   
    |
-| [ `BLOOM_EXISTS`](#bloom_exists)                                             
    |
-| [ `BLOOM_INIT`](#bloom_init)                                                 
    |
-| [ `BLOOM_MERGE`](#bloom_merge)                                               
    |
+|                                                                              
                      |
+| ----------                                                                   
                      |
+| [ `ABS`](../../metron-analytics/metron-statistics#abs)                       
                      |
+| [ `APPEND_IF_MISSING`](#append_if_missing)                                   
                      |
+| [ `BIN`](../../metron-analytics/metron-statistics#bin)                       
                      |
+| [ `BLOOM_ADD`](#bloom_add)                                                   
                      |
+| [ `BLOOM_EXISTS`](#bloom_exists)                                             
                      |
+| [ `BLOOM_INIT`](#bloom_init)                                                 
                      |
+| [ `BLOOM_MERGE`](#bloom_merge)                                               
                      |
 | [ `CEILING`](#ceiling)                                                       
    |
 | [ `COS`](#cos)                                                               
    |
-| [ `CHOP`](#chop)                                                             
    |
-| [ `CHOMP`](#chomp)                                                           
    |
-| [ `COUNT_MATCHES`](#count_matches)                                           
    |
-| [ `DAY_OF_MONTH`](#day_of_month)                                             
    |
-| [ `DAY_OF_WEEK`](#day_of_week)                                               
    |
-| [ `DAY_OF_YEAR`](#day_of_year)                                               
    |
-| [ `DECODE`](#decode)                                                         
    |
-| [ `DOMAIN_REMOVE_SUBDOMAINS`](#domain_remove_subdomains)                     
    |
-| [ `DOMAIN_REMOVE_TLD`](#domain_remove_tld)                                   
    |
-| [ `DOMAIN_TO_TLD`](#domain_to_tld)                                           
    |
-| [ `ENCODE`](#encode)                                                         
    |
-| [ `ENDS_WITH`](#ends_with)                                                   
    |
-| [ `ENRICHMENT_EXISTS`](#enrichment_exists)                                   
    |
-| [ `ENRICHMENT_GET`](#enrichment_get)                                         
    |
+| [ `CHOP`](#chop)                                                             
                      |
+| [ `CHOMP`](#chomp)                                                           
                      |
+| [ `COUNT_MATCHES`](#count_matches)                                           
                      |
+| [ `DAY_OF_MONTH`](#day_of_month)                                             
                      |
+| [ `DAY_OF_WEEK`](#day_of_week)                                               
                      |
+| [ `DAY_OF_YEAR`](#day_of_year)                                               
                      |
+| [ `DECODE`](#decode)                                                         
                      |
+| [ `DOMAIN_REMOVE_SUBDOMAINS`](#domain_remove_subdomains)                     
                      |
+| [ `DOMAIN_REMOVE_TLD`](#domain_remove_tld)                                   
                      |
+| [ `DOMAIN_TO_TLD`](#domain_to_tld)                                           
                      |
+| [ `ENCODE`](#encode)                                                         
                      |
+| [ `ENDS_WITH`](#ends_with)                                                   
                      |
+| [ `ENRICHMENT_EXISTS`](#enrichment_exists)                                   
                      |
+| [ `ENRICHMENT_GET`](#enrichment_get)                                         
                      |
 | [ `EXP`](#exp)                                                               
    |
-| [ `FILL_LEFT`](#fill_left)                                                   
    |
-| [ `FILL_RIGHT`](#fill_right)                                                 
    |
-| [ `FILTER`](#filter)                                                         
    |
+| [ `FILL_LEFT`](#fill_left)                                                   
                      |
+| [ `FILL_RIGHT`](#fill_right)                                                 
                      |
+| [ `FILTER`](#filter)                                                         
                      |
 | [ `FLOOR`](#floor)                                                           
    |
-| [ `FORMAT`](#format)                                                         
    |
-| [ 
`HLLP_CARDINALITY`](../../metron-analytics/metron-statistics#hllp_cardinality) |
-| [ `HLLP_INIT`](../../metron-analytics/metron-statistics#hllp_init)           
    |
-| [ `HLLP_MERGE`](../../metron-analytics/metron-statistics#hllp_merge)         
    |
-| [ `HLLP_OFFER`](../../metron-analytics/metron-statistics#hllp_offer)         
    |
-| [ `GEO_GET`](#geo_get)                                                       
    |
-| [ `GET`](#get)                                                               
    |
-| [ `GET_FIRST`](#get_first)                                                   
    |
-| [ `GET_LAST`](#get_last)                                                     
    |
-| [ `GET_SUPPORTED_ENCODINGS`](#get_supported_encodings)                       
    |
-| [ `IN_SUBNET`](#in_subnet)                                                   
    |
-| [ `IS_DATE`](#is_date)                                                       
    |
-| [ `IS_ENCODING`](#is_encoding)                                               
    |
-| [ `IS_DOMAIN`](#is_domain)                                                   
    |
-| [ `IS_EMAIL`](#is_email)                                                     
    |
-| [ `IS_EMPTY`](#is_empty)                                                     
    |
-| [ `IS_INTEGER`](#is_integer)                                                 
    |
-| [ `IS_IP`](#is_ip)                                                           
    |
-| [ `IS_URL`](#is_url)                                                         
    |
-| [ `JOIN`](#join)                                                             
    |
-| [ `KAFKA_GET`](#kafka_get)                                                   
    |
-| [ `KAFKA_PROPS`](#kafka_props)                                               
    |
-| [ `KAFKA_PUT`](#kafka_put)                                                   
    |
-| [ `KAFKA_TAIL`](#kafka_tail)                                                 
    |
-| [ `LENGTH`](#length)                                                         
    |
-| [ `LIST_ADD`](#list_add)                                                     
                          |
+| [ `FORMAT`](#format)                                                         
                      |
+| [ `GEO_GET`](#geo_get)                                                       
                      |
+| [ `GET`](#get)                                                               
                      |
+| [ `GET_FIRST`](#get_first)                                                   
                      |
+| [ `GET_LAST`](#get_last)                                                     
                      |
+| [ `GET_SUPPORTED_ENCODINGS`](#get_supported_encodings)                       
                    |
+| [ `HASH`](#hash)                                                             
              |
+| [ 
`HLLP_CARDINALITY`](../../metron-analytics/metron-statistics#hllp_cardinality)  
                 |
+| [ `HLLP_INIT`](../../metron-analytics/metron-statistics#hllp_init)           
                      |
+| [ `HLLP_MERGE`](../../metron-analytics/metron-statistics#hllp_merge)         
                      |
+| [ `HLLP_OFFER`](../../metron-analytics/metron-statistics#hllp_offer)         
                      |
+| [ `IN_SUBNET`](#in_subnet)                                                   
                      |
+| [ `IS_DATE`](#is_date)                                                       
                      |
+| [ `IS_ENCODING`](#is_encoding)                                               
                      |
+| [ `IS_DOMAIN`](#is_domain)                                                   
                      |
+| [ `IS_EMAIL`](#is_email)                                                     
                      |
+| [ `IS_EMPTY`](#is_empty)                                                     
                      |
+| [ `IS_INTEGER`](#is_integer)                                                 
                      |
+| [ `IS_IP`](#is_ip)                                                           
                      |
+| [ `IS_URL`](#is_url)                                                         
                      |
+| [ `JOIN`](#join)                                                             
                      |
+| [ `KAFKA_GET`](#kafka_get)                                                   
                      |
+| [ `KAFKA_PROPS`](#kafka_props)                                               
                      |
+| [ `KAFKA_PUT`](#kafka_put)                                                   
                      |
+| [ `KAFKA_TAIL`](#kafka_tail)                                                 
                      |
+| [ `LENGTH`](#length)                                                         
                      |
+| [ `LIST_ADD`](#list_add)                                                     
                      |
 | [ `LOG2`](#log2)                                                             
                  |
 | [ `LOG10`](#log10)                                                           
                    |
 | [ `LN`](#ln)                                                                 
              |
@@ -431,6 +432,10 @@ In the core language functions, we support basic 
functional programming primitiv
   * Input:
     * input - List
   * Returns: First element of the list
+  
+### `GET_HASHES_AVAILABLE`
+  * Description: Will return all available hashing algorithms available to 
'HASH'.
+  * Returns: A list containing all supported hashing algorithms.
 
 ### `GET_LAST`
   * Description: Returns the last element of the list
@@ -438,6 +443,15 @@ In the core language functions, we support basic 
functional programming primitiv
     * input - List
   * Returns: Last element of the list
 
+### `HASH`
+  * Description: Hashes a given value using the given hashing algorithm and 
returns a hex encoded string.
+  * Input: 
+    * toHash - value to hash.
+    * hashType - A valid string representation of a hashing algorithm. See 
'GET_HASHES_AVAILABLE'.
+  * Returns: A hex encoded string of a hashed value using the given algorithm. 
If 'hashType' is null 
+  then '00', padded to the necessary length, will be returned. If 'toHash' is 
not able to be hashed or 
+  'hashType' is null then null is returned.
+
 ### `IN_SUBNET`
   * Description: Returns true if an IP is within a subnet range.
   * Input:

http://git-wip-us.apache.org/repos/asf/metron/blob/a2bae0bc/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/hashing/DefaultHasher.java
----------------------------------------------------------------------
diff --git 
a/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/hashing/DefaultHasher.java
 
b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/hashing/DefaultHasher.java
new file mode 100644
index 0000000..c950a19
--- /dev/null
+++ 
b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/hashing/DefaultHasher.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.metron.stellar.common.utils.hashing;
+
+import org.apache.commons.codec.BinaryEncoder;
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.Serializable;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class DefaultHasher implements Hasher {
+
+  private String algorithm;
+  private BinaryEncoder encoder;
+  private Charset charset;
+
+  /**
+   * Builds a utility to hash values based on a given algorithm.
+   * @param algorithm The algorithm used when hashing a value.
+   * @param encoder The encoder to use to encode the hashed value.
+   * @param charset The charset that will be used during hashing and encoding.
+   * @see java.security.Security
+   * @see java.security.MessageDigest
+   */
+  public DefaultHasher(final String algorithm, final BinaryEncoder encoder, 
final Charset charset) {
+    this.algorithm = algorithm;
+    this.encoder = encoder;
+    this.charset = charset;
+  }
+
+  /**
+   * Builds a utility to hash values based on a given algorithm. Uses {@link 
StandardCharsets#UTF_8} for encoding.
+   * @param algorithm The algorithm used when hashing a value.
+   * @param encoder The encoder to use to encode the hashed value.
+   * @see java.security.Security
+   * @see java.security.MessageDigest
+   */
+  public DefaultHasher(final String algorithm, final BinaryEncoder encoder) {
+    this.algorithm = algorithm;
+    this.encoder = encoder;
+    this.charset = StandardCharsets.UTF_8;
+  }
+
+  /**
+   * {@inheritDoc}
+   *
+   * Returns a hash which has been encoded using the supplied encoder. If 
input is null then a string
+   * containing all '0' will be returned. The length of the string is 
determined by the hashing algorithm
+   * used.
+   * @param toHash The value to hash.
+   * @return A hash of {@code toHash} that has been encoded.
+   * @throws EncoderException If unable to encode the hash then this exception 
occurs.
+   * @throws NoSuchAlgorithmException If the supplied algorithm is not known.
+   */
+  @Override
+  public String getHash(final Object toHash) throws EncoderException, 
NoSuchAlgorithmException {
+    final MessageDigest messageDigest = MessageDigest.getInstance(algorithm);
+
+    if (toHash == null) {
+      return StringUtils.repeat("00", messageDigest.getDigestLength());
+    } else if (toHash instanceof String) {
+      return getHash(messageDigest, toHash.toString().getBytes(charset));
+    } else if (toHash instanceof Serializable) {
+      final byte[] serialized = SerializationUtils.serialize((Serializable) 
toHash);
+      return getHash(messageDigest, serialized);
+    }
+
+    return null;
+  }
+
+  private String getHash(final MessageDigest messageDigest, final byte[] 
toHash) throws EncoderException {
+    messageDigest.update(toHash);
+    final byte[] encode = encoder.encode(messageDigest.digest());
+
+    return new String(encode, charset);
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/a2bae0bc/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/hashing/Hasher.java
----------------------------------------------------------------------
diff --git 
a/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/hashing/Hasher.java
 
b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/hashing/Hasher.java
new file mode 100644
index 0000000..08e8f72
--- /dev/null
+++ 
b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/utils/hashing/Hasher.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.metron.stellar.common.utils.hashing;
+
+import org.apache.commons.codec.EncoderException;
+
+import java.security.NoSuchAlgorithmException;
+
+public interface Hasher {
+
+  /**
+   * Returns an encoded string representation of the hash value of the input. 
It is expected that
+   * this implementation does throw exceptions when the input is null.
+   * @param toHash The value to hash.
+   * @return A hash of {@code toHash} that has been encoded.
+   * @throws EncoderException If unable to encode the hash then this exception 
occurs.
+   * @throws NoSuchAlgorithmException If the supplied algorithm is not known.
+   */
+  String getHash(final Object toHash) throws EncoderException, 
NoSuchAlgorithmException;
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/a2bae0bc/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/HashFunctions.java
----------------------------------------------------------------------
diff --git 
a/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/HashFunctions.java
 
b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/HashFunctions.java
new file mode 100644
index 0000000..5e59b6e
--- /dev/null
+++ 
b/metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/dsl/functions/HashFunctions.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.metron.stellar.dsl.functions;
+
+import org.apache.commons.codec.EncoderException;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.metron.stellar.common.utils.hashing.DefaultHasher;
+import org.apache.metron.stellar.dsl.BaseStellarFunction;
+import org.apache.metron.stellar.dsl.Stellar;
+
+import java.nio.charset.StandardCharsets;
+import java.security.NoSuchAlgorithmException;
+import java.security.Security;
+import java.util.ArrayList;
+import java.util.List;
+
+public class HashFunctions {
+
+  @Stellar(
+    name = "GET_HASHES_AVAILABLE",
+    description = "Will return all available hashing algorithms available to 
'HASH'.",
+    returns = "A list containing all supported hashing algorithms."
+  )
+  public static class ListSupportedHashTypes extends BaseStellarFunction {
+
+    @Override
+    public List<String> apply(final List<Object> args) {
+      if (args == null || args.size() != 0) {
+        throw new IllegalArgumentException("Invalid call. This function does 
not expect any arguments.");
+      }
+
+      return new ArrayList<>(Security.getAlgorithms("MessageDigest"));
+    }
+  }
+
+  @Stellar(
+    name = "HASH",
+    description = "Hashes a given value using the given hashing algorithm and 
returns a hex encoded string.",
+    params = {
+      "toHash - value to hash.",
+      "hashType - A valid string representation of a hashing algorithm. See 
'GET_HASHES_AVAILABLE'.",
+    },
+    returns = "A hex encoded string of a hashed value using the given 
algorithm. If 'hashType' is null " +
+      "then '00', padded to the necessary length, will be returned. If 
'toHash' is not able to be hashed or " +
+      "'hashType' is null then null is returned."
+  )
+  public static class Hash extends BaseStellarFunction {
+
+    @Override
+    public Object apply(final List<Object> args) {
+      if (args == null || args.size() != 2) {
+        throw new IllegalArgumentException("Invalid number of arguments: " + 
(args == null ? 0 : args.size()));
+      }
+
+      final Object toHash = args.get(0);
+      final Object hashType = args.get(1);
+
+      if (hashType == null) {
+        return null;
+      }
+
+      try {
+        return new DefaultHasher(hashType.toString(), new 
Hex(StandardCharsets.UTF_8)).getHash(toHash);
+      } catch (final EncoderException e) {
+        return null;
+      } catch (final NoSuchAlgorithmException e) {
+        throw new IllegalArgumentException("Invalid hash type: " + 
hashType.toString());
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/metron/blob/a2bae0bc/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/utils/hashing/DefaultHasherTest.java
----------------------------------------------------------------------
diff --git 
a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/utils/hashing/DefaultHasherTest.java
 
b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/utils/hashing/DefaultHasherTest.java
new file mode 100644
index 0000000..6ab2184
--- /dev/null
+++ 
b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/common/utils/hashing/DefaultHasherTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.metron.stellar.common.utils.hashing;
+
+import org.apache.commons.codec.BinaryEncoder;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.Test;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+
+public class DefaultHasherTest {
+  private Charset charset = StandardCharsets.UTF_8;
+  private BinaryEncoder encoder = new Hex(charset);
+
+  @Test
+  public void getHashAsHexOfNullValueReturnsPadded00() throws Exception {
+    assertEquals(StringUtils.repeat("00", 16), new DefaultHasher("md5", 
encoder).getHash(null));
+    assertEquals(StringUtils.repeat("00", 32), new DefaultHasher("sha-256", 
encoder).getHash(null));
+  }
+
+  @Test
+  public void nonSerializableShouldReturnNull() throws Exception {
+    assertNull(new DefaultHasher("md5", encoder, charset).getHash(new 
Object()));
+  }
+
+  @Test
+  public void hashingStringWithEmptyString() throws Exception {
+    assertEquals("d41d8cd98f00b204e9800998ecf8427e", new DefaultHasher("md5", 
encoder).getHash(""));
+  }
+
+  @Test
+  public void hashingSerializableObject() throws Exception {
+    final Collection<String> serializable = Collections.emptyList();
+    assertEquals("ef5e8c8d27af3a953b4674065c99a52a", new DefaultHasher("md5", 
encoder, charset).getHash(serializable));
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/metron/blob/a2bae0bc/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/HashFunctionsTest.java
----------------------------------------------------------------------
diff --git 
a/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/HashFunctionsTest.java
 
b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/HashFunctionsTest.java
new file mode 100644
index 0000000..31bc6d3
--- /dev/null
+++ 
b/metron-stellar/stellar-common/src/test/java/org/apache/metron/stellar/dsl/functions/HashFunctionsTest.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.metron.stellar.dsl.functions;
+
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.lang.SerializationUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.Test;
+
+import java.io.Serializable;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Security;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.metron.stellar.common.utils.StellarProcessorUtils.run;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class HashFunctionsTest {
+  static final Hex HEX = new Hex(StandardCharsets.UTF_8);
+  final HashFunctions.ListSupportedHashTypes listSupportedHashTypes = new 
HashFunctions.ListSupportedHashTypes();
+  final HashFunctions.Hash hash = new HashFunctions.Hash();
+
+  @Test(expected = IllegalArgumentException.class)
+  public void nullArgumentsShouldFail() throws Exception {
+    listSupportedHashTypes.apply(null);
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void getSupportedHashAlgorithmsCalledWithParametersShouldFail() 
throws Exception {
+    listSupportedHashTypes.apply(Collections.singletonList("bogus"));
+  }
+
+  @Test
+  public void 
listSupportedHashTypesReturnsAtMinimumTheHashingAlgorithmsThatMustBeSupported() 
throws Exception {
+    final List<String> requiredAlgorithmsByJava = Arrays.asList("MD5", "SHA", 
"SHA-256"); // These are required for all Java platforms (see 
java.security.MessageDigest). Note: SHA is SHA-1
+    final Collection<String> supportedHashes = 
listSupportedHashTypes.apply(Collections.emptyList());
+    requiredAlgorithmsByJava.forEach(a -> 
assertTrue(supportedHashes.contains(a)));
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void nullArgumentListShouldThrowException() throws Exception {
+    hash.apply(null);
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void emptyArgumentListShouldThrowException() throws Exception {
+    hash.apply(Collections.emptyList());
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void singleArgumentListShouldThrowException() throws Exception {
+    hash.apply(Collections.singletonList("some value."));
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void argumentListWithMoreThanTwoValuesShouldThrowException3() throws 
Exception {
+    hash.apply(Arrays.asList("1", "2", "3"));
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void argumentListWithMoreThanTwoValuesShouldThrowException4() throws 
Exception {
+    hash.apply(Arrays.asList("1", "2", "3", "4"));
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void invalidAlgorithmArgumentShouldThrowException() throws Exception {
+    hash.apply(Arrays.asList("value to hash", "invalidAlgorithm"));
+  }
+
+  @Test
+  public void invalidNullAlgorithmArgumentShouldReturnNull() throws Exception {
+    assertNull(hash.apply(Arrays.asList("value to hash", null)));
+  }
+
+  @Test
+  public void nullInputForValueToHashShouldReturnHashedEncodedValueOf0x00() 
throws Exception {
+    assertEquals(StringUtils.repeat('0', 32), hash.apply(Arrays.asList(null, 
"md5")));
+  }
+
+  @Test
+  public void 
nullInputForValueToHashShouldReturnHashedEncodedValueOf0x00InDirectStellarCall()
 throws Exception {
+    final String algorithm = "'md5'";
+    final Map<String, Object> variables = new HashMap<>();
+    variables.put("toHash", null);
+
+    assertEquals(StringUtils.repeat('0', 32), run("HASH(toHash, " + algorithm 
+ ")", variables));
+  }
+
+  @Test
+  public void allAlgorithmsForMessageDigestShouldBeAbleToHash() throws 
Exception {
+    final String valueToHash = "My value to hash";
+    final Set<String> algorithms = Security.getAlgorithms("MessageDigest");
+
+    algorithms.forEach(algorithm -> {
+      try {
+        final MessageDigest expected = MessageDigest.getInstance(algorithm);
+        expected.update(valueToHash.getBytes(StandardCharsets.UTF_8));
+
+        assertEquals(expectedHexString(expected), 
hash.apply(Arrays.asList(valueToHash, algorithm)));
+      } catch (NoSuchAlgorithmException e) {
+        throw new RuntimeException(e);
+      }
+    });
+  }
+
+  @Test
+  public void 
allAlgorithmsForMessageDigestShouldBeAbleToHashDirectStellarCall() throws 
Exception {
+    final String valueToHash = "My value to hash";
+    final Set<String> algorithms = Security.getAlgorithms("MessageDigest");
+
+    algorithms.forEach(algorithm -> {
+      try {
+        final Object actual = run("HASH('" + valueToHash + "', '" + algorithm 
+ "')", Collections.emptyMap());
+
+        final MessageDigest expected = MessageDigest.getInstance(algorithm);
+        expected.update(valueToHash.getBytes(StandardCharsets.UTF_8));
+
+        assertEquals(expectedHexString(expected), actual);
+      } catch (NoSuchAlgorithmException e) {
+        throw new RuntimeException(e);
+      }
+    });
+  }
+
+  @Test
+  public void nonStringValueThatIsSerializableHashesSuccessfully() throws 
Exception {
+    final String algorithm = "'md5'";
+    final String valueToHash = "'My value to hash'";
+    final Serializable input = (Serializable) 
Collections.singletonList(valueToHash);
+
+    final MessageDigest expected = 
MessageDigest.getInstance(algorithm.replace("'", ""));
+    expected.update(SerializationUtils.serialize(input));
+
+    final Map<String, Object> variables = new HashMap<>();
+    variables.put("toHash", input);
+
+    assertEquals(expectedHexString(expected), run("HASH(toHash, " + algorithm 
+ ")", variables));
+  }
+
+  @Test
+  public void callingHashFunctionsWithVariablesAsInputHashesSuccessfully() 
throws Exception {
+    final String algorithm = "md5";
+    final String valueToHash = "'My value to hash'";
+    final Serializable input = (Serializable) 
Collections.singletonList(valueToHash);
+
+    final MessageDigest expected = MessageDigest.getInstance(algorithm);
+    expected.update(SerializationUtils.serialize(input));
+
+    final Map<String, Object> variables = new HashMap<>();
+    variables.put("toHash", input);
+    variables.put("hashType", algorithm);
+
+    assertEquals(expectedHexString(expected), run("HASH(toHash, hashType)", 
variables));
+  }
+
+  @Test
+  public void 
callingHashFunctionWhereOnlyHashTypeIsAVariableHashesSuccessfully() throws 
Exception {
+    final String algorithm = "md5";
+    final String valueToHash = "'My value to hash'";
+
+    final MessageDigest expected = MessageDigest.getInstance(algorithm);
+    expected.update(valueToHash.replace("'", 
"").getBytes(StandardCharsets.UTF_8));
+
+    final Map<String, Object> variables = new HashMap<>();
+    variables.put("hashType", algorithm);
+
+    assertEquals(expectedHexString(expected), run("HASH(" + valueToHash + ", 
hashType)", variables));
+  }
+
+  @Test
+  public void aNonNullNonSerializableObjectReturnsAValueOfNull() throws 
Exception {
+    final Map<String, Object> variables = new HashMap<>();
+    variables.put("toHash", new Object());
+    assertNull(run("HASH(toHash, 'md5')", variables));
+  }
+
+  private String expectedHexString(MessageDigest expected) {
+    return new String(HEX.encode(expected.digest()), StandardCharsets.UTF_8);
+  }
+}

Reply via email to