diff options
Diffstat (limited to 'policy-management/src/main/java')
20 files changed, 3447 insertions, 3249 deletions
diff --git a/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsControllerFactory.java b/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsControllerFactory.java index f91e6dc4..b641f24a 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsControllerFactory.java +++ b/policy-management/src/main/java/org/onap/policy/drools/controller/DroolsControllerFactory.java @@ -20,25 +20,11 @@ package org.onap.policy.drools.controller; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Properties; -import org.onap.policy.common.endpoints.event.comm.Topic; -import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure; import org.onap.policy.common.endpoints.event.comm.TopicSink; import org.onap.policy.common.endpoints.event.comm.TopicSource; -import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties; -import org.onap.policy.drools.controller.internal.MavenDroolsController; -import org.onap.policy.drools.controller.internal.NullDroolsController; -import org.onap.policy.drools.properties.DroolsProperties; -import org.onap.policy.drools.protocol.coders.JsonProtocolFilter; import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration; -import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder; -import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.PotentialCoderFilter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Drools Controller Factory to manage controller creation, destruction, and retrieval for @@ -120,322 +106,3 @@ public interface DroolsControllerFactory { */ public List<DroolsController> inventory(); } - - -/* ---------------- implementation ----------------- */ - -/** - * Factory of Drools Controllers indexed by the Maven coordinates. - */ -class IndexedDroolsControllerFactory implements DroolsControllerFactory { - - /** - * logger. - */ - private static Logger logger = LoggerFactory.getLogger(MavenDroolsController.class); - - /** - * Policy Controller Name Index. - */ - protected HashMap<String, DroolsController> droolsControllers = new HashMap<>(); - - /** - * Null Drools Controller. - */ - protected NullDroolsController nullDroolsController = new NullDroolsController(); - - - public IndexedDroolsControllerFactory() { - - /* Add a NULL controller which will always be present in the hash */ - - DroolsController controller = new NullDroolsController(); - String controllerId = controller.getGroupId() + ":" + controller.getArtifactId(); - - synchronized (this) { - droolsControllers.put(controllerId, controller); - } - } - - @Override - public DroolsController build(Properties properties, List<? extends TopicSource> eventSources, - List<? extends TopicSink> eventSinks) throws LinkageError { - - String groupId = properties.getProperty(DroolsProperties.RULES_GROUPID); - if (groupId == null || groupId.isEmpty()) { - groupId = DroolsController.NO_GROUP_ID; - } - - String artifactId = properties.getProperty(DroolsProperties.RULES_ARTIFACTID); - if (artifactId == null || artifactId.isEmpty()) { - artifactId = DroolsController.NO_ARTIFACT_ID; - } - - String version = properties.getProperty(DroolsProperties.RULES_VERSION); - if (version == null || version.isEmpty()) { - version = DroolsController.NO_VERSION; - } - - List<TopicCoderFilterConfiguration> topics2DecodedClasses2Filters = codersAndFilters(properties, eventSources); - - List<TopicCoderFilterConfiguration> topics2EncodedClasses2Filters = codersAndFilters(properties, eventSinks); - - return this.build(groupId, artifactId, version, topics2DecodedClasses2Filters, topics2EncodedClasses2Filters); - } - - @Override - public DroolsController build(String newGroupId, String newArtifactId, String newVersion, - List<TopicCoderFilterConfiguration> decoderConfigurations, - List<TopicCoderFilterConfiguration> encoderConfigurations) throws LinkageError { - - if (newGroupId == null || newGroupId.isEmpty()) { - throw new IllegalArgumentException("Missing maven group-id coordinate"); - } - - if (newArtifactId == null || newArtifactId.isEmpty()) { - throw new IllegalArgumentException("Missing maven artifact-id coordinate"); - } - - if (newVersion == null || newVersion.isEmpty()) { - throw new IllegalArgumentException("Missing maven version coordinate"); - } - - String controllerId = newGroupId + ":" + newArtifactId; - DroolsController controllerCopy = null; - synchronized (this) { - /* - * The Null Drools Controller for no maven coordinates is always here so when no - * coordinates present, this is the return point - * - * assert (controllerCopy instanceof NullDroolsController) - */ - if (droolsControllers.containsKey(controllerId)) { - controllerCopy = droolsControllers.get(controllerId); - if (controllerCopy.getVersion().equalsIgnoreCase(newVersion)) { - return controllerCopy; - } - } - } - - if (controllerCopy != null) { - /* - * a controller keyed by group id + artifact id exists but with different version => - * version upgrade/downgrade - */ - - controllerCopy.updateToVersion(newGroupId, newArtifactId, newVersion, decoderConfigurations, - encoderConfigurations); - - return controllerCopy; - } - - /* new drools controller */ - - DroolsController controller = new MavenDroolsController(newGroupId, newArtifactId, newVersion, - decoderConfigurations, encoderConfigurations); - - synchronized (this) { - droolsControllers.put(controllerId, controller); - } - - return controller; - } - - /** - * find out decoder classes and filters. - * - * @param properties properties with information about decoders - * @param topicEntities topic sources - * @return list of topics, each with associated decoder classes, each with a list of associated - * filters - * @throws IllegalArgumentException invalid input data - */ - protected List<TopicCoderFilterConfiguration> codersAndFilters(Properties properties, - List<? extends Topic> topicEntities) { - - String propertyTopicEntityPrefix; - - List<TopicCoderFilterConfiguration> topics2DecodedClasses2Filters = new ArrayList<>(); - - if (topicEntities == null || topicEntities.isEmpty()) { - return topics2DecodedClasses2Filters; - } - - for (Topic topic : topicEntities) { - - /* source or sink ? ueb or dmaap? */ - boolean isSource = topic instanceof TopicSource; - CommInfrastructure commInfra = topic.getTopicCommInfrastructure(); - if (commInfra == CommInfrastructure.UEB) { - if (isSource) { - propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_UEB_SOURCE_TOPICS + "."; - } else { - propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_UEB_SINK_TOPICS + "."; - } - } else if (commInfra == CommInfrastructure.DMAAP) { - if (isSource) { - propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_DMAAP_SOURCE_TOPICS + "."; - } else { - propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_DMAAP_SINK_TOPICS + "."; - } - } else if (commInfra == CommInfrastructure.NOOP) { - if (isSource) { - propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_NOOP_SOURCE_TOPICS + "."; - } else { - propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_NOOP_SINK_TOPICS + "."; - } - } else { - throw new IllegalArgumentException("Invalid Communication Infrastructure: " + commInfra); - } - - // 1. first the topic - - String firstTopic = topic.getTopic(); - - // 2. check if there is a custom decoder for this topic that the user prefers to use - // instead of the ones provided in the platform - - String customGson = properties.getProperty(propertyTopicEntityPrefix + firstTopic - + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_CUSTOM_MODEL_CODER_GSON_SUFFIX); - - CustomGsonCoder customGsonCoder = null; - if (customGson != null && !customGson.isEmpty()) { - try { - customGsonCoder = new CustomGsonCoder(customGson); - } catch (IllegalArgumentException e) { - logger.warn("{}: cannot create custom-gson-coder {} because of {}", this, customGson, - e.getMessage(), e); - } - } - - // 3. second the list of classes associated with each topic - - String eventClasses = properties - .getProperty(propertyTopicEntityPrefix + firstTopic - + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_SUFFIX); - - if (eventClasses == null || eventClasses.isEmpty()) { - logger.warn("There are no event classes for topic {}", firstTopic); - continue; - } - - List<PotentialCoderFilter> classes2Filters = new ArrayList<>(); - - List<String> topicClasses = new ArrayList<>(Arrays.asList(eventClasses.split("\\s*,\\s*"))); - - for (String theClass : topicClasses) { - - - // 4. third, for each coder class, get the filter expression - - String filter = properties - .getProperty(propertyTopicEntityPrefix + firstTopic - + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_SUFFIX - + "." + theClass + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_FILTER_SUFFIX); - - JsonProtocolFilter protocolFilter = new JsonProtocolFilter(filter); - PotentialCoderFilter class2Filters = new PotentialCoderFilter(theClass, protocolFilter); - classes2Filters.add(class2Filters); - } - - TopicCoderFilterConfiguration topic2Classes2Filters = - new TopicCoderFilterConfiguration(firstTopic, classes2Filters, customGsonCoder); - topics2DecodedClasses2Filters.add(topic2Classes2Filters); - } - - return topics2DecodedClasses2Filters; - } - - @Override - public void destroy(DroolsController controller) { - unmanage(controller); - controller.halt(); - } - - @Override - public void destroy() { - List<DroolsController> controllers = this.inventory(); - for (DroolsController controller : controllers) { - controller.halt(); - } - - synchronized (this) { - this.droolsControllers.clear(); - } - } - - /** - * unmanage the drools controller. - * - * @param controller the controller - */ - protected void unmanage(DroolsController controller) { - if (controller == null) { - throw new IllegalArgumentException("No controller provided"); - } - - if (!controller.isBrained()) { - logger.info("Drools Controller is NOT OPERATIONAL - nothing to destroy"); - return; - } - - String controllerId = controller.getGroupId() + ":" + controller.getArtifactId(); - synchronized (this) { - if (!this.droolsControllers.containsKey(controllerId)) { - return; - } - - droolsControllers.remove(controllerId); - } - } - - @Override - public void shutdown(DroolsController controller) { - this.unmanage(controller); - controller.shutdown(); - } - - @Override - public void shutdown() { - List<DroolsController> controllers = this.inventory(); - for (DroolsController controller : controllers) { - controller.shutdown(); - } - - synchronized (this) { - this.droolsControllers.clear(); - } - } - - @Override - public DroolsController get(String groupId, String artifactId, String version) { - - if (groupId == null || artifactId == null || groupId.isEmpty() || artifactId.isEmpty()) { - throw new IllegalArgumentException("Missing maven coordinates: " + groupId + ":" + artifactId); - } - - String controllerId = groupId + ":" + artifactId; - - synchronized (this) { - if (this.droolsControllers.containsKey(controllerId)) { - return droolsControllers.get(controllerId); - } else { - throw new IllegalStateException("DroolController for " + controllerId + " not found"); - } - } - } - - @Override - public List<DroolsController> inventory() { - return new ArrayList<>(this.droolsControllers.values()); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("IndexedDroolsControllerFactory [#droolsControllers=").append(droolsControllers.size()) - .append("]"); - return builder.toString(); - } - -} diff --git a/policy-management/src/main/java/org/onap/policy/drools/controller/IndexedDroolsControllerFactory.java b/policy-management/src/main/java/org/onap/policy/drools/controller/IndexedDroolsControllerFactory.java new file mode 100644 index 00000000..89e2a1a8 --- /dev/null +++ b/policy-management/src/main/java/org/onap/policy/drools/controller/IndexedDroolsControllerFactory.java @@ -0,0 +1,359 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 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.controller; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Properties; +import org.onap.policy.common.endpoints.event.comm.Topic; +import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure; +import org.onap.policy.common.endpoints.event.comm.TopicSink; +import org.onap.policy.common.endpoints.event.comm.TopicSource; +import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties; +import org.onap.policy.drools.controller.internal.MavenDroolsController; +import org.onap.policy.drools.controller.internal.NullDroolsController; +import org.onap.policy.drools.properties.DroolsProperties; +import org.onap.policy.drools.protocol.coders.JsonProtocolFilter; +import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration; +import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder; +import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.PotentialCoderFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Factory of Drools Controllers indexed by the Maven coordinates. + */ +class IndexedDroolsControllerFactory implements DroolsControllerFactory { + + /** + * logger. + */ + private static Logger logger = LoggerFactory.getLogger(MavenDroolsController.class); + + /** + * Policy Controller Name Index. + */ + protected HashMap<String, DroolsController> droolsControllers = new HashMap<>(); + + /** + * Null Drools Controller. + */ + protected NullDroolsController nullDroolsController = new NullDroolsController(); + + /** + * Constructs the object. + */ + public IndexedDroolsControllerFactory() { + + /* Add a NULL controller which will always be present in the hash */ + + DroolsController controller = new NullDroolsController(); + String controllerId = controller.getGroupId() + ":" + controller.getArtifactId(); + + synchronized (this) { + droolsControllers.put(controllerId, controller); + } + } + + @Override + public DroolsController build(Properties properties, List<? extends TopicSource> eventSources, + List<? extends TopicSink> eventSinks) throws LinkageError { + + String groupId = properties.getProperty(DroolsProperties.RULES_GROUPID); + if (groupId == null || groupId.isEmpty()) { + groupId = DroolsController.NO_GROUP_ID; + } + + String artifactId = properties.getProperty(DroolsProperties.RULES_ARTIFACTID); + if (artifactId == null || artifactId.isEmpty()) { + artifactId = DroolsController.NO_ARTIFACT_ID; + } + + String version = properties.getProperty(DroolsProperties.RULES_VERSION); + if (version == null || version.isEmpty()) { + version = DroolsController.NO_VERSION; + } + + List<TopicCoderFilterConfiguration> topics2DecodedClasses2Filters = codersAndFilters(properties, eventSources); + + List<TopicCoderFilterConfiguration> topics2EncodedClasses2Filters = codersAndFilters(properties, eventSinks); + + return this.build(groupId, artifactId, version, topics2DecodedClasses2Filters, topics2EncodedClasses2Filters); + } + + @Override + public DroolsController build(String newGroupId, String newArtifactId, String newVersion, + List<TopicCoderFilterConfiguration> decoderConfigurations, + List<TopicCoderFilterConfiguration> encoderConfigurations) throws LinkageError { + + if (newGroupId == null || newGroupId.isEmpty()) { + throw new IllegalArgumentException("Missing maven group-id coordinate"); + } + + if (newArtifactId == null || newArtifactId.isEmpty()) { + throw new IllegalArgumentException("Missing maven artifact-id coordinate"); + } + + if (newVersion == null || newVersion.isEmpty()) { + throw new IllegalArgumentException("Missing maven version coordinate"); + } + + String controllerId = newGroupId + ":" + newArtifactId; + DroolsController controllerCopy = null; + synchronized (this) { + /* + * The Null Drools Controller for no maven coordinates is always here so when no + * coordinates present, this is the return point + * + * assert (controllerCopy instanceof NullDroolsController) + */ + if (droolsControllers.containsKey(controllerId)) { + controllerCopy = droolsControllers.get(controllerId); + if (controllerCopy.getVersion().equalsIgnoreCase(newVersion)) { + return controllerCopy; + } + } + } + + if (controllerCopy != null) { + /* + * a controller keyed by group id + artifact id exists but with different version => + * version upgrade/downgrade + */ + + controllerCopy.updateToVersion(newGroupId, newArtifactId, newVersion, decoderConfigurations, + encoderConfigurations); + + return controllerCopy; + } + + /* new drools controller */ + + DroolsController controller = new MavenDroolsController(newGroupId, newArtifactId, newVersion, + decoderConfigurations, encoderConfigurations); + + synchronized (this) { + droolsControllers.put(controllerId, controller); + } + + return controller; + } + + /** + * find out decoder classes and filters. + * + * @param properties properties with information about decoders + * @param topicEntities topic sources + * @return list of topics, each with associated decoder classes, each with a list of associated + * filters + * @throws IllegalArgumentException invalid input data + */ + protected List<TopicCoderFilterConfiguration> codersAndFilters(Properties properties, + List<? extends Topic> topicEntities) { + + String propertyTopicEntityPrefix; + + List<TopicCoderFilterConfiguration> topics2DecodedClasses2Filters = new ArrayList<>(); + + if (topicEntities == null || topicEntities.isEmpty()) { + return topics2DecodedClasses2Filters; + } + + for (Topic topic : topicEntities) { + + /* source or sink ? ueb or dmaap? */ + boolean isSource = topic instanceof TopicSource; + CommInfrastructure commInfra = topic.getTopicCommInfrastructure(); + if (commInfra == CommInfrastructure.UEB) { + if (isSource) { + propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_UEB_SOURCE_TOPICS + "."; + } else { + propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_UEB_SINK_TOPICS + "."; + } + } else if (commInfra == CommInfrastructure.DMAAP) { + if (isSource) { + propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_DMAAP_SOURCE_TOPICS + "."; + } else { + propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_DMAAP_SINK_TOPICS + "."; + } + } else if (commInfra == CommInfrastructure.NOOP) { + if (isSource) { + propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_NOOP_SOURCE_TOPICS + "."; + } else { + propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_NOOP_SINK_TOPICS + "."; + } + } else { + throw new IllegalArgumentException("Invalid Communication Infrastructure: " + commInfra); + } + + // 1. first the topic + + String firstTopic = topic.getTopic(); + + // 2. check if there is a custom decoder for this topic that the user prefers to use + // instead of the ones provided in the platform + + String customGson = properties.getProperty(propertyTopicEntityPrefix + firstTopic + + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_CUSTOM_MODEL_CODER_GSON_SUFFIX); + + CustomGsonCoder customGsonCoder = null; + if (customGson != null && !customGson.isEmpty()) { + try { + customGsonCoder = new CustomGsonCoder(customGson); + } catch (IllegalArgumentException e) { + logger.warn("{}: cannot create custom-gson-coder {} because of {}", this, customGson, + e.getMessage(), e); + } + } + + // 3. second the list of classes associated with each topic + + String eventClasses = properties + .getProperty(propertyTopicEntityPrefix + firstTopic + + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_SUFFIX); + + if (eventClasses == null || eventClasses.isEmpty()) { + logger.warn("There are no event classes for topic {}", firstTopic); + continue; + } + + List<PotentialCoderFilter> classes2Filters = new ArrayList<>(); + + List<String> topicClasses = new ArrayList<>(Arrays.asList(eventClasses.split("\\s*,\\s*"))); + + for (String theClass : topicClasses) { + + + // 4. third, for each coder class, get the filter expression + + String filter = properties + .getProperty(propertyTopicEntityPrefix + firstTopic + + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_SUFFIX + + "." + theClass + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_FILTER_SUFFIX); + + JsonProtocolFilter protocolFilter = new JsonProtocolFilter(filter); + PotentialCoderFilter class2Filters = new PotentialCoderFilter(theClass, protocolFilter); + classes2Filters.add(class2Filters); + } + + TopicCoderFilterConfiguration topic2Classes2Filters = + new TopicCoderFilterConfiguration(firstTopic, classes2Filters, customGsonCoder); + topics2DecodedClasses2Filters.add(topic2Classes2Filters); + } + + return topics2DecodedClasses2Filters; + } + + @Override + public void destroy(DroolsController controller) { + unmanage(controller); + controller.halt(); + } + + @Override + public void destroy() { + List<DroolsController> controllers = this.inventory(); + for (DroolsController controller : controllers) { + controller.halt(); + } + + synchronized (this) { + this.droolsControllers.clear(); + } + } + + /** + * unmanage the drools controller. + * + * @param controller the controller + */ + protected void unmanage(DroolsController controller) { + if (controller == null) { + throw new IllegalArgumentException("No controller provided"); + } + + if (!controller.isBrained()) { + logger.info("Drools Controller is NOT OPERATIONAL - nothing to destroy"); + return; + } + + String controllerId = controller.getGroupId() + ":" + controller.getArtifactId(); + synchronized (this) { + if (!this.droolsControllers.containsKey(controllerId)) { + return; + } + + droolsControllers.remove(controllerId); + } + } + + @Override + public void shutdown(DroolsController controller) { + this.unmanage(controller); + controller.shutdown(); + } + + @Override + public void shutdown() { + List<DroolsController> controllers = this.inventory(); + for (DroolsController controller : controllers) { + controller.shutdown(); + } + + synchronized (this) { + this.droolsControllers.clear(); + } + } + + @Override + public DroolsController get(String groupId, String artifactId, String version) { + + if (groupId == null || artifactId == null || groupId.isEmpty() || artifactId.isEmpty()) { + throw new IllegalArgumentException("Missing maven coordinates: " + groupId + ":" + artifactId); + } + + String controllerId = groupId + ":" + artifactId; + + synchronized (this) { + if (this.droolsControllers.containsKey(controllerId)) { + return droolsControllers.get(controllerId); + } else { + throw new IllegalStateException("DroolController for " + controllerId + " not found"); + } + } + } + + @Override + public List<DroolsController> inventory() { + return new ArrayList<>(this.droolsControllers.values()); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("IndexedDroolsControllerFactory [#droolsControllers=").append(droolsControllers.size()) + .append("]"); + return builder.toString(); + } + +} diff --git a/policy-management/src/main/java/org/onap/policy/drools/controller/internal/MavenDroolsController.java b/policy-management/src/main/java/org/onap/policy/drools/controller/internal/MavenDroolsController.java index 97312042..d0b98450 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/controller/internal/MavenDroolsController.java +++ b/policy-management/src/main/java/org/onap/policy/drools/controller/internal/MavenDroolsController.java @@ -45,7 +45,7 @@ import org.onap.policy.drools.controller.DroolsController; import org.onap.policy.drools.core.PolicyContainer; import org.onap.policy.drools.core.PolicySession; import org.onap.policy.drools.core.jmx.PdpJmx; -import org.onap.policy.drools.features.DroolsControllerFeatureAPI; +import org.onap.policy.drools.features.DroolsControllerFeatureApi; import org.onap.policy.drools.protocol.coders.EventProtocolCoder; import org.onap.policy.drools.protocol.coders.EventProtocolParams; import org.onap.policy.drools.protocol.coders.JsonProtocolFilter; @@ -538,7 +538,7 @@ public class MavenDroolsController implements DroolsController { // Broadcast - for (DroolsControllerFeatureAPI feature : DroolsControllerFeatureAPI.providers.getList()) { + for (DroolsControllerFeatureApi feature : DroolsControllerFeatureApi.providers.getList()) { try { if (feature.beforeInsert(this, event)) { return true; @@ -554,7 +554,7 @@ public class MavenDroolsController implements DroolsController { logger.warn(this + "Failed to inject into PolicyContainer {}", this.getSessionNames()); } - for (DroolsControllerFeatureAPI feature : DroolsControllerFeatureAPI.providers.getList()) { + for (DroolsControllerFeatureApi feature : DroolsControllerFeatureApi.providers.getList()) { try { if (feature.afterInsert(this, event, successInject)) { return true; @@ -576,7 +576,7 @@ public class MavenDroolsController implements DroolsController { logger.info("{}DELIVER: {} FROM {} TO {}", this, event, this, sink); } - for (DroolsControllerFeatureAPI feature : DroolsControllerFeatureAPI.providers.getList()) { + for (DroolsControllerFeatureApi feature : DroolsControllerFeatureApi.providers.getList()) { try { if (feature.beforeDeliver(this, sink, event)) { return true; @@ -613,7 +613,7 @@ public class MavenDroolsController implements DroolsController { boolean success = sink.send(json); - for (DroolsControllerFeatureAPI feature : DroolsControllerFeatureAPI.providers.getList()) { + for (DroolsControllerFeatureApi feature : DroolsControllerFeatureApi.providers.getList()) { try { if (feature.afterDeliver(this, sink, event, json, success)) { return true; diff --git a/policy-management/src/main/java/org/onap/policy/drools/features/DroolsControllerFeatureAPI.java b/policy-management/src/main/java/org/onap/policy/drools/features/DroolsControllerFeatureApi.java index d11bd145..d5a16a12 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/features/DroolsControllerFeatureAPI.java +++ b/policy-management/src/main/java/org/onap/policy/drools/features/DroolsControllerFeatureApi.java @@ -28,7 +28,7 @@ import org.onap.policy.drools.controller.DroolsController; /** * Drools Controller Feature API. Hooks into the Drools Controller operations. */ -public interface DroolsControllerFeatureAPI extends OrderedService { +public interface DroolsControllerFeatureApi extends OrderedService { /** * intercepts before the Drools Controller gives the Policy Container a fact to @@ -78,6 +78,6 @@ public interface DroolsControllerFeatureAPI extends OrderedService { /** * Feature providers implementing this interface. */ - public static final OrderedServiceImpl<DroolsControllerFeatureAPI> providers = - new OrderedServiceImpl<>(DroolsControllerFeatureAPI.class); + public static final OrderedServiceImpl<DroolsControllerFeatureApi> providers = + new OrderedServiceImpl<>(DroolsControllerFeatureApi.class); } diff --git a/policy-management/src/main/java/org/onap/policy/drools/features/PolicyControllerFeatureAPI.java b/policy-management/src/main/java/org/onap/policy/drools/features/PolicyControllerFeatureApi.java index 914b073f..c9e78c6a 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/features/PolicyControllerFeatureAPI.java +++ b/policy-management/src/main/java/org/onap/policy/drools/features/PolicyControllerFeatureApi.java @@ -26,13 +26,13 @@ import org.onap.policy.common.utils.services.OrderedService; import org.onap.policy.common.utils.services.OrderedServiceImpl; import org.onap.policy.drools.system.PolicyController; -public interface PolicyControllerFeatureAPI extends OrderedService { +public interface PolicyControllerFeatureApi extends OrderedService { /** * Feature providers implementing this interface. */ - OrderedServiceImpl<PolicyControllerFeatureAPI> providers = - new OrderedServiceImpl<>(PolicyControllerFeatureAPI.class); + OrderedServiceImpl<PolicyControllerFeatureApi> providers = + new OrderedServiceImpl<>(PolicyControllerFeatureApi.class); /** * called before creating a controller with name 'name' and diff --git a/policy-management/src/main/java/org/onap/policy/drools/features/PolicyEngineFeatureAPI.java b/policy-management/src/main/java/org/onap/policy/drools/features/PolicyEngineFeatureApi.java index 9b099909..bd834c4d 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/features/PolicyEngineFeatureAPI.java +++ b/policy-management/src/main/java/org/onap/policy/drools/features/PolicyEngineFeatureApi.java @@ -31,12 +31,12 @@ import org.onap.policy.drools.system.PolicyEngine; * Policy Engine Feature API. * Provides Interception Points during the Policy Engine lifecycle. */ -public interface PolicyEngineFeatureAPI extends OrderedService { +public interface PolicyEngineFeatureApi extends OrderedService { /** * Feature providers implementing this interface. */ - public static final OrderedServiceImpl<PolicyEngineFeatureAPI> providers = - new OrderedServiceImpl<>(PolicyEngineFeatureAPI.class); + public static final OrderedServiceImpl<PolicyEngineFeatureApi> providers = + new OrderedServiceImpl<>(PolicyEngineFeatureApi.class); /** * intercept before the Policy Engine is commanded to boot. 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 1297ff8c..62a0cbe2 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 @@ -21,15 +21,8 @@ package org.onap.policy.drools.protocol.coders; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; import java.util.List; -import java.util.Map; import org.onap.policy.drools.controller.DroolsController; -import org.onap.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Coder (Encoder/Decoder) of Events. @@ -349,959 +342,3 @@ public interface EventProtocolCoder { */ public String encode(String topic, Object event, DroolsController droolsController); } - -/** - * Protocol Coder that does its best attempt to decode/encode, selecting the best class and best fitted json parsing - * tools. - */ -class MultiplexorEventProtocolCoder implements EventProtocolCoder { - - /** - * Logger. - */ - private static Logger logger = LoggerFactory.getLogger(MultiplexorEventProtocolCoder.class); - - /** - * Decoders. - */ - protected EventProtocolDecoder decoders = new EventProtocolDecoder(); - - /** - * Encoders. - */ - protected EventProtocolEncoder encoders = new EventProtocolEncoder(); - - /** - * {@inheritDoc}. - */ - @Override - public void addDecoder(EventProtocolParams eventProtocolParams) { - logger.info( - "{}: add-decoder {}:{}:{}:{}:{}:{}:{}", - this, - eventProtocolParams.getGroupId(), - eventProtocolParams.getArtifactId(), - eventProtocolParams.getTopic(), - eventProtocolParams.getEventClass(), - eventProtocolParams.getProtocolFilter(), - eventProtocolParams.getCustomGsonCoder(), - eventProtocolParams.getModelClassLoaderHash()); - this.decoders.add(eventProtocolParams); - } - - /** - * {@inheritDoc}. - * - * @param eventProtocolParams parameter object for event encoder - */ - @Override - public void addEncoder(EventProtocolParams eventProtocolParams) { - logger.info( - "{}: add-decoder {}:{}:{}:{}:{}:{}:{}", - this, - eventProtocolParams.getGroupId(), - eventProtocolParams.getArtifactId(), - eventProtocolParams.getTopic(), - eventProtocolParams.getEventClass(), - eventProtocolParams.getProtocolFilter(), - eventProtocolParams.getCustomGsonCoder(), - eventProtocolParams.getModelClassLoaderHash()); - this.encoders.add(eventProtocolParams); - } - - /** - * {@inheritDoc}. - */ - @Override - public void removeDecoders(String groupId, String artifactId, String topic) { - logger.info("{}: remove-decoder {}:{}:{}", this, groupId, artifactId, topic); - this.decoders.remove(groupId, artifactId, topic); - } - - /** - * {@inheritDoc}. - */ - @Override - public void removeEncoders(String groupId, String artifactId, String topic) { - logger.info("{}: remove-encoder {}:{}:{}", this, groupId, artifactId, topic); - this.encoders.remove(groupId, artifactId, topic); - } - - /** - * {@inheritDoc}. - */ - @Override - public boolean isDecodingSupported(String groupId, String artifactId, String topic) { - return this.decoders.isCodingSupported(groupId, artifactId, topic); - } - - /** - * {@inheritDoc}. - */ - @Override - public boolean isEncodingSupported(String groupId, String artifactId, String topic) { - return this.encoders.isCodingSupported(groupId, artifactId, topic); - } - - /** - * {@inheritDoc}. - */ - @Override - public Object decode(String groupId, String artifactId, String topic, String json) { - logger.debug("{}: decode {}:{}:{}:{}", this, groupId, artifactId, topic, json); - return this.decoders.decode(groupId, artifactId, topic, json); - } - - /** - * {@inheritDoc}. - */ - @Override - public String encode(String groupId, String artifactId, String topic, Object event) { - logger.debug("{}: encode {}:{}:{}:{}", this, groupId, artifactId, topic, event); - return this.encoders.encode(groupId, artifactId, topic, event); - } - - /** - * {@inheritDoc}. - */ - @Override - public String encode(String topic, Object event) { - logger.debug("{}: encode {}:{}", this, topic, event); - return this.encoders.encode(topic, event); - } - - /** - * {@inheritDoc}. - */ - @Override - public String encode(String topic, Object event, DroolsController droolsController) { - logger.debug("{}: encode {}:{}:{}", this, topic, event, droolsController); - return this.encoders.encode(topic, event, droolsController); - } - - /** - * {@inheritDoc}. - */ - @Override - public List<CoderFilters> getDecoderFilters(String groupId, String artifactId, String topic) { - return this.decoders.getFilters(groupId, artifactId, topic); - } - - /** - * {@inheritDoc}. - */ - @Override - public CoderFilters getDecoderFilters( - String groupId, String artifactId, String topic, String classname) { - return this.decoders.getFilters(groupId, artifactId, topic, classname); - } - - /** - * {@inheritDoc}. - */ - @Override - public List<CoderFilters> getDecoderFilters(String groupId, String artifactId) { - return this.decoders.getFilters(groupId, artifactId); - } - - /** - * {@inheritDoc}. - */ - @Override - public ProtocolCoderToolset getDecoders(String groupId, String artifactId, String topic) { - ProtocolCoderToolset decoderToolsets = - this.decoders.getCoders(groupId, artifactId, topic); - if (decoderToolsets == null) { - throw new IllegalArgumentException( - "Decoders not found for " + groupId + ":" + artifactId + ":" + topic); - } - - return decoderToolsets; - } - - /** - * get all deocders by maven coordinates and topic. - * - * @param groupId group id - * @param artifactId artifact id - * @return list of decoders - * @throws IllegalArgumentException if invalid input - */ - @Override - public List<ProtocolCoderToolset> getDecoders(String groupId, String artifactId) { - - List<ProtocolCoderToolset> decoderToolsets = - this.decoders.getCoders(groupId, artifactId); - if (decoderToolsets == null) { - throw new IllegalArgumentException("Decoders not found for " + groupId + ":" + artifactId); - } - - return new ArrayList<>(decoderToolsets); - } - - /** - * {@inheritDoc}. - */ - @Override - public List<CoderFilters> getEncoderFilters(String groupId, String artifactId, String topic) { - return this.encoders.getFilters(groupId, artifactId, topic); - } - - /** - * {@inheritDoc}. - */ - @Override - public CoderFilters getEncoderFilters( - String groupId, String artifactId, String topic, String classname) { - return this.encoders.getFilters(groupId, artifactId, topic, classname); - } - - /** - * {@inheritDoc}. - */ - @Override - public List<CoderFilters> getEncoderFilters(String groupId, String artifactId) { - return this.encoders.getFilters(groupId, artifactId); - } - - /** - * {@inheritDoc}. - */ - @Override - public List<CoderFilters> getReverseEncoderFilters(String topic, String encodedClass) { - return this.encoders.getReverseFilters(topic, encodedClass); - } - - /** - * {@inheritDoc}. - */ - @Override - public DroolsController getDroolsController(String topic, Object encodedClass) { - return this.encoders.getDroolsController(topic, encodedClass); - } - - /** - * {@inheritDoc}. - */ - @Override - public List<DroolsController> getDroolsControllers(String topic, Object encodedClass) { - return this.encoders.getDroolsControllers(topic, encodedClass); - } - - /** - * {@inheritDoc}. - */ - @Override - public String toString() { - return "MultiplexorEventProtocolCoder [decoders=" - + decoders - + ", encoders=" - + encoders - + "]"; - } -} - -/** - * This protocol Coder that does its best attempt to decode/encode, selecting the best class and best fitted json - * parsing tools. - */ -abstract class GenericEventProtocolCoder { - - private static final String INVALID_ARTIFACT_ID_MSG = "Invalid artifact id"; - - private static final String INVALID_GROUP_ID_MSG = "Invalid group id"; - - private static final String INVALID_TOPIC_MSG = "Invalid Topic"; - - private static final String UNSUPPORTED_MSG = "Unsupported"; - - private static final String MISSING_CLASS = "class must be provided"; - - private static Logger logger = LoggerFactory.getLogger(GenericEventProtocolCoder.class); - - /** - * Mapping topic:controller-id -> /<protocol-decoder-toolset/> where protocol-coder-toolset contains - * a gson-protocol-coder-toolset. - */ - protected final HashMap<String, ProtocolCoderToolset> coders = - new HashMap<>(); - - /** - * Mapping topic + classname -> Protocol Set. - */ - protected final HashMap<String, List<ProtocolCoderToolset>> - reverseCoders = new HashMap<>(); - - GenericEventProtocolCoder() { - super(); - } - - /** - * Index a new coder. - * - * @param eventProtocolParams parameter object for event encoder - * @throw IllegalArgumentException if an invalid parameter is passed - */ - public void add(EventProtocolParams eventProtocolParams) { - if (eventProtocolParams.getGroupId() == null || eventProtocolParams.getGroupId().isEmpty()) { - throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); - } - - if (eventProtocolParams.getArtifactId() == null || eventProtocolParams.getArtifactId().isEmpty()) { - throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); - } - - if (eventProtocolParams.getTopic() == null || eventProtocolParams.getTopic().isEmpty()) { - throw new IllegalArgumentException(INVALID_TOPIC_MSG); - } - - if (eventProtocolParams.getEventClass() == null) { - throw new IllegalArgumentException("Invalid Event Class"); - } - - String key = this.codersKey(eventProtocolParams.getGroupId(), eventProtocolParams.getArtifactId(), - eventProtocolParams.getTopic()); - String reverseKey = this.reverseCodersKey(eventProtocolParams.getTopic(), eventProtocolParams.getEventClass()); - - synchronized (this) { - if (coders.containsKey(key)) { - ProtocolCoderToolset toolset = coders.get(key); - - logger.info("{}: adding coders for existing {}: {}", this, key, toolset); - - toolset - .addCoder( - eventProtocolParams.getEventClass(), - eventProtocolParams.getProtocolFilter(), - eventProtocolParams.getModelClassLoaderHash()); - - if (!reverseCoders.containsKey(reverseKey)) { - logger.info( - "{}: adding new reverse coders (multiple classes case) for {}:{}: {}", - this, - reverseKey, - key, - toolset); - - List<ProtocolCoderToolset> reverseMappings = - new ArrayList<>(); - reverseMappings.add(toolset); - reverseCoders.put(reverseKey, reverseMappings); - } - return; - } - - GsonProtocolCoderToolset coderTools = - new GsonProtocolCoderToolset(eventProtocolParams, key); - - logger.info("{}: adding coders for new {}: {}", this, key, coderTools); - - coders.put(key, coderTools); - - if (reverseCoders.containsKey(reverseKey)) { - // There is another controller (different group id/artifact id/topic) - // that shares the class and the topic. - - List<ProtocolCoderToolset> toolsets = - reverseCoders.get(reverseKey); - boolean present = false; - for (ProtocolCoderToolset parserSet : toolsets) { - // just doublecheck - present = parserSet.getControllerId().equals(key); - if (present) { - /* anomaly */ - logger.error( - "{}: unexpected toolset reverse mapping found for {}:{}: {}", - this, - reverseKey, - key, - parserSet); - } - } - - if (present) { - return; - } else { - logger.info("{}: adding coder set for {}: {} ", this, reverseKey, coderTools); - toolsets.add(coderTools); - } - } else { - List<ProtocolCoderToolset> toolsets = new ArrayList<>(); - toolsets.add(coderTools); - - logger.info("{}: adding toolset for reverse key {}: {}", this, reverseKey, toolsets); - reverseCoders.put(reverseKey, toolsets); - } - } - } - - /** - * produces key for indexing toolset entries. - * - * @param groupId group id - * @param artifactId artifact id - * @param topic topic - * @return index key - */ - protected String codersKey(String groupId, String artifactId, String topic) { - return groupId + ":" + artifactId + ":" + topic; - } - - /** - * produces a key for the reverse index. - * - * @param topic topic - * @param eventClass coded class - * @return reverse index key - */ - protected String reverseCodersKey(String topic, String eventClass) { - return topic + ":" + eventClass; - } - - /** - * remove coder. - * - * @param groupId group id - * @param artifactId artifact id - * @param topic topic - * @throws IllegalArgumentException if invalid input - */ - public void remove(String groupId, String artifactId, String topic) { - - if (groupId == null || groupId.isEmpty()) { - throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); - } - - if (artifactId == null || artifactId.isEmpty()) { - throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); - } - - if (topic == null || topic.isEmpty()) { - throw new IllegalArgumentException(INVALID_TOPIC_MSG); - } - - String key = this.codersKey(groupId, artifactId, topic); - - synchronized (this) { - if (coders.containsKey(key)) { - ProtocolCoderToolset coderToolset = coders.remove(key); - - logger.info("{}: removed toolset for {}: {}", this, key, coderToolset); - - for (CoderFilters codeFilter : coderToolset.getCoders()) { - String className = codeFilter.getCodedClass(); - String reverseKey = this.reverseCodersKey(topic, className); - if (this.reverseCoders.containsKey(reverseKey)) { - List<ProtocolCoderToolset> toolsets = - this.reverseCoders.get(reverseKey); - Iterator<ProtocolCoderToolset> toolsetsIter = - toolsets.iterator(); - while (toolsetsIter.hasNext()) { - ProtocolCoderToolset toolset = toolsetsIter.next(); - if (toolset.getControllerId().equals(key)) { - logger.info( - "{}: removed coder from toolset for {} from reverse mapping", this, reverseKey); - toolsetsIter.remove(); - } - } - - if (this.reverseCoders.get(reverseKey).isEmpty()) { - logger.info("{}: removing reverse mapping for {}: ", this, reverseKey); - this.reverseCoders.remove(reverseKey); - } - } - } - } - } - } - - /** - * does it support coding. - * - * @param groupId group id - * @param artifactId artifact id - * @param topic topic - * @return true if its is codable - */ - public boolean isCodingSupported(String groupId, String artifactId, String topic) { - - if (groupId == null || groupId.isEmpty()) { - throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); - } - - if (artifactId == null || artifactId.isEmpty()) { - throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); - } - - if (topic == null || topic.isEmpty()) { - throw new IllegalArgumentException(INVALID_TOPIC_MSG); - } - - String key = this.codersKey(groupId, artifactId, topic); - synchronized (this) { - return coders.containsKey(key); - } - } - - /** - * decode a json string into an Object. - * - * @param groupId group id - * @param artifactId artifact id - * @param topic topic - * @param json json string to convert to object - * @return the decoded object - * @throws IllegalArgumentException if invalid argument is provided - * @throws UnsupportedOperationException if the operation cannot be performed - */ - public Object decode(String groupId, String artifactId, String topic, String json) { - - if (!isCodingSupported(groupId, artifactId, topic)) { - throw new IllegalArgumentException( - "Unsupported:" + codersKey(groupId, artifactId, topic) + " for encoding"); - } - - String key = this.codersKey(groupId, artifactId, topic); - ProtocolCoderToolset coderTools = coders.get(key); - try { - Object event = coderTools.decode(json); - if (event != null) { - return event; - } - } catch (Exception e) { - logger.debug("{}, cannot decode {}", this, json, e); - } - - throw new UnsupportedOperationException("Cannot decode with gson"); - } - - /** - * encode an object into a json string. - * - * @param groupId group id - * @param artifactId artifact id - * @param topic topic - * @param event object to convert to string - * @return the json string - * @throws IllegalArgumentException if invalid argument is provided - * @throws UnsupportedOperationException if the operation cannot be performed - */ - public String encode(String groupId, String artifactId, String topic, Object event) { - - if (!isCodingSupported(groupId, artifactId, topic)) { - throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic)); - } - - if (event == null) { - throw new IllegalArgumentException("Unsupported topic:" + topic); - } - - // reuse the decoder set, since there must be affinity in the model - String key = this.codersKey(groupId, artifactId, topic); - return this.encodeInternal(key, event); - } - - /** - * encode an object into a json string. - * - * @param topic topic - * @param event object to convert to string - * @return the json string - * @throws IllegalArgumentException if invalid argument is provided - * @throws UnsupportedOperationException if the operation cannot be performed - */ - public String encode(String topic, Object event) { - - if (event == null) { - throw new IllegalArgumentException("Invalid encoded class"); - } - - if (topic == null || topic.isEmpty()) { - throw new IllegalArgumentException("Invalid topic"); - } - - String reverseKey = this.reverseCodersKey(topic, event.getClass().getName()); - if (!this.reverseCoders.containsKey(reverseKey)) { - throw new IllegalArgumentException("no reverse coder has been found"); - } - - List<ProtocolCoderToolset> toolsets = - this.reverseCoders.get(reverseKey); - - String key = - codersKey( - toolsets.get(0).getGroupId(), toolsets.get(0).getArtifactId(), topic); - return this.encodeInternal(key, event); - } - - /** - * encode an object into a json string. - * - * @param topic topic - * @param encodedClass object to convert to string - * @return the json string - * @throws IllegalArgumentException if invalid argument is provided - * @throws UnsupportedOperationException if the operation cannot be performed - */ - public String encode(String topic, Object encodedClass, DroolsController droolsController) { - - if (encodedClass == null) { - throw new IllegalArgumentException("Invalid encoded class"); - } - - if (topic == null || topic.isEmpty()) { - throw new IllegalArgumentException("Invalid topic"); - } - - String key = codersKey(droolsController.getGroupId(), droolsController.getArtifactId(), topic); - return this.encodeInternal(key, encodedClass); - } - - /** - * encode an object into a json string. - * - * @param key identifier - * @param event object to convert to string - * @return the json string - * @throws IllegalArgumentException if invalid argument is provided - * @throws UnsupportedOperationException if the operation cannot be performed - */ - protected String encodeInternal(String key, Object event) { - - logger.debug("{}: encode for {}: {}", this, key, event); - - ProtocolCoderToolset coderTools = coders.get(key); - try { - String json = coderTools.encode(event); - if (json != null && !json.isEmpty()) { - return json; - } - } catch (Exception e) { - logger.warn("{}: cannot encode (first) for {}: {}", this, key, event, e); - } - - throw new UnsupportedOperationException("Cannot decode with gson"); - } - - /** - * Drools creators. - * - * @param topic topic - * @param encodedClass encoded class - * @return list of controllers - * @throws IllegalStateException illegal state - * @throws IllegalArgumentException argument - */ - protected List<DroolsController> droolsCreators(String topic, Object encodedClass) { - - List<DroolsController> droolsControllers = new ArrayList<>(); - - String reverseKey = this.reverseCodersKey(topic, encodedClass.getClass().getName()); - if (!this.reverseCoders.containsKey(reverseKey)) { - logger.warn("{}: no reverse mapping for {}", this, reverseKey); - return droolsControllers; - } - - List<ProtocolCoderToolset> toolsets = - this.reverseCoders.get(reverseKey); - - // There must be multiple toolsets associated with <topic,classname> reverseKey - // case 2 different controllers use the same models and register the same encoder for - // the same topic. This is assumed not to occur often but for the purpose of encoding - // but there should be no side-effects. Ownership is crosscheck against classname and - // classloader reference. - - if (toolsets == null || toolsets.isEmpty()) { - throw new IllegalStateException( - "No Encoders toolsets available for topic " - + topic - + " encoder " - + encodedClass.getClass().getName()); - } - - for (ProtocolCoderToolset encoderSet : toolsets) { - // figure out the right toolset - String groupId = encoderSet.getGroupId(); - String artifactId = encoderSet.getArtifactId(); - List<CoderFilters> coderFilters = encoderSet.getCoders(); - for (CoderFilters coder : coderFilters) { - if (coder.getCodedClass().equals(encodedClass.getClass().getName())) { - DroolsController droolsController = DroolsController.factory.get(groupId, artifactId, ""); - if (droolsController.ownsCoder( - encodedClass.getClass(), coder.getModelClassLoaderHash())) { - droolsControllers.add(droolsController); - } - } - } - } - - if (droolsControllers.isEmpty()) { - throw new IllegalStateException( - "No Encoders toolsets available for " - + topic - + ":" - + encodedClass.getClass().getName()); - } - - return droolsControllers; - } - - /** - * get all filters by maven coordinates and topic. - * - * @param groupId group id - * @param artifactId artifact id - * @param topic topic - * @return list of coders - * @throws IllegalArgumentException if invalid input - */ - public List<CoderFilters> getFilters(String groupId, String artifactId, String topic) { - - if (!isCodingSupported(groupId, artifactId, topic)) { - throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic)); - } - - String key = this.codersKey(groupId, artifactId, topic); - ProtocolCoderToolset coderTools = coders.get(key); - return coderTools.getCoders(); - } - - /** - * get all coders by maven coordinates and topic. - * - * @param groupId group id - * @param artifactId artifact id - * @return list of coders - * @throws IllegalArgumentException if invalid input - */ - public List<CoderFilters> getFilters(String groupId, String artifactId) { - - if (groupId == null || groupId.isEmpty()) { - throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); - } - - if (artifactId == null || artifactId.isEmpty()) { - throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); - } - - String key = this.codersKey(groupId, artifactId, ""); - - List<CoderFilters> codersFilters = new ArrayList<>(); - for (Map.Entry<String, ProtocolCoderToolset> entry : - coders.entrySet()) { - if (entry.getKey().startsWith(key)) { - codersFilters.addAll(entry.getValue().getCoders()); - } - } - - return codersFilters; - } - - /** - * get all filters by maven coordinates, topic, and classname. - * - * @param groupId group id - * @param artifactId artifact id - * @param topic topic - * @param classname classname - * @return list of coders - * @throws IllegalArgumentException if invalid input - */ - public CoderFilters getFilters( - String groupId, String artifactId, String topic, String classname) { - - if (!isCodingSupported(groupId, artifactId, topic)) { - throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic)); - } - - if (classname == null || classname.isEmpty()) { - throw new IllegalArgumentException("classname must be provided"); - } - - String key = this.codersKey(groupId, artifactId, topic); - ProtocolCoderToolset coderTools = coders.get(key); - return coderTools.getCoder(classname); - } - - /** - * get all coders by maven coordinates and topic. - * - * @param groupId group id - * @param artifactId artifact id - * @param topic topic - * @return list of coders - * @throws IllegalArgumentException if invalid input - */ - public ProtocolCoderToolset getCoders( - String groupId, String artifactId, String topic) { - - if (!isCodingSupported(groupId, artifactId, topic)) { - throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic)); - } - - String key = this.codersKey(groupId, artifactId, topic); - return coders.get(key); - } - - /** - * get all coders by maven coordinates and topic. - * - * @param groupId group id - * @param artifactId artifact id - * @return list of coders - * @throws IllegalArgumentException if invalid input - */ - public List<ProtocolCoderToolset> getCoders( - String groupId, String artifactId) { - - if (groupId == null || groupId.isEmpty()) { - throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); - } - - if (artifactId == null || artifactId.isEmpty()) { - throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); - } - - String key = this.codersKey(groupId, artifactId, ""); - - List<ProtocolCoderToolset> coderToolset = new ArrayList<>(); - for (Map.Entry<String, ProtocolCoderToolset> entry : - coders.entrySet()) { - if (entry.getKey().startsWith(key)) { - coderToolset.add(entry.getValue()); - } - } - - return coderToolset; - } - - /** - * get coded based on class and topic. - * - * @param topic topic - * @param codedClass class - * @return list of reverse filters - */ - public List<CoderFilters> getReverseFilters(String topic, String codedClass) { - - if (topic == null || topic.isEmpty()) { - throw new IllegalArgumentException(UNSUPPORTED_MSG); - } - - if (codedClass == null) { - throw new IllegalArgumentException(MISSING_CLASS); - } - - String key = this.reverseCodersKey(topic, codedClass); - List<ProtocolCoderToolset> toolsets = this.reverseCoders.get(key); - if (toolsets == null) { - throw new IllegalArgumentException("No Coder found for " + key); - } - - List<CoderFilters> coderFilters = new ArrayList<>(); - for (ProtocolCoderToolset toolset : toolsets) { - coderFilters.addAll(toolset.getCoders()); - } - - return coderFilters; - } - - /** - * returns group and artifact id of the creator of the encoder. - * - * @param topic topic - * @param fact fact - * @return the drools controller - */ - DroolsController getDroolsController(String topic, Object fact) { - - if (topic == null || topic.isEmpty()) { - throw new IllegalArgumentException(UNSUPPORTED_MSG); - } - - if (fact == null) { - throw new IllegalArgumentException(MISSING_CLASS); - } - - List<DroolsController> droolsControllers = droolsCreators(topic, fact); - - if (droolsControllers.isEmpty()) { - throw new IllegalArgumentException("Invalid Topic: " + topic); - } - - if (droolsControllers.size() > 1) { - logger.warn( - "{}: multiple drools-controller {} for {}:{} ", - this, - droolsControllers, - topic, - fact.getClass().getName()); - // continue - } - return droolsControllers.get(0); - } - - /** - * returns group and artifact id of the creator of the encoder. - * - * @param topic topic - * @param fact fact - * @return list of drools controllers - */ - List<DroolsController> getDroolsControllers(String topic, Object fact) { - - if (topic == null || topic.isEmpty()) { - throw new IllegalArgumentException(UNSUPPORTED_MSG); - } - - if (fact == null) { - throw new IllegalArgumentException(MISSING_CLASS); - } - - List<DroolsController> droolsControllers = droolsCreators(topic, fact); - if (droolsControllers.size() > 1) { - // unexpected - logger.warn( - "{}: multiple drools-controller {} for {}:{} ", - this, - droolsControllers, - topic, - fact.getClass().getName()); - // continue - } - return droolsControllers; - } - - @Override - public String toString() { - return "GenericEventProtocolCoder [coders=" - + coders.keySet() - + ", reverseCoders=" - + reverseCoders.keySet() - + "]"; - } -} - -class EventProtocolDecoder extends GenericEventProtocolCoder { - - public EventProtocolDecoder() { - super(); - } - - @Override - public String toString() { - return "EventProtocolDecoder [toString()=" + super.toString() + "]"; - } -} - -class EventProtocolEncoder extends GenericEventProtocolCoder { - - public EventProtocolEncoder() { - super(); - } - - @Override - public String toString() { - return "EventProtocolEncoder [toString()=" + super.toString() + "]"; - } -} diff --git a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/EventProtocolDecoder.java b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/EventProtocolDecoder.java new file mode 100644 index 00000000..6bb3a623 --- /dev/null +++ b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/EventProtocolDecoder.java @@ -0,0 +1,33 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 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.protocol.coders; + +class EventProtocolDecoder extends GenericEventProtocolCoder { + + public EventProtocolDecoder() { + super(); + } + + @Override + public String toString() { + return "EventProtocolDecoder [toString()=" + super.toString() + "]"; + } +} diff --git a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/EventProtocolEncoder.java b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/EventProtocolEncoder.java new file mode 100644 index 00000000..461c66a8 --- /dev/null +++ b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/EventProtocolEncoder.java @@ -0,0 +1,33 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 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.protocol.coders; + +class EventProtocolEncoder extends GenericEventProtocolCoder { + + public EventProtocolEncoder() { + super(); + } + + @Override + public String toString() { + return "EventProtocolEncoder [toString()=" + super.toString() + "]"; + } +} diff --git a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/GenericEventProtocolCoder.java b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/GenericEventProtocolCoder.java new file mode 100644 index 00000000..8643da3d --- /dev/null +++ b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/GenericEventProtocolCoder.java @@ -0,0 +1,712 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 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.protocol.coders; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import org.onap.policy.drools.controller.DroolsController; +import org.onap.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This protocol Coder that does its best attempt to decode/encode, selecting the best class and best fitted json + * parsing tools. + */ +abstract class GenericEventProtocolCoder { + + private static final String INVALID_ARTIFACT_ID_MSG = "Invalid artifact id"; + + private static final String INVALID_GROUP_ID_MSG = "Invalid group id"; + + private static final String INVALID_TOPIC_MSG = "Invalid Topic"; + + private static final String UNSUPPORTED_MSG = "Unsupported"; + + private static final String MISSING_CLASS = "class must be provided"; + + private static Logger logger = LoggerFactory.getLogger(GenericEventProtocolCoder.class); + + /** + * Mapping topic:controller-id -> /<protocol-decoder-toolset/> where protocol-coder-toolset contains + * a gson-protocol-coder-toolset. + */ + protected final HashMap<String, ProtocolCoderToolset> coders = + new HashMap<>(); + + /** + * Mapping topic + classname -> Protocol Set. + */ + protected final HashMap<String, List<ProtocolCoderToolset>> + reverseCoders = new HashMap<>(); + + GenericEventProtocolCoder() { + super(); + } + + /** + * Index a new coder. + * + * @param eventProtocolParams parameter object for event encoder + * @throw IllegalArgumentException if an invalid parameter is passed + */ + public void add(EventProtocolParams eventProtocolParams) { + if (eventProtocolParams.getGroupId() == null || eventProtocolParams.getGroupId().isEmpty()) { + throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); + } + + if (eventProtocolParams.getArtifactId() == null || eventProtocolParams.getArtifactId().isEmpty()) { + throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); + } + + if (eventProtocolParams.getTopic() == null || eventProtocolParams.getTopic().isEmpty()) { + throw new IllegalArgumentException(INVALID_TOPIC_MSG); + } + + if (eventProtocolParams.getEventClass() == null) { + throw new IllegalArgumentException("Invalid Event Class"); + } + + String key = this.codersKey(eventProtocolParams.getGroupId(), eventProtocolParams.getArtifactId(), + eventProtocolParams.getTopic()); + String reverseKey = this.reverseCodersKey(eventProtocolParams.getTopic(), eventProtocolParams.getEventClass()); + + synchronized (this) { + if (coders.containsKey(key)) { + ProtocolCoderToolset toolset = coders.get(key); + + logger.info("{}: adding coders for existing {}: {}", this, key, toolset); + + toolset + .addCoder( + eventProtocolParams.getEventClass(), + eventProtocolParams.getProtocolFilter(), + eventProtocolParams.getModelClassLoaderHash()); + + if (!reverseCoders.containsKey(reverseKey)) { + logger.info( + "{}: adding new reverse coders (multiple classes case) for {}:{}: {}", + this, + reverseKey, + key, + toolset); + + List<ProtocolCoderToolset> reverseMappings = + new ArrayList<>(); + reverseMappings.add(toolset); + reverseCoders.put(reverseKey, reverseMappings); + } + return; + } + + GsonProtocolCoderToolset coderTools = + new GsonProtocolCoderToolset(eventProtocolParams, key); + + logger.info("{}: adding coders for new {}: {}", this, key, coderTools); + + coders.put(key, coderTools); + + if (reverseCoders.containsKey(reverseKey)) { + // There is another controller (different group id/artifact id/topic) + // that shares the class and the topic. + + List<ProtocolCoderToolset> toolsets = + reverseCoders.get(reverseKey); + boolean present = false; + for (ProtocolCoderToolset parserSet : toolsets) { + // just doublecheck + present = parserSet.getControllerId().equals(key); + if (present) { + /* anomaly */ + logger.error( + "{}: unexpected toolset reverse mapping found for {}:{}: {}", + this, + reverseKey, + key, + parserSet); + } + } + + if (present) { + return; + } else { + logger.info("{}: adding coder set for {}: {} ", this, reverseKey, coderTools); + toolsets.add(coderTools); + } + } else { + List<ProtocolCoderToolset> toolsets = new ArrayList<>(); + toolsets.add(coderTools); + + logger.info("{}: adding toolset for reverse key {}: {}", this, reverseKey, toolsets); + reverseCoders.put(reverseKey, toolsets); + } + } + } + + /** + * produces key for indexing toolset entries. + * + * @param groupId group id + * @param artifactId artifact id + * @param topic topic + * @return index key + */ + protected String codersKey(String groupId, String artifactId, String topic) { + return groupId + ":" + artifactId + ":" + topic; + } + + /** + * produces a key for the reverse index. + * + * @param topic topic + * @param eventClass coded class + * @return reverse index key + */ + protected String reverseCodersKey(String topic, String eventClass) { + return topic + ":" + eventClass; + } + + /** + * remove coder. + * + * @param groupId group id + * @param artifactId artifact id + * @param topic topic + * @throws IllegalArgumentException if invalid input + */ + public void remove(String groupId, String artifactId, String topic) { + + if (groupId == null || groupId.isEmpty()) { + throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); + } + + if (artifactId == null || artifactId.isEmpty()) { + throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); + } + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException(INVALID_TOPIC_MSG); + } + + String key = this.codersKey(groupId, artifactId, topic); + + synchronized (this) { + if (coders.containsKey(key)) { + ProtocolCoderToolset coderToolset = coders.remove(key); + + logger.info("{}: removed toolset for {}: {}", this, key, coderToolset); + + for (CoderFilters codeFilter : coderToolset.getCoders()) { + String className = codeFilter.getCodedClass(); + String reverseKey = this.reverseCodersKey(topic, className); + if (this.reverseCoders.containsKey(reverseKey)) { + List<ProtocolCoderToolset> toolsets = + this.reverseCoders.get(reverseKey); + Iterator<ProtocolCoderToolset> toolsetsIter = + toolsets.iterator(); + while (toolsetsIter.hasNext()) { + ProtocolCoderToolset toolset = toolsetsIter.next(); + if (toolset.getControllerId().equals(key)) { + logger.info( + "{}: removed coder from toolset for {} from reverse mapping", this, reverseKey); + toolsetsIter.remove(); + } + } + + if (this.reverseCoders.get(reverseKey).isEmpty()) { + logger.info("{}: removing reverse mapping for {}: ", this, reverseKey); + this.reverseCoders.remove(reverseKey); + } + } + } + } + } + } + + /** + * does it support coding. + * + * @param groupId group id + * @param artifactId artifact id + * @param topic topic + * @return true if its is codable + */ + public boolean isCodingSupported(String groupId, String artifactId, String topic) { + + if (groupId == null || groupId.isEmpty()) { + throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); + } + + if (artifactId == null || artifactId.isEmpty()) { + throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); + } + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException(INVALID_TOPIC_MSG); + } + + String key = this.codersKey(groupId, artifactId, topic); + synchronized (this) { + return coders.containsKey(key); + } + } + + /** + * decode a json string into an Object. + * + * @param groupId group id + * @param artifactId artifact id + * @param topic topic + * @param json json string to convert to object + * @return the decoded object + * @throws IllegalArgumentException if invalid argument is provided + * @throws UnsupportedOperationException if the operation cannot be performed + */ + public Object decode(String groupId, String artifactId, String topic, String json) { + + if (!isCodingSupported(groupId, artifactId, topic)) { + throw new IllegalArgumentException( + "Unsupported:" + codersKey(groupId, artifactId, topic) + " for encoding"); + } + + String key = this.codersKey(groupId, artifactId, topic); + ProtocolCoderToolset coderTools = coders.get(key); + try { + Object event = coderTools.decode(json); + if (event != null) { + return event; + } + } catch (Exception e) { + logger.debug("{}, cannot decode {}", this, json, e); + } + + throw new UnsupportedOperationException("Cannot decode with gson"); + } + + /** + * encode an object into a json string. + * + * @param groupId group id + * @param artifactId artifact id + * @param topic topic + * @param event object to convert to string + * @return the json string + * @throws IllegalArgumentException if invalid argument is provided + * @throws UnsupportedOperationException if the operation cannot be performed + */ + public String encode(String groupId, String artifactId, String topic, Object event) { + + if (!isCodingSupported(groupId, artifactId, topic)) { + throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic)); + } + + if (event == null) { + throw new IllegalArgumentException("Unsupported topic:" + topic); + } + + // reuse the decoder set, since there must be affinity in the model + String key = this.codersKey(groupId, artifactId, topic); + return this.encodeInternal(key, event); + } + + /** + * encode an object into a json string. + * + * @param topic topic + * @param event object to convert to string + * @return the json string + * @throws IllegalArgumentException if invalid argument is provided + * @throws UnsupportedOperationException if the operation cannot be performed + */ + public String encode(String topic, Object event) { + + if (event == null) { + throw new IllegalArgumentException("Invalid encoded class"); + } + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException("Invalid topic"); + } + + String reverseKey = this.reverseCodersKey(topic, event.getClass().getName()); + if (!this.reverseCoders.containsKey(reverseKey)) { + throw new IllegalArgumentException("no reverse coder has been found"); + } + + List<ProtocolCoderToolset> toolsets = + this.reverseCoders.get(reverseKey); + + String key = + codersKey( + toolsets.get(0).getGroupId(), toolsets.get(0).getArtifactId(), topic); + return this.encodeInternal(key, event); + } + + /** + * encode an object into a json string. + * + * @param topic topic + * @param encodedClass object to convert to string + * @return the json string + * @throws IllegalArgumentException if invalid argument is provided + * @throws UnsupportedOperationException if the operation cannot be performed + */ + public String encode(String topic, Object encodedClass, DroolsController droolsController) { + + if (encodedClass == null) { + throw new IllegalArgumentException("Invalid encoded class"); + } + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException("Invalid topic"); + } + + String key = codersKey(droolsController.getGroupId(), droolsController.getArtifactId(), topic); + return this.encodeInternal(key, encodedClass); + } + + /** + * encode an object into a json string. + * + * @param key identifier + * @param event object to convert to string + * @return the json string + * @throws IllegalArgumentException if invalid argument is provided + * @throws UnsupportedOperationException if the operation cannot be performed + */ + protected String encodeInternal(String key, Object event) { + + logger.debug("{}: encode for {}: {}", this, key, event); + + ProtocolCoderToolset coderTools = coders.get(key); + try { + String json = coderTools.encode(event); + if (json != null && !json.isEmpty()) { + return json; + } + } catch (Exception e) { + logger.warn("{}: cannot encode (first) for {}: {}", this, key, event, e); + } + + throw new UnsupportedOperationException("Cannot decode with gson"); + } + + /** + * Drools creators. + * + * @param topic topic + * @param encodedClass encoded class + * @return list of controllers + * @throws IllegalStateException illegal state + * @throws IllegalArgumentException argument + */ + protected List<DroolsController> droolsCreators(String topic, Object encodedClass) { + + List<DroolsController> droolsControllers = new ArrayList<>(); + + String reverseKey = this.reverseCodersKey(topic, encodedClass.getClass().getName()); + if (!this.reverseCoders.containsKey(reverseKey)) { + logger.warn("{}: no reverse mapping for {}", this, reverseKey); + return droolsControllers; + } + + List<ProtocolCoderToolset> toolsets = + this.reverseCoders.get(reverseKey); + + // There must be multiple toolsets associated with <topic,classname> reverseKey + // case 2 different controllers use the same models and register the same encoder for + // the same topic. This is assumed not to occur often but for the purpose of encoding + // but there should be no side-effects. Ownership is crosscheck against classname and + // classloader reference. + + if (toolsets == null || toolsets.isEmpty()) { + throw new IllegalStateException( + "No Encoders toolsets available for topic " + + topic + + " encoder " + + encodedClass.getClass().getName()); + } + + for (ProtocolCoderToolset encoderSet : toolsets) { + // figure out the right toolset + String groupId = encoderSet.getGroupId(); + String artifactId = encoderSet.getArtifactId(); + List<CoderFilters> coderFilters = encoderSet.getCoders(); + for (CoderFilters coder : coderFilters) { + if (coder.getCodedClass().equals(encodedClass.getClass().getName())) { + DroolsController droolsController = DroolsController.factory.get(groupId, artifactId, ""); + if (droolsController.ownsCoder( + encodedClass.getClass(), coder.getModelClassLoaderHash())) { + droolsControllers.add(droolsController); + } + } + } + } + + if (droolsControllers.isEmpty()) { + throw new IllegalStateException( + "No Encoders toolsets available for " + + topic + + ":" + + encodedClass.getClass().getName()); + } + + return droolsControllers; + } + + /** + * get all filters by maven coordinates and topic. + * + * @param groupId group id + * @param artifactId artifact id + * @param topic topic + * @return list of coders + * @throws IllegalArgumentException if invalid input + */ + public List<CoderFilters> getFilters(String groupId, String artifactId, String topic) { + + if (!isCodingSupported(groupId, artifactId, topic)) { + throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic)); + } + + String key = this.codersKey(groupId, artifactId, topic); + ProtocolCoderToolset coderTools = coders.get(key); + return coderTools.getCoders(); + } + + /** + * get all coders by maven coordinates and topic. + * + * @param groupId group id + * @param artifactId artifact id + * @return list of coders + * @throws IllegalArgumentException if invalid input + */ + public List<CoderFilters> getFilters(String groupId, String artifactId) { + + if (groupId == null || groupId.isEmpty()) { + throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); + } + + if (artifactId == null || artifactId.isEmpty()) { + throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); + } + + String key = this.codersKey(groupId, artifactId, ""); + + List<CoderFilters> codersFilters = new ArrayList<>(); + for (Map.Entry<String, ProtocolCoderToolset> entry : + coders.entrySet()) { + if (entry.getKey().startsWith(key)) { + codersFilters.addAll(entry.getValue().getCoders()); + } + } + + return codersFilters; + } + + /** + * get all filters by maven coordinates, topic, and classname. + * + * @param groupId group id + * @param artifactId artifact id + * @param topic topic + * @param classname classname + * @return list of coders + * @throws IllegalArgumentException if invalid input + */ + public CoderFilters getFilters( + String groupId, String artifactId, String topic, String classname) { + + if (!isCodingSupported(groupId, artifactId, topic)) { + throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic)); + } + + if (classname == null || classname.isEmpty()) { + throw new IllegalArgumentException("classname must be provided"); + } + + String key = this.codersKey(groupId, artifactId, topic); + ProtocolCoderToolset coderTools = coders.get(key); + return coderTools.getCoder(classname); + } + + /** + * get all coders by maven coordinates and topic. + * + * @param groupId group id + * @param artifactId artifact id + * @param topic topic + * @return list of coders + * @throws IllegalArgumentException if invalid input + */ + public ProtocolCoderToolset getCoders( + String groupId, String artifactId, String topic) { + + if (!isCodingSupported(groupId, artifactId, topic)) { + throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic)); + } + + String key = this.codersKey(groupId, artifactId, topic); + return coders.get(key); + } + + /** + * get all coders by maven coordinates and topic. + * + * @param groupId group id + * @param artifactId artifact id + * @return list of coders + * @throws IllegalArgumentException if invalid input + */ + public List<ProtocolCoderToolset> getCoders( + String groupId, String artifactId) { + + if (groupId == null || groupId.isEmpty()) { + throw new IllegalArgumentException(INVALID_GROUP_ID_MSG); + } + + if (artifactId == null || artifactId.isEmpty()) { + throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG); + } + + String key = this.codersKey(groupId, artifactId, ""); + + List<ProtocolCoderToolset> coderToolset = new ArrayList<>(); + for (Map.Entry<String, ProtocolCoderToolset> entry : + coders.entrySet()) { + if (entry.getKey().startsWith(key)) { + coderToolset.add(entry.getValue()); + } + } + + return coderToolset; + } + + /** + * get coded based on class and topic. + * + * @param topic topic + * @param codedClass class + * @return list of reverse filters + */ + public List<CoderFilters> getReverseFilters(String topic, String codedClass) { + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException(UNSUPPORTED_MSG); + } + + if (codedClass == null) { + throw new IllegalArgumentException(MISSING_CLASS); + } + + String key = this.reverseCodersKey(topic, codedClass); + List<ProtocolCoderToolset> toolsets = this.reverseCoders.get(key); + if (toolsets == null) { + throw new IllegalArgumentException("No Coder found for " + key); + } + + List<CoderFilters> coderFilters = new ArrayList<>(); + for (ProtocolCoderToolset toolset : toolsets) { + coderFilters.addAll(toolset.getCoders()); + } + + return coderFilters; + } + + /** + * returns group and artifact id of the creator of the encoder. + * + * @param topic topic + * @param fact fact + * @return the drools controller + */ + DroolsController getDroolsController(String topic, Object fact) { + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException(UNSUPPORTED_MSG); + } + + if (fact == null) { + throw new IllegalArgumentException(MISSING_CLASS); + } + + List<DroolsController> droolsControllers = droolsCreators(topic, fact); + + if (droolsControllers.isEmpty()) { + throw new IllegalArgumentException("Invalid Topic: " + topic); + } + + if (droolsControllers.size() > 1) { + logger.warn( + "{}: multiple drools-controller {} for {}:{} ", + this, + droolsControllers, + topic, + fact.getClass().getName()); + // continue + } + return droolsControllers.get(0); + } + + /** + * returns group and artifact id of the creator of the encoder. + * + * @param topic topic + * @param fact fact + * @return list of drools controllers + */ + List<DroolsController> getDroolsControllers(String topic, Object fact) { + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException(UNSUPPORTED_MSG); + } + + if (fact == null) { + throw new IllegalArgumentException(MISSING_CLASS); + } + + List<DroolsController> droolsControllers = droolsCreators(topic, fact); + if (droolsControllers.size() > 1) { + // unexpected + logger.warn( + "{}: multiple drools-controller {} for {}:{} ", + this, + droolsControllers, + topic, + fact.getClass().getName()); + // continue + } + return droolsControllers; + } + + @Override + public String toString() { + return "GenericEventProtocolCoder [coders=" + + coders.keySet() + + ", reverseCoders=" + + reverseCoders.keySet() + + "]"; + } +} diff --git a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/GsonProtocolCoderToolset.java b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/GsonProtocolCoderToolset.java new file mode 100644 index 00000000..57976a3e --- /dev/null +++ b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/GsonProtocolCoderToolset.java @@ -0,0 +1,249 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 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.protocol.coders; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +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 org.onap.policy.common.gson.annotation.GsonJsonIgnore; +import org.onap.policy.drools.controller.DroolsController; +import org.onap.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Tools used for encoding/decoding using GSON. + */ +class GsonProtocolCoderToolset extends ProtocolCoderToolset { + /** + * Logger. + */ + private static final Logger logger = LoggerFactory.getLogger(GsonProtocolCoderToolset.class); + + /** + * Formatter for JSON encoding/decoding. + */ + @JsonIgnore + @GsonJsonIgnore + public static final DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSxxx"); + + @JsonIgnore + @GsonJsonIgnore + public static final DateTimeFormatter zuluFormat = DateTimeFormatter.ISO_INSTANT; + + /** + * Adapter for ZonedDateTime. + */ + public static class GsonUtcAdapter implements JsonSerializer<ZonedDateTime>, JsonDeserializer<ZonedDateTime> { + @Override + public ZonedDateTime deserialize(JsonElement element, Type type, + JsonDeserializationContext context) { + try { + return ZonedDateTime.parse(element.getAsString(), format); + } catch (final Exception e) { + logger.info("GsonUTCAdapter: cannot parse {} because of {}", element, e.getMessage(), e); + } + return null; + } + + @Override + public JsonElement serialize(ZonedDateTime datetime, Type type, + JsonSerializationContext context) { + return new JsonPrimitive(datetime.format(format)); + } + } + + public static class GsonInstantAdapter implements JsonSerializer<Instant>, JsonDeserializer<Instant> { + + @Override + public Instant deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { + return Instant.ofEpochMilli(json.getAsLong()); + } + + @Override + public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src.toEpochMilli()); + } + + } + + + /** + * decoder. + */ + @JsonIgnore + @GsonJsonIgnore + protected final Gson decoder = new GsonBuilder().disableHtmlEscaping() + .registerTypeAdapter(ZonedDateTime.class, new GsonUtcAdapter()) + .registerTypeAdapter(Instant.class, new GsonInstantAdapter()).create(); + + /** + * encoder. + */ + @JsonIgnore + @GsonJsonIgnore + protected final Gson encoder = new GsonBuilder().disableHtmlEscaping() + .registerTypeAdapter(ZonedDateTime.class, new GsonUtcAdapter()) + .registerTypeAdapter(Instant.class, new GsonInstantAdapter()).create(); + + /** + * Toolset to encode/decode tools associated with a topic. + * + * @param eventProtocolParams parameter object for event encoder + * @param controllerId controller id + */ + public GsonProtocolCoderToolset(EventProtocolParams eventProtocolParams, String controllerId) { + super(eventProtocolParams, controllerId); + } + + /** + * gets the Gson decoder. + * + * @return the Gson decoder + */ + @JsonIgnore + @GsonJsonIgnore + protected Gson getDecoder() { + return this.decoder; + } + + /** + * gets the Gson encoder. + * + * @return the Gson encoder + */ + @JsonIgnore + @GsonJsonIgnore + protected Gson getEncoder() { + return this.encoder; + } + + /** + * {@inheritDoc}. + */ + @Override + public Object decode(String json) { + + final DroolsController droolsController = + DroolsController.factory.get(this.groupId, this.artifactId, ""); + if (droolsController == null) { + logger.warn("{}: no drools-controller to process {}", this, json); + throw new IllegalStateException("no drools-controller to process event"); + } + + final CoderFilters decoderFilter = this.filter(json); + if (decoderFilter == null) { + logger.debug("{}: no decoder to process {}", this, json); + throw new UnsupportedOperationException("no decoder to process event"); + } + + Class<?> decoderClass; + try { + decoderClass = droolsController.fetchModelClass(decoderFilter.getCodedClass()); + if (decoderClass == null) { + logger.warn("{}: cannot fetch application class {}", this, decoderFilter.getCodedClass()); + throw new IllegalStateException( + "cannot fetch application class " + decoderFilter.getCodedClass()); + } + } catch (final Exception e) { + logger.warn("{}: cannot fetch application class {} because of {}", this, + decoderFilter.getCodedClass(), e.getMessage()); + throw new UnsupportedOperationException( + "cannot fetch application class " + decoderFilter.getCodedClass(), e); + } + + if (this.customCoder != null) { + try { + final Class<?> gsonClassContainer = + droolsController.fetchModelClass(this.customCoder.getClassContainer()); + final Field gsonField = gsonClassContainer.getField(this.customCoder.staticCoderField); + final Object gsonObject = gsonField.get(null); + final Method fromJsonMethod = gsonObject.getClass().getDeclaredMethod("fromJson", + new Class[] {String.class, Class.class}); + return fromJsonMethod.invoke(gsonObject, json, decoderClass); + } catch (final Exception e) { + logger.warn("{}: cannot fetch application class {} because of {}", this, + decoderFilter.getCodedClass(), e.getMessage()); + throw new UnsupportedOperationException( + "cannot fetch application class " + decoderFilter.getCodedClass(), e); + } + } else { + try { + return this.decoder.fromJson(json, decoderClass); + } catch (final Exception e) { + logger.warn("{} cannot decode {} into {} because of {}", this, json, decoderClass.getName(), + e.getMessage(), e); + throw new UnsupportedOperationException( + "cannont decode into " + decoderFilter.getCodedClass(), e); + } + } + } + + /** + * {@inheritDoc}. + */ + @Override + public String encode(Object event) { + + if (this.customCoder != null) { + try { + final DroolsController droolsController = + DroolsController.factory.get(this.groupId, this.artifactId, null); + final Class<?> gsonClassContainer = + droolsController.fetchModelClass(this.customCoder.getClassContainer()); + final Field gsonField = gsonClassContainer.getField(this.customCoder.staticCoderField); + final Object gsonObject = gsonField.get(null); + final Method toJsonMethod = + gsonObject.getClass().getDeclaredMethod("toJson", new Class[] {Object.class}); + return (String) toJsonMethod.invoke(gsonObject, event); + } catch (final Exception e) { + logger.warn("{} cannot custom-encode {} because of {}", this, event, e.getMessage(), e); + throw new UnsupportedOperationException("event cannot be encoded", e); + } + } else { + try { + return this.encoder.toJson(event); + } catch (final Exception e) { + logger.warn("{} cannot encode {} because of {}", this, event, e.getMessage(), e); + throw new UnsupportedOperationException("event cannot be encoded", e); + } + } + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("GsonProtocolCoderToolset [toString()=").append(super.toString()).append("]"); + return builder.toString(); + } +} diff --git a/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/MultiplexorEventProtocolCoder.java b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/MultiplexorEventProtocolCoder.java new file mode 100644 index 00000000..a8f3c3a3 --- /dev/null +++ b/policy-management/src/main/java/org/onap/policy/drools/protocol/coders/MultiplexorEventProtocolCoder.java @@ -0,0 +1,278 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 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.protocol.coders; + +import java.util.ArrayList; +import java.util.List; +import org.onap.policy.drools.controller.DroolsController; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Protocol Coder that does its best attempt to decode/encode, selecting the best class and best fitted json parsing + * tools. + */ +class MultiplexorEventProtocolCoder implements EventProtocolCoder { + + /** + * Logger. + */ + private static Logger logger = LoggerFactory.getLogger(MultiplexorEventProtocolCoder.class); + + /** + * Decoders. + */ + protected EventProtocolDecoder decoders = new EventProtocolDecoder(); + + /** + * Encoders. + */ + protected EventProtocolEncoder encoders = new EventProtocolEncoder(); + + /** + * {@inheritDoc}. + */ + @Override + public void addDecoder(EventProtocolParams eventProtocolParams) { + logger.info( + "{}: add-decoder {}:{}:{}:{}:{}:{}:{}", + this, + eventProtocolParams.getGroupId(), + eventProtocolParams.getArtifactId(), + eventProtocolParams.getTopic(), + eventProtocolParams.getEventClass(), + eventProtocolParams.getProtocolFilter(), + eventProtocolParams.getCustomGsonCoder(), + eventProtocolParams.getModelClassLoaderHash()); + this.decoders.add(eventProtocolParams); + } + + /** + * {@inheritDoc}. + * + * @param eventProtocolParams parameter object for event encoder + */ + @Override + public void addEncoder(EventProtocolParams eventProtocolParams) { + logger.info( + "{}: add-decoder {}:{}:{}:{}:{}:{}:{}", + this, + eventProtocolParams.getGroupId(), + eventProtocolParams.getArtifactId(), + eventProtocolParams.getTopic(), + eventProtocolParams.getEventClass(), + eventProtocolParams.getProtocolFilter(), + eventProtocolParams.getCustomGsonCoder(), + eventProtocolParams.getModelClassLoaderHash()); + this.encoders.add(eventProtocolParams); + } + + /** + * {@inheritDoc}. + */ + @Override + public void removeDecoders(String groupId, String artifactId, String topic) { + logger.info("{}: remove-decoder {}:{}:{}", this, groupId, artifactId, topic); + this.decoders.remove(groupId, artifactId, topic); + } + + /** + * {@inheritDoc}. + */ + @Override + public void removeEncoders(String groupId, String artifactId, String topic) { + logger.info("{}: remove-encoder {}:{}:{}", this, groupId, artifactId, topic); + this.encoders.remove(groupId, artifactId, topic); + } + + /** + * {@inheritDoc}. + */ + @Override + public boolean isDecodingSupported(String groupId, String artifactId, String topic) { + return this.decoders.isCodingSupported(groupId, artifactId, topic); + } + + /** + * {@inheritDoc}. + */ + @Override + public boolean isEncodingSupported(String groupId, String artifactId, String topic) { + return this.encoders.isCodingSupported(groupId, artifactId, topic); + } + + /** + * {@inheritDoc}. + */ + @Override + public Object decode(String groupId, String artifactId, String topic, String json) { + logger.debug("{}: decode {}:{}:{}:{}", this, groupId, artifactId, topic, json); + return this.decoders.decode(groupId, artifactId, topic, json); + } + + /** + * {@inheritDoc}. + */ + @Override + public String encode(String groupId, String artifactId, String topic, Object event) { + logger.debug("{}: encode {}:{}:{}:{}", this, groupId, artifactId, topic, event); + return this.encoders.encode(groupId, artifactId, topic, event); + } + + /** + * {@inheritDoc}. + */ + @Override + public String encode(String topic, Object event) { + logger.debug("{}: encode {}:{}", this, topic, event); + return this.encoders.encode(topic, event); + } + + /** + * {@inheritDoc}. + */ + @Override + public String encode(String topic, Object event, DroolsController droolsController) { + logger.debug("{}: encode {}:{}:{}", this, topic, event, droolsController); + return this.encoders.encode(topic, event, droolsController); + } + + /** + * {@inheritDoc}. + */ + @Override + public List<CoderFilters> getDecoderFilters(String groupId, String artifactId, String topic) { + return this.decoders.getFilters(groupId, artifactId, topic); + } + + /** + * {@inheritDoc}. + */ + @Override + public CoderFilters getDecoderFilters( + String groupId, String artifactId, String topic, String classname) { + return this.decoders.getFilters(groupId, artifactId, topic, classname); + } + + /** + * {@inheritDoc}. + */ + @Override + public List<CoderFilters> getDecoderFilters(String groupId, String artifactId) { + return this.decoders.getFilters(groupId, artifactId); + } + + /** + * {@inheritDoc}. + */ + @Override + public ProtocolCoderToolset getDecoders(String groupId, String artifactId, String topic) { + ProtocolCoderToolset decoderToolsets = + this.decoders.getCoders(groupId, artifactId, topic); + if (decoderToolsets == null) { + throw new IllegalArgumentException( + "Decoders not found for " + groupId + ":" + artifactId + ":" + topic); + } + + return decoderToolsets; + } + + /** + * get all deocders by maven coordinates and topic. + * + * @param groupId group id + * @param artifactId artifact id + * @return list of decoders + * @throws IllegalArgumentException if invalid input + */ + @Override + public List<ProtocolCoderToolset> getDecoders(String groupId, String artifactId) { + + List<ProtocolCoderToolset> decoderToolsets = + this.decoders.getCoders(groupId, artifactId); + if (decoderToolsets == null) { + throw new IllegalArgumentException("Decoders not found for " + groupId + ":" + artifactId); + } + + return new ArrayList<>(decoderToolsets); + } + + /** + * {@inheritDoc}. + */ + @Override + public List<CoderFilters> getEncoderFilters(String groupId, String artifactId, String topic) { + return this.encoders.getFilters(groupId, artifactId, topic); + } + + /** + * {@inheritDoc}. + */ + @Override + public CoderFilters getEncoderFilters( + String groupId, String artifactId, String topic, String classname) { + return this.encoders.getFilters(groupId, artifactId, topic, classname); + } + + /** + * {@inheritDoc}. + */ + @Override + public List<CoderFilters> getEncoderFilters(String groupId, String artifactId) { + return this.encoders.getFilters(groupId, artifactId); + } + + /** + * {@inheritDoc}. + */ + @Override + public List<CoderFilters> getReverseEncoderFilters(String topic, String encodedClass) { + return this.encoders.getReverseFilters(topic, encodedClass); + } + + /** + * {@inheritDoc}. + */ + @Override + public DroolsController getDroolsController(String topic, Object encodedClass) { + return this.encoders.getDroolsController(topic, encodedClass); + } + + /** + * {@inheritDoc}. + */ + @Override + public List<DroolsController> getDroolsControllers(String topic, Object encodedClass) { + return this.encoders.getDroolsControllers(topic, encodedClass); + } + + /** + * {@inheritDoc}. + */ + @Override + public String toString() { + return "MultiplexorEventProtocolCoder [decoders=" + + decoders + + ", encoders=" + + encoders + + "]"; + } +} 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 394e73af..a4add9ce 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 @@ -21,27 +21,10 @@ package org.onap.policy.drools.protocol.coders; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; import com.google.gson.JsonParser; -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.common.gson.annotation.GsonJsonIgnore; -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.slf4j.Logger; @@ -309,210 +292,3 @@ public abstract class ProtocolCoderToolset { return builder.toString(); } } - -/** - * Tools used for encoding/decoding using GSON. - */ -class GsonProtocolCoderToolset extends ProtocolCoderToolset { - /** - * Logger. - */ - private static final Logger logger = LoggerFactory.getLogger(GsonProtocolCoderToolset.class); - - /** - * Formatter for JSON encoding/decoding. - */ - @JsonIgnore - @GsonJsonIgnore - public static final DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSxxx"); - - @JsonIgnore - @GsonJsonIgnore - public static final DateTimeFormatter zuluFormat = DateTimeFormatter.ISO_INSTANT; - - /** - * Adapter for ZonedDateTime. - */ - public static class GsonUTCAdapter implements JsonSerializer<ZonedDateTime>, JsonDeserializer<ZonedDateTime> { - @Override - public ZonedDateTime deserialize(JsonElement element, Type type, - JsonDeserializationContext context) { - try { - return ZonedDateTime.parse(element.getAsString(), format); - } catch (final Exception e) { - logger.info("GsonUTCAdapter: cannot parse {} because of {}", element, e.getMessage(), e); - } - return null; - } - - @Override - public JsonElement serialize(ZonedDateTime datetime, Type type, - JsonSerializationContext context) { - return new JsonPrimitive(datetime.format(format)); - } - } - - public static class GsonInstantAdapter implements JsonSerializer<Instant>, JsonDeserializer<Instant> { - - @Override - public Instant deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { - return Instant.ofEpochMilli(json.getAsLong()); - } - - @Override - public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) { - return new JsonPrimitive(src.toEpochMilli()); - } - - } - - - /** - * decoder. - */ - @JsonIgnore - @GsonJsonIgnore - protected final Gson decoder = new GsonBuilder().disableHtmlEscaping() - .registerTypeAdapter(ZonedDateTime.class, new GsonUTCAdapter()) - .registerTypeAdapter(Instant.class, new GsonInstantAdapter()).create(); - - /** - * encoder. - */ - @JsonIgnore - @GsonJsonIgnore - protected final Gson encoder = new GsonBuilder().disableHtmlEscaping() - .registerTypeAdapter(ZonedDateTime.class, new GsonUTCAdapter()) - .registerTypeAdapter(Instant.class, new GsonInstantAdapter()).create(); - - /** - * Toolset to encode/decode tools associated with a topic. - * - * @param eventProtocolParams parameter object for event encoder - * @param controllerId controller id - */ - public GsonProtocolCoderToolset(EventProtocolParams eventProtocolParams, String controllerId) { - super(eventProtocolParams, controllerId); - } - - /** - * gets the Gson decoder. - * - * @return the Gson decoder - */ - @JsonIgnore - @GsonJsonIgnore - protected Gson getDecoder() { - return this.decoder; - } - - /** - * gets the Gson encoder. - * - * @return the Gson encoder - */ - @JsonIgnore - @GsonJsonIgnore - protected Gson getEncoder() { - return this.encoder; - } - - /** - * {@inheritDoc}. - */ - @Override - public Object decode(String json) { - - final DroolsController droolsController = - DroolsController.factory.get(this.groupId, this.artifactId, ""); - if (droolsController == null) { - logger.warn("{}: no drools-controller to process {}", this, json); - throw new IllegalStateException("no drools-controller to process event"); - } - - final CoderFilters decoderFilter = this.filter(json); - if (decoderFilter == null) { - logger.debug("{}: no decoder to process {}", this, json); - throw new UnsupportedOperationException("no decoder to process event"); - } - - Class<?> decoderClass; - try { - decoderClass = droolsController.fetchModelClass(decoderFilter.getCodedClass()); - if (decoderClass == null) { - logger.warn("{}: cannot fetch application class {}", this, decoderFilter.getCodedClass()); - throw new IllegalStateException( - "cannot fetch application class " + decoderFilter.getCodedClass()); - } - } catch (final Exception e) { - logger.warn("{}: cannot fetch application class {} because of {}", this, - decoderFilter.getCodedClass(), e.getMessage()); - throw new UnsupportedOperationException( - "cannot fetch application class " + decoderFilter.getCodedClass(), e); - } - - if (this.customCoder != null) { - try { - final Class<?> gsonClassContainer = - droolsController.fetchModelClass(this.customCoder.getClassContainer()); - final Field gsonField = gsonClassContainer.getField(this.customCoder.staticCoderField); - final Object gsonObject = gsonField.get(null); - final Method fromJsonMethod = gsonObject.getClass().getDeclaredMethod("fromJson", - new Class[] {String.class, Class.class}); - return fromJsonMethod.invoke(gsonObject, json, decoderClass); - } catch (final Exception e) { - logger.warn("{}: cannot fetch application class {} because of {}", this, - decoderFilter.getCodedClass(), e.getMessage()); - throw new UnsupportedOperationException( - "cannot fetch application class " + decoderFilter.getCodedClass(), e); - } - } else { - try { - return this.decoder.fromJson(json, decoderClass); - } catch (final Exception e) { - logger.warn("{} cannot decode {} into {} because of {}", this, json, decoderClass.getName(), - e.getMessage(), e); - throw new UnsupportedOperationException( - "cannont decode into " + decoderFilter.getCodedClass(), e); - } - } - } - - /** - * {@inheritDoc}. - */ - @Override - public String encode(Object event) { - - if (this.customCoder != null) { - try { - final DroolsController droolsController = - DroolsController.factory.get(this.groupId, this.artifactId, null); - final Class<?> gsonClassContainer = - droolsController.fetchModelClass(this.customCoder.getClassContainer()); - final Field gsonField = gsonClassContainer.getField(this.customCoder.staticCoderField); - final Object gsonObject = gsonField.get(null); - final Method toJsonMethod = - gsonObject.getClass().getDeclaredMethod("toJson", new Class[] {Object.class}); - return (String) toJsonMethod.invoke(gsonObject, event); - } catch (final Exception e) { - logger.warn("{} cannot custom-encode {} because of {}", this, event, e.getMessage(), e); - throw new UnsupportedOperationException("event cannot be encoded", e); - } - } else { - try { - return this.encoder.toJson(event); - } catch (final Exception e) { - logger.warn("{} cannot encode {} because of {}", this, event, e.getMessage(), e); - throw new UnsupportedOperationException("event cannot be encoded", e); - } - } - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("GsonProtocolCoderToolset [toString()=").append(super.toString()).append("]"); - return builder.toString(); - } -} diff --git a/policy-management/src/main/java/org/onap/policy/drools/protocol/configuration/PdpdConfiguration.java b/policy-management/src/main/java/org/onap/policy/drools/protocol/configuration/PdpdConfiguration.java index f83f480e..572bc14f 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/protocol/configuration/PdpdConfiguration.java +++ b/policy-management/src/main/java/org/onap/policy/drools/protocol/configuration/PdpdConfiguration.java @@ -7,9 +7,9 @@ * 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. @@ -50,7 +50,7 @@ public class PdpdConfiguration { /** Unique Transaction ID. This is an UUID. (Required) */ @JsonProperty("requestID") @GsonJsonProperty("requestID") - private String requestID; + private String requestId; /* Set of entities on which configuration can be performed: controller (Required) */ @JsonProperty("entity") @GsonJsonProperty("entity") @@ -70,14 +70,14 @@ public class PdpdConfiguration { /** * Constructor. - * - * @param requestID request id + * + * @param requestId request id * @param entity entity * @param controllers controllers */ public PdpdConfiguration( - String requestID, String entity, List<ControllerConfiguration> controllers) { - this.requestID = requestID; + String requestId, String entity, List<ControllerConfiguration> controllers) { + this.requestId = requestId; this.entity = entity; this.controllers = controllers; } @@ -89,23 +89,23 @@ public class PdpdConfiguration { */ @JsonProperty("requestID") @GsonJsonProperty("requestID") - public String getRequestID() { - return requestID; + public String getRequestId() { + return requestId; } /** * Unique Transaction ID. This is an UUID. (Required) * - * @param requestID The requestID + * @param requestId The requestID */ @JsonProperty("requestID") @GsonJsonProperty("requestID") - public void setRequestID(String requestID) { - this.requestID = requestID; + public void setRequestId(String requestId) { + this.requestId = requestId; } - public PdpdConfiguration withRequestID(String requestID) { - this.requestID = requestID; + public PdpdConfiguration withRequestId(String requestId) { + this.requestId = requestId; return this; } @@ -204,7 +204,7 @@ public class PdpdConfiguration { protected Object declaredPropertyOrNotFound(String name, Object notFoundValue) { switch (name) { case "requestID": - return getRequestID(); + return getRequestId(); case "entity": return getEntity(); case "controllers": @@ -216,7 +216,7 @@ public class PdpdConfiguration { /** * Get. - * + * * @param name name * @return object */ @@ -232,7 +232,7 @@ public class PdpdConfiguration { /** * Set property. - * + * * @param name name * @param value value */ @@ -255,7 +255,7 @@ public class PdpdConfiguration { @Override public int hashCode() { return new HashCodeBuilder() - .append(requestID) + .append(requestId) .append(entity) .append(controllers) .append(additionalProperties) @@ -272,7 +272,7 @@ public class PdpdConfiguration { } PdpdConfiguration rhs = (PdpdConfiguration) other; return new EqualsBuilder() - .append(requestID, rhs.requestID) + .append(requestId, rhs.requestId) .append(entity, rhs.entity) .append(controllers, rhs.controllers) .append(additionalProperties, rhs.additionalProperties) @@ -281,12 +281,12 @@ public class PdpdConfiguration { /** * Call set request id. - * + * * @param value value */ public void callSetRequestId(Object value) { if (value instanceof String) { - setRequestID((String) value); + setRequestId((String) value); } else { throw new IllegalArgumentException( "property \"requestID\" is of type \"java.lang.String\", but got " @@ -296,7 +296,7 @@ public class PdpdConfiguration { /** * Call set entity. - * + * * @param value value */ public void callSetEntity(Object value) { @@ -311,7 +311,7 @@ public class PdpdConfiguration { /** * Call set controllers. - * + * * @param value value */ @SuppressWarnings("unchecked") diff --git a/policy-management/src/main/java/org/onap/policy/drools/server/restful/RestManager.java b/policy-management/src/main/java/org/onap/policy/drools/server/restful/RestManager.java index 726e7217..a5fbfdd3 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/server/restful/RestManager.java +++ b/policy-management/src/main/java/org/onap/policy/drools/server/restful/RestManager.java @@ -56,8 +56,8 @@ import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager; import org.onap.policy.common.endpoints.event.comm.TopicSink; import org.onap.policy.common.endpoints.event.comm.TopicSource; import org.onap.policy.drools.controller.DroolsController; -import org.onap.policy.drools.features.PolicyControllerFeatureAPI; -import org.onap.policy.drools.features.PolicyEngineFeatureAPI; +import org.onap.policy.drools.features.PolicyControllerFeatureApi; +import org.onap.policy.drools.features.PolicyEngineFeatureApi; import org.onap.policy.drools.properties.DroolsProperties; import org.onap.policy.drools.protocol.coders.EventProtocolCoder; import org.onap.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters; @@ -140,7 +140,7 @@ public class RestManager { @Path("engine/features/inventory") @ApiOperation(value = "Engine Detailed Feature Inventory", notes = "Provides detailed list of loaded features using the PolicyEngineFeatureAPI", - responseContainer = "List", response = PolicyEngineFeatureAPI.class) + responseContainer = "List", response = PolicyEngineFeatureApi.class) public Response engineFeaturesInventory() { return Response.status(Response.Status.OK).entity(PolicyEngine.manager.getFeatureProviders()).build(); } @@ -153,7 +153,7 @@ public class RestManager { @GET @Path("engine/features/{featureName}") @ApiOperation(value = "Engine Feature", notes = "Provides Details for a given feature Engine Provider", - response = PolicyEngineFeatureAPI.class) + response = PolicyEngineFeatureApi.class) @ApiResponses(value = {@ApiResponse(code = 404, message = "The feature cannot be found")}) public Response engineFeature( @ApiParam(value = "Feature Name", required = true) @PathParam("featureName") String featureName) { @@ -492,7 +492,7 @@ public class RestManager { @Path("engine/controllers/features/inventory") @ApiOperation(value = "Detailed Controllers Feature Inventory", notes = "Provides detailed list of loaded features using the PolicyControllerFeatureAPI", - responseContainer = "List", response = PolicyControllerFeatureAPI.class) + responseContainer = "List", response = PolicyControllerFeatureApi.class) public Response controllerFeaturesInventory() { return Response.status(Response.Status.OK).entity(PolicyController.factory.getFeatureProviders()).build(); } @@ -506,7 +506,7 @@ public class RestManager { @Path("engine/controllers/features/{featureName}") @ApiOperation(value = "Controller Feature", notes = "Provides Details for a given Policy Controller feature provider", - response = PolicyControllerFeatureAPI.class) + response = PolicyControllerFeatureApi.class) @ApiResponses(value = {@ApiResponse(code = 404, message = "The feature cannot be found")}) public Response controllerFeature( @ApiParam(value = "Feature Name", required = true) @PathParam("featureName") String featureName) { diff --git a/policy-management/src/main/java/org/onap/policy/drools/system/IndexedPolicyControllerFactory.java b/policy-management/src/main/java/org/onap/policy/drools/system/IndexedPolicyControllerFactory.java new file mode 100644 index 00000000..38085101 --- /dev/null +++ b/policy-management/src/main/java/org/onap/policy/drools/system/IndexedPolicyControllerFactory.java @@ -0,0 +1,387 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 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.system; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Properties; +import org.onap.policy.common.gson.annotation.GsonJsonIgnore; +import org.onap.policy.drools.controller.DroolsController; +import org.onap.policy.drools.features.PolicyControllerFeatureApi; +import org.onap.policy.drools.protocol.configuration.DroolsConfiguration; +import org.onap.policy.drools.system.internal.AggregatedPolicyController; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Factory of Policy Controllers indexed by the name of the Policy Controller. + */ +class IndexedPolicyControllerFactory implements PolicyControllerFactory { + // get an instance of logger + private static final Logger logger = LoggerFactory.getLogger(PolicyControllerFactory.class); + + /** + * Policy Controller Name Index. + */ + private final HashMap<String,PolicyController> policyControllers = + new HashMap<>(); + + /** + * Group/Artifact Ids Index. + */ + private final HashMap<String,PolicyController> coordinates2Controller = + new HashMap<>(); + + /** + * produces key for indexing controller names. + * + * @param groupId group id + * @param artifactId artifact id + * @return index key + */ + private String toKey(String groupId, String artifactId) { + return groupId + ":" + artifactId; + } + + /** + * {@inheritDoc}. + */ + @Override + public synchronized PolicyController build(String name, Properties properties) { + + if (this.policyControllers.containsKey(name)) { + return this.policyControllers.get(name); + } + + /* A PolicyController does not exist */ + + PolicyController controller = newPolicyController(name, properties); + + String coordinates = toKey(controller.getDrools().getGroupId(), + controller.getDrools().getArtifactId()); + + this.policyControllers.put(name, controller); + + + if (controller.getDrools().isBrained()) { + this.coordinates2Controller.put(coordinates, controller); + } + + return controller; + } + + /** + * {@inheritDoc}. + */ + @Override + public synchronized PolicyController patch(String name, DroolsConfiguration droolsConfig) { + + if (name == null || name.isEmpty() || !this.policyControllers.containsKey(name)) { + throw makeArgEx(name); + } + + PolicyController controller = this.get(name); + + if (controller == null) { + logger.warn("A POLICY CONTROLLER of name {} does not exist for patch operation: {}", name, droolsConfig); + + throw new IllegalArgumentException("Not a valid controller of name " + name); + } + + this.patch(controller, droolsConfig); + + logger.info("UPDATED drools configuration: {} on {}", droolsConfig, this); + + return controller; + } + + + /** + * {@inheritDoc}. + */ + @Override + public void patch(PolicyController controller, DroolsConfiguration droolsConfig) { + + if (controller == null) { + throw new IllegalArgumentException("Not a valid controller: null"); + } + + if (droolsConfig == null) { + throw new IllegalArgumentException("Invalid Drools Configuration"); + } + + if (!controller.updateDrools(droolsConfig)) { + logger.warn("Cannot update drools configuration: {} on {}", droolsConfig, this); + throw new IllegalArgumentException("Cannot update drools configuration Drools Configuration"); + } + + logger.info("UPDATED drools configuration: {} on {}", droolsConfig, this); + + String coordinates = toKey(controller.getDrools().getGroupId(), + controller.getDrools().getArtifactId()); + + if (controller.getDrools().isBrained()) { + this.coordinates2Controller.put(coordinates, controller); + } + + } + + /** + * {@inheritDoc}. + */ + @Override + public void shutdown(String controllerName) { + + if (controllerName == null || controllerName.isEmpty()) { + throw makeArgEx(controllerName); + } + + synchronized (this) { + if (!this.policyControllers.containsKey(controllerName)) { + return; + } + + PolicyController controller = this.policyControllers.get(controllerName); + this.shutdown(controller); + } + } + + /** + * {@inheritDoc}. + */ + @Override + public void shutdown(PolicyController controller) { + this.unmanage(controller); + controller.shutdown(); + } + + /** + * {@inheritDoc}. + */ + @Override + public void shutdown() { + List<PolicyController> controllers = this.inventory(); + for (PolicyController controller: controllers) { + controller.shutdown(); + } + + synchronized (this) { + this.policyControllers.clear(); + this.coordinates2Controller.clear(); + } + } + + /** + * unmanage the controller. + * + * @param controller controller + * @throws IllegalArgumentException exception + */ + private void unmanage(PolicyController controller) { + PolicyController tempController = controller; + if (tempController == null) { + throw new IllegalArgumentException("Invalid Controller"); + } + + synchronized (this) { + if (!this.policyControllers.containsKey(tempController.getName())) { + return; + } + tempController = this.policyControllers.remove(tempController.getName()); + + String coordinates = toKey(tempController.getDrools().getGroupId(), + tempController.getDrools().getArtifactId()); + this.coordinates2Controller.remove(coordinates); + } + } + + /** + * {@inheritDoc}. + */ + @Override + public void destroy(String controllerName) { + + if (controllerName == null || controllerName.isEmpty()) { + throw makeArgEx(controllerName); + } + + synchronized (this) { + if (!this.policyControllers.containsKey(controllerName)) { + return; + } + + PolicyController controller = this.policyControllers.get(controllerName); + this.destroy(controller); + } + } + + /** + * {@inheritDoc}. + */ + @Override + public void destroy(PolicyController controller) { + this.unmanage(controller); + controller.halt(); + } + + /** + * {@inheritDoc}. + */ + @Override + public void destroy() { + List<PolicyController> controllers = this.inventory(); + for (PolicyController controller: controllers) { + controller.halt(); + } + + synchronized (this) { + this.policyControllers.clear(); + this.coordinates2Controller.clear(); + } + } + + /** + * {@inheritDoc}. + */ + @Override + public PolicyController get(String name) { + + if (name == null || name.isEmpty()) { + throw makeArgEx(name); + } + + synchronized (this) { + if (this.policyControllers.containsKey(name)) { + return this.policyControllers.get(name); + } else { + throw makeArgEx(name); + } + } + } + + /** + * {@inheritDoc}. + */ + @Override + public PolicyController get(String groupId, String artifactId) { + + if (groupId == null || groupId.isEmpty() + || artifactId == null || artifactId.isEmpty()) { + throw new IllegalArgumentException("Invalid group/artifact ids"); + } + + synchronized (this) { + String key = toKey(groupId,artifactId); + if (this.coordinates2Controller.containsKey(key)) { + return this.coordinates2Controller.get(key); + } else { + throw makeArgEx(key); + } + } + } + + /** + * {@inheritDoc}. + */ + @Override + public PolicyController get(DroolsController droolsController) { + + if (droolsController == null) { + throw new IllegalArgumentException("No Drools Controller provided"); + } + + synchronized (this) { + String key = toKey(droolsController.getGroupId(), droolsController.getArtifactId()); + if (this.coordinates2Controller.containsKey(key)) { + return this.coordinates2Controller.get(key); + } else { + logger.error("Drools Controller not associated with Policy Controller {}:{}", droolsController, this); + throw new IllegalStateException("Drools Controller not associated with Policy Controller " + + droolsController + ":" + this); + } + } + } + + /** + * {@inheritDoc}. + */ + @Override + public List<PolicyController> inventory() { + return new ArrayList<>(this.policyControllers.values()); + } + + /** + * {@inheritDoc}. + */ + @Override + public List<String> getFeatures() { + List<String> features = new ArrayList<>(); + for (PolicyControllerFeatureApi feature : getProviders()) { + features.add(feature.getName()); + } + return features; + } + + /** + * {@inheritDoc}. + */ + @JsonIgnore + @GsonJsonIgnore + @Override + public List<PolicyControllerFeatureApi> getFeatureProviders() { + return getProviders(); + } + + /** + * {@inheritDoc}. + */ + @Override + public PolicyControllerFeatureApi getFeatureProvider(String featureName) { + if (featureName == null || featureName.isEmpty()) { + throw new IllegalArgumentException("A feature name must be provided"); + } + + for (PolicyControllerFeatureApi feature : getProviders()) { + if (feature.getName().equals(featureName)) { + return feature; + } + } + + throw new IllegalArgumentException("Invalid Feature Name: " + featureName); + } + + private IllegalArgumentException makeArgEx(String argName) { + return new IllegalArgumentException("Invalid " + argName); + } + + // these methods can be overridden by junit tests + + protected PolicyController newPolicyController(String name, Properties properties) { + return new AggregatedPolicyController(name, properties); + } + + protected List<PolicyControllerFeatureApi> getProviders() { + return PolicyControllerFeatureApi.providers.getList(); + } +} diff --git a/policy-management/src/main/java/org/onap/policy/drools/system/PolicyControllerFactory.java b/policy-management/src/main/java/org/onap/policy/drools/system/PolicyControllerFactory.java index 8baf667a..05f15ead 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/system/PolicyControllerFactory.java +++ b/policy-management/src/main/java/org/onap/policy/drools/system/PolicyControllerFactory.java @@ -7,9 +7,9 @@ * 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. @@ -20,19 +20,11 @@ package org.onap.policy.drools.system; -import com.fasterxml.jackson.annotation.JsonIgnore; - -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Properties; -import org.onap.policy.common.gson.annotation.GsonJsonIgnore; import org.onap.policy.drools.controller.DroolsController; -import org.onap.policy.drools.features.PolicyControllerFeatureAPI; +import org.onap.policy.drools.features.PolicyControllerFeatureApi; import org.onap.policy.drools.protocol.configuration.DroolsConfiguration; -import org.onap.policy.drools.system.internal.AggregatedPolicyController; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** @@ -149,21 +141,21 @@ public interface PolicyControllerFactory { /** * get features attached to the Policy Controllers. - * + * * @return list of features */ - List<PolicyControllerFeatureAPI> getFeatureProviders(); + List<PolicyControllerFeatureApi> getFeatureProviders(); /** * get named feature attached to the Policy Controllers. - * + * * @return the feature */ - PolicyControllerFeatureAPI getFeatureProvider(String featureName); + PolicyControllerFeatureApi getFeatureProvider(String featureName); /** * get features attached to the Policy Controllers. - * + * * @return list of features */ List<String> getFeatures(); @@ -175,355 +167,3 @@ public interface PolicyControllerFactory { */ List<PolicyController> inventory(); } - -/** - * Factory of Policy Controllers indexed by the name of the Policy Controller. - */ -class IndexedPolicyControllerFactory implements PolicyControllerFactory { - // get an instance of logger - private static final Logger logger = LoggerFactory.getLogger(PolicyControllerFactory.class); - - /** - * Policy Controller Name Index. - */ - private final HashMap<String,PolicyController> policyControllers = - new HashMap<>(); - - /** - * Group/Artifact Ids Index. - */ - private final HashMap<String,PolicyController> coordinates2Controller = - new HashMap<>(); - - /** - * produces key for indexing controller names. - * - * @param groupId group id - * @param artifactId artifact id - * @return index key - */ - private String toKey(String groupId, String artifactId) { - return groupId + ":" + artifactId; - } - - /** - * {@inheritDoc}. - */ - @Override - public synchronized PolicyController build(String name, Properties properties) { - - if (this.policyControllers.containsKey(name)) { - return this.policyControllers.get(name); - } - - /* A PolicyController does not exist */ - - PolicyController controller = newPolicyController(name, properties); - - String coordinates = toKey(controller.getDrools().getGroupId(), - controller.getDrools().getArtifactId()); - - this.policyControllers.put(name, controller); - - - if (controller.getDrools().isBrained()) { - this.coordinates2Controller.put(coordinates, controller); - } - - return controller; - } - - /** - * {@inheritDoc}. - */ - @Override - public synchronized PolicyController patch(String name, DroolsConfiguration droolsConfig) { - - if (name == null || name.isEmpty() || !this.policyControllers.containsKey(name)) { - throw makeArgEx(name); - } - - PolicyController controller = this.get(name); - - if (controller == null) { - logger.warn("A POLICY CONTROLLER of name {} does not exist for patch operation: {}", name, droolsConfig); - - throw new IllegalArgumentException("Not a valid controller of name " + name); - } - - this.patch(controller, droolsConfig); - - logger.info("UPDATED drools configuration: {} on {}", droolsConfig, this); - - return controller; - } - - - /** - * {@inheritDoc}. - */ - @Override - public void patch(PolicyController controller, DroolsConfiguration droolsConfig) { - - if (controller == null) { - throw new IllegalArgumentException("Not a valid controller: null"); - } - - if (droolsConfig == null) { - throw new IllegalArgumentException("Invalid Drools Configuration"); - } - - if (!controller.updateDrools(droolsConfig)) { - logger.warn("Cannot update drools configuration: {} on {}", droolsConfig, this); - throw new IllegalArgumentException("Cannot update drools configuration Drools Configuration"); - } - - logger.info("UPDATED drools configuration: {} on {}", droolsConfig, this); - - String coordinates = toKey(controller.getDrools().getGroupId(), - controller.getDrools().getArtifactId()); - - if (controller.getDrools().isBrained()) { - this.coordinates2Controller.put(coordinates, controller); - } - - } - - /** - * {@inheritDoc}. - */ - @Override - public void shutdown(String controllerName) { - - if (controllerName == null || controllerName.isEmpty()) { - throw makeArgEx(controllerName); - } - - synchronized (this) { - if (!this.policyControllers.containsKey(controllerName)) { - return; - } - - PolicyController controller = this.policyControllers.get(controllerName); - this.shutdown(controller); - } - } - - /** - * {@inheritDoc}. - */ - @Override - public void shutdown(PolicyController controller) { - this.unmanage(controller); - controller.shutdown(); - } - - /** - * {@inheritDoc}. - */ - @Override - public void shutdown() { - List<PolicyController> controllers = this.inventory(); - for (PolicyController controller: controllers) { - controller.shutdown(); - } - - synchronized (this) { - this.policyControllers.clear(); - this.coordinates2Controller.clear(); - } - } - - /** - * unmanage the controller. - * - * @param controller controller - * @throws IllegalArgumentException exception - */ - private void unmanage(PolicyController controller) { - PolicyController tempController = controller; - if (tempController == null) { - throw new IllegalArgumentException("Invalid Controller"); - } - - synchronized (this) { - if (!this.policyControllers.containsKey(tempController.getName())) { - return; - } - tempController = this.policyControllers.remove(tempController.getName()); - - String coordinates = toKey(tempController.getDrools().getGroupId(), - tempController.getDrools().getArtifactId()); - this.coordinates2Controller.remove(coordinates); - } - } - - /** - * {@inheritDoc}. - */ - @Override - public void destroy(String controllerName) { - - if (controllerName == null || controllerName.isEmpty()) { - throw makeArgEx(controllerName); - } - - synchronized (this) { - if (!this.policyControllers.containsKey(controllerName)) { - return; - } - - PolicyController controller = this.policyControllers.get(controllerName); - this.destroy(controller); - } - } - - /** - * {@inheritDoc}. - */ - @Override - public void destroy(PolicyController controller) { - this.unmanage(controller); - controller.halt(); - } - - /** - * {@inheritDoc}. - */ - @Override - public void destroy() { - List<PolicyController> controllers = this.inventory(); - for (PolicyController controller: controllers) { - controller.halt(); - } - - synchronized (this) { - this.policyControllers.clear(); - this.coordinates2Controller.clear(); - } - } - - /** - * {@inheritDoc}. - */ - @Override - public PolicyController get(String name) { - - if (name == null || name.isEmpty()) { - throw makeArgEx(name); - } - - synchronized (this) { - if (this.policyControllers.containsKey(name)) { - return this.policyControllers.get(name); - } else { - throw makeArgEx(name); - } - } - } - - /** - * {@inheritDoc}. - */ - @Override - public PolicyController get(String groupId, String artifactId) { - - if (groupId == null || groupId.isEmpty() - || artifactId == null || artifactId.isEmpty()) { - throw new IllegalArgumentException("Invalid group/artifact ids"); - } - - synchronized (this) { - String key = toKey(groupId,artifactId); - if (this.coordinates2Controller.containsKey(key)) { - return this.coordinates2Controller.get(key); - } else { - throw makeArgEx(key); - } - } - } - - /** - * {@inheritDoc}. - */ - @Override - public PolicyController get(DroolsController droolsController) { - - if (droolsController == null) { - throw new IllegalArgumentException("No Drools Controller provided"); - } - - synchronized (this) { - String key = toKey(droolsController.getGroupId(), droolsController.getArtifactId()); - if (this.coordinates2Controller.containsKey(key)) { - return this.coordinates2Controller.get(key); - } else { - logger.error("Drools Controller not associated with Policy Controller {}:{}", droolsController, this); - throw new IllegalStateException("Drools Controller not associated with Policy Controller " - + droolsController + ":" + this); - } - } - } - - /** - * {@inheritDoc}. - */ - @Override - public List<PolicyController> inventory() { - return new ArrayList<>(this.policyControllers.values()); - } - - /** - * {@inheritDoc}. - */ - @Override - public List<String> getFeatures() { - List<String> features = new ArrayList<>(); - for (PolicyControllerFeatureAPI feature : getProviders()) { - features.add(feature.getName()); - } - return features; - } - - /** - * {@inheritDoc}. - */ - @JsonIgnore - @GsonJsonIgnore - @Override - public List<PolicyControllerFeatureAPI> getFeatureProviders() { - return getProviders(); - } - - /** - * {@inheritDoc}. - */ - @Override - public PolicyControllerFeatureAPI getFeatureProvider(String featureName) { - if (featureName == null || featureName.isEmpty()) { - throw new IllegalArgumentException("A feature name must be provided"); - } - - for (PolicyControllerFeatureAPI feature : getProviders()) { - if (feature.getName().equals(featureName)) { - return feature; - } - } - - throw new IllegalArgumentException("Invalid Feature Name: " + featureName); - } - - private IllegalArgumentException makeArgEx(String argName) { - return new IllegalArgumentException("Invalid " + argName); - } - - // these methods can be overridden by junit tests - - protected PolicyController newPolicyController(String name, Properties properties) { - return new AggregatedPolicyController(name, properties); - } - - protected List<PolicyControllerFeatureAPI> getProviders() { - return PolicyControllerFeatureAPI.providers.getList(); - } -} diff --git a/policy-management/src/main/java/org/onap/policy/drools/system/PolicyEngine.java b/policy-management/src/main/java/org/onap/policy/drools/system/PolicyEngine.java index 811a9c80..eb2ed3df 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/system/PolicyEngine.java +++ b/policy-management/src/main/java/org/onap/policy/drools/system/PolicyEngine.java @@ -20,47 +20,18 @@ package org.onap.policy.drools.system; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import java.util.ArrayList; import java.util.List; import java.util.Properties; - import org.onap.policy.common.capabilities.Lockable; import org.onap.policy.common.capabilities.Startable; -import org.onap.policy.common.endpoints.event.comm.Topic; import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure; -import org.onap.policy.common.endpoints.event.comm.TopicEndpoint; -import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager; import org.onap.policy.common.endpoints.event.comm.TopicListener; import org.onap.policy.common.endpoints.event.comm.TopicSink; import org.onap.policy.common.endpoints.event.comm.TopicSource; import org.onap.policy.common.endpoints.http.server.HttpServletServer; -import org.onap.policy.common.endpoints.http.server.HttpServletServerFactory; -import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance; -import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties; -import org.onap.policy.common.gson.annotation.GsonJsonIgnore; -import org.onap.policy.common.gson.annotation.GsonJsonProperty; -import org.onap.policy.drools.controller.DroolsController; -import org.onap.policy.drools.core.PolicyContainer; -import org.onap.policy.drools.core.jmx.PdpJmxListener; -import org.onap.policy.drools.features.PolicyControllerFeatureAPI; -import org.onap.policy.drools.features.PolicyEngineFeatureAPI; -import org.onap.policy.drools.persistence.SystemPersistence; -import org.onap.policy.drools.properties.DroolsProperties; -import org.onap.policy.drools.protocol.coders.EventProtocolCoder; +import org.onap.policy.drools.features.PolicyEngineFeatureApi; import org.onap.policy.drools.protocol.configuration.ControllerConfiguration; import org.onap.policy.drools.protocol.configuration.PdpdConfiguration; -import org.onap.policy.drools.server.restful.RestManager; -import org.onap.policy.drools.server.restful.aaf.AafTelemetryAuthFilter; -import org.onap.policy.drools.utils.PropertyUtil; -import org.onap.policy.drools.utils.logging.LoggerUtil; -import org.onap.policy.drools.utils.logging.MDCTransaction; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Policy Engine, the top abstraction for the Drools PDP Policy Engine. It abstracts away a Drools @@ -249,14 +220,14 @@ public interface PolicyEngine extends Startable, Lockable, TopicListener { * * @return list of features */ - List<PolicyEngineFeatureAPI> getFeatureProviders(); + List<PolicyEngineFeatureApi> getFeatureProviders(); /** * get named feature attached to the Policy Engine. * * @return the feature */ - PolicyEngineFeatureAPI getFeatureProvider(String featureName); + PolicyEngineFeatureApi getFeatureProvider(String featureName); /** * get features attached to the Policy Engine. @@ -336,1268 +307,3 @@ public interface PolicyEngine extends Startable, Lockable, TopicListener { */ Properties defaultTelemetryConfig(); } - - -/** - * Policy Engine Manager Implementation. - */ -class PolicyEngineManager implements PolicyEngine { - - /** - * String literals. - */ - private static final String INVALID_TOPIC_MSG = "Invalid Topic"; - private static final String INVALID_EVENT_MSG = "Invalid Event"; - - private static final String ENGINE_STOPPED_MSG = "Policy Engine is stopped"; - private static final String ENGINE_LOCKED_MSG = "Policy Engine is locked"; - - /** - * logger. - */ - private static final Logger logger = LoggerFactory.getLogger(PolicyEngineManager.class); - - /** - * Is the Policy Engine running. - */ - private volatile boolean alive = false; - - /** - * Is the engine locked. - */ - private volatile boolean locked = false; - - /** - * Properties used to initialize the engine. - */ - private Properties properties; - - /** - * Environment Properties. - */ - private final Properties environment = new Properties(); - - /** - * Policy Engine Sources. - */ - private List<? extends TopicSource> sources = new ArrayList<>(); - - /** - * Policy Engine Sinks. - */ - private List<? extends TopicSink> sinks = new ArrayList<>(); - - /** - * Policy Engine HTTP Servers. - */ - private List<HttpServletServer> httpServers = new ArrayList<>(); - - /** - * gson parser to decode configuration requests. - */ - private final Gson decoder = new GsonBuilder().disableHtmlEscaping().create(); - - - @Override - public synchronized void boot(String[] cliArgs) { - - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.beforeBoot(this, cliArgs)) { - return; - } - } catch (final Exception e) { - logger.error("{}: feature {} before-boot failure because of {}", this, feature.getClass().getName(), - e.getMessage(), e); - } - } - - try { - globalInitContainer(cliArgs); - } catch (final Exception e) { - logger.error("{}: cannot init policy-container because of {}", this, e.getMessage(), e); - } - - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.afterBoot(this)) { - return; - } - } catch (final Exception e) { - logger.error("{}: feature {} after-boot failure because of {}", this, feature.getClass().getName(), - e.getMessage(), e); - } - } - } - - @Override - public synchronized void setEnvironment(Properties properties) { - this.environment.putAll(PropertyUtil.getInterpolatedProperties(properties)); - } - - @JsonIgnore - @GsonJsonIgnore - @Override - public synchronized Properties getEnvironment() { - return this.environment; - } - - @Override - public synchronized String getEnvironmentProperty(String envKey) { - String value = this.environment.getProperty(envKey); - if (value == null) { - value = System.getProperty(envKey); - if (value == null) { - value = System.getenv(envKey); - } - } - return value; - } - - @Override - public synchronized String setEnvironmentProperty(String envKey, String envValue) { - return (String) this.environment.setProperty(envKey, envValue); - } - - @Override - public final Properties defaultTelemetryConfig() { - final Properties defaultConfig = new Properties(); - - defaultConfig.put(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES, "TELEMETRY"); - defaultConfig.put(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + TELEMETRY_SERVER_DEFAULT_NAME - + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX, TELEMETRY_SERVER_DEFAULT_HOST); - defaultConfig.put( - PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + TELEMETRY_SERVER_DEFAULT_NAME - + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX, - "" + Integer.toString(TELEMETRY_SERVER_DEFAULT_PORT)); - defaultConfig.put( - PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + TELEMETRY_SERVER_DEFAULT_NAME - + PolicyEndPointProperties.PROPERTY_HTTP_REST_PACKAGES_SUFFIX, - RestManager.class.getPackage().getName()); - defaultConfig.put(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + TELEMETRY_SERVER_DEFAULT_NAME - + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX, "" + Boolean.TRUE); - defaultConfig.put(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + TELEMETRY_SERVER_DEFAULT_NAME - + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX, "" + Boolean.FALSE); - - return defaultConfig; - } - - @Override - public synchronized void configure(Properties properties) { - - if (properties == null) { - logger.warn("No properties provided"); - throw new IllegalArgumentException("No properties provided"); - } - - /* policy-engine dispatch pre configure hook */ - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.beforeConfigure(this, properties)) { - return; - } - } catch (final Exception e) { - logger.error("{}: feature {} before-configure failure because of {}", this, - feature.getClass().getName(), e.getMessage(), e); - } - } - - this.properties = properties; - - try { - this.sources = getTopicEndpointManager().addTopicSources(properties); - for (final TopicSource source : this.sources) { - source.register(this); - } - } catch (final Exception e) { - logger.error("{}: add-sources failed", this, e); - } - - try { - this.sinks = getTopicEndpointManager().addTopicSinks(properties); - } catch (final IllegalArgumentException e) { - logger.error("{}: add-sinks failed", this, e); - } - - try { - this.httpServers = getServletFactory().build(properties); - for (HttpServletServer server : this.httpServers) { - if (server.isAaf()) { - server.addFilterClass(null, AafTelemetryAuthFilter.class.getName()); - } - } - } catch (final IllegalArgumentException e) { - logger.error("{}: add-http-servers failed", this, e); - } - - /* policy-engine dispatch post configure hook */ - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.afterConfigure(this)) { - return; - } - } catch (final Exception e) { - logger.error("{}: feature {} after-configure failure because of {}", this, feature.getClass().getName(), - e.getMessage(), e); - } - } - } - - @Override - public boolean configure(PdpdConfiguration config) { - - if (config == null) { - throw new IllegalArgumentException("No configuration provided"); - } - - final String entity = config.getEntity(); - - MDCTransaction mdcTrans = MDCTransaction.newTransaction(config.getRequestID(), "brmsgw"); - if (this.getSources().size() == 1) { - Topic topic = this.getSources().get(0); - mdcTrans.setServiceName(topic.getTopic()).setRemoteHost(topic.getServers().toString()) - .setTargetEntity(config.getEntity()); - } - - switch (entity) { - case PdpdConfiguration.CONFIG_ENTITY_CONTROLLER: - boolean success = controllerConfig(config); - mdcTrans.resetSubTransaction().setStatusCode(success).transaction(); - return success; - default: - final String msg = "Configuration Entity is not supported: " + entity; - mdcTrans.resetSubTransaction().setStatusCode(false).setResponseDescription(msg).flush(); - logger.warn(LoggerUtil.TRANSACTION_LOG_MARKER_NAME, msg); - throw new IllegalArgumentException(msg); - } - } - - @Override - public synchronized PolicyController createPolicyController(String name, Properties properties) { - - String tempName = name; - // check if a PROPERTY_CONTROLLER_NAME property is present - // if so, override the given name - - final String propertyControllerName = properties.getProperty(DroolsProperties.PROPERTY_CONTROLLER_NAME); - if (propertyControllerName != null && !propertyControllerName.isEmpty()) { - if (!propertyControllerName.equals(tempName)) { - throw new IllegalStateException("Proposed name (" + tempName + ") and properties name (" - + propertyControllerName + ") don't match"); - } - tempName = propertyControllerName; - } - - PolicyController controller; - for (final PolicyControllerFeatureAPI controllerFeature : getControllerProviders()) { - try { - controller = controllerFeature.beforeCreate(tempName, properties); - if (controller != null) { - return controller; - } - } catch (final Exception e) { - logger.error("{}: feature {} before-controller-create failure because of {}", this, - controllerFeature.getClass().getName(), e.getMessage(), e); - } - } - - controller = getControllerFactory().build(tempName, properties); - if (this.isLocked()) { - controller.lock(); - } - - // feature hook - for (final PolicyControllerFeatureAPI controllerFeature : getControllerProviders()) { - try { - if (controllerFeature.afterCreate(controller)) { - return controller; - } - } catch (final Exception e) { - logger.error("{}: feature {} after-controller-create failure because of {}", this, - controllerFeature.getClass().getName(), e.getMessage(), e); - } - } - - return controller; - } - - - @Override - public List<PolicyController> updatePolicyControllers(List<ControllerConfiguration> configControllers) { - - final List<PolicyController> policyControllers = new ArrayList<>(); - if (configControllers == null || configControllers.isEmpty()) { - logger.info("No controller configuration provided: {}", configControllers); - return policyControllers; - } - - for (final ControllerConfiguration configController : configControllers) { - MDCTransaction mdcTrans = MDCTransaction.newSubTransaction(null).setTargetEntity(configController.getName()) - .setTargetServiceName(configController.getOperation()) - .setTargetVirtualEntity("" + configController.getDrools()); - try { - final PolicyController policyController = this.updatePolicyController(configController); - policyControllers.add(policyController); - mdcTrans.setStatusCode(true).transaction(); - } catch (final Exception e) { - mdcTrans.setStatusCode(false).setResponseCode(e.getClass().getName()) - .setResponseDescription(e.getMessage()).flush(); - logger.error(LoggerUtil.TRANSACTION_LOG_MARKER_NAME, - "{}: cannot update-policy-controllers because of {}", this, e.getMessage(), e); - } - } - - return policyControllers; - } - - @Override - public synchronized PolicyController updatePolicyController(ControllerConfiguration configController) { - - if (configController == null) { - throw new IllegalArgumentException("No controller configuration has been provided"); - } - - final String controllerName = configController.getName(); - if (controllerName == null || controllerName.isEmpty()) { - logger.warn("controller-name must be provided"); - throw new IllegalArgumentException("No controller configuration has been provided"); - } - - PolicyController policyController = null; - try { - final String operation = configController.getOperation(); - if (operation == null || operation.isEmpty()) { - logger.warn("operation must be provided"); - throw new IllegalArgumentException("operation must be provided"); - } - - try { - policyController = getControllerFactory().get(controllerName); - } catch (final IllegalArgumentException e) { - // not found - logger.warn("Policy Controller " + controllerName + " not found", e); - } - - if (policyController == null) { - - if (operation.equalsIgnoreCase(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_LOCK) - || operation.equalsIgnoreCase(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UNLOCK)) { - throw new IllegalArgumentException(controllerName + " is not available for operation " + operation); - } - - /* Recovery case */ - - logger.warn("controller {} does not exist. Attempting recovery from disk", controllerName); - - final Properties controllerProperties = - getPersistenceManager().getControllerProperties(controllerName); - - /* - * returned properties cannot be null (per implementation) assert (properties != - * null) - */ - - if (controllerProperties == null) { - throw new IllegalArgumentException(controllerName + " is invalid"); - } - - logger.warn("controller being recovered. {} Reset controller's bad maven coordinates to brainless", - controllerName); - - /* - * try to bring up bad controller in brainless mode, after having it working, apply - * the new create/update operation. - */ - controllerProperties.setProperty(DroolsProperties.RULES_GROUPID, DroolsController.NO_GROUP_ID); - controllerProperties.setProperty(DroolsProperties.RULES_ARTIFACTID, DroolsController.NO_ARTIFACT_ID); - controllerProperties.setProperty(DroolsProperties.RULES_VERSION, DroolsController.NO_VERSION); - - policyController = getPolicyEngine().createPolicyController(controllerName, controllerProperties); - - /* fall through to do brain update operation */ - } - - switch (operation) { - case ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_CREATE: - getControllerFactory().patch(policyController, configController.getDrools()); - break; - case ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UPDATE: - policyController.unlock(); - getControllerFactory().patch(policyController, configController.getDrools()); - break; - case ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_LOCK: - policyController.lock(); - break; - case ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UNLOCK: - policyController.unlock(); - break; - default: - final String msg = "Controller Operation Configuration is not supported: " + operation + " for " - + controllerName; - logger.warn(msg); - throw new IllegalArgumentException(msg); - } - - return policyController; - } catch (final Exception e) { - logger.error("{}: cannot update-policy-controller because of {}", this, e.getMessage(), e); - throw e; - } catch (final LinkageError e) { - logger.error("{}: cannot update-policy-controllers (rules) because of {}", this, e.getMessage(), e); - throw new IllegalStateException(e); - } - } - - @Override - public synchronized boolean start() { - - /* policy-engine dispatch pre start hook */ - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.beforeStart(this)) { - return true; - } - } catch (final Exception e) { - logger.error("{}: feature {} before-start failure because of {}", this, feature.getClass().getName(), - e.getMessage(), e); - } - } - - boolean success = true; - if (this.locked) { - throw new IllegalStateException(ENGINE_LOCKED_MSG); - } - - this.alive = true; - - /* Start Policy Engine exclusively-owned (unmanaged) http servers */ - - for (final HttpServletServer httpServer : this.httpServers) { - try { - if (!httpServer.waitedStart(10 * 1000L)) { - success = false; - } - } catch (final Exception e) { - logger.error("{}: cannot start http-server {} because of {}", this, httpServer, e.getMessage(), e); - } - } - - /* Start Policy Engine exclusively-owned (unmanaged) sources */ - - for (final TopicSource source : this.sources) { - try { - if (!source.start()) { - success = false; - } - } catch (final Exception e) { - logger.error("{}: cannot start topic-source {} because of {}", this, source, e.getMessage(), e); - } - } - - /* Start Policy Engine owned (unmanaged) sinks */ - - for (final TopicSink sink : this.sinks) { - try { - if (!sink.start()) { - success = false; - } - } catch (final Exception e) { - logger.error("{}: cannot start topic-sink {} because of {}", this, sink, e.getMessage(), e); - } - } - - /* Start Policy Controllers */ - - final List<PolicyController> controllers = getControllerFactory().inventory(); - for (final PolicyController controller : controllers) { - try { - if (!controller.start()) { - success = false; - } - } catch (final Exception e) { - logger.error("{}: cannot start policy-controller {} because of {}", this, controller, e.getMessage(), - e); - success = false; - } - } - - /* Start managed Topic Endpoints */ - - try { - if (!getTopicEndpointManager().start()) { - success = false; - } - } catch (final IllegalStateException e) { - logger.warn("{}: Topic Endpoint Manager is in an invalid state because of {}", this, e.getMessage(), e); - } - - - // Start the JMX listener - - startPdpJmxListener(); - - /* policy-engine dispatch after start hook */ - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.afterStart(this)) { - return success; - } - } catch (final Exception e) { - logger.error("{}: feature {} after-start failure because of {}", this, feature.getClass().getName(), - e.getMessage(), e); - } - } - - return success; - } - - @Override - public synchronized boolean stop() { - - /* policy-engine dispatch pre stop hook */ - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.beforeStop(this)) { - return true; - } - } catch (final Exception e) { - logger.error("{}: feature {} before-stop failure because of {}", this, feature.getClass().getName(), - e.getMessage(), e); - } - } - - /* stop regardless of the lock state */ - - boolean success = true; - if (!this.alive) { - return true; - } - - this.alive = false; - - final List<PolicyController> controllers = getControllerFactory().inventory(); - for (final PolicyController controller : controllers) { - try { - if (!controller.stop()) { - success = false; - } - } catch (final Exception e) { - logger.error("{}: cannot stop policy-controller {} because of {}", this, controller, e.getMessage(), e); - success = false; - } - } - - /* Stop Policy Engine owned (unmanaged) sources */ - for (final TopicSource source : this.sources) { - try { - if (!source.stop()) { - success = false; - } - } catch (final Exception e) { - logger.error("{}: cannot start topic-source {} because of {}", this, source, e.getMessage(), e); - } - } - - /* Stop Policy Engine owned (unmanaged) sinks */ - for (final TopicSink sink : this.sinks) { - try { - if (!sink.stop()) { - success = false; - } - } catch (final Exception e) { - logger.error("{}: cannot start topic-sink {} because of {}", this, sink, e.getMessage(), e); - } - } - - /* stop all managed topics sources and sinks */ - if (!getTopicEndpointManager().stop()) { - success = false; - } - - /* stop all unmanaged http servers */ - for (final HttpServletServer httpServer : this.httpServers) { - try { - if (!httpServer.stop()) { - success = false; - } - } catch (final Exception e) { - logger.error("{}: cannot start http-server {} because of {}", this, httpServer, e.getMessage(), e); - } - } - - // stop JMX? - - /* policy-engine dispatch pre stop hook */ - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.afterStop(this)) { - return success; - } - } catch (final Exception e) { - logger.error("{}: feature {} after-stop failure because of {}", this, feature.getClass().getName(), - e.getMessage(), e); - } - } - - return success; - } - - @Override - public synchronized void shutdown() { - - /* - * shutdown activity even when underlying subcomponents (features, controllers, topics, etc - * ..) are stuck - */ - - Thread exitThread = makeShutdownThread(); - exitThread.start(); - - /* policy-engine dispatch pre shutdown hook */ - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.beforeShutdown(this)) { - return; - } - } catch (final Exception e) { - logger.error("{}: feature {} before-shutdown failure because of {}", this, feature.getClass().getName(), - e.getMessage(), e); - } - } - - this.alive = false; - - /* Shutdown Policy Engine owned (unmanaged) sources */ - for (final TopicSource source : this.sources) { - try { - source.shutdown(); - } catch (final Exception e) { - logger.error("{}: cannot shutdown topic-source {} because of {}", this, source, e.getMessage(), e); - } - } - - /* Shutdown Policy Engine owned (unmanaged) sinks */ - for (final TopicSink sink : this.sinks) { - try { - sink.shutdown(); - } catch (final Exception e) { - logger.error("{}: cannot shutdown topic-sink {} because of {}", this, sink, e.getMessage(), e); - } - } - - /* Shutdown managed resources */ - getControllerFactory().shutdown(); - getTopicEndpointManager().shutdown(); - getServletFactory().destroy(); - - // Stop the JMX listener - - stopPdpJmxListener(); - - /* policy-engine dispatch post shutdown hook */ - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.afterShutdown(this)) { - return; - } - } catch (final Exception e) { - logger.error("{}: feature {} after-shutdown failure because of {}", this, feature.getClass().getName(), - e.getMessage(), e); - } - } - - exitThread.interrupt(); - logger.info("{}: normal termination", this); - } - - /** - * Thread that shuts down http servers. - */ - protected class ShutdownThread extends Thread { - private static final long SHUTDOWN_MAX_GRACE_TIME = 30000L; - - @Override - public void run() { - try { - doSleep(SHUTDOWN_MAX_GRACE_TIME); - logger.warn("{}: abnormal termination - shutdown graceful time period expiration", - PolicyEngineManager.this); - } catch (final InterruptedException e) { - /* courtesy to shutdown() to allow it to return */ - synchronized (PolicyEngineManager.this) { - } - logger.info("{}: finishing a graceful shutdown ", PolicyEngineManager.this, e); - } finally { - /* - * shut down the Policy Engine owned http servers as the very last thing - */ - for (final HttpServletServer httpServer : PolicyEngineManager.this.getHttpServers()) { - try { - httpServer.shutdown(); - } catch (final Exception e) { - logger.error("{}: cannot shutdown http-server {} because of {}", PolicyEngineManager.this, - httpServer, e.getMessage(), e); - } - } - - logger.info("{}: exit", PolicyEngineManager.this); - doExit(0); - } - } - - // these may be overridden by junit tests - - protected void doSleep(long sleepMs) throws InterruptedException { - Thread.sleep(sleepMs); - } - - protected void doExit(int code) { - System.exit(code); - } - } - - @Override - public boolean isAlive() { - return this.alive; - } - - @Override - public synchronized boolean lock() { - - /* policy-engine dispatch pre lock hook */ - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.beforeLock(this)) { - return true; - } - } catch (final Exception e) { - logger.error("{}: feature {} before-lock failure because of {}", this, feature.getClass().getName(), - e.getMessage(), e); - } - } - - if (this.locked) { - return true; - } - - this.locked = true; - - boolean success = true; - final List<PolicyController> controllers = getControllerFactory().inventory(); - for (final PolicyController controller : controllers) { - try { - success = controller.lock() && success; - } catch (final Exception e) { - logger.error("{}: cannot lock policy-controller {} because of {}", this, controller, e.getMessage(), e); - success = false; - } - } - - success = getTopicEndpointManager().lock() && success; - - /* policy-engine dispatch post lock hook */ - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.afterLock(this)) { - return success; - } - } catch (final Exception e) { - logger.error("{}: feature {} after-lock failure because of {}", this, feature.getClass().getName(), - e.getMessage(), e); - } - } - - return success; - } - - @Override - public synchronized boolean unlock() { - - /* policy-engine dispatch pre unlock hook */ - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.beforeUnlock(this)) { - return true; - } - } catch (final Exception e) { - logger.error("{}: feature {} before-unlock failure because of {}", this, feature.getClass().getName(), - e.getMessage(), e); - } - } - - if (!this.locked) { - return true; - } - - this.locked = false; - - boolean success = true; - final List<PolicyController> controllers = getControllerFactory().inventory(); - for (final PolicyController controller : controllers) { - try { - success = controller.unlock() && success; - } catch (final Exception e) { - logger.error("{}: cannot unlock policy-controller {} because of {}", this, controller, e.getMessage(), - e); - success = false; - } - } - - success = getTopicEndpointManager().unlock() && success; - - /* policy-engine dispatch after unlock hook */ - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.afterUnlock(this)) { - return success; - } - } catch (final Exception e) { - logger.error("{}: feature {} after-unlock failure because of {}", this, feature.getClass().getName(), - e.getMessage(), e); - } - } - - return success; - } - - @Override - public boolean isLocked() { - return this.locked; - } - - @Override - public void removePolicyController(String name) { - getControllerFactory().destroy(name); - } - - @Override - public void removePolicyController(PolicyController controller) { - getControllerFactory().destroy(controller); - } - - @JsonIgnore - @GsonJsonIgnore - @Override - public List<PolicyController> getPolicyControllers() { - return getControllerFactory().inventory(); - } - - @JsonProperty("controllers") - @GsonJsonProperty("controllers") - @Override - public List<String> getPolicyControllerIds() { - final List<String> controllerNames = new ArrayList<>(); - for (final PolicyController controller : getControllerFactory().inventory()) { - controllerNames.add(controller.getName()); - } - return controllerNames; - } - - @Override - @JsonIgnore - @GsonJsonIgnore - public Properties getProperties() { - return this.properties; - } - - - @SuppressWarnings("unchecked") - @Override - public List<TopicSource> getSources() { - return (List<TopicSource>) this.sources; - } - - @SuppressWarnings("unchecked") - @Override - public List<TopicSink> getSinks() { - return (List<TopicSink>) this.sinks; - } - - @Override - public List<HttpServletServer> getHttpServers() { - return this.httpServers; - } - - @Override - public List<String> getFeatures() { - final List<String> features = new ArrayList<>(); - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - features.add(feature.getName()); - } - return features; - } - - @JsonIgnore - @GsonJsonIgnore - @Override - public List<PolicyEngineFeatureAPI> getFeatureProviders() { - return getEngineProviders(); - } - - @Override - public PolicyEngineFeatureAPI getFeatureProvider(String featureName) { - if (featureName == null || featureName.isEmpty()) { - throw new IllegalArgumentException("A feature name must be provided"); - } - - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - if (feature.getName().equals(featureName)) { - return feature; - } - } - - throw new IllegalArgumentException("Invalid Feature Name: " + featureName); - } - - @Override - public void onTopicEvent(CommInfrastructure commType, String topic, String event) { - /* policy-engine pre topic event hook */ - for (final PolicyEngineFeatureAPI feature : getFeatureProviders()) { - try { - if (feature.beforeOnTopicEvent(this, commType, topic, event)) { - return; - } - } catch (final Exception e) { - logger.error("{}: feature {} beforeOnTopicEvent failure on event {} because of {}", this, - feature.getClass().getName(), event, e.getMessage(), e); - } - } - - /* configuration request */ - PdpdConfiguration configuration = null; - try { - configuration = this.decoder.fromJson(event, PdpdConfiguration.class); - this.configure(configuration); - } catch (final Exception e) { - logger.error("{}: configuration-error due to {} because of {}", this, event, e.getMessage(), e); - } - - /* policy-engine after topic event hook */ - for (final PolicyEngineFeatureAPI feature : getFeatureProviders()) { - try { - if (feature.afterOnTopicEvent(this, configuration, commType, topic, event)) { - return; - } - } catch (final Exception e) { - logger.error("{}: feature {} afterOnTopicEvent failure on event {} because of {}", this, - feature.getClass().getName(), event, e.getMessage(), e); - } - } - } - - @Override - public boolean deliver(String topic, Object event) { - - /* - * Note this entry point is usually from the DRL - */ - - if (topic == null || topic.isEmpty()) { - throw new IllegalArgumentException(INVALID_TOPIC_MSG); - } - - if (event == null) { - throw new IllegalArgumentException(INVALID_EVENT_MSG); - } - - if (!this.isAlive()) { - throw new IllegalStateException(ENGINE_STOPPED_MSG); - } - - if (this.isLocked()) { - throw new IllegalStateException(ENGINE_LOCKED_MSG); - } - - final List<? extends TopicSink> topicSinks = getTopicEndpointManager().getTopicSinks(topic); - if (topicSinks == null || topicSinks.size() != 1) { - throw new IllegalStateException("Cannot ensure correct delivery on topic " + topic + ": " + topicSinks); - } - - return this.deliver(topicSinks.get(0).getTopicCommInfrastructure(), topic, event); - } - - @Override - public boolean deliver(String busType, String topic, Object event) { - - /* - * Note this entry point is usually from the DRL (one of the reasons busType is String. - */ - - if (busType == null || busType.isEmpty()) { - throw new IllegalArgumentException("Invalid Communication Infrastructure"); - } - - if (topic == null || topic.isEmpty()) { - throw new IllegalArgumentException(INVALID_TOPIC_MSG); - } - - if (event == null) { - throw new IllegalArgumentException(INVALID_EVENT_MSG); - } - - boolean valid = false; - for (final Topic.CommInfrastructure comm : Topic.CommInfrastructure.values()) { - if (comm.name().equals(busType)) { - valid = true; - } - } - - if (!valid) { - throw new IllegalArgumentException("Invalid Communication Infrastructure: " + busType); - } - - - if (!this.isAlive()) { - throw new IllegalStateException(ENGINE_STOPPED_MSG); - } - - if (this.isLocked()) { - throw new IllegalStateException(ENGINE_LOCKED_MSG); - } - - - return this.deliver(Topic.CommInfrastructure.valueOf(busType), topic, event); - } - - @Override - public boolean deliver(Topic.CommInfrastructure busType, String topic, Object event) { - - if (topic == null || topic.isEmpty()) { - throw new IllegalArgumentException(INVALID_TOPIC_MSG); - } - - if (event == null) { - throw new IllegalArgumentException(INVALID_EVENT_MSG); - } - - if (!this.isAlive()) { - throw new IllegalStateException(ENGINE_STOPPED_MSG); - } - - if (this.isLocked()) { - throw new IllegalStateException(ENGINE_LOCKED_MSG); - } - - /* - * Try to send through the controller, this is the preferred way, since it may want to apply - * additional processing - */ - try { - final DroolsController droolsController = getProtocolCoder().getDroolsController(topic, event); - final PolicyController controller = getControllerFactory().get(droolsController); - if (controller != null) { - return controller.deliver(busType, topic, event); - } - } catch (final Exception e) { - logger.warn("{}: cannot find policy-controller to deliver {} over {}:{} because of {}", this, event, - busType, topic, e.getMessage(), e); - - /* continue (try without routing through the controller) */ - } - - /* - * cannot route through the controller, send directly through the topic sink - */ - try { - final String json = getProtocolCoder().encode(topic, event); - return this.deliver(busType, topic, json); - - } catch (final Exception e) { - logger.warn("{}: cannot deliver {} over {}:{} because of {}", this, event, busType, topic, e.getMessage(), - e); - throw e; - } - } - - @Override - public boolean deliver(Topic.CommInfrastructure busType, String topic, String event) { - - if (topic == null || topic.isEmpty()) { - throw new IllegalArgumentException(INVALID_TOPIC_MSG); - } - - if (event == null || event.isEmpty()) { - throw new IllegalArgumentException(INVALID_EVENT_MSG); - } - - if (!this.isAlive()) { - throw new IllegalStateException(ENGINE_STOPPED_MSG); - } - - if (this.isLocked()) { - throw new IllegalStateException(ENGINE_LOCKED_MSG); - } - - try { - final TopicSink sink = getTopicEndpointManager().getTopicSink(busType, topic); - - if (sink == null) { - throw new IllegalStateException("Inconsistent State: " + this); - } - - return sink.send(event); - - } catch (final Exception e) { - logger.warn("{}: cannot deliver {} over {}:{} because of {}", this, event, busType, topic, e.getMessage(), - e); - throw e; - } - } - - @Override - public synchronized void activate() { - - /* policy-engine dispatch pre activate hook */ - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.beforeActivate(this)) { - return; - } - } catch (final Exception e) { - logger.error("{}: feature {} before-activate failure because of {}", this, feature.getClass().getName(), - e.getMessage(), e); - } - } - - // activate 'policy-management' - for (final PolicyController policyController : this.getPolicyControllers()) { - try { - policyController.unlock(); - policyController.start(); - } catch (final Exception e) { - logger.error("{}: cannot activate of policy-controller {} because of {}", this, policyController, - e.getMessage(), e); - } catch (final LinkageError e) { - logger.error("{}: cannot activate (rules compilation) of policy-controller {} because of {}", this, - policyController, e.getMessage(), e); - } - } - - this.unlock(); - - /* policy-engine dispatch post activate hook */ - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.afterActivate(this)) { - return; - } - } catch (final Exception e) { - logger.error("{}: feature {} after-activate failure because of {}", this, feature.getClass().getName(), - e.getMessage(), e); - } - } - } - - @Override - public synchronized void deactivate() { - - /* policy-engine dispatch pre deactivate hook */ - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.beforeDeactivate(this)) { - return; - } - } catch (final Exception e) { - logger.error("{}: feature {} before-deactivate failure because of {}", this, - feature.getClass().getName(), e.getMessage(), e); - } - } - - this.lock(); - - for (final PolicyController policyController : this.getPolicyControllers()) { - try { - policyController.stop(); - } catch (final Exception | LinkageError e) { - logger.error("{}: cannot deactivate (stop) policy-controller {} because of {}", this, policyController, - e.getMessage(), e); - } - } - - /* policy-engine dispatch post deactivate hook */ - for (final PolicyEngineFeatureAPI feature : getEngineProviders()) { - try { - if (feature.afterDeactivate(this)) { - return; - } - } catch (final Exception e) { - logger.error("{}: feature {} after-deactivate failure because of {}", this, - feature.getClass().getName(), e.getMessage(), e); - } - } - } - - private boolean controllerConfig(PdpdConfiguration config) { - /* only this one supported for now */ - final List<ControllerConfiguration> configControllers = config.getControllers(); - if (configControllers == null || configControllers.isEmpty()) { - logger.info("No controller configuration provided: {}", config); - return false; - } - - final List<PolicyController> policyControllers = this.updatePolicyControllers(config.getControllers()); - boolean success = false; - if (!(policyControllers == null || policyControllers.isEmpty()) - && (policyControllers.size() == configControllers.size())) { - success = true; - } - return success; - } - - @Override - public String toString() { - return "PolicyEngineManager [alive=" + this.alive + ", locked=" + this.locked + "]"; - } - - // these methods may be overridden by junit tests - - protected List<PolicyEngineFeatureAPI> getEngineProviders() { - return PolicyEngineFeatureAPI.providers.getList(); - } - - protected List<PolicyControllerFeatureAPI> getControllerProviders() { - return PolicyControllerFeatureAPI.providers.getList(); - } - - protected void globalInitContainer(String[] cliArgs) { - PolicyContainer.globalInit(cliArgs); - } - - protected TopicEndpoint getTopicEndpointManager() { - return TopicEndpointManager.getManager(); - } - - protected HttpServletServerFactory getServletFactory() { - return HttpServletServerFactoryInstance.getServerFactory(); - } - - protected PolicyControllerFactory getControllerFactory() { - return PolicyController.factory; - } - - protected void startPdpJmxListener() { - PdpJmxListener.start(); - } - - protected void stopPdpJmxListener() { - PdpJmxListener.stop(); - } - - protected Thread makeShutdownThread() { - return new ShutdownThread(); - } - - protected EventProtocolCoder getProtocolCoder() { - return EventProtocolCoder.manager; - } - - protected SystemPersistence getPersistenceManager() { - return SystemPersistence.manager; - } - - protected PolicyEngine getPolicyEngine() { - return PolicyEngine.manager; - } -} - - diff --git a/policy-management/src/main/java/org/onap/policy/drools/system/PolicyEngineManager.java b/policy-management/src/main/java/org/onap/policy/drools/system/PolicyEngineManager.java new file mode 100644 index 00000000..07202fb9 --- /dev/null +++ b/policy-management/src/main/java/org/onap/policy/drools/system/PolicyEngineManager.java @@ -0,0 +1,1321 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 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.system; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import org.onap.policy.common.endpoints.event.comm.Topic; +import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure; +import org.onap.policy.common.endpoints.event.comm.TopicEndpoint; +import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager; +import org.onap.policy.common.endpoints.event.comm.TopicSink; +import org.onap.policy.common.endpoints.event.comm.TopicSource; +import org.onap.policy.common.endpoints.http.server.HttpServletServer; +import org.onap.policy.common.endpoints.http.server.HttpServletServerFactory; +import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance; +import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties; +import org.onap.policy.common.gson.annotation.GsonJsonIgnore; +import org.onap.policy.common.gson.annotation.GsonJsonProperty; +import org.onap.policy.drools.controller.DroolsController; +import org.onap.policy.drools.core.PolicyContainer; +import org.onap.policy.drools.core.jmx.PdpJmxListener; +import org.onap.policy.drools.features.PolicyControllerFeatureApi; +import org.onap.policy.drools.features.PolicyEngineFeatureApi; +import org.onap.policy.drools.persistence.SystemPersistence; +import org.onap.policy.drools.properties.DroolsProperties; +import org.onap.policy.drools.protocol.coders.EventProtocolCoder; +import org.onap.policy.drools.protocol.configuration.ControllerConfiguration; +import org.onap.policy.drools.protocol.configuration.PdpdConfiguration; +import org.onap.policy.drools.server.restful.RestManager; +import org.onap.policy.drools.server.restful.aaf.AafTelemetryAuthFilter; +import org.onap.policy.drools.utils.PropertyUtil; +import org.onap.policy.drools.utils.logging.LoggerUtil; +import org.onap.policy.drools.utils.logging.MDCTransaction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * Policy Engine Manager Implementation. + */ +class PolicyEngineManager implements PolicyEngine { + + /** + * String literals. + */ + private static final String INVALID_TOPIC_MSG = "Invalid Topic"; + private static final String INVALID_EVENT_MSG = "Invalid Event"; + + private static final String ENGINE_STOPPED_MSG = "Policy Engine is stopped"; + private static final String ENGINE_LOCKED_MSG = "Policy Engine is locked"; + + /** + * logger. + */ + private static final Logger logger = LoggerFactory.getLogger(PolicyEngineManager.class); + + /** + * Is the Policy Engine running. + */ + private volatile boolean alive = false; + + /** + * Is the engine locked. + */ + private volatile boolean locked = false; + + /** + * Properties used to initialize the engine. + */ + private Properties properties; + + /** + * Environment Properties. + */ + private final Properties environment = new Properties(); + + /** + * Policy Engine Sources. + */ + private List<? extends TopicSource> sources = new ArrayList<>(); + + /** + * Policy Engine Sinks. + */ + private List<? extends TopicSink> sinks = new ArrayList<>(); + + /** + * Policy Engine HTTP Servers. + */ + private List<HttpServletServer> httpServers = new ArrayList<>(); + + /** + * gson parser to decode configuration requests. + */ + private final Gson decoder = new GsonBuilder().disableHtmlEscaping().create(); + + + @Override + public synchronized void boot(String[] cliArgs) { + + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.beforeBoot(this, cliArgs)) { + return; + } + } catch (final Exception e) { + logger.error("{}: feature {} before-boot failure because of {}", this, feature.getClass().getName(), + e.getMessage(), e); + } + } + + try { + globalInitContainer(cliArgs); + } catch (final Exception e) { + logger.error("{}: cannot init policy-container because of {}", this, e.getMessage(), e); + } + + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.afterBoot(this)) { + return; + } + } catch (final Exception e) { + logger.error("{}: feature {} after-boot failure because of {}", this, feature.getClass().getName(), + e.getMessage(), e); + } + } + } + + @Override + public synchronized void setEnvironment(Properties properties) { + this.environment.putAll(PropertyUtil.getInterpolatedProperties(properties)); + } + + @JsonIgnore + @GsonJsonIgnore + @Override + public synchronized Properties getEnvironment() { + return this.environment; + } + + @Override + public synchronized String getEnvironmentProperty(String envKey) { + String value = this.environment.getProperty(envKey); + if (value == null) { + value = System.getProperty(envKey); + if (value == null) { + value = System.getenv(envKey); + } + } + return value; + } + + @Override + public synchronized String setEnvironmentProperty(String envKey, String envValue) { + return (String) this.environment.setProperty(envKey, envValue); + } + + @Override + public final Properties defaultTelemetryConfig() { + final Properties defaultConfig = new Properties(); + + defaultConfig.put(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES, "TELEMETRY"); + defaultConfig.put(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + TELEMETRY_SERVER_DEFAULT_NAME + + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX, TELEMETRY_SERVER_DEFAULT_HOST); + defaultConfig.put( + PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + TELEMETRY_SERVER_DEFAULT_NAME + + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX, + "" + Integer.toString(TELEMETRY_SERVER_DEFAULT_PORT)); + defaultConfig.put( + PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + TELEMETRY_SERVER_DEFAULT_NAME + + PolicyEndPointProperties.PROPERTY_HTTP_REST_PACKAGES_SUFFIX, + RestManager.class.getPackage().getName()); + defaultConfig.put(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + TELEMETRY_SERVER_DEFAULT_NAME + + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX, "" + Boolean.TRUE); + defaultConfig.put(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + TELEMETRY_SERVER_DEFAULT_NAME + + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX, "" + Boolean.FALSE); + + return defaultConfig; + } + + @Override + public synchronized void configure(Properties properties) { + + if (properties == null) { + logger.warn("No properties provided"); + throw new IllegalArgumentException("No properties provided"); + } + + /* policy-engine dispatch pre configure hook */ + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.beforeConfigure(this, properties)) { + return; + } + } catch (final Exception e) { + logger.error("{}: feature {} before-configure failure because of {}", this, + feature.getClass().getName(), e.getMessage(), e); + } + } + + this.properties = properties; + + try { + this.sources = getTopicEndpointManager().addTopicSources(properties); + for (final TopicSource source : this.sources) { + source.register(this); + } + } catch (final Exception e) { + logger.error("{}: add-sources failed", this, e); + } + + try { + this.sinks = getTopicEndpointManager().addTopicSinks(properties); + } catch (final IllegalArgumentException e) { + logger.error("{}: add-sinks failed", this, e); + } + + try { + this.httpServers = getServletFactory().build(properties); + for (HttpServletServer server : this.httpServers) { + if (server.isAaf()) { + server.addFilterClass(null, AafTelemetryAuthFilter.class.getName()); + } + } + } catch (final IllegalArgumentException e) { + logger.error("{}: add-http-servers failed", this, e); + } + + /* policy-engine dispatch post configure hook */ + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.afterConfigure(this)) { + return; + } + } catch (final Exception e) { + logger.error("{}: feature {} after-configure failure because of {}", this, feature.getClass().getName(), + e.getMessage(), e); + } + } + } + + @Override + public boolean configure(PdpdConfiguration config) { + + if (config == null) { + throw new IllegalArgumentException("No configuration provided"); + } + + final String entity = config.getEntity(); + + MDCTransaction mdcTrans = MDCTransaction.newTransaction(config.getRequestId(), "brmsgw"); + if (this.getSources().size() == 1) { + Topic topic = this.getSources().get(0); + mdcTrans.setServiceName(topic.getTopic()).setRemoteHost(topic.getServers().toString()) + .setTargetEntity(config.getEntity()); + } + + switch (entity) { + case PdpdConfiguration.CONFIG_ENTITY_CONTROLLER: + boolean success = controllerConfig(config); + mdcTrans.resetSubTransaction().setStatusCode(success).transaction(); + return success; + default: + final String msg = "Configuration Entity is not supported: " + entity; + mdcTrans.resetSubTransaction().setStatusCode(false).setResponseDescription(msg).flush(); + logger.warn(LoggerUtil.TRANSACTION_LOG_MARKER_NAME, msg); + throw new IllegalArgumentException(msg); + } + } + + @Override + public synchronized PolicyController createPolicyController(String name, Properties properties) { + + String tempName = name; + // check if a PROPERTY_CONTROLLER_NAME property is present + // if so, override the given name + + final String propertyControllerName = properties.getProperty(DroolsProperties.PROPERTY_CONTROLLER_NAME); + if (propertyControllerName != null && !propertyControllerName.isEmpty()) { + if (!propertyControllerName.equals(tempName)) { + throw new IllegalStateException("Proposed name (" + tempName + ") and properties name (" + + propertyControllerName + ") don't match"); + } + tempName = propertyControllerName; + } + + PolicyController controller; + for (final PolicyControllerFeatureApi controllerFeature : getControllerProviders()) { + try { + controller = controllerFeature.beforeCreate(tempName, properties); + if (controller != null) { + return controller; + } + } catch (final Exception e) { + logger.error("{}: feature {} before-controller-create failure because of {}", this, + controllerFeature.getClass().getName(), e.getMessage(), e); + } + } + + controller = getControllerFactory().build(tempName, properties); + if (this.isLocked()) { + controller.lock(); + } + + // feature hook + for (final PolicyControllerFeatureApi controllerFeature : getControllerProviders()) { + try { + if (controllerFeature.afterCreate(controller)) { + return controller; + } + } catch (final Exception e) { + logger.error("{}: feature {} after-controller-create failure because of {}", this, + controllerFeature.getClass().getName(), e.getMessage(), e); + } + } + + return controller; + } + + + @Override + public List<PolicyController> updatePolicyControllers(List<ControllerConfiguration> configControllers) { + + final List<PolicyController> policyControllers = new ArrayList<>(); + if (configControllers == null || configControllers.isEmpty()) { + logger.info("No controller configuration provided: {}", configControllers); + return policyControllers; + } + + for (final ControllerConfiguration configController : configControllers) { + MDCTransaction mdcTrans = MDCTransaction.newSubTransaction(null).setTargetEntity(configController.getName()) + .setTargetServiceName(configController.getOperation()) + .setTargetVirtualEntity("" + configController.getDrools()); + try { + final PolicyController policyController = this.updatePolicyController(configController); + policyControllers.add(policyController); + mdcTrans.setStatusCode(true).transaction(); + } catch (final Exception e) { + mdcTrans.setStatusCode(false).setResponseCode(e.getClass().getName()) + .setResponseDescription(e.getMessage()).flush(); + logger.error(LoggerUtil.TRANSACTION_LOG_MARKER_NAME, + "{}: cannot update-policy-controllers because of {}", this, e.getMessage(), e); + } + } + + return policyControllers; + } + + @Override + public synchronized PolicyController updatePolicyController(ControllerConfiguration configController) { + + if (configController == null) { + throw new IllegalArgumentException("No controller configuration has been provided"); + } + + final String controllerName = configController.getName(); + if (controllerName == null || controllerName.isEmpty()) { + logger.warn("controller-name must be provided"); + throw new IllegalArgumentException("No controller configuration has been provided"); + } + + PolicyController policyController = null; + try { + final String operation = configController.getOperation(); + if (operation == null || operation.isEmpty()) { + logger.warn("operation must be provided"); + throw new IllegalArgumentException("operation must be provided"); + } + + try { + policyController = getControllerFactory().get(controllerName); + } catch (final IllegalArgumentException e) { + // not found + logger.warn("Policy Controller " + controllerName + " not found", e); + } + + if (policyController == null) { + + if (operation.equalsIgnoreCase(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_LOCK) + || operation.equalsIgnoreCase(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UNLOCK)) { + throw new IllegalArgumentException(controllerName + " is not available for operation " + operation); + } + + /* Recovery case */ + + logger.warn("controller {} does not exist. Attempting recovery from disk", controllerName); + + final Properties controllerProperties = + getPersistenceManager().getControllerProperties(controllerName); + + /* + * returned properties cannot be null (per implementation) assert (properties != + * null) + */ + + if (controllerProperties == null) { + throw new IllegalArgumentException(controllerName + " is invalid"); + } + + logger.warn("controller being recovered. {} Reset controller's bad maven coordinates to brainless", + controllerName); + + /* + * try to bring up bad controller in brainless mode, after having it working, apply + * the new create/update operation. + */ + controllerProperties.setProperty(DroolsProperties.RULES_GROUPID, DroolsController.NO_GROUP_ID); + controllerProperties.setProperty(DroolsProperties.RULES_ARTIFACTID, DroolsController.NO_ARTIFACT_ID); + controllerProperties.setProperty(DroolsProperties.RULES_VERSION, DroolsController.NO_VERSION); + + policyController = getPolicyEngine().createPolicyController(controllerName, controllerProperties); + + /* fall through to do brain update operation */ + } + + switch (operation) { + case ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_CREATE: + getControllerFactory().patch(policyController, configController.getDrools()); + break; + case ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UPDATE: + policyController.unlock(); + getControllerFactory().patch(policyController, configController.getDrools()); + break; + case ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_LOCK: + policyController.lock(); + break; + case ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UNLOCK: + policyController.unlock(); + break; + default: + final String msg = "Controller Operation Configuration is not supported: " + operation + " for " + + controllerName; + logger.warn(msg); + throw new IllegalArgumentException(msg); + } + + return policyController; + } catch (final Exception e) { + logger.error("{}: cannot update-policy-controller because of {}", this, e.getMessage(), e); + throw e; + } catch (final LinkageError e) { + logger.error("{}: cannot update-policy-controllers (rules) because of {}", this, e.getMessage(), e); + throw new IllegalStateException(e); + } + } + + @Override + public synchronized boolean start() { + + /* policy-engine dispatch pre start hook */ + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.beforeStart(this)) { + return true; + } + } catch (final Exception e) { + logger.error("{}: feature {} before-start failure because of {}", this, feature.getClass().getName(), + e.getMessage(), e); + } + } + + boolean success = true; + if (this.locked) { + throw new IllegalStateException(ENGINE_LOCKED_MSG); + } + + this.alive = true; + + /* Start Policy Engine exclusively-owned (unmanaged) http servers */ + + for (final HttpServletServer httpServer : this.httpServers) { + try { + if (!httpServer.waitedStart(10 * 1000L)) { + success = false; + } + } catch (final Exception e) { + logger.error("{}: cannot start http-server {} because of {}", this, httpServer, e.getMessage(), e); + } + } + + /* Start Policy Engine exclusively-owned (unmanaged) sources */ + + for (final TopicSource source : this.sources) { + try { + if (!source.start()) { + success = false; + } + } catch (final Exception e) { + logger.error("{}: cannot start topic-source {} because of {}", this, source, e.getMessage(), e); + } + } + + /* Start Policy Engine owned (unmanaged) sinks */ + + for (final TopicSink sink : this.sinks) { + try { + if (!sink.start()) { + success = false; + } + } catch (final Exception e) { + logger.error("{}: cannot start topic-sink {} because of {}", this, sink, e.getMessage(), e); + } + } + + /* Start Policy Controllers */ + + final List<PolicyController> controllers = getControllerFactory().inventory(); + for (final PolicyController controller : controllers) { + try { + if (!controller.start()) { + success = false; + } + } catch (final Exception e) { + logger.error("{}: cannot start policy-controller {} because of {}", this, controller, e.getMessage(), + e); + success = false; + } + } + + /* Start managed Topic Endpoints */ + + try { + if (!getTopicEndpointManager().start()) { + success = false; + } + } catch (final IllegalStateException e) { + logger.warn("{}: Topic Endpoint Manager is in an invalid state because of {}", this, e.getMessage(), e); + } + + + // Start the JMX listener + + startPdpJmxListener(); + + /* policy-engine dispatch after start hook */ + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.afterStart(this)) { + return success; + } + } catch (final Exception e) { + logger.error("{}: feature {} after-start failure because of {}", this, feature.getClass().getName(), + e.getMessage(), e); + } + } + + return success; + } + + @Override + public synchronized boolean stop() { + + /* policy-engine dispatch pre stop hook */ + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.beforeStop(this)) { + return true; + } + } catch (final Exception e) { + logger.error("{}: feature {} before-stop failure because of {}", this, feature.getClass().getName(), + e.getMessage(), e); + } + } + + /* stop regardless of the lock state */ + + boolean success = true; + if (!this.alive) { + return true; + } + + this.alive = false; + + final List<PolicyController> controllers = getControllerFactory().inventory(); + for (final PolicyController controller : controllers) { + try { + if (!controller.stop()) { + success = false; + } + } catch (final Exception e) { + logger.error("{}: cannot stop policy-controller {} because of {}", this, controller, e.getMessage(), e); + success = false; + } + } + + /* Stop Policy Engine owned (unmanaged) sources */ + for (final TopicSource source : this.sources) { + try { + if (!source.stop()) { + success = false; + } + } catch (final Exception e) { + logger.error("{}: cannot start topic-source {} because of {}", this, source, e.getMessage(), e); + } + } + + /* Stop Policy Engine owned (unmanaged) sinks */ + for (final TopicSink sink : this.sinks) { + try { + if (!sink.stop()) { + success = false; + } + } catch (final Exception e) { + logger.error("{}: cannot start topic-sink {} because of {}", this, sink, e.getMessage(), e); + } + } + + /* stop all managed topics sources and sinks */ + if (!getTopicEndpointManager().stop()) { + success = false; + } + + /* stop all unmanaged http servers */ + for (final HttpServletServer httpServer : this.httpServers) { + try { + if (!httpServer.stop()) { + success = false; + } + } catch (final Exception e) { + logger.error("{}: cannot start http-server {} because of {}", this, httpServer, e.getMessage(), e); + } + } + + // stop JMX? + + /* policy-engine dispatch pre stop hook */ + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.afterStop(this)) { + return success; + } + } catch (final Exception e) { + logger.error("{}: feature {} after-stop failure because of {}", this, feature.getClass().getName(), + e.getMessage(), e); + } + } + + return success; + } + + @Override + public synchronized void shutdown() { + + /* + * shutdown activity even when underlying subcomponents (features, controllers, topics, etc + * ..) are stuck + */ + + Thread exitThread = makeShutdownThread(); + exitThread.start(); + + /* policy-engine dispatch pre shutdown hook */ + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.beforeShutdown(this)) { + return; + } + } catch (final Exception e) { + logger.error("{}: feature {} before-shutdown failure because of {}", this, feature.getClass().getName(), + e.getMessage(), e); + } + } + + this.alive = false; + + /* Shutdown Policy Engine owned (unmanaged) sources */ + for (final TopicSource source : this.sources) { + try { + source.shutdown(); + } catch (final Exception e) { + logger.error("{}: cannot shutdown topic-source {} because of {}", this, source, e.getMessage(), e); + } + } + + /* Shutdown Policy Engine owned (unmanaged) sinks */ + for (final TopicSink sink : this.sinks) { + try { + sink.shutdown(); + } catch (final Exception e) { + logger.error("{}: cannot shutdown topic-sink {} because of {}", this, sink, e.getMessage(), e); + } + } + + /* Shutdown managed resources */ + getControllerFactory().shutdown(); + getTopicEndpointManager().shutdown(); + getServletFactory().destroy(); + + // Stop the JMX listener + + stopPdpJmxListener(); + + /* policy-engine dispatch post shutdown hook */ + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.afterShutdown(this)) { + return; + } + } catch (final Exception e) { + logger.error("{}: feature {} after-shutdown failure because of {}", this, feature.getClass().getName(), + e.getMessage(), e); + } + } + + exitThread.interrupt(); + logger.info("{}: normal termination", this); + } + + /** + * Thread that shuts down http servers. + */ + protected class ShutdownThread extends Thread { + private static final long SHUTDOWN_MAX_GRACE_TIME = 30000L; + + @Override + public void run() { + try { + doSleep(SHUTDOWN_MAX_GRACE_TIME); + logger.warn("{}: abnormal termination - shutdown graceful time period expiration", + PolicyEngineManager.this); + } catch (final InterruptedException e) { + /* courtesy to shutdown() to allow it to return */ + synchronized (PolicyEngineManager.this) { + } + logger.info("{}: finishing a graceful shutdown ", PolicyEngineManager.this, e); + } finally { + /* + * shut down the Policy Engine owned http servers as the very last thing + */ + for (final HttpServletServer httpServer : PolicyEngineManager.this.getHttpServers()) { + try { + httpServer.shutdown(); + } catch (final Exception e) { + logger.error("{}: cannot shutdown http-server {} because of {}", PolicyEngineManager.this, + httpServer, e.getMessage(), e); + } + } + + logger.info("{}: exit", PolicyEngineManager.this); + doExit(0); + } + } + + // these may be overridden by junit tests + + protected void doSleep(long sleepMs) throws InterruptedException { + Thread.sleep(sleepMs); + } + + protected void doExit(int code) { + System.exit(code); + } + } + + @Override + public boolean isAlive() { + return this.alive; + } + + @Override + public synchronized boolean lock() { + + /* policy-engine dispatch pre lock hook */ + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.beforeLock(this)) { + return true; + } + } catch (final Exception e) { + logger.error("{}: feature {} before-lock failure because of {}", this, feature.getClass().getName(), + e.getMessage(), e); + } + } + + if (this.locked) { + return true; + } + + this.locked = true; + + boolean success = true; + final List<PolicyController> controllers = getControllerFactory().inventory(); + for (final PolicyController controller : controllers) { + try { + success = controller.lock() && success; + } catch (final Exception e) { + logger.error("{}: cannot lock policy-controller {} because of {}", this, controller, e.getMessage(), e); + success = false; + } + } + + success = getTopicEndpointManager().lock() && success; + + /* policy-engine dispatch post lock hook */ + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.afterLock(this)) { + return success; + } + } catch (final Exception e) { + logger.error("{}: feature {} after-lock failure because of {}", this, feature.getClass().getName(), + e.getMessage(), e); + } + } + + return success; + } + + @Override + public synchronized boolean unlock() { + + /* policy-engine dispatch pre unlock hook */ + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.beforeUnlock(this)) { + return true; + } + } catch (final Exception e) { + logger.error("{}: feature {} before-unlock failure because of {}", this, feature.getClass().getName(), + e.getMessage(), e); + } + } + + if (!this.locked) { + return true; + } + + this.locked = false; + + boolean success = true; + final List<PolicyController> controllers = getControllerFactory().inventory(); + for (final PolicyController controller : controllers) { + try { + success = controller.unlock() && success; + } catch (final Exception e) { + logger.error("{}: cannot unlock policy-controller {} because of {}", this, controller, e.getMessage(), + e); + success = false; + } + } + + success = getTopicEndpointManager().unlock() && success; + + /* policy-engine dispatch after unlock hook */ + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.afterUnlock(this)) { + return success; + } + } catch (final Exception e) { + logger.error("{}: feature {} after-unlock failure because of {}", this, feature.getClass().getName(), + e.getMessage(), e); + } + } + + return success; + } + + @Override + public boolean isLocked() { + return this.locked; + } + + @Override + public void removePolicyController(String name) { + getControllerFactory().destroy(name); + } + + @Override + public void removePolicyController(PolicyController controller) { + getControllerFactory().destroy(controller); + } + + @JsonIgnore + @GsonJsonIgnore + @Override + public List<PolicyController> getPolicyControllers() { + return getControllerFactory().inventory(); + } + + @JsonProperty("controllers") + @GsonJsonProperty("controllers") + @Override + public List<String> getPolicyControllerIds() { + final List<String> controllerNames = new ArrayList<>(); + for (final PolicyController controller : getControllerFactory().inventory()) { + controllerNames.add(controller.getName()); + } + return controllerNames; + } + + @Override + @JsonIgnore + @GsonJsonIgnore + public Properties getProperties() { + return this.properties; + } + + + @SuppressWarnings("unchecked") + @Override + public List<TopicSource> getSources() { + return (List<TopicSource>) this.sources; + } + + @SuppressWarnings("unchecked") + @Override + public List<TopicSink> getSinks() { + return (List<TopicSink>) this.sinks; + } + + @Override + public List<HttpServletServer> getHttpServers() { + return this.httpServers; + } + + @Override + public List<String> getFeatures() { + final List<String> features = new ArrayList<>(); + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + features.add(feature.getName()); + } + return features; + } + + @JsonIgnore + @GsonJsonIgnore + @Override + public List<PolicyEngineFeatureApi> getFeatureProviders() { + return getEngineProviders(); + } + + @Override + public PolicyEngineFeatureApi getFeatureProvider(String featureName) { + if (featureName == null || featureName.isEmpty()) { + throw new IllegalArgumentException("A feature name must be provided"); + } + + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + if (feature.getName().equals(featureName)) { + return feature; + } + } + + throw new IllegalArgumentException("Invalid Feature Name: " + featureName); + } + + @Override + public void onTopicEvent(CommInfrastructure commType, String topic, String event) { + /* policy-engine pre topic event hook */ + for (final PolicyEngineFeatureApi feature : getFeatureProviders()) { + try { + if (feature.beforeOnTopicEvent(this, commType, topic, event)) { + return; + } + } catch (final Exception e) { + logger.error("{}: feature {} beforeOnTopicEvent failure on event {} because of {}", this, + feature.getClass().getName(), event, e.getMessage(), e); + } + } + + /* configuration request */ + PdpdConfiguration configuration = null; + try { + configuration = this.decoder.fromJson(event, PdpdConfiguration.class); + this.configure(configuration); + } catch (final Exception e) { + logger.error("{}: configuration-error due to {} because of {}", this, event, e.getMessage(), e); + } + + /* policy-engine after topic event hook */ + for (final PolicyEngineFeatureApi feature : getFeatureProviders()) { + try { + if (feature.afterOnTopicEvent(this, configuration, commType, topic, event)) { + return; + } + } catch (final Exception e) { + logger.error("{}: feature {} afterOnTopicEvent failure on event {} because of {}", this, + feature.getClass().getName(), event, e.getMessage(), e); + } + } + } + + @Override + public boolean deliver(String topic, Object event) { + + /* + * Note this entry point is usually from the DRL + */ + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException(INVALID_TOPIC_MSG); + } + + if (event == null) { + throw new IllegalArgumentException(INVALID_EVENT_MSG); + } + + if (!this.isAlive()) { + throw new IllegalStateException(ENGINE_STOPPED_MSG); + } + + if (this.isLocked()) { + throw new IllegalStateException(ENGINE_LOCKED_MSG); + } + + final List<? extends TopicSink> topicSinks = getTopicEndpointManager().getTopicSinks(topic); + if (topicSinks == null || topicSinks.size() != 1) { + throw new IllegalStateException("Cannot ensure correct delivery on topic " + topic + ": " + topicSinks); + } + + return this.deliver(topicSinks.get(0).getTopicCommInfrastructure(), topic, event); + } + + @Override + public boolean deliver(String busType, String topic, Object event) { + + /* + * Note this entry point is usually from the DRL (one of the reasons busType is String. + */ + + if (busType == null || busType.isEmpty()) { + throw new IllegalArgumentException("Invalid Communication Infrastructure"); + } + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException(INVALID_TOPIC_MSG); + } + + if (event == null) { + throw new IllegalArgumentException(INVALID_EVENT_MSG); + } + + boolean valid = false; + for (final Topic.CommInfrastructure comm : Topic.CommInfrastructure.values()) { + if (comm.name().equals(busType)) { + valid = true; + } + } + + if (!valid) { + throw new IllegalArgumentException("Invalid Communication Infrastructure: " + busType); + } + + + if (!this.isAlive()) { + throw new IllegalStateException(ENGINE_STOPPED_MSG); + } + + if (this.isLocked()) { + throw new IllegalStateException(ENGINE_LOCKED_MSG); + } + + + return this.deliver(Topic.CommInfrastructure.valueOf(busType), topic, event); + } + + @Override + public boolean deliver(Topic.CommInfrastructure busType, String topic, Object event) { + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException(INVALID_TOPIC_MSG); + } + + if (event == null) { + throw new IllegalArgumentException(INVALID_EVENT_MSG); + } + + if (!this.isAlive()) { + throw new IllegalStateException(ENGINE_STOPPED_MSG); + } + + if (this.isLocked()) { + throw new IllegalStateException(ENGINE_LOCKED_MSG); + } + + /* + * Try to send through the controller, this is the preferred way, since it may want to apply + * additional processing + */ + try { + final DroolsController droolsController = getProtocolCoder().getDroolsController(topic, event); + final PolicyController controller = getControllerFactory().get(droolsController); + if (controller != null) { + return controller.deliver(busType, topic, event); + } + } catch (final Exception e) { + logger.warn("{}: cannot find policy-controller to deliver {} over {}:{} because of {}", this, event, + busType, topic, e.getMessage(), e); + + /* continue (try without routing through the controller) */ + } + + /* + * cannot route through the controller, send directly through the topic sink + */ + try { + final String json = getProtocolCoder().encode(topic, event); + return this.deliver(busType, topic, json); + + } catch (final Exception e) { + logger.warn("{}: cannot deliver {} over {}:{} because of {}", this, event, busType, topic, e.getMessage(), + e); + throw e; + } + } + + @Override + public boolean deliver(Topic.CommInfrastructure busType, String topic, String event) { + + if (topic == null || topic.isEmpty()) { + throw new IllegalArgumentException(INVALID_TOPIC_MSG); + } + + if (event == null || event.isEmpty()) { + throw new IllegalArgumentException(INVALID_EVENT_MSG); + } + + if (!this.isAlive()) { + throw new IllegalStateException(ENGINE_STOPPED_MSG); + } + + if (this.isLocked()) { + throw new IllegalStateException(ENGINE_LOCKED_MSG); + } + + try { + final TopicSink sink = getTopicEndpointManager().getTopicSink(busType, topic); + + if (sink == null) { + throw new IllegalStateException("Inconsistent State: " + this); + } + + return sink.send(event); + + } catch (final Exception e) { + logger.warn("{}: cannot deliver {} over {}:{} because of {}", this, event, busType, topic, e.getMessage(), + e); + throw e; + } + } + + @Override + public synchronized void activate() { + + /* policy-engine dispatch pre activate hook */ + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.beforeActivate(this)) { + return; + } + } catch (final Exception e) { + logger.error("{}: feature {} before-activate failure because of {}", this, feature.getClass().getName(), + e.getMessage(), e); + } + } + + // activate 'policy-management' + for (final PolicyController policyController : this.getPolicyControllers()) { + try { + policyController.unlock(); + policyController.start(); + } catch (final Exception e) { + logger.error("{}: cannot activate of policy-controller {} because of {}", this, policyController, + e.getMessage(), e); + } catch (final LinkageError e) { + logger.error("{}: cannot activate (rules compilation) of policy-controller {} because of {}", this, + policyController, e.getMessage(), e); + } + } + + this.unlock(); + + /* policy-engine dispatch post activate hook */ + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.afterActivate(this)) { + return; + } + } catch (final Exception e) { + logger.error("{}: feature {} after-activate failure because of {}", this, feature.getClass().getName(), + e.getMessage(), e); + } + } + } + + @Override + public synchronized void deactivate() { + + /* policy-engine dispatch pre deactivate hook */ + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.beforeDeactivate(this)) { + return; + } + } catch (final Exception e) { + logger.error("{}: feature {} before-deactivate failure because of {}", this, + feature.getClass().getName(), e.getMessage(), e); + } + } + + this.lock(); + + for (final PolicyController policyController : this.getPolicyControllers()) { + try { + policyController.stop(); + } catch (final Exception | LinkageError e) { + logger.error("{}: cannot deactivate (stop) policy-controller {} because of {}", this, policyController, + e.getMessage(), e); + } + } + + /* policy-engine dispatch post deactivate hook */ + for (final PolicyEngineFeatureApi feature : getEngineProviders()) { + try { + if (feature.afterDeactivate(this)) { + return; + } + } catch (final Exception e) { + logger.error("{}: feature {} after-deactivate failure because of {}", this, + feature.getClass().getName(), e.getMessage(), e); + } + } + } + + private boolean controllerConfig(PdpdConfiguration config) { + /* only this one supported for now */ + final List<ControllerConfiguration> configControllers = config.getControllers(); + if (configControllers == null || configControllers.isEmpty()) { + logger.info("No controller configuration provided: {}", config); + return false; + } + + final List<PolicyController> policyControllers = this.updatePolicyControllers(config.getControllers()); + boolean success = false; + if (!(policyControllers == null || policyControllers.isEmpty()) + && (policyControllers.size() == configControllers.size())) { + success = true; + } + return success; + } + + @Override + public String toString() { + return "PolicyEngineManager [alive=" + this.alive + ", locked=" + this.locked + "]"; + } + + // these methods may be overridden by junit tests + + protected List<PolicyEngineFeatureApi> getEngineProviders() { + return PolicyEngineFeatureApi.providers.getList(); + } + + protected List<PolicyControllerFeatureApi> getControllerProviders() { + return PolicyControllerFeatureApi.providers.getList(); + } + + protected void globalInitContainer(String[] cliArgs) { + PolicyContainer.globalInit(cliArgs); + } + + protected TopicEndpoint getTopicEndpointManager() { + return TopicEndpointManager.getManager(); + } + + protected HttpServletServerFactory getServletFactory() { + return HttpServletServerFactoryInstance.getServerFactory(); + } + + protected PolicyControllerFactory getControllerFactory() { + return PolicyController.factory; + } + + protected void startPdpJmxListener() { + PdpJmxListener.start(); + } + + protected void stopPdpJmxListener() { + PdpJmxListener.stop(); + } + + protected Thread makeShutdownThread() { + return new ShutdownThread(); + } + + protected EventProtocolCoder getProtocolCoder() { + return EventProtocolCoder.manager; + } + + protected SystemPersistence getPersistenceManager() { + return SystemPersistence.manager; + } + + protected PolicyEngine getPolicyEngine() { + return PolicyEngine.manager; + } +} diff --git a/policy-management/src/main/java/org/onap/policy/drools/system/internal/AggregatedPolicyController.java b/policy-management/src/main/java/org/onap/policy/drools/system/internal/AggregatedPolicyController.java index bdcb19ad..51280eb3 100644 --- a/policy-management/src/main/java/org/onap/policy/drools/system/internal/AggregatedPolicyController.java +++ b/policy-management/src/main/java/org/onap/policy/drools/system/internal/AggregatedPolicyController.java @@ -36,7 +36,7 @@ import org.onap.policy.common.endpoints.event.comm.TopicSource; import org.onap.policy.common.gson.annotation.GsonJsonIgnore; import org.onap.policy.drools.controller.DroolsController; import org.onap.policy.drools.controller.DroolsControllerFactory; -import org.onap.policy.drools.features.PolicyControllerFeatureAPI; +import org.onap.policy.drools.features.PolicyControllerFeatureApi; import org.onap.policy.drools.persistence.SystemPersistence; import org.onap.policy.drools.properties.DroolsProperties; import org.onap.policy.drools.protocol.configuration.DroolsConfiguration; @@ -268,7 +268,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen public boolean start() { logger.info("{}: start", this); - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.beforeStart(this)) { return true; @@ -307,7 +307,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen } } - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.afterStart(this)) { return true; @@ -328,7 +328,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen public boolean stop() { logger.info("{}: stop", this); - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.beforeStop(this)) { return true; @@ -357,7 +357,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen boolean success = this.droolsController.stop(); - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.afterStop(this)) { return true; @@ -378,7 +378,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen public void shutdown() { logger.info("{}: shutdown", this); - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.beforeShutdown(this)) { return; @@ -393,7 +393,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen getDroolsFactory().shutdown(this.droolsController); - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.afterShutdown(this)) { return; @@ -412,7 +412,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen public void halt() { logger.info("{}: halt", this); - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.beforeHalt(this)) { return; @@ -427,7 +427,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen getDroolsFactory().destroy(this.droolsController); getPersistenceManager().deleteController(this.name); - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.afterHalt(this)) { return; @@ -450,7 +450,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen return; } - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.beforeOffer(this, commType, topic, event)) { return; @@ -463,7 +463,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen boolean success = this.droolsController.offer(topic, event); - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.afterOffer(this, commType, topic, event, success)) { return; @@ -483,7 +483,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen return true; } - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.beforeOffer(this, event)) { return true; @@ -496,7 +496,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen boolean success = this.droolsController.offer(event); - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.afterOffer(this, event, success)) { return success; @@ -522,7 +522,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen logger.debug("{}: deliver event to {}:{}: {}", this, commType, topic, event); - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.beforeDeliver(this, commType, topic, event)) { return true; @@ -557,7 +557,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen boolean success = this.droolsController.deliver(this.topic2Sinks.get(topic), event); - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.afterDeliver(this, commType, topic, event, success)) { return success; @@ -586,7 +586,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen public boolean lock() { logger.info("{}: lock", this); - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.beforeLock(this)) { return true; @@ -610,7 +610,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen boolean success = this.droolsController.lock(); - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.afterLock(this)) { return true; @@ -632,7 +632,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen logger.info("{}: unlock", this); - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.beforeUnlock(this)) { return true; @@ -653,7 +653,7 @@ public class AggregatedPolicyController implements PolicyController, TopicListen boolean success = this.droolsController.unlock(); - for (PolicyControllerFeatureAPI feature : getProviders()) { + for (PolicyControllerFeatureApi feature : getProviders()) { try { if (feature.afterUnlock(this)) { return true; @@ -730,8 +730,8 @@ public class AggregatedPolicyController implements PolicyController, TopicListen return DroolsController.factory; } - protected List<PolicyControllerFeatureAPI> getProviders() { - return PolicyControllerFeatureAPI.providers.getList(); + protected List<PolicyControllerFeatureApi> getProviders() { + return PolicyControllerFeatureApi.providers.getList(); } } |