expose immutable list of filters to its users

+ additional related junits

Change-Id: I00293cd9aa911dfb3d658cad4ee0441ad3410e9c
Issue-ID: POLICY-164
Signed-off-by: Jorge Hernandez <jh1730@att.com>
diff --git a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/EventProtocolCoder.java b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/EventProtocolCoder.java
index 7f1d9b7..5446677 100644
--- a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/EventProtocolCoder.java
+++ b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/EventProtocolCoder.java
@@ -1,8 +1,8 @@
 /*-
  * ============LICENSE_START=======================================================
- * policy-management
+ * ONAP
  * ================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -86,14 +86,14 @@
 		/**
 		 * @return the filter
 		 */
-		public synchronized JsonProtocolFilter getFilter() {
+		public JsonProtocolFilter getFilter() {
 			return filter;
 		}
 
 		/**
 		 * @param filter the filter to set
 		 */
-		public synchronized void setFilter(JsonProtocolFilter filter) {
+		public void setFilter(JsonProtocolFilter filter) {
 			this.filter = filter;
 		}
 
diff --git a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilter.java b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilter.java
index 12d0ffe..c5f82a4 100644
--- a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilter.java
+++ b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilter.java
@@ -1,8 +1,8 @@
 /*-
  * ============LICENSE_START=======================================================
- * policy-management
+ * ONAP
  * ================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import java.util.concurrent.CopyOnWriteArrayList;
 import org.onap.policy.drools.utils.Pair;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -32,15 +33,14 @@
 import com.google.gson.JsonParser;
 
 /**
- * JSON Protocol Filter.  Evaluates an JSON string and evaluates if it
- * passes its filters.
+ * JSON Protocol Filter.
  */
 public class JsonProtocolFilter {
 	
 	/**
 	 * Logger
 	 */
-	private static Logger logger = LoggerFactory.getLogger(JsonProtocolFilter.class);
+	private static final Logger logger = LoggerFactory.getLogger(JsonProtocolFilter.class);
 	
 	/**
 	 * Helper class to collect Filter information
@@ -49,12 +49,12 @@
 		/**
 		 * Field name
 		 */
-		protected String name;
+		private String name;
 		
 		/**
 		 * Field Value regex
 		 */
-		protected String regex;
+		private String regex;
 		
 		/**
 		 * Filter Constructor
@@ -63,8 +63,8 @@
 		 * @param regex field regex value
 		 */
 		public FilterRule(String name, String regex) {
-			this.name = name;
-			this.regex = regex;
+			this.setName(name);
+			this.setRegex(regex);
 		}
 
 		/**
@@ -97,6 +97,9 @@
 		 * @param name field name
 		 */
 		public void setName(String name) {
+			if (name == null || name.isEmpty())
+				throw new IllegalArgumentException("filter field name must be provided");
+
 			this.name = name;
 		}
 
@@ -105,6 +108,9 @@
 		 * @param regex
 		 */
 		public void setRegex(String regex) {
+		    if (regex == null || regex.isEmpty())
+		    	this.regex = ".*";
+
 			this.regex = regex;
 		}
 
@@ -119,7 +125,7 @@
 	/**
 	 * all the filters to be applied
 	 */
-	protected List<FilterRule> rules = new ArrayList<>();
+	protected List<FilterRule> rules = new CopyOnWriteArrayList<>();
 	
 	/**
 	 * 
@@ -154,12 +160,25 @@
 	
 	/**
 	 * 
-	 * @param rawFilters raw filter initialization
+	 * @param filters filter list
 	 * 
 	 * @throws IllegalArgumentException an invalid input has been provided
 	 */
 	public JsonProtocolFilter(List<FilterRule> filters) throws IllegalArgumentException {
-		this.rules = filters;
+		List<FilterRule> temp = new ArrayList<>();
+		for (FilterRule rule : filters) {
+			if (rule.getName() == null || rule.getName().isEmpty()) {
+					continue;
+			}
+
+			if (rule.getRegex() == null || rule.getRegex().isEmpty()) {
+				rule.setRegex(".*");
+			}
+
+			temp.add(rule);
+		}
+
+		this.rules.addAll(temp);
 	}
 
 	/**
@@ -179,38 +198,38 @@
 	 * 
 	 * @throws IllegalArgumentException an invalid input has been provided
 	 */
-	public synchronized boolean accept(JsonElement json) throws IllegalArgumentException {
+	public boolean accept(JsonElement json) throws IllegalArgumentException {
 		if (json == null) {
 			throw new IllegalArgumentException("no JSON provided");
 		}
-		
+
+		if (!json.isJsonObject()) {
+			return false;
+		}
+
 		if (rules.isEmpty()) {
 			return true;
 		}
-		
+
 		try {
-			if (!json.isJsonObject()) {
-				return false;
-			}
-			
 			JsonObject event = json.getAsJsonObject();
 			for (FilterRule filter: rules) {
-				if (filter.regex == null || 
-					filter.regex.isEmpty() ||  
-					".*".equals(filter.regex)) {
+				if (filter.getRegex() == null ||
+					filter.getRegex().isEmpty() ||
+					".*".equals(filter.getRegex())) {
 					
 					// Only check for presence
-					if (!event.has(filter.name)) {
+					if (!event.has(filter.getName())) {
 						return false;
 					}
 				} else {
-					JsonElement field = event.get(filter.name);
+					JsonElement field = event.get(filter.getName());
 					if (field == null) {
 						return false;
 					}
 					
 					String fieldValue = field.getAsString();
-					if (!fieldValue.matches(filter.regex)) {
+					if (!fieldValue.matches(filter.getRegex())) {
 						return false;
 					}
 				}
@@ -229,7 +248,7 @@
 	 * 
 	 * @throws IllegalArgumentException an invalid input has been provided
 	 */
-	public synchronized boolean accept(String json) throws IllegalArgumentException {
+	public boolean accept(String json) throws IllegalArgumentException {
 		if (json == null || json.isEmpty()) {
 			throw new IllegalArgumentException("no JSON provided");
 		}
@@ -255,47 +274,78 @@
 	}
 
 	public List<FilterRule> getRules() {
-		return rules;
+		return new ArrayList<>(this.rules);
 	}
 
-	public synchronized void setRules(List<FilterRule> rulesFilters) {
-		this.rules = rulesFilters;
-	}
-	
-	public synchronized void deleteRules(String name) {
-		for (FilterRule rule : new ArrayList<>(this.rules)) {
-		    if (rule.name.equals(name)) {
-		    	this.rules.remove(rule);
-		    }
-		}
-	}
-	
 	public List<FilterRule> getRules(String name) {
+		if (name == null || name.isEmpty())
+			throw new IllegalArgumentException("no rule name provided");
+
 		ArrayList<FilterRule> temp = new ArrayList<>();
-		for (FilterRule rule : new ArrayList<>(this.rules)) {
-		    if (rule.name.equals(name)) {
-		    	temp.add(rule);
-		    }
+		for (FilterRule rule : this.rules) {
+			if (rule.getName().equals(name)) {
+				temp.add(rule);
+			}
 		}
 		return temp;
 	}
-	
-	public synchronized void deleteRule(String name, String regex) {
-		for (FilterRule rule : new ArrayList<>(this.rules)) {
-		    if (rule.name.equals(name) && rule.regex.equals(regex)) {
-		    	this.rules.remove(rule);
-		    }
-		}
+
+	public void setRules(List<FilterRule> rulesFilters) {
+		if (rulesFilters == null)
+			throw new IllegalArgumentException("no rules provided");
+
+	    this.rules.clear();
+	    this.rules.addAll(rulesFilters);
 	}
 	
-	public synchronized void addRule(String name, String regex) {
-		for (FilterRule rule : new ArrayList<>(this.rules)) {
-		    if (rule.name.equals(name) && rule.regex.equals(regex)) {
-		    	return;
+	public void deleteRules(String name) {
+		if (name == null || name.isEmpty())
+			throw new IllegalArgumentException("no rule name provided");
+
+		List<FilterRule> temp = new ArrayList<>();
+		for (FilterRule rule : this.rules) {
+			if (rule.name.equals(name)) {
+				temp.add(rule);
+			}
+		}
+		this.rules.removeAll(temp);
+	}
+
+	public void deleteRule(String name, String regex) {
+		if (name == null || name.isEmpty())
+			throw new IllegalArgumentException("no rule name provided");
+
+		String nonNullRegex = regex;
+		if (regex == null || regex.isEmpty()) {
+			nonNullRegex = ".*";
+		}
+
+		List<FilterRule> temp = new ArrayList<>();
+		for (FilterRule rule : this.rules) {
+		    if (rule.name.equals(name) && rule.getRegex().equals(nonNullRegex)) {
+		    	temp.add(rule);
 		    }
 		}
-		
-		this.rules.add(new FilterRule(name,regex));
+
+		this.rules.removeAll(temp);
+	}
+	
+	public void addRule(String name, String regex) {
+		if (name == null || name.isEmpty())
+			throw new IllegalArgumentException("no rule name provided");
+
+		String nonNullRegex = regex;
+		if (regex == null || regex.isEmpty()) {
+			nonNullRegex = ".*";
+		}
+
+		for (FilterRule rule : this.rules) {
+		    if (rule.getName().equals(name) && rule.getRegex().equals(regex)) {
+					return;
+		    }
+		}
+
+		this.rules.add(new FilterRule(name, nonNullRegex));
 	}
 
 	@Override
diff --git a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/ProtocolCoderToolset.java b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/ProtocolCoderToolset.java
index 07206f9..cb039ee 100644
--- a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/ProtocolCoderToolset.java
+++ b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/ProtocolCoderToolset.java
@@ -1,8 +1,8 @@
 /*-
  * ============LICENSE_START=======================================================
- * policy-management
+ * ONAP
  * ================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,24 +20,6 @@
 
 package org.onap.policy.drools.protocol.coders;
 
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Type;
-import java.time.Instant;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import org.onap.policy.drools.controller.DroolsController;
-import org.onap.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters;
-import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomCoder;
-import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
-import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomJacksonCoder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.DeserializationFeature;
@@ -53,6 +35,22 @@
 import com.google.gson.JsonPrimitive;
 import com.google.gson.JsonSerializationContext;
 import com.google.gson.JsonSerializer;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import org.onap.policy.drools.controller.DroolsController;
+import org.onap.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters;
+import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomCoder;
+import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
+import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomJacksonCoder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Protocol Coding/Decoding Toolset
@@ -87,7 +85,7 @@
   /**
    * Protocols and associated Filters
    */
-  protected final List<CoderFilters> coders = new ArrayList<>();
+  protected final List<CoderFilters> coders = new CopyOnWriteArrayList<>();
 
   /**
    * Tree model (instead of class model) generic parsing to be able to inspect elements
@@ -115,7 +113,6 @@
 
     if (topic == null || controllerId == null || groupId == null || artifactId == null
         || codedClass == null || filters == null || topic.isEmpty() || controllerId.isEmpty()) {
-      // TODO
       throw new IllegalArgumentException("Invalid input");
     }
 
@@ -134,6 +131,9 @@
    * @return the decoder filters or null if not found
    */
   public CoderFilters getCoder(String classname) {
+    if (classname == null || classname.isEmpty())
+      throw new IllegalArgumentException("no classname provided");
+
     for (final CoderFilters decoder : this.coders) {
       if (decoder.factClass.equals(classname)) {
         return decoder;
@@ -143,12 +143,12 @@
   }
 
   /**
-   * get all coder filters in use
+   * get a copy of the coder filters in use
    *
    * @return coder filters
    */
   public List<CoderFilters> getCoders() {
-    return this.coders;
+    return new ArrayList<>(this.coders);
   }
 
   /**
@@ -158,38 +158,35 @@
    * @param filter filter
    */
   public void addCoder(String eventClass, JsonProtocolFilter filter, int modelClassLoaderHash) {
-    synchronized (this) {
-      for (final CoderFilters coder : this.coders) {
-        if (coder.factClass.equals(eventClass)) {
-          // this is a better check than checking pointers, just
-          // in case classloader is different and this is just an update
-          coder.factClass = eventClass;
-          coder.filter = filter;
-          coder.modelClassLoaderHash = modelClassLoaderHash;
-          return;
-        }
+    if (eventClass == null || eventClass.isEmpty())
+      throw new IllegalArgumentException("no event class provided");
+
+    for (final CoderFilters coder : this.coders) {
+      if (coder.getCodedClass().equals(eventClass)) {
+        coder.setFilter(filter);
+        coder.setFromClassLoaderHash(modelClassLoaderHash);
+        return;
       }
     }
-
     this.coders.add(new CoderFilters(eventClass, filter, modelClassLoaderHash));
   }
 
   /**
    * remove coder
-   *
-   * @param eventClass decoder
-   * @param filter filter
+   * @param eventClass event class
    */
   public void removeCoders(String eventClass) {
-    synchronized (this) {
-      final Iterator<CoderFilters> codersIt = this.coders.iterator();
-      while (codersIt.hasNext()) {
-        final CoderFilters coder = codersIt.next();
-        if (coder.factClass.equals(eventClass)) {
-          codersIt.remove();
-        }
+    if (eventClass == null || eventClass.isEmpty())
+      throw new IllegalArgumentException("no event class provided");
+
+    List<CoderFilters> temp = new ArrayList<>();
+    for (final CoderFilters coder : this.coders) {
+      if (coder.factClass.equals(eventClass)) {
+        temp.add(coder);
       }
     }
+
+    this.coders.removeAll(temp);
   }
 
   /**
@@ -258,7 +255,6 @@
     // Don't parse if it is not necessary
 
     if (this.coders.isEmpty()) {
-      // TODO this is an error
       throw new IllegalStateException("No coders available");
     }
 
diff --git a/policy-management/src/test/java/org/onap/policy/drools/controller/internal/MavenDroolsControllerTest.java b/policy-management/src/test/java/org/onap/policy/drools/controller/internal/MavenDroolsControllerTest.java
index a938bf2..a41c3c1 100644
--- a/policy-management/src/test/java/org/onap/policy/drools/controller/internal/MavenDroolsControllerTest.java
+++ b/policy-management/src/test/java/org/onap/policy/drools/controller/internal/MavenDroolsControllerTest.java
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- * policy-management
+ * ONAP
  * ================================================================================
  * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
@@ -31,11 +31,11 @@
 
 public class MavenDroolsControllerTest {
 
-    private static final String JUNIT_ECHO_KSESSION = "echo";
-    private static final String JUNIT_ECHO_KMODULE_DRL_PATH = "src/test/resources/echo.drl";
-    private static final String JUNIT_ECHO_KMODULE_POM_PATH = "src/test/resources/echo.pom";
-    private static final String JUNIT_ECHO_KMODULE_PATH = "src/test/resources/echo.kmodule";
-    private static final String JUNIT_ECHO_KJAR_DRL_PATH =
+    public static final String JUNIT_ECHO_KSESSION = "echo";
+    public static final String JUNIT_ECHO_KMODULE_DRL_PATH = "src/test/resources/echo.drl";
+    public static final String JUNIT_ECHO_KMODULE_POM_PATH = "src/test/resources/echo.pom";
+    public static final String JUNIT_ECHO_KMODULE_PATH = "src/test/resources/echo.kmodule";
+    public static final String JUNIT_ECHO_KJAR_DRL_PATH =
         "src/main/resources/kbEcho/org/onap/policy/drools/test/echo.drl";
 
     private static volatile ReleaseId releaseId;
@@ -74,6 +74,9 @@
     }
 
     private DroolsController createDroolsController(long courtesyStartTimeMs) throws InterruptedException {
+        if (releaseId == null)
+            throw new IllegalStateException("no prereq artifact installed in maven repository");
+
         DroolsController controller = new MavenDroolsController(releaseId.getGroupId(),
             releaseId.getArtifactId(), releaseId.getVersion(), null, null);
 
diff --git a/policy-management/src/test/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilterTest.java b/policy-management/src/test/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilterTest.java
index 682cbac..624bc2c 100644
--- a/policy-management/src/test/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilterTest.java
+++ b/policy-management/src/test/java/org/onap/policy/drools/protocol/coders/JsonProtocolFilterTest.java
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * ONAP
  * ================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -44,8 +44,8 @@
     private static final String REGEX3 = "regex3";
 
     private static final String NAME4 = "name4";
-    private static final String REGEX4a = "regex4a";
-    private static final String REGEX4b = "regex4b";
+    private static final String REGEX4a = "^regex4a.*";
+    private static final String REGEX4b = ".*regex4b$";
     
 
     @Test
@@ -149,27 +149,80 @@
 
         //      ************   D E L E T E   f i l t e r s   f r o m   p r o t o c o l F i l t e r B       ***********
         //      DELETE specific filter from protocolFilterB by passing both the name & regex values
-        protocolFilterB.deleteRule(NAME3, REGEX3);
+
+        assertTrue(protocolFilterB.getRules(NAME3).size() == 1);
+        assertTrue(protocolFilterB.getRules(NAME3).get(0).getName().equals(NAME3));
+        assertTrue(protocolFilterB.getRules(NAME3).get(0).getRegex().equals(REGEX3));
+
+        assertTrue(protocolFilterB.getRules(NAME4).size() == 2);
+        assertTrue(protocolFilterB.getRules(NAME4).get(0).getName().equals(NAME4));
+        assertTrue(protocolFilterB.getRules(NAME4).get(0).getRegex().equals(REGEX4a));
+        assertTrue(protocolFilterB.getRules(NAME4).get(1).getName().equals(NAME4));
+        assertTrue(protocolFilterB.getRules(NAME4).get(1).getRegex().equals(REGEX4b));
+
+        String jsonA = "{ \"name1\":\"regex1\",\"name2\":\"regex2\"," +
+                        "\"name3\":\"regex3\",\"name4\":\"regex4a\",\"name4\":\"regex4b\"}";
+        String jsonB = "{ \"name1\":\"regex1\",\"name2\":\"regex2\"," +
+                        "\"name3\":\"regex3\",\"name4\":\"regex4a-regex4b\"}";
+        String jsonC = "{ \"name1\":\"regex1\",\"name2\":\"regex2\"," +
+                        "\"name3\":\"regex3\",\"name4\":\"regex4a\"}";
+        String jsonD = "{ \"name1\":\"regex1\",\"name2\":\"regex2\"," +
+                        "\"name3\":\"regex3\",\"name4\":\"regex4b\"}";
+
+        assertFalse(protocolFilterB.accept(jsonA));
+        assertTrue(protocolFilterB.accept(jsonB));
+        assertFalse(protocolFilterB.accept(jsonC));
+        assertFalse(protocolFilterB.accept(jsonD));
+
+        protocolFilterB.deleteRule(NAME4, REGEX4a);
+
+        assertTrue(protocolFilterB.accept(jsonA));
+        assertTrue(protocolFilterB.accept(jsonB));
+        assertFalse(protocolFilterB.accept(jsonC));
+        assertTrue(protocolFilterB.accept(jsonD));
+
+        protocolFilterB.addRule(NAME4, REGEX4a);
+
+        assertTrue(protocolFilterB.getRules(NAME4).size() == 2);
+        assertTrue(protocolFilterB.getRules(NAME4).get(0).getName().equals(NAME4));
+        assertTrue(protocolFilterB.getRules(NAME4).get(0).getRegex().equals(REGEX4b));
+        assertTrue(protocolFilterB.getRules(NAME4).get(1).getName().equals(NAME4));
+        assertTrue(protocolFilterB.getRules(NAME4).get(1).getRegex().equals(REGEX4a));
+
+        assertFalse(protocolFilterB.accept(jsonA));
+        assertTrue(protocolFilterB.accept(jsonB));
+        assertFalse(protocolFilterB.accept(jsonC));
+        assertFalse(protocolFilterB.accept(jsonD));
 
         //      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         //      DELETE all filters from protocolFilterB that have a match to the same name value
         protocolFilterB.deleteRules(NAME4);
 
+        assertTrue(protocolFilterB.getRules(NAME4).isEmpty());
+        assertTrue(protocolFilterB.accept(jsonA));
+        assertTrue(protocolFilterB.accept(jsonB));
+        assertTrue(protocolFilterB.accept(jsonC));
+        assertTrue(protocolFilterB.accept(jsonD));
+
+        assertTrue(protocolFilterB.getRules(NAME3).size() == 1);
+        protocolFilterB.addRule(NAME3, REGEX3);
+        assertTrue(protocolFilterB.getRules(NAME3).size() == 1);
+        protocolFilterB.deleteRule(NAME3, REGEX3);
+        assertTrue(protocolFilterB.getRules(NAME3).isEmpty());
+
         //      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         //      VALIDATE that protocolFilterB now only contains the filters that were originally passed using filtersA
         assertEquals(protocolFilterB.getRules(), filtersA);
 
-
-
         //      ************   A C C E P T   J S O N   I F   I T   P A S S E S   A L L   F I L T E R S     ***********
         //      ACCEPT TRUE a JSON that passes all filters
-        String jsonA = "{ \"name1\":\"regex1\",\"name2\":\"regex2\"}";
-        assertTrue(protocolFilterA.accept(jsonA));
+        String jsonE = "{ \"name1\":\"regex1\",\"name2\":\"regex2\"}";
+        assertTrue(protocolFilterA.accept(jsonE));
+
         //      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         //      ACCEPT FALSE a JSON that does NOT pass all filters
-        String jsonB = "{ \"name1\":\"regex1\"}";
-        assertFalse(protocolFilterA.accept(jsonB));
-
+        String jsonF = "{ \"name1\":\"regex1\"}";
+        assertFalse(protocolFilterA.accept(jsonF));
     }
 
 }
\ No newline at end of file
diff --git a/policy-utils/src/main/java/org/onap/policy/drools/utils/Triple.java b/policy-utils/src/main/java/org/onap/policy/drools/utils/Triple.java
index 530d57a..66179aa 100644
--- a/policy-utils/src/main/java/org/onap/policy/drools/utils/Triple.java
+++ b/policy-utils/src/main/java/org/onap/policy/drools/utils/Triple.java
@@ -1,8 +1,8 @@
 /*-
  * ============LICENSE_START=======================================================
- * policy-utils
+ * ONAP
  * ================================================================================
- * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -25,21 +25,62 @@
     private F first;
     private S second;
     private T third;
-    
+
+    public Triple() {
+        // empty constructor
+    }
+
     public Triple(F first, S second, T third){
         this.first = first;
         this.second = second;
         this.third = third;
     }
-    public F first(){ return this.first; }
-    
-    public S second(){ return this.second; }
-    
-    public T third(){ return this.third; }
-    
-    public void first(F first){ this.first = first; }
-    
-    public void second(S second){ this.second = second; }
-    
-    public void third(T third){ this.third = third; }
+
+    public F first(){
+        return this.getFirst();
+    }
+
+    public F getFirst() {
+        return first;
+    }
+
+    public void first(F first) {
+        this.setFirst(first);
+    }
+
+    public void setFirst(F first) {
+        this.first = first;
+    }
+
+    public S second() {
+        return this.getSecond();
+    }
+
+    public S getSecond() {
+        return second;
+    }
+
+    public void second(S second) {
+        this.setSecond(second);
+    }
+
+    public void setSecond(S second) {
+        this.second = second;
+    }
+
+    public T third() {
+        return this.getThird();
+    }
+
+    public T getThird() {
+        return this.third;
+    }
+
+    public void third(T third) {
+        this.setThird(third);
+    }
+
+    public void setThird(T third) {
+        this.third = third;
+    }
 }
diff --git a/policy-utils/src/test/java/org/onap/policy/drools/utils/TripleTest.java b/policy-utils/src/test/java/org/onap/policy/drools/utils/TripleTest.java
new file mode 100644
index 0000000..cdb93e8
--- /dev/null
+++ b/policy-utils/src/test/java/org/onap/policy/drools/utils/TripleTest.java
@@ -0,0 +1,60 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2018 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.utils;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TripleTest {
+
+    @Test
+    public void test() {
+        Triple<String, String, String> triple  =
+                new Triple("one", "two", "three");
+
+        Assert.assertTrue("one".equals(triple.first()));
+        Assert.assertTrue("one".equals(triple.getFirst()));
+
+        Assert.assertTrue("two".equals(triple.second()));
+        Assert.assertTrue("two".equals(triple.getSecond()));
+
+        Assert.assertTrue("three".equals(triple.third()));
+        Assert.assertTrue("three".equals(triple.getThird()));
+
+        triple.first("I");
+        Assert.assertTrue("I".equals(triple.first()));
+
+        triple.setFirst("1");
+        Assert.assertTrue("1".equals(triple.first()));
+
+        triple.second("2");
+        Assert.assertTrue("2".equals(triple.second()));
+
+        triple.setSecond("II");
+        Assert.assertTrue("II".equals(triple.second()));
+
+        triple.third("3");
+        Assert.assertTrue("3".equals(triple.third()));
+
+        triple.setThird("III");
+        Assert.assertTrue("III".equals(triple.third()));
+    }
+}
\ No newline at end of file