From 0e16acf4d8579fd82349d6cd24e236275735a818 Mon Sep 17 00:00:00 2001 From: Pamela Dragosh Date: Tue, 14 Feb 2017 19:45:48 -0500 Subject: Initial OpenECOMP policy/drools-pdp commit Change-Id: I0072ccab6f40ed32da39667f9f8523b6d6dad2e2 Signed-off-by: Pamela Dragosh --- .../policy/drools/utils/OrderedService.java | 35 ++ .../policy/drools/utils/OrderedServiceImpl.java | 117 ++++++ .../org/openecomp/policy/drools/utils/Pair.java | 51 +++ .../policy/drools/utils/PropertyUtil.java | 403 +++++++++++++++++++++ .../policy/drools/utils/ReflectionUtil.java | 92 +++++ .../org/openecomp/policy/drools/utils/Triple.java | 45 +++ 6 files changed, 743 insertions(+) create mode 100644 policy-utils/src/main/java/org/openecomp/policy/drools/utils/OrderedService.java create mode 100644 policy-utils/src/main/java/org/openecomp/policy/drools/utils/OrderedServiceImpl.java create mode 100644 policy-utils/src/main/java/org/openecomp/policy/drools/utils/Pair.java create mode 100644 policy-utils/src/main/java/org/openecomp/policy/drools/utils/PropertyUtil.java create mode 100644 policy-utils/src/main/java/org/openecomp/policy/drools/utils/ReflectionUtil.java create mode 100644 policy-utils/src/main/java/org/openecomp/policy/drools/utils/Triple.java (limited to 'policy-utils/src/main/java') 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 00000000..b50e6e85 --- /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 00000000..2747e85e --- /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 +{ + // sorted list of instances implementing the service + private List implementers = null; + + // 'ServiceLoader' that is used to discover and create the services + private ServiceLoader 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 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 rebuildList() + { + // build a list of all of the current implementors + List tmp = new LinkedList(); + 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() + { + 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 00000000..a5e34695 --- /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 { + + 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 00000000..34ddcc1c --- /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 changedKeys); + } + + // this table maps canonical file into a 'ListenerRegistration' instance + static private HashMap registrations = + new HashMap(); + + /** + * 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 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(); + + // 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 changedProperties = + new HashSet(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 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 tmpChangedProperties = + new HashSet(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 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 00000000..0b86c2aa --- /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 00000000..214d949e --- /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 { + + 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; } +} -- cgit