Initial OpenECOMP policy/drools-pdp commit

Change-Id: I0072ccab6f40ed32da39667f9f8523b6d6dad2e2
Signed-off-by: Pamela Dragosh <pdragosh@research.att.com>
diff --git a/policy-utils/pom.xml b/policy-utils/pom.xml
new file mode 100644
index 0000000..b7ac7fb
--- /dev/null
+++ b/policy-utils/pom.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ============LICENSE_START=======================================================
+  ECOMP Policy Engine - Drools PDP
+  ================================================================================
+  Copyright (C) 2017 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=========================================================
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>policy-utils</artifactId>
+
+  <parent>
+    <groupId>org.openecomp.policy.drools-pdp</groupId>
+    <artifactId>drools-pdp</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+  </parent>
+
+
+	<dependencies>
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.11</version>
+		</dependency>
+        <dependency>
+                <groupId>log4j</groupId>
+                <artifactId>log4j</artifactId>
+                <version>1.2.17</version>
+		        <scope>provided</scope>
+        </dependency>       	
+        <dependency>
+			<groupId>com.att.eelf</groupId>
+			<artifactId>eelf-core</artifactId>
+			<version>0.0.1</version>
+		</dependency>   		
+		<dependency>
+			<groupId>ch.qos.logback</groupId>
+			<artifactId>logback-classic</artifactId>
+			<version>1.1.1</version>
+		</dependency>
+		<dependency>
+			<groupId>ch.qos.logback</groupId>
+			<artifactId>logback-core</artifactId>
+			<version>1.1.1</version>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+			<version>1.7.6</version>
+		</dependency>  	    
+		<dependency>
+			<groupId>org.openecomp.policy.common</groupId>
+			<artifactId>ECOMP-Logging</artifactId>
+			<version>${common-modules.version}</version>
+		</dependency>	
+  	</dependencies>
+
+    
+</project>
diff --git a/policy-utils/src/main/java/org/openecomp/policy/drools/utils/OrderedService.java b/policy-utils/src/main/java/org/openecomp/policy/drools/utils/OrderedService.java
new file mode 100644
index 0000000..b50e6e8
--- /dev/null
+++ b/policy-utils/src/main/java/org/openecomp/policy/drools/utils/OrderedService.java
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-utils
+ * ================================================================================
+ * Copyright (C) 2017 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.openecomp.policy.drools.utils;
+
+/**
+ * This is a base interface that is used to control the order of a list
+ * of services (features) discovered via 'ServiceLoader'. See
+ * 'OrderedServiceImpl' for more details.
+ */
+public interface OrderedService
+{
+  /**
+   * @return an integer sequence number, which determines the order of a list
+   *	of objects implementing this interface
+   */
+  public int getSequenceNumber();
+}
diff --git a/policy-utils/src/main/java/org/openecomp/policy/drools/utils/OrderedServiceImpl.java b/policy-utils/src/main/java/org/openecomp/policy/drools/utils/OrderedServiceImpl.java
new file mode 100644
index 0000000..2747e85
--- /dev/null
+++ b/policy-utils/src/main/java/org/openecomp/policy/drools/utils/OrderedServiceImpl.java
@@ -0,0 +1,117 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-utils
+ * ================================================================================
+ * Copyright (C) 2017 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.openecomp.policy.drools.utils;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ServiceLoader;
+
+/**
+ * This class is a template for building a sorted list of service instances,
+ * which are discovered and created using 'ServiceLoader'. 
+ */
+public class OrderedServiceImpl<T extends OrderedService>
+{
+  // sorted list of instances implementing the service
+  private List<T> implementers = null;
+
+  // 'ServiceLoader' that is used to discover and create the services
+  private ServiceLoader<T> serviceLoader = null; //ServiceLoader.load(T.class);
+
+  /**
+   * Constructor - create the 'ServiceLoader' instance
+   *
+   * @param clazz the class object associated with 'T' (I supposed it could
+   *	be a subclass, but I'm not sure this is useful)
+   */
+  public OrderedServiceImpl(Class clazz)
+  {
+	// This constructor wouldn't be needed if 'T.class' was legal
+	serviceLoader = ServiceLoader.load(clazz);
+  }
+
+  /**
+   * @return the sorted list of services implementing interface 'T' discovered
+   *	by 'ServiceLoader'.
+   */
+  public synchronized List<T> getList()
+  {
+	if (implementers == null)
+	  {
+		rebuildList();
+	  }
+	return(implementers);
+  }
+
+  /**
+   * This method is called by 'getList', but could also be called directly if
+   * we were running with a 'ClassLoader' that supported the dynamic addition
+   * of JAR files. In this case, it could be invoked in order to discover any
+   * new services implementing interface 'T'. This is probably a relatively
+   * expensive operation in terms of CPU and elapsed time, so it is best if it
+   * isn't invoked too frequently.
+   *
+   * @return the sorted list of services implementing interface 'T' discovered
+   *	by 'ServiceLoader'.
+   */
+  public synchronized List<T> rebuildList()
+  {
+	// build a list of all of the current implementors
+	List<T> tmp = new LinkedList<T>();
+	for (T service : serviceLoader)
+	  {
+		tmp.add(service);
+	  }
+
+	// Sort the list according to sequence number, and then alphabetically
+	// according to full class name.
+	Collections.sort(tmp, new Comparator<T>()
+					 {
+					   public int compare(T o1, T o2)
+						 {
+						   int s1 = o1.getSequenceNumber();
+						   int s2 = o2.getSequenceNumber();
+						   int rval;
+						   if (s1 < s2)
+							 {
+							   rval = -1;
+							 }
+						   else if (s1 > s2)
+							 {
+							   rval = 1;
+							 }
+						   else
+							 {
+							   rval = o1.getClass().getName().compareTo
+								 (o2.getClass().getName());
+							 }
+						   return(rval);
+						 }
+					 });
+
+	// create an unmodifiable version of this list
+	implementers = Collections.unmodifiableList(tmp);
+	System.out.println("***** OrderedServiceImpl implementers:\n" + implementers);
+	return(implementers);
+  }
+}
diff --git a/policy-utils/src/main/java/org/openecomp/policy/drools/utils/Pair.java b/policy-utils/src/main/java/org/openecomp/policy/drools/utils/Pair.java
new file mode 100644
index 0000000..a5e3469
--- /dev/null
+++ b/policy-utils/src/main/java/org/openecomp/policy/drools/utils/Pair.java
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-utils
+ * ================================================================================
+ * Copyright (C) 2017 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.openecomp.policy.drools.utils;
+
+public class Pair<F,S> {
+	
+    protected F first;
+    protected S second;
+    
+	public Pair(F first, S second){
+        this.first = first;
+        this.second = second;
+    }
+	
+    public F first() {return this.first;}
+    
+    public S second() {return this.second;}
+    
+    public F getFirst() {return this.first;}
+
+	public S getSecond() {return this.second;}
+    
+    public void first(F first) {this.first = first;}
+    
+    public void second(S second) {this.second = second;}
+    
+	@Override
+	public String toString() {
+		StringBuilder builder = new StringBuilder();
+		builder.append("Pair [first=").append(first).append(", second=").append(second).append("]");
+		return builder.toString();
+	}
+}
diff --git a/policy-utils/src/main/java/org/openecomp/policy/drools/utils/PropertyUtil.java b/policy-utils/src/main/java/org/openecomp/policy/drools/utils/PropertyUtil.java
new file mode 100644
index 0000000..34ddcc1
--- /dev/null
+++ b/policy-utils/src/main/java/org/openecomp/policy/drools/utils/PropertyUtil.java
@@ -0,0 +1,403 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-utils
+ * ================================================================================
+ * Copyright (C) 2017 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.openecomp.policy.drools.utils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * This class provides utilities to read properties from a properties
+ * file, and optionally get notifications of future changes
+ */
+public class PropertyUtil
+{
+  /**
+   * Read in a properties file
+   * @param file the properties file
+   * @return a Properties object, containing the associated properties
+   * @throws IOException - subclass 'FileNotFoundException' if the file
+   *	does not exist or can't be opened, and 'IOException' if there is
+   *	a problem loading the properties file.
+   */
+  static public Properties getProperties(File file) throws IOException
+  {
+	// create an InputStream (may throw a FileNotFoundException)
+	FileInputStream fis = new FileInputStream(file);
+	try
+	  {
+		// create the properties instance
+		Properties rval = new Properties();
+
+		// load properties (may throw an IOException)
+		rval.load(fis);
+		return(rval);
+	  }
+	finally
+	  {
+		// close input stream
+		fis.close();
+	  }
+  }
+
+  /**
+   * Read in a properties file
+   * @param fileName the properties file
+   * @return a Properties object, containing the associated properties
+   * @throws IOException - subclass 'FileNotFoundException' if the file
+   *	does not exist or can't be opened, and 'IOException' if there is
+   *	a problem loading the properties file.
+   */
+  static public Properties getProperties(String fileName) throws IOException
+  {
+	return(getProperties(new File(fileName)));
+  }
+
+  /* ============================================================ */
+
+  // timer thread used for polling for property file changes
+  private static Timer timer = null;
+
+  /**
+   * This is the callback interface, used for sending notifications of
+   * changes in the properties file.
+   */
+  public interface Listener
+  {
+	/**
+	 * Notification of a properties file change
+	 * @param properties the new properties
+	 * @param the set of property names that have changed, including
+	 *		additions and removals
+	 */
+	void propertiesChanged(Properties properties, Set<String> changedKeys);
+  }
+
+  // this table maps canonical file into a 'ListenerRegistration' instance
+  static private HashMap<File, ListenerRegistration> registrations =
+	new HashMap<File, ListenerRegistration>();
+
+  /**
+   * This is an internal class - one instance of this exists for each
+   * property file that is being monitored. Note that multiple listeners
+   * can be registered for the same file.
+   */
+  private static class ListenerRegistration
+  {
+	// the canonical path of the file being monitored
+	File file;
+
+	// the most recent value of 'file.lastModified()'
+	long lastModified;
+
+	// the most recent set of properties
+	Properties properties;
+
+	// the set of listeners monitoring this file
+	LinkedList<Listener> listeners;
+
+	// the 'TimerTask' instance, used for periodic polling
+	TimerTask timerTask;
+
+	/**
+	 * Constructor - create a 'ListenerRegistration' instance for this
+	 * file, but with no listeners
+	 */
+	ListenerRegistration(File file) throws IOException
+	{
+	  this.file = file;
+
+	  // The initial value of 'lastModified' is set to 0 to ensure that we
+	  // correctly handle the case where the file is modified within the
+	  // same second that polling begins.
+	  lastModified = 0;
+
+	  // fetch current properties
+	  properties = getProperties(file);
+
+	  // no listeners yet
+	  listeners = new LinkedList<Listener>();
+
+	  // add to static table, so this instance can be shared
+	  registrations.put(file, this);
+
+	  if (timer == null)
+		{
+		  // still need to create a timer thread
+		  synchronized(PropertyUtil.class)
+			{
+			  // an additional check is added inside the 'synchronized' block,
+			  // just in case someone beat us to it
+			  if (timer == null)
+				{
+				  timer = new Timer("PropertyUtil-Timer", true);
+				}
+			}
+		}
+
+	  // create and schedule the timer task, so this is periodically polled
+	  timerTask = new TimerTask()
+		{
+		  public void run()
+		  {
+			try
+			  {
+				poll();
+			  }
+			catch (Exception e)
+			  {
+				System.err.println(e);
+			  }
+		  }
+		};
+	  timer.schedule(timerTask, 10000L, 10000L);
+	}
+
+	/**
+	 * Add a listener to the notification list
+	 * @param listener this is the listener to add to the list
+	 * @return the properties at the moment the listener was added to the list
+	 */
+	synchronized Properties addListener(Listener listener)
+	{
+	  listeners.add(listener);
+	  return((Properties)properties.clone());
+	}
+
+	/**
+	 * Remove a listener from the notification list
+	 * @param listener this is the listener to remove
+	 */
+	synchronized void removeListener(Listener listener)
+	{
+	  listeners.remove(listener);
+
+	  // See if we need to remove this 'ListenerRegistration' instance
+	  // from the table. The 'synchronized' block is needed in case
+	  // another listener is being added at about the same time that this
+	  // one is being removed.
+	  synchronized(registrations)
+		{
+		  if (listeners.size() == 0)
+			{
+			  timerTask.cancel();
+			  registrations.remove(file);
+			}
+		}
+	}
+
+	/**
+	 * This method is periodically called to check for property list updates
+	 * @throws IOException if there is an error in reading the properties file
+	 */
+	synchronized void poll() throws IOException
+	{
+	  long timestamp = file.lastModified();
+	  if (timestamp != lastModified)
+		{
+		  // update the record, and send out the notifications
+		  lastModified = timestamp;
+
+		  // Save old set, and initial set of changed properties.
+		  Properties oldProperties = properties;
+		  HashSet<String> changedProperties =
+			new HashSet<String>(oldProperties.stringPropertyNames());
+
+		  // Fetch the list of listeners that we will potentially notify,
+		  // and the new properties. Note that this is in a 'synchronized'
+		  // block to ensure that all listeners receiving notifications
+		  // actually have a newer list of properties than the one
+		  // returned on the initial 'getProperties' call.
+		  properties = getProperties(file);
+		  
+		  Set<String> newPropertyNames = properties.stringPropertyNames();
+		  changedProperties.addAll(newPropertyNames);
+
+		  // At this point, 'changedProperties' is the union of all properties
+		  // in both the old and new properties files. Iterate through all
+		  // of the entries in the new properties file - if the entry
+		  // matches the one in the old file, remove it from
+		  // 'changedProperties'.
+		  for (String name : newPropertyNames)
+			{
+			  if (properties.getProperty(name).equals
+				  (oldProperties.getProperty(name)))
+				{
+				  // Apparently, any property that exists must be of type
+				  // 'String', and can't be null. For this reason, we don't
+				  // need to worry about the case where
+				  // 'properties.getProperty(name)' returns 'null'. Note that
+				  // 'oldProperties.getProperty(name)' may be 'null' if the
+				  // old property does not exist.
+				  changedProperties.remove(name);
+				}
+			}
+
+		  // 'changedProperties' should be correct at this point
+		  if (changedProperties.size() != 0)
+			{
+			  // there were changes - notify everyone in 'listeners'
+			  for (final Listener notify : listeners)
+				{
+				  // Copy 'properties' and 'changedProperties', so it doesn't
+				  // cause problems if the recipient makes changes.
+				  final Properties tmpProperties =
+					(Properties)(properties.clone());
+				  final HashSet<String> tmpChangedProperties =
+					new HashSet<String>(changedProperties);
+
+				  // Do the notification in a separate thread, so blocking
+				  // won't cause any problems.
+				  new Thread()
+				  {
+					public void run()
+					{
+					  notify.propertiesChanged
+						(tmpProperties, tmpChangedProperties);
+					}
+				  }.start();
+				}
+			}
+		}
+	}
+  }
+
+  /**
+   * Read in a properties file, and register for update notifications.
+   * NOTE: it is possible that the first callback will occur while this
+   * method is still in progress. To avoid this problem, use 'synchronized'
+   * blocks around this invocation and in the callback -- that will ensure
+   * that the processing of the initial properties complete before any
+   * updates are processed.
+   *
+   * @param file the properties file
+   * @param notify if not null, this is a callback interface that is used for
+   *	notifications of changes
+   * @return a Properties object, containing the associated properties
+   * @throws IOException - subclass 'FileNotFoundException' if the file
+   *	does not exist or can't be opened, and 'IOException' if there is
+   *	a problem loading the properties file.
+   */
+  static public Properties getProperties(File file, Listener listener)
+	throws IOException
+  {
+	if (listener == null)
+	  {
+		// no listener specified -- just fetch the properties
+		return(getProperties(file));
+	  }
+
+	// Convert the file to a canonical form in order to avoid the situation
+	// where different names refer to the same file.
+	file = file.getCanonicalFile();
+
+	// See if there is an existing registration. The 'synchronized' block
+	// is needed to handle the case where a new listener is added at about
+	// the same time that another one is being removed.
+	synchronized(registrations)
+	  {
+		ListenerRegistration reg = registrations.get(file);
+		if (reg == null)
+		  {
+			// a new registration is needed
+			reg = new ListenerRegistration(file);
+		  }
+		return(reg.addListener(listener));
+	  }
+  }
+
+  /**
+   * Read in a properties file, and register for update notifications.
+   * NOTE: it is possible that the first callback will occur while this
+   * method is still in progress. To avoid this problem, use 'synchronized'
+   * blocks around this invocation and in the callback -- that will ensure
+   * that the processing of the initial properties complete before any
+   * updates are processed.
+   *
+   * @param fileName the properties file
+   * @param notify if not null, this is a callback interface that is used for
+   *	notifications of changes
+   * @return a Properties object, containing the associated properties
+   * @throws IOException - subclass 'FileNotFoundException' if the file
+   *	does not exist or can't be opened, and 'IOException' if there is
+   *	a problem loading the properties file.
+   */
+  static public Properties getProperties(String fileName, Listener listener)
+	throws IOException
+  {
+	return(getProperties(new File(fileName), listener));
+  }
+
+  /**
+   * Stop listenening for updates
+   * @param file the properties file
+   * @param notify if not null, this is a callback interface that was used for
+   *	notifications of changes
+   */
+  static public void stopListening(File file, Listener listener)
+  {
+	if (listener != null)
+	  {
+		ListenerRegistration reg = registrations.get(file);
+		if (reg != null)
+		  {
+			reg.removeListener(listener);
+		  }
+	  }
+  }
+
+  /**
+   * Stop listenening for updates
+   * @param fileName the properties file
+   * @param notify if not null, this is a callback interface that was used for
+   *	notifications of changes
+   */
+  static public void stopListening(String fileName, Listener listener)
+  {
+	stopListening(new File(fileName), listener);
+  }
+
+  /* ============================================================ */
+
+  // TEMPORARY - used to test callback interface
+  static public class Test implements Listener
+  {
+	String name;
+
+	public Test(String name)
+	  {
+		this.name = name;
+	  }
+
+	public void propertiesChanged(Properties properties, Set<String> changedKeys)
+	  {
+		System.out.println("Test(" + name + ")\nproperties = " + properties
+						   + "\nchangedKeys = " + changedKeys);
+	  }
+  }
+}
diff --git a/policy-utils/src/main/java/org/openecomp/policy/drools/utils/ReflectionUtil.java b/policy-utils/src/main/java/org/openecomp/policy/drools/utils/ReflectionUtil.java
new file mode 100644
index 0000000..0b86c2a
--- /dev/null
+++ b/policy-utils/src/main/java/org/openecomp/policy/drools/utils/ReflectionUtil.java
@@ -0,0 +1,92 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-utils
+ * ================================================================================
+ * Copyright (C) 2017 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.openecomp.policy.drools.utils;
+
+import org.openecomp.policy.common.logging.eelf.PolicyLogger;
+
+/**
+ * Reflection utilities
+ *
+ */
+public class ReflectionUtil {
+	
+	/**
+	 * returns (if exists) a class fetched from a given classloader
+	 * 
+	 * @param classLoader the class loader
+	 * @param classname the class name
+	 * @return the PolicyEvent class
+	 * @throws IllegalArgumentException if an invalid parameter has been passed in
+	 */
+	public static Class<?> fetchClass(ClassLoader classLoader, 
+			                          String classname) 
+		throws IllegalArgumentException {
+		
+		PolicyLogger.info("FETCH-CLASS: " +  classname + " FROM " + classLoader);
+		
+		if (classLoader == null)
+			throw new IllegalArgumentException("A class loader must be provided");
+		
+		if (classname == null)
+			throw new IllegalArgumentException("A class name to be fetched in class loader " +
+		                                       classLoader + " must be provided");
+		
+		try {
+			Class<?> aClass = Class.forName(classname, 
+					                        true, 
+					                        classLoader);
+			return aClass;
+		} catch (Exception e) {
+			e.printStackTrace();
+			PolicyLogger.error("FETCH-CLASS: " + classname + " IN " + classLoader + " does NOT exist");
+		}
+		
+		return null;
+	}
+	
+	/**
+	 * 
+	 * @param classLoader target class loader
+	 * @param classname class name to fetch
+	 * @return true if exists
+	 * @throws IllegalArgumentException if an invalid parameter has been passed in
+	 */
+	public static boolean isClass(ClassLoader classLoader, String classname) 
+           throws IllegalArgumentException {
+		return fetchClass(classLoader, classname) != null;
+	}
+	
+	/**
+	 * is a subclass?
+	 * @param parent superclass
+	 * @param presumedSubclass subclass
+	 * @return
+	 */
+	public static boolean isSubclass(Class<?> parent, Class<?> presumedSubclass) {		
+		PolicyLogger.debug("IS-SUBCLASS: superclass: " +  parent.getCanonicalName() + 
+				          " subclass: " + presumedSubclass.getCanonicalName());
+		return (parent.isAssignableFrom(presumedSubclass));
+	}
+
+}
diff --git a/policy-utils/src/main/java/org/openecomp/policy/drools/utils/Triple.java b/policy-utils/src/main/java/org/openecomp/policy/drools/utils/Triple.java
new file mode 100644
index 0000000..214d949
--- /dev/null
+++ b/policy-utils/src/main/java/org/openecomp/policy/drools/utils/Triple.java
@@ -0,0 +1,45 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-utils
+ * ================================================================================
+ * Copyright (C) 2017 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.openecomp.policy.drools.utils;
+
+public class Triple<F,S,T> {
+	
+    private F first;
+    private S second;
+    private T third;
+    
+    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; }
+}
diff --git a/policy-utils/src/test/java/org/openecomp/policy/drools/utils/PropertyUtilTest.java b/policy-utils/src/test/java/org/openecomp/policy/drools/utils/PropertyUtilTest.java
new file mode 100644
index 0000000..5509171
--- /dev/null
+++ b/policy-utils/src/test/java/org/openecomp/policy/drools/utils/PropertyUtilTest.java
@@ -0,0 +1,204 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-utils
+ * ================================================================================
+ * Copyright (C) 2017 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.openecomp.policy.drools.utils;
+
+import static org.junit.Assert.*;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.UUID;
+
+import org.apache.log4j.Logger;
+
+import org.openecomp.policy.common.logging.eelf.PolicyLogger;
+
+public class PropertyUtilTest
+{
+ // private static final Logger PolicyLogger =
+//	Logger.getLogger(PropertyUtilTest.class.getName());
+
+  private static File directory = null;
+
+  /**
+   * Test Setup -- Create a directory for temporary files
+   */
+  @BeforeClass
+	static public void setup()
+  {
+	PolicyLogger.info("setup: creating a temporary directory");
+
+	// create a directory for temporary files
+	directory = new File(UUID.randomUUID().toString());
+	directory.mkdir();
+  }
+
+  /**
+   * Test Cleanup -- Remove temporary files
+   */
+  @AfterClass
+	static public void teardown()
+  {
+	PolicyLogger.info("teardown: remove the temporary directory");
+
+	// the assumption is that we only have one level of temporary files
+	for (File file : directory.listFiles())
+	  {
+		file.delete();
+	  }
+	directory.delete();
+  }
+
+  /**
+   * Utility method to write a properties file
+   *
+   * @param name the file name, relative to the temporary directory
+   * @param the properties to store in the file
+   * @return a File instance associated with the newly-created file
+   * @throws IOException if the file can't be created for some reason
+   */
+  File createFile(String name, Properties p) throws IOException
+  {
+	File file = new File(directory, name);
+	FileOutputStream fos = new FileOutputStream(file);
+	try
+	  {
+		p.store(fos, "Property file '" + name + "'");
+	  }
+	finally
+	  {
+		fos.close();
+	  }
+	return(file);
+  }
+
+  /**
+   * Create a 'PropertyUtil.Listener' subclass, which receives property
+   * file updates. It stores the latest values in an array, and notifies
+   * any thread waiting on this array.
+   *
+   * @param returns this is an array of length 2 -- the first entry will
+   *	contain the 'properties' value, and the second will contain
+   *	'changedKeys'. It is also used to signal any waiting thread
+   *	using 'returns.notifyAll()'.
+   */
+  PropertyUtil.Listener createListenerThread(final Object[] returns)
+  {
+	return(new PropertyUtil.Listener()
+	  {
+		public void propertiesChanged
+		  (Properties properties, Set<String> changedKeys)
+		{
+		  // When a notification is received, store the values in the
+		  // 'returns' array, and signal using the same array.
+		  PolicyLogger.info("Listener invoked: properties=" + properties
+					  + ", changedKeys=" + changedKeys);
+		  returns[0] = properties;
+		  returns[1] = changedKeys;
+		  synchronized(returns)
+			{
+			  returns.notifyAll();
+			}
+		}
+	  });
+  }
+
+  /**
+   * Test the basic properties file interface.
+   */
+  @Test
+	public void testGetProperties() throws Exception
+  {
+	PolicyLogger.info("testGetProperties: test the basic properties file interface");
+
+	// copy system properties
+	PolicyLogger.info("Copy system properties to a file");
+	Properties prop1 = System.getProperties();
+	File file1 = createFile("createAndReadPropertyFile-1", prop1);
+
+	// read in properties, and compare
+	PolicyLogger.info("Read in properties from new file");
+	Properties prop2 = PropertyUtil.getProperties(file1);
+
+	// they should match
+	assertEquals(prop1, prop2);
+  }
+
+  /**
+   * This tests the 'PropertyUtil.Listener' interface.
+   */
+  @Test
+	public void testListenerInterface() throws Exception
+  {
+	PolicyLogger.info("testListenerInterface: test receipt of dynamic updates");
+
+	// create initial property file
+	Properties prop1 = new Properties();
+	prop1.setProperty("p1", "p1 value");
+	prop1.setProperty("p2", "p2 value");
+	prop1.setProperty("p3", "p3 value");
+	PolicyLogger.info("Create initial properties file: " + prop1);
+	File file1 = createFile("createAndReadPropertyFile-2", prop1);
+
+	// create a listener for the notification interface
+	Object[] returns = new Object[2];
+	PropertyUtil.Listener listener = createListenerThread(returns);
+
+	// read it in, and do a comparison
+	Properties prop2 = PropertyUtil.getProperties(file1, listener);
+	PolicyLogger.info("Read in properties: " + prop2);
+	assertEquals(prop1, prop2);
+	assertEquals(prop2.getProperty("p1"), "p1 value");
+	assertEquals(prop2.getProperty("p2"), "p2 value");
+	assertEquals(prop2.getProperty("p3"), "p3 value");
+
+	// make some changes, and update the file (p3 is left unchanged)
+	prop2.remove("p1");		// remove one property
+	prop2.setProperty("p2", "new p2 value");	// change one property
+	prop2.setProperty("p4", "p4 value");		// add a new property
+	PolicyLogger.info("Modified properties: " + prop2);
+
+	// now, update the file, and wait for notification
+	synchronized(returns)
+	  {
+		createFile("createAndReadPropertyFile-2", prop2);
+
+		// wait up to 60 seconds, although we should receive notification
+		// in 10 seconds or less (if things are working)
+		returns.wait(60000L);
+	  }
+
+	// verify we have the updates
+	assertEquals(prop2, returns[0]);
+
+	// verify that we have the expected set of keys
+	assertEquals(new TreeSet(Arrays.asList(new String[]{"p1", "p2", "p4"})),
+				 returns[1]);
+  }
+}
diff --git a/policy-utils/src/test/resources/log4j.properties b/policy-utils/src/test/resources/log4j.properties
new file mode 100644
index 0000000..0063f10
--- /dev/null
+++ b/policy-utils/src/test/resources/log4j.properties
@@ -0,0 +1,26 @@
+###
+# ============LICENSE_START=======================================================
+# policy-utils
+# ================================================================================
+# Copyright (C) 2017 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=========================================================
+###
+
+log4j.rootLogger=INFO, out
+
+# CONSOLE appender not used by default
+log4j.appender.out=org.apache.log4j.ConsoleAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d %-5p %-30.30c{1} %4L - %m%n