aboutsummaryrefslogtreecommitdiffstats
path: root/policy-management/src/main/java/org/openecomp/policy/drools
diff options
context:
space:
mode:
Diffstat (limited to 'policy-management/src/main/java/org/openecomp/policy/drools')
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/controller/DroolsController.java170
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/controller/DroolsControllerFactory.java540
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/controller/internal/MavenDroolsController.java718
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/controller/internal/NullDroolsController.java219
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/persistence/SystemPersistence.java255
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/protocol/coders/EventProtocolCoder.java1451
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/protocol/coders/JsonProtocolFilter.java304
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/protocol/coders/ProtocolCoderToolset.java668
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/protocol/coders/TopicCoderFilterConfiguration.java309
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/protocol/configuration/ControllerConfiguration.java280
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/protocol/configuration/DroolsConfiguration.java278
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/protocol/configuration/PdpdConfiguration.java283
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/server/restful/RestManager.java1181
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/system/Main.java131
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/system/PolicyController.java110
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/system/PolicyControllerFactory.java464
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/system/PolicyEngine.java1182
-rw-r--r--policy-management/src/main/java/org/openecomp/policy/drools/system/internal/AggregatedPolicyController.java462
18 files changed, 9005 insertions, 0 deletions
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/controller/DroolsController.java b/policy-management/src/main/java/org/openecomp/policy/drools/controller/DroolsController.java
new file mode 100644
index 00000000..72f8e0b2
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/controller/DroolsController.java
@@ -0,0 +1,170 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-management
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.policy.drools.controller;
+
+import java.util.List;
+
+import org.openecomp.policy.drools.event.comm.TopicSink;
+import org.openecomp.policy.drools.properties.Lockable;
+import org.openecomp.policy.drools.properties.Startable;
+import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration;
+
+/**
+ * Drools Controller is the abstractions that wraps the
+ * drools layer (policy-core)
+ */
+public interface DroolsController extends Startable, Lockable {
+
+ /**
+ * No Group ID identifier
+ */
+ public static final String NO_GROUP_ID = "NO-GROUP-ID";
+
+ /**
+ * No Artifact ID identifier
+ */
+ public static final String NO_ARTIFACT_ID = "NO-ARTIFACT-ID";
+
+ /**
+ * No version identifier
+ */
+ public static final String NO_VERSION = "NO-VERSION";
+
+ /**
+ * get group id
+ * @return group id
+ */
+ public String getGroupId();
+
+ /**
+ * get artifact id
+ * @return artifact id
+ */
+ public String getArtifactId();
+
+ /**
+ * get version
+ * @return version
+ */
+ public String getVersion();
+
+
+ /**
+ * return the policy session names
+ *
+ * @return policy session
+ */
+ public List<String> getSessionNames();
+
+ /**
+ * offers an event to this controller for processing
+ *
+ * @param topic topic associated with the event
+ * @param event the event
+ *
+ * @return true if the operation was successful
+ */
+ public boolean offer(String topic, String event);
+
+ /**
+ * delivers "event" to "sink"
+ *
+ * @param sink destination
+ * @param event
+ * @return true if successful, false if a failure has occurred.
+ * @throws IllegalArgumentException when invalid or insufficient
+ * properties are provided
+ * @throws IllegalStateException when the engine is in a state where
+ * this operation is not permitted (ie. locked or stopped).
+ * @throws UnsupportedOperationException when the engine cannot deliver due
+ * to the functionality missing (ie. communication infrastructure
+ * not supported.
+ */
+ public boolean deliver(TopicSink sink, Object event)
+ throws IllegalArgumentException, IllegalStateException,
+ UnsupportedOperationException;
+
+ /**
+ *
+ * @return the most recent received events
+ */
+ public Object[] getRecentSourceEvents();
+
+ /**
+ *
+ * @return the most recent delivered events
+ */
+ public String[] getRecentSinkEvents();
+
+ /**
+ * Supports this encoder?
+ *
+ * @param encodedObject
+ * @return
+ */
+ public boolean ownsCoder(Class<? extends Object> coderClass, int modelHash) throws IllegalStateException;
+
+ /**
+ * fetches a class from the model
+ *
+ * @param className the class to fetch
+ * @return the actual class object, or null if not found
+ */
+ public Class<?> fetchModelClass(String className) throws IllegalArgumentException;
+
+ /**
+ * is this controller Smart?
+ */
+ public boolean isBrained();
+
+ /**
+ * update the new version of the maven jar rules file
+ *
+ * @param newGroupId - new group id
+ * @param newArtifactId - new artifact id
+ * @param newVersion - new version
+ * @param decoderConfigurations - decoder configurations
+ * @param encoderConfigurations - encoder configurations
+ *
+ * @throws Exception from within drools libraries
+ * @throws LinkageError from within drools libraries
+ * @throws ArgumentException bad parameter passed in
+ */
+ public void updateToVersion(String newGroupId, String newArtifactId, String newVersion,
+ List<TopicCoderFilterConfiguration> decoderConfigurations,
+ List<TopicCoderFilterConfiguration> encoderConfigurations)
+ throws IllegalArgumentException, LinkageError, Exception;
+
+
+ /**
+ * halts and permanently releases all resources
+ * @throws IllegalStateException
+ */
+ public void halt() throws IllegalStateException;
+
+ /**
+ * Factory to track and manage drools controllers
+ */
+ public static final DroolsControllerFactory factory =
+ new IndexedDroolsControllerFactory();
+
+
+}
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/controller/DroolsControllerFactory.java b/policy-management/src/main/java/org/openecomp/policy/drools/controller/DroolsControllerFactory.java
new file mode 100644
index 00000000..d94e773c
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/controller/DroolsControllerFactory.java
@@ -0,0 +1,540 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-management
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.policy.drools.controller;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Properties;
+
+import org.openecomp.policy.drools.controller.internal.MavenDroolsController;
+import org.openecomp.policy.drools.controller.internal.NullDroolsController;
+import org.openecomp.policy.drools.event.comm.Topic;
+import org.openecomp.policy.drools.event.comm.Topic.CommInfrastructure;
+import org.openecomp.policy.common.logging.flexlogger.FlexLogger;
+import org.openecomp.policy.common.logging.flexlogger.Logger;
+import org.openecomp.policy.drools.event.comm.TopicSource;
+import org.openecomp.policy.drools.event.comm.TopicSink;
+import org.openecomp.policy.drools.properties.PolicyProperties;
+import org.openecomp.policy.drools.protocol.coders.JsonProtocolFilter;
+import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration;
+import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
+import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomJacksonCoder;
+import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration.PotentialCoderFilter;
+import org.openecomp.policy.drools.utils.Pair;
+
+/**
+ * Drools Controller Factory to manage controller creation, destruction,
+ * and retrieval for management interfaces
+ */
+public interface DroolsControllerFactory {
+
+ /**
+ * Constructs a Drools Controller based on properties
+ *
+ * @param properties properties containing initialization parameters
+ * @param eventSources list of event sources
+ * @param eventSinks list of event sinks
+ *
+ * @return the instantiated Drools Controller
+ * @throws IllegalArgumentException with invalid parameters
+ * @throws LinkageError Failure to link rules and models in Drools Libraries
+ * @throws Exception Exception from Drools Libraries
+ */
+ public DroolsController build(Properties properties,
+ List<? extends TopicSource> eventSources,
+ List<? extends TopicSink> eventSinks)
+ throws IllegalArgumentException, LinkageError, Exception;
+
+ /**
+ * Explicit construction of a Drools Controller
+ *
+ * @param groupId maven group id of drools artifact
+ * @param artifactId maven artifact id of drools artifact
+ * @param version maven version id of drools artifact
+ * @param decoderConfigurations list of decoder configurations
+ * @param encoderConfigurations list of encoder configurations
+ *
+ * @return the instantiated Drools Controller
+ * @throws IllegalArgumentException with invalid parameters
+ * @throws LinkageError Failure to link rules and models in Drools Libraries
+ * @throws Exception Exception from Drools Libraries
+ */
+ public DroolsController build(String groupId,
+ String artifactId,
+ String version,
+ List<TopicCoderFilterConfiguration> decoderConfigurations,
+ List<TopicCoderFilterConfiguration> encoderConfigurations)
+ throws IllegalArgumentException, LinkageError, Exception;
+
+ /**
+ * Releases the Drools Controller from operation
+ *
+ * @param controller the Drools Controller to shut down
+ */
+ public void shutdown(DroolsController controller);
+
+ /**
+ * Disables all Drools Controllers from operation
+ */
+ public void shutdown();
+
+ /**
+ * Destroys and releases resources for a Drools Controller
+ *
+ * @param controller the Drools Controller to destroy
+ */
+ public void destroy(DroolsController controller);
+
+ /**
+ * Destroys all Drools Controllers
+ */
+ public void destroy();
+
+ /**
+ * Gets the Drools Controller associated with the maven group
+ * and artifact id
+ *
+ * @param groupId maven group id of drools artifact
+ * @param artifactId maven artifact id of drools artifact
+ * @param version maven version id of drools artifact
+ *
+ * @return the Drools Controller
+ * @throws IllegalArgumentException with invalid parameters
+ */
+ public DroolsController get(String groupId,
+ String artifactId,
+ String version)
+ throws IllegalArgumentException;
+
+ /**
+ * returns the current inventory of Drools Controllers
+ *
+ * @return a list of Drools Controllers
+ */
+ public List<DroolsController> inventory();
+}
+
+/* ---------------- implementation -----------------*/
+
+/**
+ * Factory of Drools Controllers indexed by the Maven coordinates
+ */
+class IndexedDroolsControllerFactory implements DroolsControllerFactory {
+
+ /**
+ * logger
+ */
+ private static Logger logger = FlexLogger.getLogger(MavenDroolsController.class);
+
+ /**
+ * Policy Controller Name Index
+ */
+ protected HashMap<String, DroolsController> droolsControllers =
+ new HashMap<String, DroolsController>();
+
+ /**
+ * 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);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public DroolsController build(Properties properties,
+ List<? extends TopicSource> eventSources,
+ List<? extends TopicSink> eventSinks)
+ throws IllegalArgumentException, LinkageError, Exception {
+
+ String groupId = properties.getProperty(PolicyProperties.RULES_GROUPID);
+ if (groupId == null || groupId.isEmpty())
+ groupId = DroolsController.NO_GROUP_ID;
+
+ String artifactId = properties.getProperty(PolicyProperties.RULES_ARTIFACTID);
+ if (artifactId == null || artifactId.isEmpty())
+ artifactId = DroolsController.NO_ARTIFACT_ID;
+
+ String version = properties.getProperty(PolicyProperties.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);
+ }
+
+ /**
+ * 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)
+ throws IllegalArgumentException {
+
+ String PROPERTY_TOPIC_ENTITY_PREFIX;
+
+ List<TopicCoderFilterConfiguration>
+ topics2DecodedClasses2Filters =
+ new ArrayList<TopicCoderFilterConfiguration>();
+
+ if (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) {
+ PROPERTY_TOPIC_ENTITY_PREFIX = PolicyProperties.PROPERTY_UEB_SOURCE_TOPICS + ".";
+ } else {
+ PROPERTY_TOPIC_ENTITY_PREFIX = PolicyProperties.PROPERTY_UEB_SINK_TOPICS + ".";
+ }
+ } else if (commInfra == CommInfrastructure.DMAAP) {
+ if (isSource) {
+ PROPERTY_TOPIC_ENTITY_PREFIX = PolicyProperties.PROPERTY_DMAAP_SOURCE_TOPICS + ".";
+ } else {
+ PROPERTY_TOPIC_ENTITY_PREFIX = PolicyProperties.PROPERTY_DMAAP_SINK_TOPICS + ".";
+ }
+ } else {
+ throw new IllegalArgumentException("Invalid Communication Infrastructure: " + commInfra);
+ }
+
+ // 1. first the topic
+
+ String aTopic = 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
+ (PROPERTY_TOPIC_ENTITY_PREFIX +
+ aTopic +
+ PolicyProperties.PROPERTY_TOPIC_EVENTS_CUSTOM_MODEL_CODER_GSON_SUFFIX);
+
+ CustomGsonCoder customGsonCoder = null;
+ if (customGson != null && !customGson.isEmpty()) {
+ try {
+ customGsonCoder = new CustomGsonCoder(customGson);
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ }
+ }
+
+ String customJackson = properties.getProperty
+ (PROPERTY_TOPIC_ENTITY_PREFIX +
+ aTopic +
+ PolicyProperties.PROPERTY_TOPIC_EVENTS_CUSTOM_MODEL_CODER_JACKSON_SUFFIX);
+
+ CustomJacksonCoder customJacksonCoder = null;
+ if (customJackson != null && !customJackson.isEmpty()) {
+ try {
+ customJacksonCoder = new CustomJacksonCoder(customJackson);
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ }
+ }
+
+ // 3. second the list of classes associated with each topic
+
+ String eventClasses =
+ properties.getProperty(PROPERTY_TOPIC_ENTITY_PREFIX + aTopic + PolicyProperties.PROPERTY_TOPIC_EVENTS_SUFFIX);
+
+ if (eventClasses == null || eventClasses.isEmpty()) {
+ // TODO warn
+ continue;
+ }
+
+ List<PotentialCoderFilter> classes2Filters = new ArrayList<PotentialCoderFilter>();
+
+ List<String> aTopicClasses =
+ new ArrayList<String>(Arrays.asList(eventClasses.split("\\s*,\\s*")));
+
+ for (String aClass: aTopicClasses) {
+
+
+ // 4. third, for each coder class, get the list of field filters
+
+ String filter = properties.getProperty
+ (PROPERTY_TOPIC_ENTITY_PREFIX +
+ aTopic +
+ PolicyProperties.PROPERTY_TOPIC_EVENTS_SUFFIX +
+ "." + aClass +
+ PolicyProperties.PROPERTY_TOPIC_EVENTS_FILTER_SUFFIX);
+
+ List<Pair<String,String>> filters = new ArrayList<Pair<String,String>>();
+
+ if (filter == null || filter.isEmpty()) {
+ // 4. topic -> class -> with no filters
+
+ JsonProtocolFilter protocolFilter = JsonProtocolFilter.fromRawFilters(filters);
+ PotentialCoderFilter class2Filters =
+ new PotentialCoderFilter(aClass, protocolFilter);
+ classes2Filters.add(class2Filters);
+ continue;
+ }
+
+ // There are filters associated with the applicability of
+ // this class for decoding.
+ List<String> listOfFilters =
+ new ArrayList<String>(Arrays.asList(filter.split("\\s*,\\s*")));
+
+ for (String nameValue: listOfFilters) {
+ String fieldName;
+ String regexValue;
+
+ String[] nameValueSplit = nameValue.split("\\s*=\\s*");
+ if (nameValueSplit.length <= 0 || nameValueSplit.length > 2) {
+ // TODO warn
+ // skip
+ continue;
+ }
+
+ if (nameValueSplit.length == 2) {
+ fieldName = nameValueSplit[0];
+ regexValue = nameValueSplit[1];
+ } else if (nameValueSplit.length == 1) {
+ fieldName = nameValueSplit[0];
+ regexValue = null;
+ } else {
+ // unreachable
+ continue;
+ }
+
+ filters.add(new Pair<String,String>(fieldName, regexValue));
+ }
+
+ JsonProtocolFilter protocolFilter = JsonProtocolFilter.fromRawFilters(filters);
+ PotentialCoderFilter class2Filters =
+ new PotentialCoderFilter(aClass, protocolFilter);
+ classes2Filters.add(class2Filters);
+ }
+
+ TopicCoderFilterConfiguration topic2Classes2Filters =
+ new TopicCoderFilterConfiguration(aTopic,classes2Filters, customGsonCoder, customJacksonCoder);
+ topics2DecodedClasses2Filters.add(topic2Classes2Filters);
+ }
+
+ return topics2DecodedClasses2Filters;
+ }
+
+ /**
+ * {@inheritDoc}
+ * @param decoderConfiguration
+ */
+ @Override
+ public DroolsController build(String newGroupId,
+ String newArtifactId,
+ String newVersion,
+ List<TopicCoderFilterConfiguration> decoderConfigurations,
+ List<TopicCoderFilterConfiguration> encoderConfigurations)
+ throws IllegalArgumentException, LinkageError, Exception {
+
+ if (newGroupId == null || newArtifactId == null || newVersion == null ||
+ newGroupId.isEmpty() || newArtifactId.isEmpty() || newVersion.isEmpty()) {
+ throw new IllegalArgumentException("Missing maven coordinates: " +
+ newGroupId + ":" + newArtifactId + ":" +
+ newVersion);
+ }
+
+ 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;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void destroy(DroolsController controller) throws IllegalArgumentException {
+ unmanage(controller);
+ controller.halt();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @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
+ * @return
+ * @throws IllegalArgumentException
+ */
+ protected void unmanage(DroolsController controller) throws IllegalArgumentException {
+ 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);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void shutdown(DroolsController controller) throws IllegalArgumentException {
+ this.unmanage(controller);
+ controller.shutdown();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void shutdown() {
+ List<DroolsController> controllers = this.inventory();
+ for (DroolsController controller: controllers) {
+ controller.shutdown();
+ }
+
+ synchronized(this) {
+ this.droolsControllers.clear();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public DroolsController get(String groupId,
+ String artifactId,
+ String version)
+ throws IllegalArgumentException, IllegalStateException {
+
+ 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");
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<DroolsController> inventory() {
+ List<DroolsController> controllers =
+ new ArrayList<DroolsController>(this.droolsControllers.values());
+ return controllers;
+ }
+
+}
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/controller/internal/MavenDroolsController.java b/policy-management/src/main/java/org/openecomp/policy/drools/controller/internal/MavenDroolsController.java
new file mode 100644
index 00000000..2c5708d3
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/controller/internal/MavenDroolsController.java
@@ -0,0 +1,718 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-management
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.policy.drools.controller.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.collections4.queue.CircularFifoQueue;
+
+import org.openecomp.policy.common.logging.eelf.MessageCodes;
+import org.openecomp.policy.common.logging.flexlogger.FlexLogger;
+import org.openecomp.policy.common.logging.flexlogger.Logger;
+import org.openecomp.policy.drools.controller.DroolsController;
+import org.openecomp.policy.drools.core.PolicyContainer;
+import org.openecomp.policy.drools.core.PolicySession;
+import org.openecomp.policy.drools.core.jmx.PdpJmx;
+import org.openecomp.policy.drools.event.comm.TopicSink;
+import org.openecomp.policy.drools.protocol.coders.EventProtocolCoder;
+import org.openecomp.policy.drools.protocol.coders.JsonProtocolFilter;
+import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration;
+import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
+import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomJacksonCoder;
+import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration.PotentialCoderFilter;
+import org.openecomp.policy.drools.utils.ReflectionUtil;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+/**
+ * Maven-based Drools Controller that interacts with the
+ * policy-core PolicyContainer and PolicySession to manage
+ * Drools containers instantiated using Maven.
+ */
+public class MavenDroolsController implements DroolsController {
+
+ /**
+ * logger
+ */
+ private static Logger logger = FlexLogger.getLogger(MavenDroolsController.class);
+
+ /**
+ * Policy Container, the access object to the policy-core layer
+ */
+ @JsonIgnore
+ protected final PolicyContainer policyContainer;
+
+ /**
+ * alive status of this drools controller,
+ * reflects invocation of start()/stop() only
+ */
+ protected volatile boolean alive = false;
+
+ /**
+ * locked status of this drools controller,
+ * reflects if i/o drools related operations are permitted,
+ * more specifically: offer() and deliver().
+ * It does not affect the ability to start and stop
+ * underlying drools infrastructure
+ */
+ protected volatile boolean locked = false;
+
+ /**
+ * list of topics, each with associated decoder classes, each
+ * with a list of associated filters.
+ */
+ protected List<TopicCoderFilterConfiguration> decoderConfigurations;
+
+ /**
+ * list of topics, each with associated encoder classes, each
+ * with a list of associated filters.
+ */
+ protected List<TopicCoderFilterConfiguration> encoderConfigurations;
+
+ /**
+ * recent source events processed
+ */
+ protected final CircularFifoQueue<Object> recentSourceEvents = new CircularFifoQueue<Object>(10);
+
+ /**
+ * recent sink events processed
+ */
+ protected final CircularFifoQueue<String> recentSinkEvents = new CircularFifoQueue<String>(10);
+
+ /**
+ * original Drools Model/Rules classloader hash
+ */
+ protected int modelClassLoaderHash;
+
+ /**
+ * Expanded version of the constructor
+ *
+ * @param groupId maven group id
+ * @param artifactId maven artifact id
+ * @param version maven version
+ * @param decoderConfiguration list of topic -> decoders -> filters mapping
+ * @param encoderConfiguration list of topic -> encoders -> filters mapping
+ *
+ * @throws IllegalArgumentException invalid arguments passed in
+ */
+ public MavenDroolsController(String groupId,
+ String artifactId,
+ String version,
+ List<TopicCoderFilterConfiguration> decoderConfigurations,
+ List<TopicCoderFilterConfiguration> encoderConfigurations)
+ throws IllegalArgumentException {
+
+ if (logger.isInfoEnabled())
+ logger.info("DROOLS CONTROLLER: instantiation " + this +
+ " -> {" + groupId + ":" + artifactId + ":" + version + "}");
+
+ if (groupId == null || artifactId == null || version == null ||
+ groupId.isEmpty() || artifactId.isEmpty() || version.isEmpty()) {
+ throw new IllegalArgumentException("Missing maven coordinates: " +
+ groupId + ":" + artifactId + ":" +
+ version);
+ }
+
+ this.policyContainer= new PolicyContainer(groupId, artifactId, version);
+ this.init(decoderConfigurations, encoderConfigurations);
+
+ if (logger.isInfoEnabled())
+ logger.info("DROOLS CONTROLLER: instantiation completed " + this);
+ }
+
+ /**
+ * init encoding/decoding configuration
+ * @param decoderConfiguration list of topic -> decoders -> filters mapping
+ * @param encoderConfiguration list of topic -> encoders -> filters mapping
+ */
+ protected void init(List<TopicCoderFilterConfiguration> decoderConfigurations,
+ List<TopicCoderFilterConfiguration> encoderConfigurations) {
+
+ this.decoderConfigurations = decoderConfigurations;
+ this.encoderConfigurations = encoderConfigurations;
+
+ this.initCoders(decoderConfigurations, true);
+ this.initCoders(encoderConfigurations, false);
+
+ this.modelClassLoaderHash = this.policyContainer.getClassLoader().hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void updateToVersion(String newGroupId, String newArtifactId, String newVersion,
+ List<TopicCoderFilterConfiguration> decoderConfigurations,
+ List<TopicCoderFilterConfiguration> encoderConfigurations)
+ throws IllegalArgumentException, LinkageError, Exception {
+
+ if (logger.isInfoEnabled())
+ logger.info("UPDATE-TO-VERSION: " + this + " -> {" + newGroupId + ":" + newArtifactId + ":" + newVersion + "}");
+
+ if (newGroupId == null || newArtifactId == null || newVersion == null ||
+ newGroupId.isEmpty() || newArtifactId.isEmpty() || newVersion.isEmpty()) {
+ throw new IllegalArgumentException("Missing maven coordinates: " +
+ newGroupId + ":" + newArtifactId + ":" +
+ newVersion);
+ }
+
+ if (newGroupId.equalsIgnoreCase(DroolsController.NO_GROUP_ID) ||
+ newArtifactId.equalsIgnoreCase(DroolsController.NO_ARTIFACT_ID) ||
+ newVersion.equalsIgnoreCase(DroolsController.NO_VERSION)) {
+ throw new IllegalArgumentException("BRAINLESS maven coordinates provided: " +
+ newGroupId + ":" + newArtifactId + ":" +
+ newVersion);
+ }
+
+ if (newGroupId.equalsIgnoreCase(this.getGroupId()) &&
+ newArtifactId.equalsIgnoreCase(this.getArtifactId()) &&
+ newVersion.equalsIgnoreCase(this.getVersion())) {
+ logger.warn("Al in the right version: " + newGroupId + ":" +
+ newArtifactId + ":" + newVersion + " vs. " + this);
+ return;
+ }
+
+ if (!newGroupId.equalsIgnoreCase(this.getGroupId()) ||
+ !newArtifactId.equalsIgnoreCase(this.getArtifactId())) {
+ throw new IllegalArgumentException("Group ID and Artifact ID maven coordinates must be identical for the upgrade: " +
+ newGroupId + ":" + newArtifactId + ":" +
+ newVersion + " vs. " + this);
+ }
+
+ /* upgrade */
+ String messages = this.policyContainer.updateToVersion(newVersion);
+ if (logger.isWarnEnabled())
+ logger.warn(this + "UPGRADE results: " + messages);
+
+ /*
+ * If all sucessful (can load new container), now we can remove all coders from previous sessions
+ */
+ this.removeCoders();
+
+ /*
+ * add the new coders
+ */
+ this.init(decoderConfigurations, encoderConfigurations);
+
+ if (logger.isInfoEnabled())
+ logger.info("UPDATE-TO-VERSION: completed " + this);
+ }
+
+ /**
+ * initialize decoders for all the topics supported by this controller
+ * Note this is critical to be done after the Policy Container is
+ * instantiated to be able to fetch the corresponding classes.
+ *
+ * @param decoderConfiguration list of topic -> decoders -> filters mapping
+ */
+ protected void initCoders(List<TopicCoderFilterConfiguration> coderConfigurations,
+ boolean decoder)
+ throws IllegalArgumentException {
+
+ if (logger.isInfoEnabled())
+ logger.info("INIT-CODERS: " + this);
+
+ if (coderConfigurations == null) {
+ return;
+ }
+
+
+ for (TopicCoderFilterConfiguration coderConfig: coderConfigurations) {
+ String topic = coderConfig.getTopic();
+
+ CustomGsonCoder customGsonCoder = coderConfig.getCustomGsonCoder();
+ if (coderConfig.getCustomGsonCoder() != null &&
+ coderConfig.getCustomGsonCoder().getClassContainer() != null &&
+ !coderConfig.getCustomGsonCoder().getClassContainer().isEmpty()) {
+
+ String customGsonCoderClass = coderConfig.getCustomGsonCoder().getClassContainer();
+ if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(),
+ customGsonCoderClass)) {
+ logger.error(customGsonCoderClass + " cannot be retrieved");
+ throw new IllegalArgumentException(customGsonCoderClass + " cannot be retrieved");
+ } else {
+ if (logger.isInfoEnabled())
+ logger.info("CLASS FETCHED " + customGsonCoderClass);
+ }
+ }
+
+ CustomJacksonCoder customJacksonCoder = coderConfig.getCustomJacksonCoder();
+ if (coderConfig.getCustomJacksonCoder() != null &&
+ coderConfig.getCustomJacksonCoder().getClassContainer() != null &&
+ !coderConfig.getCustomJacksonCoder().getClassContainer().isEmpty()) {
+
+ String customJacksonCoderClass = coderConfig.getCustomJacksonCoder().getClassContainer();
+ if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(),
+ customJacksonCoderClass)) {
+ logger.error(customJacksonCoderClass + " cannot be retrieved");
+ throw new IllegalArgumentException(customJacksonCoderClass + " cannot be retrieved");
+ } else {
+ if (logger.isInfoEnabled())
+ logger.info("CLASS FETCHED " + customJacksonCoderClass);
+ }
+ }
+
+ List<PotentialCoderFilter> coderFilters = coderConfig.getCoderFilters();
+ if (coderFilters == null || coderFilters.isEmpty()) {
+ continue;
+ }
+
+ for (PotentialCoderFilter coderFilter : coderFilters) {
+ String potentialCodedClass = coderFilter.getCodedClass();
+ JsonProtocolFilter protocolFilter = coderFilter.getFilter();
+
+ if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(),
+ potentialCodedClass)) {
+ logger.error(potentialCodedClass + " cannot be retrieved");
+ throw new IllegalArgumentException(potentialCodedClass + " cannot be retrieved");
+ } else {
+ if (logger.isInfoEnabled())
+ logger.info("CLASS FETCHED " + potentialCodedClass);
+ }
+
+ if (decoder)
+ EventProtocolCoder.manager.addDecoder(this.getGroupId(), this.getArtifactId(),
+ topic, potentialCodedClass, protocolFilter,
+ customGsonCoder,
+ customJacksonCoder,
+ this.policyContainer.getClassLoader().hashCode());
+ else
+ EventProtocolCoder.manager.addEncoder(this.getGroupId(), this.getArtifactId(),
+ topic, potentialCodedClass, protocolFilter,
+ customGsonCoder,
+ customJacksonCoder,
+ this.policyContainer.getClassLoader().hashCode());
+ }
+ }
+ }
+
+
+ /**
+ * remove decoders.
+ */
+ protected void removeDecoders()
+ throws IllegalArgumentException {
+ if (logger.isInfoEnabled())
+ logger.info("REMOVE-DECODERS: " + this);
+
+ if (this.decoderConfigurations == null) {
+ return;
+ }
+
+
+ for (TopicCoderFilterConfiguration coderConfig: decoderConfigurations) {
+ String topic = coderConfig.getTopic();
+ EventProtocolCoder.manager.removeDecoders
+ (this.getGroupId(), this.getArtifactId(), topic);
+ }
+ }
+
+ /**
+ * remove decoders.
+ */
+ protected void removeEncoders()
+ throws IllegalArgumentException {
+
+ if (logger.isInfoEnabled())
+ logger.info("REMOVE-ENCODERS: " + this);
+
+ if (this.encoderConfigurations == null)
+ return;
+
+
+ for (TopicCoderFilterConfiguration coderConfig: encoderConfigurations) {
+ String topic = coderConfig.getTopic();
+ EventProtocolCoder.manager.removeEncoders
+ (this.getGroupId(), this.getArtifactId(), topic);
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean ownsCoder(Class<? extends Object> coderClass, int modelHash) throws IllegalStateException {
+ if (!ReflectionUtil.isClass
+ (this.policyContainer.getClassLoader(), coderClass.getCanonicalName())) {
+ logger.error(this + coderClass.getCanonicalName() + " cannot be retrieved. ");
+ return false;
+ }
+
+ if (modelHash == this.modelClassLoaderHash) {
+ if (logger.isInfoEnabled())
+ logger.info(coderClass.getCanonicalName() +
+ this + " class loader matches original drools controller rules classloader " +
+ coderClass.getClassLoader());
+ return true;
+ } else {
+ if (logger.isWarnEnabled())
+ logger.warn(this + coderClass.getCanonicalName() + " class loaders don't match " +
+ coderClass.getClassLoader() + " vs " +
+ this.policyContainer.getClassLoader());
+ return false;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean start() {
+
+ if (logger.isInfoEnabled())
+ logger.info("START: " + this);
+
+ synchronized (this) {
+ if (this.alive)
+ return true;
+
+ this.alive = true;
+ }
+
+ return this.policyContainer.start();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean stop() {
+
+ logger.info("STOP: " + this);
+
+ synchronized (this) {
+ if (!this.alive)
+ return true;
+
+ this.alive = false;
+ }
+
+ return this.policyContainer.stop();
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void shutdown() throws IllegalStateException {
+
+ if (logger.isInfoEnabled())
+ logger.info(this + "SHUTDOWN");
+
+ try {
+ this.stop();
+ this.removeCoders();
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, "stop", this.toString());
+ } finally {
+ this.policyContainer.shutdown();
+ }
+
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void halt() throws IllegalStateException {
+ if (logger.isInfoEnabled())
+ logger.info(this + "SHUTDOWN");
+
+ try {
+ this.stop();
+ this.removeCoders();
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, "halt", this.toString());
+ } finally {
+ this.policyContainer.destroy();
+ }
+ }
+
+ /**
+ * removes this drools controllers and encoders and decoders from operation
+ */
+ protected void removeCoders() {
+
+ if (logger.isInfoEnabled())
+ logger.info(this + "REMOVE-CODERS");
+
+ try {
+ this.removeDecoders();
+ } catch (IllegalArgumentException e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, "removeDecoders", this.toString());
+ }
+
+ try {
+ this.removeEncoders();
+ } catch (IllegalArgumentException e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, "removeEncoders", this.toString());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAlive() {
+ return this.alive;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean offer(String topic, String event) {
+
+ if (logger.isInfoEnabled())
+ logger.info("OFFER: " + topic + ":" + event + " INTO " + this);
+
+ if (this.locked)
+ return true;
+
+ if (!this.alive)
+ return true;
+
+ // 0. Check if the policy container has any sessions
+
+ if (this.policyContainer.getPolicySessions().size() <= 0) {
+ // no sessions
+ return true;
+ }
+
+ // 1. Now, check if this topic has a decoder:
+
+ if (!EventProtocolCoder.manager.isDecodingSupported(this.getGroupId(),
+ this.getArtifactId(),
+ topic)) {
+
+ logger.warn("DECODING-UNSUPPORTED: " + ":" + this.getGroupId() +
+ ":" + this.getArtifactId() + ":" + topic + " IN " + this);
+ return true;
+ }
+
+ // 2. Decode
+
+ Object anEvent;
+ try {
+ anEvent = EventProtocolCoder.manager.decode(this.getGroupId(),
+ this.getArtifactId(),
+ topic,
+ event);
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e,
+ "DECODE:"+ this.getGroupId() + ":" +
+ this.getArtifactId() + ":" + topic + ":" + event,
+ this.toString());
+ return true;
+ }
+
+ synchronized(this.recentSourceEvents) {
+ this.recentSourceEvents.add(anEvent);
+ }
+
+ // increment event count for Nagios monitoring
+ PdpJmx.getInstance().updateOccured();
+
+ // Broadcast
+
+ if (logger.isInfoEnabled())
+ logger.info(this + "BROADCAST-INJECT of " + event + " FROM " + topic + " INTO " + this.policyContainer.getName());
+
+ if (!this.policyContainer.insertAll(anEvent))
+ logger.warn(this + "Failed to inject into PolicyContainer " + this.getSessionNames());
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean deliver(TopicSink sink, Object event)
+ throws IllegalArgumentException,
+ IllegalStateException,
+ UnsupportedOperationException {
+
+ if (logger.isInfoEnabled())
+ logger.info(this + "DELIVER: " + event + " FROM " + this + " TO " + sink);
+
+ if (sink == null)
+ throw new IllegalArgumentException
+ (this + " invalid sink");
+
+ if (event == null)
+ throw new IllegalArgumentException
+ (this + " invalid event");
+
+ if (this.locked)
+ throw new IllegalStateException
+ (this + " is locked");
+
+ if (!this.alive)
+ throw new IllegalStateException
+ (this + " is stopped");
+
+ String json =
+ EventProtocolCoder.manager.encode(sink.getTopic(), event, this);
+
+ synchronized(this.recentSinkEvents) {
+ this.recentSinkEvents.add(json);
+ }
+
+ return sink.send(json);
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getVersion() {
+ return this.policyContainer.getVersion();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getArtifactId() {
+ return this.policyContainer.getArtifactId();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getGroupId() {
+ return this.policyContainer.getGroupId();
+ }
+
+ /**
+ * @return the modelClassLoaderHash
+ */
+ public int getModelClassLoaderHash() {
+ return modelClassLoaderHash;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized boolean lock() {
+ logger.info("LOCK: " + this);
+
+ this.locked = true;
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized boolean unlock() {
+ logger.info("UNLOCK: " + this);
+
+ this.locked = false;
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isLocked() {
+ return this.locked;
+ }
+
+ @Override
+ public List<String> getSessionNames() {
+ List<String> sessionNames = new ArrayList<String>();
+ try {
+ for (PolicySession session: this.policyContainer.getPolicySessions()) {
+ sessionNames.add(session.getFullName());
+ }
+ } catch (Exception e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ "Can't retrieve POLICY-CORE sessions: " + e.getMessage(),
+ this.toString());
+ sessionNames.add(e.getMessage());
+ }
+ return sessionNames;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Class<?> fetchModelClass(String className) throws IllegalStateException {
+ Class<?> modelClass =
+ ReflectionUtil.fetchClass(this.policyContainer.getClassLoader(), className);
+ return modelClass;
+ }
+
+ /**
+ * @return the recentSourceEvents
+ */
+ @Override
+ public Object[] getRecentSourceEvents() {
+ synchronized(this.recentSourceEvents) {
+ Object[] events = new Object[recentSourceEvents.size()];
+ return recentSourceEvents.toArray(events);
+ }
+ }
+
+ /**
+ * @return the recentSinkEvents
+ */
+ @Override
+ public String[] getRecentSinkEvents() {
+ synchronized(this.recentSinkEvents) {
+ String[] events = new String[recentSinkEvents.size()];
+ return recentSinkEvents.toArray(events);
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isBrained() {
+ return true;
+ }
+
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("MavenDroolsController [policyContainer=")
+ .append((policyContainer != null) ? policyContainer.getName() : "NULL").append(":")
+ .append(", alive=")
+ .append(alive).append(", locked=").append(locked).append(", decoderConfigurations=")
+ .append(decoderConfigurations).append(", encoderConfigurations=").append(encoderConfigurations)
+ .append(", modelClassLoaderHash=").append(modelClassLoaderHash).append("]");
+ return builder.toString();
+ }
+
+}
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/controller/internal/NullDroolsController.java b/policy-management/src/main/java/org/openecomp/policy/drools/controller/internal/NullDroolsController.java
new file mode 100644
index 00000000..f0c0f474
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/controller/internal/NullDroolsController.java
@@ -0,0 +1,219 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-management
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.policy.drools.controller.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.policy.drools.controller.DroolsController;
+import org.openecomp.policy.drools.event.comm.TopicSink;
+import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration;
+
+/**
+ * no-op Drools Controller
+ */
+public class NullDroolsController implements DroolsController {
+
+ /**
+ * empty cached events
+ */
+ protected static final String[] emptyRecentEvents = new String[0];
+
+ /**
+ * empty session names
+ */
+ protected static final List<String> emptySessionNames = new ArrayList<String>();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean start() throws IllegalStateException {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean stop() throws IllegalStateException {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void shutdown() throws IllegalStateException {
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void halt() throws IllegalStateException {
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAlive() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean lock() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean unlock() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isLocked() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getGroupId() {
+ return NO_GROUP_ID;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getArtifactId() {
+ return NO_ARTIFACT_ID;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getVersion() {
+ return NO_VERSION;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<String> getSessionNames() {
+ return new ArrayList<String>();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean offer(String topic, String event) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean deliver(TopicSink sink, Object event)
+ throws IllegalArgumentException, IllegalStateException, UnsupportedOperationException {
+ throw new IllegalArgumentException(this.getClass().getCanonicalName() + " invoked");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object[] getRecentSourceEvents() {
+ return NullDroolsController.emptyRecentEvents;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String[] getRecentSinkEvents() {
+ return NullDroolsController.emptyRecentEvents;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean ownsCoder(Class<? extends Object> coderClass, int modelHash) throws IllegalStateException {
+ throw new IllegalArgumentException(this.getClass().getCanonicalName() + " invoked");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Class<?> fetchModelClass(String className) throws IllegalArgumentException {
+ throw new IllegalArgumentException(this.getClass().getCanonicalName() + " invoked");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isBrained() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("NullDroolsController []");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void updateToVersion(String newGroupId, String newArtifactId, String newVersion,
+ List<TopicCoderFilterConfiguration> decoderConfigurations,
+ List<TopicCoderFilterConfiguration> encoderConfigurations)
+ throws IllegalArgumentException, LinkageError, Exception {
+ throw new IllegalArgumentException(this.getClass().getCanonicalName() + " invoked");
+ }
+
+}
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/persistence/SystemPersistence.java b/policy-management/src/main/java/org/openecomp/policy/drools/persistence/SystemPersistence.java
new file mode 100644
index 00000000..6bb1185d
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/persistence/SystemPersistence.java
@@ -0,0 +1,255 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-management
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.policy.drools.persistence;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.Properties;
+
+import org.openecomp.policy.common.logging.flexlogger.FlexLogger;
+import org.openecomp.policy.common.logging.flexlogger.Logger;
+import org.openecomp.policy.common.logging.eelf.MessageCodes;
+import org.openecomp.policy.drools.utils.PropertyUtil;
+
+public interface SystemPersistence {
+
+ /**
+ * configuration directory
+ */
+ public static String CONFIG_DIR_NAME = "config";
+
+ /**
+ * policy controllers suffix
+ */
+ public final static String CONTROLLER_SUFFIX_IDENTIFIER = "-controller";
+
+ /**
+ * policy controller properties file suffix
+ */
+ public final static String PROPERTIES_FILE_CONTROLLER_SUFFIX =
+ CONTROLLER_SUFFIX_IDENTIFIER + ".properties";
+
+ /**
+ * policy engine properties file name
+ */
+ public final static String PROPERTIES_FILE_ENGINE = "policy-engine.properties";
+
+
+ /**
+ * backs up a controller configuration.
+ *
+ * @param controllerName the controller name
+ * @return true if the configuration is backed up
+ */
+ public boolean backupController(String controllerName);
+
+ /**
+ * persists controller configuration
+ *
+ * @param controllerName the controller name
+ * @param configuration object containing the configuration
+ * @return true if storage is succesful, false otherwise
+ * @throws IllegalArgumentException if the configuration cannot be handled by the persistence manager
+ */
+ public boolean storeController(String controllerName, Object configuration)
+ throws IllegalArgumentException;
+
+ /**
+ * delete controller configuration
+ *
+ * @param controllerName the controller name
+ * @return true if storage is succesful, false otherwise
+ */
+ public boolean deleteController(String controllerName);
+
+ /**
+ * get controller properties
+ *
+ * @param controllerName controller name
+ * @return properties for this controller
+ * @throws IllegalArgumentException if the controller name does not lead to a properties configuration
+ */
+ public Properties getControllerProperties(String controllerName)
+ throws IllegalArgumentException;
+
+ /**
+ * get properties by name
+ *
+ * @param name
+ * @return properties
+ * @throws IllegalArgumentException if the name does not lead to a properties configuration
+ */
+ public Properties getProperties(String name) throws IllegalArgumentException;
+
+ /**
+ * Persistence Manager. For now it is a file-based properties management,
+ * In the future, it will probably be DB based, so manager implementation
+ * will change.
+ */
+ public static final SystemPersistence manager = new SystemPropertiesPersistence();
+}
+
+/**
+ * Properties based Persistence
+ */
+class SystemPropertiesPersistence implements SystemPersistence {
+
+ /**
+ * logger
+ */
+ private static Logger logger = FlexLogger.getLogger(SystemPropertiesPersistence.class);
+
+ /**
+ * backs up the properties-based controller configuration
+ * @param controllerName the controller name
+ * @return true if the configuration is backed up in disk or back up does not apply, false otherwise.
+ */
+ @Override
+ public boolean backupController(String controllerName) {
+ Path controllerPropertiesPath =
+ Paths.get(CONFIG_DIR_NAME, controllerName + PROPERTIES_FILE_CONTROLLER_SUFFIX);
+
+ if (Files.exists(controllerPropertiesPath)) {
+ try {
+ logger.warn("There is an existing configuration file at " +
+ controllerPropertiesPath.toString() +
+ " with contents: " + Files.readAllBytes(controllerPropertiesPath));
+ Path controllerPropertiesBakPath =
+ Paths.get(CONFIG_DIR_NAME, controllerName +
+ PROPERTIES_FILE_CONTROLLER_SUFFIX + ".bak");
+ Files.copy(controllerPropertiesPath,
+ controllerPropertiesBakPath, StandardCopyOption.REPLACE_EXISTING);
+ } catch (Exception e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, "SystemPersistenceProperties");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * persists properties-based controller configuration and makes a backup if necessary
+ * @param controllerName the controller name
+ * @return true if the properties has been stored in disk, false otherwise
+ */
+ @Override
+ public boolean storeController(String controllerName, Object configuration) {
+ if (!(configuration instanceof Properties)) {
+ throw new IllegalArgumentException("configuration must be of type properties to be handled by this manager");
+ }
+
+ Properties properties = (Properties) configuration;
+
+ Path controllerPropertiesPath =
+ Paths.get(CONFIG_DIR_NAME, controllerName + PROPERTIES_FILE_CONTROLLER_SUFFIX);
+ if (Files.exists(controllerPropertiesPath)) {
+ try {
+ Properties oldProperties = PropertyUtil.getProperties(controllerPropertiesPath.toFile());
+ if (oldProperties.equals(properties)) {
+ logger.info("A properties file with the same contents already exists for controller " +
+ controllerName +
+ ". No action is taken");
+ return true;
+ } else {
+ this.backupController(controllerName);
+ }
+ } catch (Exception e) {
+ logger.info("No existing Properties");
+ // continue
+ }
+ }
+
+ try {
+ File controllerPropertiesFile = controllerPropertiesPath.toFile();
+ FileWriter writer = new FileWriter(controllerPropertiesFile);
+ properties.store(writer, "Machine created Policy Controller Configuration");
+ } catch (Exception e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, "SystemPersistenceProperties");
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * deletes properties-based controller configuration
+ * @param controllerName the controller name
+ * @return true if the properties has been deleted from disk, false otherwise
+ */
+ @Override
+ public boolean deleteController(String controllerName) {
+
+ Path controllerPropertiesPath =
+ Paths.get(CONFIG_DIR_NAME,
+ controllerName + PROPERTIES_FILE_CONTROLLER_SUFFIX);
+
+ if (Files.exists(controllerPropertiesPath)) {
+ try {
+ Path controllerPropertiesBakPath =
+ Paths.get(CONFIG_DIR_NAME, controllerName +
+ PROPERTIES_FILE_CONTROLLER_SUFFIX + ".bak");
+ Files.move(controllerPropertiesPath,
+ controllerPropertiesBakPath,
+ StandardCopyOption.REPLACE_EXISTING);
+ } catch (Exception e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, "SystemPersistenceProperties");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public Properties getControllerProperties(String controllerName) throws IllegalArgumentException {
+ return this.getProperties(controllerName + CONTROLLER_SUFFIX_IDENTIFIER);
+ }
+
+ @Override
+ public Properties getProperties(String name) throws IllegalArgumentException {
+ Path propertiesPath =
+ Paths.get(CONFIG_DIR_NAME, name + ".properties");
+
+ if (!Files.exists(propertiesPath)) {
+ throw new IllegalArgumentException("properties for " + name + " are not persisted.");
+ }
+
+ try {
+ return PropertyUtil.getProperties(propertiesPath.toFile());
+ } catch (Exception e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ name, "SystemPersistenceProperties can't retrieved properties for " +
+ propertiesPath);
+ e.printStackTrace();
+ throw new IllegalArgumentException("can't read properties for " +
+ name + " @ " +
+ propertiesPath);
+ }
+ }
+}
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/protocol/coders/EventProtocolCoder.java b/policy-management/src/main/java/org/openecomp/policy/drools/protocol/coders/EventProtocolCoder.java
new file mode 100644
index 00000000..8f8a4ba7
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/protocol/coders/EventProtocolCoder.java
@@ -0,0 +1,1451 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-management
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.policy.drools.protocol.coders;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.openecomp.policy.common.logging.eelf.MessageCodes;
+import org.openecomp.policy.common.logging.flexlogger.FlexLogger;
+import org.openecomp.policy.common.logging.flexlogger.Logger;
+import org.openecomp.policy.drools.controller.DroolsController;
+import org.openecomp.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters;
+import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
+import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomJacksonCoder;
+import org.openecomp.policy.drools.utils.Pair;
+
+/**
+ * Coder (Encoder/Decoder) of Events.
+ */
+public interface EventProtocolCoder {
+
+ public static class CoderFilters {
+
+ /**
+ * coder class
+ */
+ protected String factClass;
+
+ /**
+ * filters to apply to the selection of the decodedClass;
+ */
+ protected JsonProtocolFilter filter;
+
+ /**
+ * classloader hash
+ */
+ protected int modelClassLoaderHash;
+
+
+ /**
+ * constructor
+ *
+ * @param codedClass coder class
+ * @param filter filters to apply
+ */
+ public CoderFilters(String codedClass, JsonProtocolFilter filter, int modelClassLoaderHash) {
+ this.factClass = codedClass;
+ this.filter = filter;
+ this.modelClassLoaderHash = modelClassLoaderHash;
+ }
+
+ /**
+ * @return the codedClass
+ */
+ public String getCodedClass() {
+ return factClass;
+ }
+
+ /**
+ * @param codedClass the decodedClass to set
+ */
+ public void setCodedClass(String codedClass) {
+ this.factClass = codedClass;
+ }
+
+ /**
+ * @return the filter
+ */
+ public synchronized JsonProtocolFilter getFilter() {
+ return filter;
+ }
+
+ /**
+ * @param filter the filter to set
+ */
+ public synchronized void setFilter(JsonProtocolFilter filter) {
+ this.filter = filter;
+ }
+
+ public int getModelClassLoaderHash() {
+ return modelClassLoaderHash;
+ }
+
+ public void setFromClassLoaderHash(int fromClassLoaderHash) {
+ this.modelClassLoaderHash = fromClassLoaderHash;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("CoderFilters [factClass=").append(factClass).append(", filter=").append(filter)
+ .append(", modelClassLoaderHash=").append(modelClassLoaderHash).append("]");
+ return builder.toString();
+ }
+
+ }
+
+ /**
+ * Adds a Decoder class to decode the protocol over this topic
+ *
+ * @param groupId of the controller
+ * @param artifactId of the controller
+ * @param topic the topic
+ * @param eventClass the event class
+ * @param protocolFilter filters to selectively choose a particular decoder
+ * when there are multiples
+ *
+ * @throw IllegalArgumentException if an invalid parameter is passed
+ */
+ public void addDecoder(String groupId, String artifactId,
+ String topic,
+ String eventClass,
+ JsonProtocolFilter protocolFilter,
+ CustomGsonCoder customGsonCoder,
+ CustomJacksonCoder customJacksonCoder,
+ int modelClassLoaderHash)
+ throws IllegalArgumentException;
+
+ /**
+ * removes all decoders associated with the controller id
+ * @param groupId of the controller
+ * @param artifactId of the controller
+ * @param topic of the controller
+ *
+ * @throws IllegalArgumentException if invalid arguments have been provided
+ */
+ void removeEncoders(String groupId, String artifactId, String topic) throws IllegalArgumentException;
+
+ /**
+ * removes decoders associated with the controller id and topic
+ * @param groupId of the controller
+ * @param artifactId of the controller
+ * @param topic the topic
+ *
+ * @throws IllegalArgumentException if invalid arguments have been provided
+ */
+ public void removeDecoders(String groupId, String artifactId, String topic) throws IllegalArgumentException;
+
+ /**
+ * Given a controller id and a topic, it gives back its filters
+ *
+ * @param groupId of the controller
+ * @param artifactId of the controller
+ * @param topic the topic
+ *
+ * return list of decoders
+ *
+ * @throw IllegalArgumentException if an invalid parameter is passed
+ */
+ public List<CoderFilters> getDecoderFilters(String groupId, String artifactId, String topic)
+ throws IllegalArgumentException;
+
+
+ /**
+ * Given a controller id and a topic, it gives back the decoding configuration
+ *
+ * @param groupId of the controller
+ * @param artifactId of the controller
+ * @param topic the topic
+ *
+ * return decoding toolset
+ *
+ * @throw IllegalArgumentException if an invalid parameter is passed
+ */
+ public ProtocolCoderToolset getDecoders(String groupId, String artifactId, String topic)
+ throws IllegalArgumentException;
+
+ /**
+ * Given a controller id and a topic, it gives back all the decoding configurations
+ *
+ * @param groupId of the controller
+ * @param artifactId of the controller
+ * @param topic the topic
+ *
+ * return decoding toolset
+ *
+ * @throw IllegalArgumentException if an invalid parameter is passed
+ */
+ public List<ProtocolCoderToolset> getDecoders(String groupId, String artifactId)
+ throws IllegalArgumentException;
+
+
+ /**
+ * gets all decoders associated with the group and artifact ids
+ * @param groupId of the controller
+ * @param artifactId of the controller
+ *
+ * @throws IllegalArgumentException if invalid arguments have been provided
+ */
+ public List<CoderFilters> getDecoderFilters(String groupId, String artifactId) throws IllegalArgumentException;
+
+
+ /**
+ * Given a controller id and a topic, it gives back the classes that implements the encoding
+ *
+ * @param groupId of the controller
+ * @param artifactId of the controller
+ * @param topic the topic
+ *
+ * return list of decoders
+ *
+ * @throw IllegalArgumentException if an invalid parameter is passed
+ */
+ public List<CoderFilters> getEncoderFilters(String groupId, String artifactId, String topic)
+ throws IllegalArgumentException;
+
+ /**
+ * gets all encoders associated with the group and artifact ids
+ * @param groupId of the controller
+ * @param artifactId of the controller
+ *
+ * @throws IllegalArgumentException if invalid arguments have been provided
+ */
+ public List<CoderFilters> getEncoderFilters(String groupId, String artifactId) throws IllegalArgumentException;
+
+ /**
+ * Given a controller id, a topic, and a classname, it gives back the classes that implements the decoding
+ *
+ * @param groupId of the controller
+ * @param artifactId of the controller
+ * @param topic the topic
+ * @param classname classname
+ *
+ * return list of decoders
+ *
+ * @throw IllegalArgumentException if an invalid parameter is passed
+ */
+ public CoderFilters getDecoderFilters(String groupId, String artifactId, String topic, String classname)
+ throws IllegalArgumentException;
+
+ /**
+ * is there a decoder supported for the controller id and topic
+ *
+ * @param groupId of the controller
+ * @param artifactId of the controller
+ * @param topic protocol
+ * @return true if supported
+ */
+ public boolean isDecodingSupported(String groupId, String artifactId, String topic);
+
+ /**
+ * Adds a Encoder class to encode the protocol over this topic
+ *
+ * @param groupId of the controller
+ * @param artifactId of the controller
+ * @param topic the topic
+ * @param eventClass the event class
+ * @param protocolFilter filters to selectively choose a particular decoder
+ * when there are multiples
+ *
+ * @throw IllegalArgumentException if an invalid parameter is passed
+ */
+ public void addEncoder(String groupId, String artifactId, String topic,
+ String eventClass,
+ JsonProtocolFilter protocolFilter,
+ CustomGsonCoder customGsonCoder,
+ CustomJacksonCoder customJacksonCoder,
+ int modelClassLoaderHash)
+ throws IllegalArgumentException;
+
+ /**
+ * is there an encoder supported for the controller id and topic
+ *
+ * @param groupId of the controller
+ * @param artifactId of the controller
+ * @param topic protocol
+ * @return true if supported
+ */
+ public boolean isEncodingSupported(String groupId, String artifactId, String topic);
+
+ /**
+ * get encoder based on coordinates and classname
+ *
+ * @param groupId of the controller
+ * @param artifactId of the controller
+ * @param topic protocol
+ * @param json event string
+ * @return
+ * @throws IllegalArgumentException invalid arguments passed in
+ */
+ public CoderFilters getEncoderFilters(String groupId, String artifactId, String topic, String classname)
+ throws IllegalArgumentException;
+
+ /**
+ * get encoder based on topic and encoded class
+ *
+ * @param topic topic
+ * @param encodedClass encoded class
+ * @return
+ * @throws IllegalArgumentException invalid arguments passed in
+ */
+ public List<CoderFilters> getReverseEncoderFilters(String topic, String encodedClass)
+ throws IllegalArgumentException;
+
+ /**
+ * gets the identifier of the creator of the encoder
+ *
+ * @param topic topic
+ * @param encodedClass encoded class
+ * @return a drools controller
+ * @throws IllegalArgumentException invalid arguments passed in
+ */
+ public DroolsController getDroolsController(String topic, Object encodedClass)
+ throws IllegalArgumentException;
+
+ /**
+ * gets the identifier of the creator of the encoder
+ *
+ * @param topic topic
+ * @param encodedClass encoded class
+ * @return list of drools controllers
+ * @throws IllegalArgumentException invalid arguments passed in
+ */
+ public List<DroolsController> getDroolsControllers(String topic, Object encodedClass)
+ throws IllegalArgumentException;
+
+ /**
+ * decode topic's stringified event (json) to corresponding Event Object.
+ *
+ * @param groupId of the controller
+ * @param artifactId of the controller
+ * @param topic protocol
+ * @param json event string
+ * @return
+ * @throws IllegalArgumentException invalid arguments passed in
+ * @throws UnsupportedOperationException if the operation is not supported
+ * @throws IllegalStateException if the system is in an illegal state
+ */
+ public Object decode(String groupId, String artifactId, String topic, String json)
+ throws IllegalArgumentException, UnsupportedOperationException, IllegalStateException;
+
+ /**
+ * encodes topic's stringified event (json) to corresponding Event Object.
+ *
+ * @param groupId of the controller
+ * @param artifactId of the controller
+ * @param topic protocol
+ * @param event Object
+ *
+ * @throws IllegalArgumentException invalid arguments passed in
+ */
+ public String encode(String groupId, String artifactId, String topic, Object event)
+ throws IllegalArgumentException, IllegalStateException, UnsupportedOperationException;
+
+ /**
+ * encodes topic's stringified event (json) to corresponding Event Object.
+ *
+ * @param topic topic
+ * @param event event object
+ *
+ * @throws IllegalArgumentException invalid arguments passed in
+ * @throws UnsupportedOperationException operation cannot be performed
+ */
+ public String encode(String topic, Object event)
+ throws IllegalArgumentException, IllegalStateException, UnsupportedOperationException;
+
+ /**
+ * encodes topic's stringified event (json) to corresponding Event Object.
+ *
+ * @param topic topic
+ * @param event event object
+ * @param droolsController
+ *
+ * @throws IllegalArgumentException invalid arguments passed in
+ * @throws UnsupportedOperationException operation cannot be performed
+ */
+ public String encode(String topic, Object event, DroolsController droolsController)
+ throws IllegalArgumentException, IllegalStateException, UnsupportedOperationException;
+
+ /**
+ * singleton reference to the global event protocol coder
+ */
+ public static EventProtocolCoder manager = new MultiplexorEventProtocolCoder();
+}
+
+/**
+ * Protocol Coder that does its best attempt to decode/encode, selecting the best
+ * class and best fitted json parsing tools.
+ */
+class MultiplexorEventProtocolCoder implements EventProtocolCoder {
+ // get an instance of logger
+ private static Logger logger = FlexLogger.getLogger(MultiplexorEventProtocolCoder.class);
+ /**
+ * Decoders
+ */
+ protected EventProtocolDecoder decoders = new EventProtocolDecoder();
+
+ /**
+ * Encoders
+ */
+ protected EventProtocolEncoder encoders = new EventProtocolEncoder();
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addDecoder(String groupId, String artifactId, String topic,
+ String eventClass,
+ JsonProtocolFilter protocolFilter,
+ CustomGsonCoder customGsonCoder,
+ CustomJacksonCoder customJacksonCoder,
+ int modelClassLoaderHash)
+ throws IllegalArgumentException {
+ logger.info("ADD-DECODER: " + groupId + ":" + artifactId + ":" +
+ topic + ":" + eventClass + ":" +
+ protocolFilter + ":" + customGsonCoder +
+ ":" + customJacksonCoder + ":" + modelClassLoaderHash +
+ " INTO " + this);
+ this.decoders.add(groupId, artifactId, topic, eventClass, protocolFilter,
+ customGsonCoder, customJacksonCoder, modelClassLoaderHash);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addEncoder(String groupId, String artifactId, String topic,
+ String eventClass,
+ JsonProtocolFilter protocolFilter,
+ CustomGsonCoder customGsonCoder,
+ CustomJacksonCoder customJacksonCoder,
+ int modelClassLoaderHash)
+ throws IllegalArgumentException {
+ logger.info("ADD-ENCODER: " + groupId + ":" + artifactId + ":" +
+ topic + ":" + eventClass + ":" +
+ protocolFilter + ":" + customGsonCoder +
+ ":" + customJacksonCoder + ":" + modelClassLoaderHash +
+ " INTO " + this);
+ this.encoders.add(groupId, artifactId, topic, eventClass, protocolFilter,
+ customGsonCoder, customJacksonCoder, modelClassLoaderHash);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void removeDecoders(String groupId, String artifactId, String topic)
+ throws IllegalArgumentException {
+ logger.info("REMOVE-DECODER: " + groupId + ":" + artifactId + ":" +
+ topic + " FROM " + this);
+ this.decoders.remove(groupId, artifactId, topic);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void removeEncoders(String groupId, String artifactId, String topic)
+ throws IllegalArgumentException {
+ logger.info("REMOVE-ENCODER: " + groupId + ":" + artifactId + ":" +
+ topic + " FROM " + this);
+ 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)
+ throws IllegalArgumentException, UnsupportedOperationException, IllegalStateException {
+ logger.info("DECODE: " + groupId + ":" + artifactId + ":" +
+ topic + ":" + json + " WITH " + this);
+ return this.decoders.decode(groupId, artifactId, topic, json);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String encode(String groupId, String artifactId, String topic, Object event)
+ throws IllegalArgumentException, IllegalStateException, UnsupportedOperationException {
+ logger.info("ENCODE: " + groupId + ":" + artifactId + ":" +
+ topic + ":" + event + " WITH " + this);
+ return this.encoders.encode(groupId, artifactId, topic, event);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String encode(String topic, Object event)
+ throws IllegalArgumentException, IllegalStateException, UnsupportedOperationException {
+ logger.info("ENCODE: " + topic + ":" + event + " WITH " + this);
+ return this.encoders.encode(topic, event);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String encode(String topic, Object event, DroolsController droolsController)
+ throws IllegalArgumentException, IllegalStateException, UnsupportedOperationException {
+ logger.info("ENCODE: " + topic + ":" + event + ":" + droolsController + " WITH " + this);
+ return this.encoders.encode(topic, event, droolsController);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<CoderFilters> getDecoderFilters(String groupId, String artifactId, String topic)
+ throws IllegalArgumentException {
+
+ return this.decoders.getFilters(groupId, artifactId, topic);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ProtocolCoderToolset getDecoders(String groupId, String artifactId, String topic)
+ throws IllegalArgumentException {
+
+ Pair<ProtocolCoderToolset,ProtocolCoderToolset> decoderToolsets = this.decoders.getCoders(groupId, artifactId, topic);
+ if (decoderToolsets == null)
+ throw new IllegalArgumentException("Decoders not found for " + groupId + ":" + artifactId + ":" + topic);
+
+ return decoderToolsets.first();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<CoderFilters> getEncoderFilters(String groupId, String artifactId, String topic)
+ throws IllegalArgumentException {
+
+ return this.encoders.getFilters(groupId, artifactId, topic);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public CoderFilters getDecoderFilters(String groupId, String artifactId, String topic, String classname)
+ throws IllegalArgumentException {
+
+ return this.decoders.getFilters(groupId, artifactId, topic, classname);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public CoderFilters getEncoderFilters(String groupId, String artifactId, String topic, String classname)
+ throws IllegalArgumentException {
+
+ return this.encoders.getFilters(groupId, artifactId, topic, classname);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<CoderFilters> getReverseEncoderFilters(String topic, String encodedClass) throws IllegalArgumentException {
+ return this.encoders.getReverseFilters(topic, encodedClass);
+ }
+
+ /**
+ * 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)
+ throws IllegalArgumentException {
+
+ List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> decoderToolsets = this.decoders.getCoders(groupId, artifactId);
+ if (decoderToolsets == null)
+ throw new IllegalArgumentException("Decoders not found for " + groupId + ":" + artifactId);
+
+ List<ProtocolCoderToolset> parser1CoderToolset = new ArrayList<>();
+ for (Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderToolsetPair : decoderToolsets) {
+ parser1CoderToolset.add(coderToolsetPair.first());
+ }
+
+ return parser1CoderToolset;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<CoderFilters> getDecoderFilters(String groupId, String artifactId) throws IllegalArgumentException {
+ return this.decoders.getFilters(groupId, artifactId);
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<CoderFilters> getEncoderFilters(String groupId, String artifactId) throws IllegalArgumentException {
+ return this.encoders.getFilters(groupId, artifactId);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public DroolsController getDroolsController(String topic, Object encodedClass) throws IllegalArgumentException {
+ return this.encoders.getDroolsController(topic, encodedClass);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<DroolsController> getDroolsControllers(String topic, Object encodedClass) throws IllegalArgumentException {
+ return this.encoders.getDroolsControllers(topic, encodedClass);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("MultiplexorEventProtocolCoder [decoders=").append(decoders).append(", encoders=")
+ .append(encoders).append("]");
+ return builder.toString();
+ }
+}
+
+/**
+ * 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 Logger logger = FlexLogger.getLogger(GenericEventProtocolCoder.class);
+
+ /**
+ * Mapping topic:controller-id -> <protocol-decoder-toolset-pair>
+ * where protocol-coder-toolset-pair contains both a jackson-protocol-coder-toolset
+ * and a gson-protocol-coder-toolset. The first value of the pair will the
+ * protocol coder toolset most likely to be successful with the encoding or decoding,
+ * and consequently the second value will be the less likely.
+ */
+ protected final HashMap<String, Pair<ProtocolCoderToolset,ProtocolCoderToolset>> coders =
+ new HashMap<String, Pair<ProtocolCoderToolset,ProtocolCoderToolset>>();
+
+ /**
+ * Mapping topic + classname -> Protocol Set
+ */
+ protected final HashMap<String, List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>>> reverseCoders =
+ new HashMap<String, List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>>>();
+
+ protected boolean multipleToolsetRetries = false;
+
+ GenericEventProtocolCoder(boolean multipleToolsetRetries) {
+ this.multipleToolsetRetries = multipleToolsetRetries;
+ }
+
+ /**
+ * Index a new coder
+ *
+ * @param groupId of the controller
+ * @param artifactId of the controller
+ * @param topic the topic
+ * @param eventClass the event class
+ * @param protocolFilter filters to selectively choose a particular decoder
+ * when there are multiples
+ *
+ * @throw IllegalArgumentException if an invalid parameter is passed
+ */
+ public void add(String groupId, String artifactId,
+ String topic,
+ String eventClass,
+ JsonProtocolFilter protocolFilter,
+ CustomGsonCoder customGsonCoder,
+ CustomJacksonCoder customJacksonCoder,
+ int modelClassLoaderHash)
+ throws IllegalArgumentException {
+ if (groupId == null || groupId.isEmpty()) {
+ throw new IllegalArgumentException("Invalid group id");
+ }
+
+ if (artifactId == null || artifactId.isEmpty()) {
+ throw new IllegalArgumentException("Invalid artifact id");
+ }
+
+ if (topic == null || topic.isEmpty()) {
+ throw new IllegalArgumentException("Invalid Topic");
+ }
+
+ if (eventClass == null) {
+ throw new IllegalArgumentException("Invalid Event Class");
+ }
+
+ String key = this.codersKey(groupId, artifactId, topic);
+ String reverseKey = this.reverseCodersKey(topic, eventClass);
+
+ synchronized(this) {
+ if (coders.containsKey(key)) {
+ Pair<ProtocolCoderToolset, ProtocolCoderToolset> toolsets = coders.get(key);
+
+ if (logger.isInfoEnabled())
+ logger.info("ADDING CODER TO EXISTING: " + toolsets + " for " + key);
+
+ toolsets.first().addCoder(eventClass, protocolFilter, modelClassLoaderHash);
+ toolsets.second().addCoder(eventClass, protocolFilter, modelClassLoaderHash);
+
+ if (!reverseCoders.containsKey(reverseKey)) {
+ if (logger.isInfoEnabled())
+ logger.info("Multiple coder classes case: " + toolsets.first() +
+ " for " + reverseKey + " - " + key);
+
+ List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> reverseMappings =
+ new ArrayList<Pair<ProtocolCoderToolset,ProtocolCoderToolset>>();
+ reverseMappings.add(toolsets);
+ reverseCoders.put(reverseKey, reverseMappings);
+ }
+ return;
+ }
+
+ GsonProtocolCoderToolset gsonCoderTools =
+ new GsonProtocolCoderToolset
+ (topic, key,
+ groupId, artifactId,
+ eventClass, protocolFilter,
+ customGsonCoder,
+ modelClassLoaderHash);
+
+ JacksonProtocolCoderToolset jacksonCoderTools =
+ new JacksonProtocolCoderToolset
+ (topic, key,
+ groupId, artifactId,
+ eventClass, protocolFilter,
+ customJacksonCoder,
+ modelClassLoaderHash);
+
+ // Use Gson as the first priority encoding/decoding toolset, and Jackson
+ // as second. This is because it has been observed that they can diverge
+ // somewhat in the encoding/decoding data types, which can produce json
+ // that may result incompatible with what some network elements are
+ // expecting. As decoding takes place, this element will reconfigure
+ // itself to set the jackson one as the favoured one first, if errors
+ // are detected in the gson encoding
+
+ Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderTools =
+ new Pair<ProtocolCoderToolset,ProtocolCoderToolset>(gsonCoderTools,
+ jacksonCoderTools);
+
+ logger.info("ADDED TOOLSET: " + key + " : " +
+ coderTools + ":" + this);
+
+ 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<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> toolsets =
+ reverseCoders.get(reverseKey);
+ boolean present = false;
+ for (Pair<ProtocolCoderToolset,ProtocolCoderToolset> parserSet: toolsets) {
+ // just doublecheck
+ present = parserSet.first().getControllerId().equals(key);
+ if (present) {
+ /* anomaly */
+ logger.error("UNEXPECTED TOOLSET REVERSE MAPPING FOUND: " + parserSet.first() +
+ " for " + reverseKey + " - " + key);
+ }
+ }
+
+ if (present) {
+ return;
+ } else {
+ logger.info("ADDING TOOLSET REVERSE MAPPING: " + reverseKey + " : " +
+ toolsets + ":" + coderTools + ":" + this);
+ toolsets.add(coderTools);
+ }
+ } else {
+ List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> toolsets =
+ new ArrayList<Pair<ProtocolCoderToolset,ProtocolCoderToolset>>();
+ logger.info("ADDING TOOLSET REVERSE MAPPING: " + reverseKey + " : " +
+ toolsets + ":" + coderTools + ":" + this);
+ toolsets.add(coderTools);
+ reverseCoders.put(reverseKey, toolsets);
+ }
+
+ }
+ }
+
+ /**
+ * produces key for indexing toolset entries
+ *
+ * @param group 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)
+ throws IllegalArgumentException {
+
+ if (groupId == null || groupId.isEmpty()) {
+ throw new IllegalArgumentException("Invalid group id");
+ }
+
+ if (artifactId == null || artifactId.isEmpty()) {
+ throw new IllegalArgumentException("Invalid artifact id");
+ }
+
+ if (topic == null || topic.isEmpty()) {
+ throw new IllegalArgumentException("Invalid Topic");
+ }
+
+ String key = this.codersKey(groupId, artifactId, topic);
+
+ synchronized(this) {
+ if (coders.containsKey(key)) {
+ Pair<ProtocolCoderToolset, ProtocolCoderToolset> p = coders.remove(key);
+ logger.info("REMOVED TOOLSET: " + key + " : " + p + " FROM " +
+ coders + " : " + this);
+
+ for (CoderFilters codeFilter : p.first().getCoders()) {
+ String className = codeFilter.getCodedClass();
+ String reverseKey = this.reverseCodersKey(topic, className);
+ if (this.reverseCoders.containsKey(reverseKey) ) {
+ List<Pair<ProtocolCoderToolset, ProtocolCoderToolset>> toolsets =
+ this.reverseCoders.get(reverseKey);
+ Iterator<Pair<ProtocolCoderToolset, ProtocolCoderToolset>> toolsetsIter =
+ toolsets.iterator();
+ while (toolsetsIter.hasNext()) {
+ Pair<ProtocolCoderToolset, ProtocolCoderToolset> toolset = toolsetsIter.next();
+ if (toolset.first().getControllerId().equals(key)) {
+ logger.info("REMOVED CODER FROM REVERSE MAPPING of TOOLSET: " + reverseKey + " : " + toolset + " FROM " +
+ reverseCoders);
+ toolsetsIter.remove();
+ }
+ }
+
+ if (this.reverseCoders.get(reverseKey).isEmpty()) {
+ logger.info("REMOVE FULL REVERSE MAPPING of TOOLSET: " + reverseKey + " FROM " +
+ reverseCoders);
+ 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");
+ }
+
+ if (artifactId == null || artifactId.isEmpty()) {
+ throw new IllegalArgumentException("Invalid artifact id");
+ }
+
+ if (topic == null || topic.isEmpty())
+ return false;
+
+ 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)
+ throws IllegalArgumentException, UnsupportedOperationException, IllegalStateException {
+
+ if (!isCodingSupported(groupId, artifactId, topic)) {
+ throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic) + " for encoding");
+ }
+
+ String key = this.codersKey(groupId, artifactId, topic);
+ Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderTools = coders.get(key);
+ try {
+ Object event = coderTools.first().decode(json);
+ if (event != null)
+ return event;
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ logger.warn("Can't decode @ " + this);
+ }
+
+ if (multipleToolsetRetries) {
+ // try the less favored toolset
+ try {
+ Object event = coderTools.second().decode(json);
+ if (event != null) {
+ // change the priority of the toolset
+ synchronized(this) {
+ ProtocolCoderToolset first = coderTools.first();
+ ProtocolCoderToolset second = coderTools.second();
+ coderTools.first(second);
+ coderTools.second(first);
+ }
+
+ return event;
+ }
+ } catch (Exception e2) {
+ // TODO Auto-generated catch block
+ e2.printStackTrace();
+ throw new UnsupportedOperationException(e2);
+ }
+ }
+
+ throw new UnsupportedOperationException("Cannot decode neither with gson or jackson");
+ }
+
+ /**
+ * 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)
+ throws IllegalArgumentException, UnsupportedOperationException {
+
+ 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 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)
+ throws IllegalArgumentException, UnsupportedOperationException {
+
+ logger.debug("ENCODE: " + key + ":" + event + this);
+
+ Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderTools = coders.get(key);
+ try {
+ String json = coderTools.first().encode(event);
+ if (json != null && !json.isEmpty())
+ return json;
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, "FIRST-ENCODE-INTERNAL: " +
+ key + ":" + event, this.toString());
+ }
+
+ if (multipleToolsetRetries) {
+ // try the less favored toolset
+ try {
+ String json = coderTools.second().encode(event);
+ if (json != null) {
+ // change the priority of the toolset
+ synchronized(this) {
+ ProtocolCoderToolset first = coderTools.first();
+ ProtocolCoderToolset second = coderTools.second();
+ coderTools.first(second);
+ coderTools.second(first);
+ }
+
+ return json;
+ }
+ } catch (Exception e2) {
+ // TODO Auto-generated catch block
+ logger.error(MessageCodes.EXCEPTION_ERROR, e2, "SECOND-ENCODE-INTERNAL: " +
+ key + ":" + event, this.toString());
+ throw new UnsupportedOperationException(e2);
+ }
+ }
+
+ throw new UnsupportedOperationException("Cannot decode neither with gson or jackson");
+ }
+
+ /**
+ * 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)
+ throws IllegalArgumentException, IllegalArgumentException, UnsupportedOperationException {
+
+ if (encodedClass == null) {
+ throw new IllegalArgumentException("Invalid encoded class");
+ }
+
+ if (topic == null || topic.isEmpty()) {
+ throw new IllegalArgumentException("Invalid topic");
+ }
+
+ logger.info("ENCODE: " + topic + ":" +
+ encodedClass.getClass().getCanonicalName() + ":" +
+ encodedClass);
+
+ List<DroolsController> droolsControllers = droolsCreators(topic, encodedClass);
+ if (droolsControllers.size() > 1) {
+ // unexpected
+ logger.warn("MULTIPLE DROOLS CONTROLLERS FOUND for: " + topic + ":" +
+ encodedClass.getClass().getCanonicalName() + ":" +
+ droolsControllers + " IN " + this);
+ // continue
+ }
+
+ String key = codersKey(droolsControllers.get(0).getGroupId(), droolsControllers.get(0).getArtifactId(), topic);
+ return this.encodeInternal(key, encodedClass);
+ }
+
+ /**
+ * 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)
+ throws IllegalArgumentException, IllegalArgumentException, UnsupportedOperationException {
+
+ if (encodedClass == null) {
+ throw new IllegalArgumentException("Invalid encoded class");
+ }
+
+ if (topic == null || topic.isEmpty()) {
+ throw new IllegalArgumentException("Invalid topic");
+ }
+
+ logger.info("ENCODE: " + topic + ":" +
+ encodedClass.getClass().getCanonicalName() + ":" +
+ encodedClass + ":" + droolsController);
+
+ String key = codersKey(droolsController.getGroupId(), droolsController.getArtifactId(), topic);
+ return this.encodeInternal(key, encodedClass);
+ }
+
+ /**
+ * @param topic
+ * @param encodedClass
+ * @param reverseKey
+ * @return
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ */
+ protected List<DroolsController> droolsCreators(String topic, Object encodedClass)
+ throws IllegalStateException, IllegalArgumentException {
+
+ List<DroolsController> droolsControllers = new ArrayList<DroolsController>();
+
+ String reverseKey = this.reverseCodersKey(topic, encodedClass.getClass().getCanonicalName());
+ if (!this.reverseCoders.containsKey(reverseKey)) {
+ logger.warn("NO MAPPING for REVERSE KEY: " + topic + ":" +
+ encodedClass.getClass().getCanonicalName() + ":" +
+ encodedClass + ":" + reverseKey + " : " + this);
+ return droolsControllers;
+ }
+
+ List<Pair<ProtocolCoderToolset, ProtocolCoderToolset>>
+ toolsets = this.reverseCoders.get(reverseKey);
+
+ // There must be multiple toolset pairs 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()) {
+ logger.warn("ENCODE: " + topic + ":" +
+ encodedClass.getClass().getCanonicalName() + ":" +
+ encodedClass + " ENCODER NOT FOUND");
+ throw new IllegalStateException("No Encoders toolsets available for topic "+ topic +
+ " encoder " + encodedClass.getClass().getCanonicalName());
+ }
+
+ for (Pair<ProtocolCoderToolset, ProtocolCoderToolset> encoderSet : toolsets) {
+ // figure out the right toolset
+ String groupId = encoderSet.first().getGroupId();
+ String artifactId = encoderSet.first().getArtifactId();
+ List<CoderFilters> coders = encoderSet.first().getCoders();
+ for (CoderFilters coder : coders) {
+ if (coder.getCodedClass().equals(encodedClass.getClass().getCanonicalName())) {
+ 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 "+ topic +
+ " : encoder " + encodedClass.getClass().getCanonicalName());
+ }
+ 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)
+ throws IllegalArgumentException {
+
+ if (!isCodingSupported(groupId, artifactId, topic)) {
+ throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic));
+ }
+
+ String key = this.codersKey(groupId, artifactId, topic);
+ Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderTools = coders.get(key);
+ return coderTools.first().getCoders();
+ }
+
+ /**
+ * 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 Pair<ProtocolCoderToolset,ProtocolCoderToolset> getCoders(String groupId, String artifactId, String topic)
+ throws IllegalArgumentException {
+
+ if (!isCodingSupported(groupId, artifactId, topic)) {
+ throw new IllegalArgumentException("Unsupported:" + codersKey(groupId, artifactId, topic));
+ }
+
+ String key = this.codersKey(groupId, artifactId, topic);
+ Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderTools = coders.get(key);
+ return coderTools;
+ }
+
+ /**
+ * 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)
+ throws IllegalArgumentException {
+
+ if (groupId == null || groupId.isEmpty()) {
+ throw new IllegalArgumentException("Invalid group id");
+ }
+
+ if (artifactId == null || artifactId.isEmpty()) {
+ throw new IllegalArgumentException("Invalid artifact id");
+ }
+
+ String key = this.codersKey(groupId, artifactId, "");
+
+ List<CoderFilters> codersFilters = new ArrayList<CoderFilters>();
+ for (Map.Entry<String, Pair<ProtocolCoderToolset,ProtocolCoderToolset>> entry : coders.entrySet()) {
+ if (entry.getKey().startsWith(key)) {
+ codersFilters.addAll(entry.getValue().first().getCoders());
+ }
+ }
+
+ return codersFilters;
+ }
+
+ /**
+ * 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<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> getCoders(String groupId, String artifactId)
+ throws IllegalArgumentException {
+
+ if (groupId == null || groupId.isEmpty()) {
+ throw new IllegalArgumentException("Invalid group id");
+ }
+
+ if (artifactId == null || artifactId.isEmpty()) {
+ throw new IllegalArgumentException("Invalid artifact id");
+ }
+
+ String key = this.codersKey(groupId, artifactId, "");
+
+ List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> coderToolset = new ArrayList<Pair<ProtocolCoderToolset,ProtocolCoderToolset>>();
+ for (Map.Entry<String, Pair<ProtocolCoderToolset,ProtocolCoderToolset>> entry : coders.entrySet()) {
+ if (entry.getKey().startsWith(key)) {
+ coderToolset.add(entry.getValue());
+ }
+ }
+
+ return coderToolset;
+ }
+
+
+ /**
+ * get all filters by maven coordinates, topic, and classname
+ *
+ * @param groupId group id
+ * @param artifactId artifact id
+ * @param topic topic
+ * @param classname
+ * @return list of coders
+ * @throws IllegalArgumentException if invalid input
+ */
+ public CoderFilters getFilters(String groupId, String artifactId, String topic, String classname)
+ throws IllegalArgumentException {
+
+ 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);
+ Pair<ProtocolCoderToolset,ProtocolCoderToolset> coderTools = coders.get(key);
+ return coderTools.first().getCoder(classname);
+ }
+
+ /**
+ * get coded based on class and topic
+ *
+ * @param topic
+ * @param codedClass
+ * @return
+ * @throws IllegalArgumentException
+ */
+ public List<CoderFilters> getReverseFilters(String topic, String codedClass)
+ throws IllegalArgumentException {
+
+ if (topic == null || topic.isEmpty()) {
+ throw new IllegalArgumentException("Unsupported");
+ }
+
+ if (codedClass == null) {
+ throw new IllegalArgumentException("class must be provided");
+ }
+
+ String key = this.reverseCodersKey(topic, codedClass);
+ List<Pair<ProtocolCoderToolset,ProtocolCoderToolset>> toolsets = this.reverseCoders.get(key);
+ if (toolsets == null)
+ throw new IllegalArgumentException("No Coder found for " + key);
+
+
+ List<CoderFilters> coders = new ArrayList<CoderFilters>();
+ for (Pair<ProtocolCoderToolset,ProtocolCoderToolset> toolset: toolsets) {
+ coders.addAll(toolset.first().getCoders());
+ }
+
+ return coders;
+ }
+
+ /**
+ * returns group and artifact id of the creator of the encoder
+ *
+ * @param topic
+ * @param fact
+ * @return
+ * @throws IllegalArgumentException
+ */
+ DroolsController getDroolsController(String topic, Object fact)
+ throws IllegalArgumentException {
+
+ if (topic == null || topic.isEmpty()) {
+ throw new IllegalArgumentException("Unsupported");
+ }
+
+ if (fact == null) {
+ throw new IllegalArgumentException("class must be provided");
+ }
+
+ List<DroolsController> droolsControllers = droolsCreators(topic, fact);
+ if (droolsControllers.size() > 1) {
+ // unexpected
+ logger.warn("MULTIPLE DROOLS CONTROLLERS FOUND for: " + topic + ":" +
+ fact.getClass().getCanonicalName() + ":" +
+ droolsControllers + " IN " + this);
+ // continue
+ }
+ return droolsControllers.get(0);
+ }
+
+ /**
+ * returns group and artifact id of the creator of the encoder
+ *
+ * @param topic
+ * @param fact
+ * @return
+ * @throws IllegalArgumentException
+ */
+ List<DroolsController> getDroolsControllers(String topic, Object fact)
+ throws IllegalArgumentException {
+
+ if (topic == null || topic.isEmpty()) {
+ throw new IllegalArgumentException("Unsupported");
+ }
+
+ if (fact == null) {
+ throw new IllegalArgumentException("class must be provided");
+ }
+
+ List<DroolsController> droolsControllers = droolsCreators(topic, fact);
+ if (droolsControllers.size() > 1) {
+ // unexpected
+ logger.warn("MULTIPLE DROOLS CONTROLLERS FOUND for: " + topic + ":" +
+ fact.getClass().getCanonicalName() + ":" +
+ droolsControllers + " IN " + this);
+ // continue
+ }
+ return droolsControllers;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("GenericEventProtocolCoder [coders=").append(coders.keySet()).append(", reverseCoders=")
+ .append(reverseCoders.keySet()).append("]");
+ return builder.toString();
+ }
+}
+
+class EventProtocolDecoder extends GenericEventProtocolCoder {
+
+ public EventProtocolDecoder(){super(false);}
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("EventProtocolDecoder [toString()=").append(super.toString()).append("]");
+ return builder.toString();
+ }
+
+}
+
+class EventProtocolEncoder extends GenericEventProtocolCoder {
+
+ public EventProtocolEncoder(){super(false);}
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("EventProtocolEncoder [toString()=").append(super.toString()).append("]");
+ return builder.toString();
+ }
+} \ No newline at end of file
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/protocol/coders/JsonProtocolFilter.java b/policy-management/src/main/java/org/openecomp/policy/drools/protocol/coders/JsonProtocolFilter.java
new file mode 100644
index 00000000..a2ce3123
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/protocol/coders/JsonProtocolFilter.java
@@ -0,0 +1,304 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-management
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.policy.drools.protocol.coders;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.openecomp.policy.drools.utils.Pair;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+/**
+ * JSON Protocol Filter. Evaluates an JSON string and evaluates if it
+ * passes its filters.
+ */
+public class JsonProtocolFilter {
+
+ /**
+ * Helper class to collect Filter information
+ */
+ public static class FilterRule {
+ /**
+ * Field name
+ */
+ protected String name;
+
+ /**
+ * Field Value regex
+ */
+ protected String regex;
+
+ /**
+ * Filter Constructor
+ *
+ * @param name field name
+ * @param regex field regex value
+ */
+ public FilterRule(String name, String regex) {
+ this.name = name;
+ this.regex = regex;
+ }
+
+ /**
+ * Default constructor (for serialization only)
+ */
+ public FilterRule() {
+ super();
+ }
+
+ /**
+ * gets name
+ *
+ * @return
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * gets regex
+ *
+ * @return
+ */
+ public String getRegex() {
+ return regex;
+ }
+
+ /**
+ * sets field name
+ * @param name field name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * sets regex name
+ * @param regex
+ */
+ public void setRegex(String regex) {
+ this.regex = regex;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Filter [name=").append(name).append(", regex=").append(regex).append("]");
+ return builder.toString();
+ }
+ }
+
+ /**
+ * all the filters to be applied
+ */
+ protected List<FilterRule> rules = new ArrayList<FilterRule>();
+
+ /**
+ *
+ * @param rawFilters raw filter initialization
+ *
+ * @throws IllegalArgumentException an invalid input has been provided
+ */
+ public static JsonProtocolFilter fromRawFilters(List<Pair<String, String>> rawFilters)
+ throws IllegalArgumentException {
+
+ if (rawFilters == null) {
+ throw new IllegalArgumentException("No raw filters provided");
+ }
+
+ List<FilterRule> filters = new ArrayList<FilterRule>();
+ for (Pair<String, String> filterPair: rawFilters) {
+ if (filterPair.first() == null || filterPair.first().isEmpty()) {
+ // TODO: warn
+ continue;
+ }
+
+ filters.add(new FilterRule(filterPair.first(), filterPair.second()));
+ }
+ return new JsonProtocolFilter(filters);
+ }
+
+ /**
+ * Create a Protocol Filter
+ *
+ * @throws IllegalArgumentException an invalid input has been provided
+ */
+ public JsonProtocolFilter() throws IllegalArgumentException {}
+
+ /**
+ *
+ * @param rawFilters raw filter initialization
+ *
+ * @throws IllegalArgumentException an invalid input has been provided
+ */
+ public JsonProtocolFilter(List<FilterRule> filters) throws IllegalArgumentException {
+ this.rules = filters;
+ }
+
+ /**
+ * are there any filters?
+ *
+ * @return true if there are filters, false otherwise
+ */
+ public boolean isRules() {
+ return !this.rules.isEmpty();
+ }
+
+ /**
+ * accept a JSON string as conformant it if passes all filters
+ *
+ * @param json json is a JSON object
+ * @return true if json string is conformant
+ *
+ * @throws IllegalArgumentException an invalid input has been provided
+ */
+ public synchronized boolean accept(JsonElement json) throws IllegalArgumentException {
+ if (json == null) {
+ throw new IllegalArgumentException("no JSON provided");
+ }
+
+ if (rules.isEmpty()) {
+ return true;
+ }
+
+ try {
+ if (json == null || !json.isJsonObject()) {
+ return false;
+ }
+
+ JsonObject event = json.getAsJsonObject();
+ for (FilterRule filter: rules) {
+ if (filter.regex == null ||
+ filter.regex.isEmpty() ||
+ filter.regex.equals(".*")) {
+
+ // Only check for presence
+ if (!event.has(filter.name)) {
+ return false;
+ }
+ } else {
+ JsonElement field = event.get(filter.name);
+ if (field == null) {
+ return false;
+ }
+
+ String fieldValue = field.getAsString();
+ if (!fieldValue.matches(filter.regex)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * accept a JSON string as conformant it if passes all filters
+ *
+ * @param json json string
+ * @return true if json string is conformant
+ *
+ * @throws IllegalArgumentException an invalid input has been provided
+ */
+ public synchronized boolean accept(String json) throws IllegalArgumentException {
+ if (json == null || json.isEmpty()) {
+ throw new IllegalArgumentException("no JSON provided");
+ }
+
+ if (rules.isEmpty()) {
+ return true;
+ }
+
+ try {
+ JsonElement element = new JsonParser().parse(json);
+ if (element == null || !element.isJsonObject()) {
+ return false;
+ }
+
+ return this.accept(element.getAsJsonObject());
+ } catch (IllegalArgumentException ile) {
+ throw ile;
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public List<FilterRule> getRules() {
+ return rules;
+ }
+
+ public synchronized void setRules(List<FilterRule> rulesFilters) {
+ this.rules = rulesFilters;
+ }
+
+ public synchronized void deleteRules(String name) {
+ for (FilterRule rule : new ArrayList<>(this.rules)) {
+ if (rule.name.equals(name)) {
+ this.rules.remove(rule);
+ }
+ }
+ }
+
+ public List<FilterRule> getRules(String name) {
+ ArrayList<FilterRule> temp = new ArrayList<>();
+ for (FilterRule rule : new ArrayList<>(this.rules)) {
+ if (rule.name.equals(name)) {
+ temp.add(rule);
+ }
+ }
+ return temp;
+ }
+
+ public synchronized void deleteRule(String name, String regex) {
+ for (FilterRule rule : new ArrayList<>(this.rules)) {
+ if (rule.name.equals(name) && rule.regex.equals(regex)) {
+ this.rules.remove(rule);
+ }
+ }
+ }
+
+ public synchronized void addRule(String name, String regex) {
+ for (FilterRule rule : new ArrayList<>(this.rules)) {
+ if (rule.name.equals(name) && rule.regex.equals(regex)) {
+ return;
+ }
+ }
+
+ this.rules.add(new FilterRule(name,regex));
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("JsonProtocolFilter [rules=").append(rules).append("]");
+ return builder.toString();
+ }
+
+}
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/protocol/coders/ProtocolCoderToolset.java b/policy-management/src/main/java/org/openecomp/policy/drools/protocol/coders/ProtocolCoderToolset.java
new file mode 100644
index 00000000..9e079ff5
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/protocol/coders/ProtocolCoderToolset.java
@@ -0,0 +1,668 @@
+package org.openecomp.policy.drools.protocol.coders;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.openecomp.policy.common.logging.eelf.MessageCodes;
+import org.openecomp.policy.common.logging.flexlogger.FlexLogger;
+import org.openecomp.policy.common.logging.flexlogger.Logger;
+import org.openecomp.policy.drools.controller.DroolsController;
+import org.openecomp.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters;
+import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomCoder;
+import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
+import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomJacksonCoder;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+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.JsonParseException;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+
+/**
+ * Protocol Coding/Decoding Toolset
+ */
+public abstract class ProtocolCoderToolset {
+
+ /**
+ * topic
+ */
+ protected final String topic;
+
+ /**
+ * controller id
+ */
+ protected final String controllerId;
+
+ /**
+ * group id
+ */
+ protected final String groupId;
+
+ /**
+ * artifact id
+ */
+ protected final String artifactId;
+
+ /**
+ * Protocols and associated Filters
+ */
+ protected final List<CoderFilters> coders = new ArrayList<CoderFilters>();
+
+ /**
+ * Tree model (instead of class model) generic parsing to be able to inspect elements
+ */
+ protected JsonParser filteringParser = new JsonParser();
+
+ /**
+ * custom coder
+ */
+ protected CustomCoder customCoder;
+
+ /**
+ * Constructor
+ *
+ * @param topic the topic
+ * @param controllerId the controller id
+ * @param codedClass the decoded class
+ * @param filters list of filters that apply to the
+ * selection of this decodedClass in case of multiplicity
+ * @throws IllegalArgumentException if invalid data has been passed in
+ */
+ public ProtocolCoderToolset(String topic,
+ String controllerId,
+ String groupId,
+ String artifactId,
+ String codedClass,
+ JsonProtocolFilter filters,
+ CustomCoder customCoder,
+ int modelClassLoaderHash)
+ throws IllegalArgumentException {
+
+ if (topic == null || controllerId == null ||
+ groupId == null || artifactId == null ||
+ codedClass == null || filters == null ||
+ topic.isEmpty() || controllerId.isEmpty()) {
+ // TODO
+ throw new IllegalArgumentException("Invalid input");
+ }
+
+ this.topic = topic;
+ this.controllerId = controllerId;
+ this.groupId = groupId;
+ this.artifactId = artifactId;
+ this.coders.add(new CoderFilters(codedClass, filters, modelClassLoaderHash));
+ this.customCoder = customCoder;
+ }
+
+ /**
+ * gets the coder + filters associated with this class name
+ *
+ * @param classname class name
+ * @return the decoder filters or null if not found
+ */
+ public CoderFilters getCoder(String classname) {
+ for (CoderFilters decoder: this.coders) {
+ if (decoder.factClass.equals(classname)) {
+ return decoder;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * get all coder filters in use
+ *
+ * @return coder filters
+ */
+ public List<CoderFilters> getCoders() {
+ return this.coders;
+ }
+
+ /**
+ * add coder or replace it exists
+ *
+ * @param eventClass decoder
+ * @param filter filter
+ */
+ public void addCoder(String eventClass, JsonProtocolFilter filter, int modelClassLoaderHash) {
+ synchronized(this) {
+ for (CoderFilters coder: this.coders) {
+ if (coder.factClass.equals(eventClass)) {
+ // this is a better check than checking pointers, just
+ // in case classloader is different and this is just an update
+ coder.factClass = eventClass;
+ coder.filter = filter;
+ coder.modelClassLoaderHash = modelClassLoaderHash;
+ return;
+ }
+ }
+ }
+
+ this.coders.add(new CoderFilters(eventClass, filter, modelClassLoaderHash));
+ }
+
+ /**
+ * remove coder
+ *
+ * @param eventClass decoder
+ * @param filter filter
+ */
+ public void removeCoders(String eventClass) {
+ synchronized(this) {
+ Iterator<CoderFilters> codersIt = this.coders.iterator();
+ while (codersIt.hasNext()) {
+ CoderFilters coder = codersIt.next();
+ if (coder.factClass.equals(eventClass)) {
+ codersIt.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * gets the topic
+ *
+ * @return the topic
+ */
+ public String getTopic() {return topic;}
+
+ /**
+ * gets the controller id
+ *
+ * @return the controller id
+ */
+ public String getControllerId() {return controllerId;}
+
+ /**
+ * @return the groupId
+ */
+ public String getGroupId() {
+ return groupId;
+ }
+
+ /**
+ * @return the artifactId
+ */
+ public String getArtifactId() {
+ return artifactId;
+ }
+
+ /**
+ * @return the customCoder
+ */
+ public CustomCoder getCustomCoder() {
+ return customCoder;
+ }
+
+ /**
+ * @param customCoder the customCoder to set
+ */
+ public void setCustomCoder(CustomCoder customCoder) {
+ this.customCoder = customCoder;
+ }
+
+ /**
+ * performs filtering on a json string
+ *
+ * @param json json string
+ * @return the decoder that passes the filter, otherwise null
+ * @throws UnsupportedOperationException can't filter
+ * @throws IllegalArgumentException invalid input
+ */
+ protected CoderFilters filter(String json)
+ throws UnsupportedOperationException, IllegalArgumentException, IllegalStateException {
+
+
+ // 1. Get list of decoding classes for this controller Id and topic
+ // 2. If there are no classes, return error
+ // 3. Otherwise, from the available classes for decoding, pick the first one that
+ // passes the filters
+
+ // Don't parse if it is not necessary
+
+ if (this.coders.isEmpty()) {
+ // TODO this is an error
+ throw new IllegalStateException("No coders available");
+ }
+
+ if (this.coders.size() == 1) {
+ JsonProtocolFilter filter = this.coders.get(0).getFilter();
+ if (!filter.isRules()) {
+ return this.coders.get(0);
+ }
+ }
+
+ JsonElement event;
+ try {
+ event = this.filteringParser.parse(json);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ throw new UnsupportedOperationException(e);
+ }
+
+ for (CoderFilters decoder: this.coders) {
+ try {
+ boolean accepted = decoder.getFilter().accept(event);
+ if (accepted) {
+ return decoder;
+ }
+ } catch (Exception e) {
+ // TODO: handle exception
+ e.printStackTrace();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Decode json into a POJO object
+ * @param json json string
+ *
+ * @return a POJO object for the json string
+ * @throws IllegalArgumentException if an invalid parameter has been received
+ * @throws UnsupportedOperationException if parsing into POJO is not possible
+ */
+ public abstract Object decode(String json)
+ throws IllegalArgumentException, UnsupportedOperationException, IllegalStateException;
+
+ /**
+ * Encodes a POJO object into a JSON String
+ *
+ * @param event JSON POJO event to be converted to String
+ * @return JSON string version of POJO object
+ * @throws IllegalArgumentException if an invalid parameter has been received
+ * @throws UnsupportedOperationException if parsing into POJO is not possible
+ */
+ public abstract String encode(Object event)
+ throws IllegalArgumentException, UnsupportedOperationException;
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("ProtocolCoderToolset [topic=").append(topic).append(", controllerId=").append(controllerId)
+ .append(", groupId=").append(groupId).append(", artifactId=").append(artifactId).append(", coders=")
+ .append(coders).append(", filteringParser=").append(filteringParser).append(", customCoder=")
+ .append(customCoder).append("]");
+ return builder.toString();
+ }
+}
+
+/**
+ * Tools used for encoding/decoding using Jackson
+ */
+class JacksonProtocolCoderToolset extends ProtocolCoderToolset {
+ private static Logger logger = FlexLogger.getLogger(JacksonProtocolCoderToolset.class);
+ /**
+ * decoder
+ */
+ @JsonIgnore
+ protected final ObjectMapper decoder = new ObjectMapper();
+
+ /**
+ * encoder
+ */
+ @JsonIgnore
+ protected final ObjectMapper encoder = new ObjectMapper();
+
+ /**
+ * Toolset to encode/decode tools associated with a topic
+ *
+ * @param topic topic
+ * @param decodedClass decoded class of an event
+ * @param filter
+ */
+ public JacksonProtocolCoderToolset(String topic, String controllerId,
+ String groupId, String artifactId,
+ String decodedClass,
+ JsonProtocolFilter filter,
+ CustomJacksonCoder customJacksonCoder,
+ int modelClassLoaderHash) {
+ super(topic, controllerId, groupId, artifactId, decodedClass, filter, customJacksonCoder, modelClassLoaderHash);
+ decoder.registerModule(new JavaTimeModule());
+ decoder.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
+ false);
+ }
+
+ /**
+ * gets the Jackson decoder
+ *
+ * @return the Jackson decoder
+ */
+ @JsonIgnore
+ protected ObjectMapper getDecoder() {return decoder;}
+
+ /**
+ * gets the Jackson encoder
+ *
+ * @return the Jackson encoder
+ */
+ @JsonIgnore
+ protected ObjectMapper getEncoder() {return encoder;}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object decode(String json)
+ throws IllegalArgumentException, UnsupportedOperationException, IllegalStateException {
+
+ // 0. Use custom coder if available
+
+ if (this.customCoder != null) {
+ throw new UnsupportedOperationException
+ ("Jackon Custom Decoder is not supported at this time");
+ }
+
+ DroolsController droolsController =
+ DroolsController.factory.get(groupId, artifactId, "");
+ if (droolsController == null) {
+ String error = "NO-DROOLS-CONTROLLER for: " + json + " IN " + this;
+ logger.warn(error);
+ throw new IllegalStateException(error);
+ }
+
+ CoderFilters decoderFilter = filter(json);
+ if (decoderFilter == null) {
+ String error = "NO-DECODER for: " + json + " IN " + this;
+ logger.warn(error);
+ throw new UnsupportedOperationException(error);
+ }
+
+ Class<?> decoderClass;
+ try {
+ decoderClass =
+ droolsController.fetchModelClass(decoderFilter.getCodedClass());
+ if (decoderClass == null) {
+ String error = "DECODE-ERROR FETCHING MODEL CLASS: " + ":" + json + ":" + this;
+ logger.error(error);
+ throw new IllegalStateException(error);
+ }
+ } catch (Exception e) {
+ String error = "DECODE-ERROR FETCHING MODEL CLASS: "+ e.getMessage() + ":" + json + ":" + this;
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e, error);
+ throw new UnsupportedOperationException(error, e);
+ }
+
+
+ try {
+ Object fact = this.decoder.readValue(json, decoderClass);
+ return fact;
+ } catch (Exception e) {
+ String error = "DECODE-ERROR FROM PDP-D FRAMEWORK: "+ json + ":" + e.getMessage() + ":" + this;
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
+ throw new UnsupportedOperationException(error, e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String encode(Object event)
+ throws IllegalArgumentException, UnsupportedOperationException {
+
+ // 0. Use custom coder if available
+
+ if (this.customCoder != null) {
+ throw new UnsupportedOperationException
+ ("Jackon Custom Encoder is not supported at this time");
+ }
+
+ try {
+ String encodedEvent = this.encoder.writeValueAsString(event);
+ return encodedEvent;
+ } catch (JsonProcessingException e) {
+ String error = "ENCODE-ERROR: "+ event + " IN " + this;
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
+ throw new UnsupportedOperationException(error, e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("JacksonProtocolCoderToolset [toString()=").append(super.toString()).append("]");
+ return builder.toString();
+ }
+
+}
+
+/**
+ * Tools used for encoding/decoding using Jackson
+ */
+class GsonProtocolCoderToolset extends ProtocolCoderToolset {
+
+ private static Logger logger = FlexLogger.getLogger(GsonProtocolCoderToolset.class);
+ /**
+ * Formatter for JSON encoding/decoding
+ */
+ @JsonIgnore
+ public static DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSxxx");
+
+ @JsonIgnore
+ public static DateTimeFormatter zuluFormat = DateTimeFormatter.ISO_INSTANT;
+
+ /**
+ * Adapter for ZonedDateTime
+ */
+
+ public static class GsonUTCAdapter implements JsonSerializer<ZonedDateTime>, JsonDeserializer<ZonedDateTime> {
+
+ public ZonedDateTime deserialize(JsonElement element, Type type, JsonDeserializationContext context)
+ throws JsonParseException {
+ try {
+ return ZonedDateTime.parse(element.getAsString(), format);
+ } catch (Exception e) {
+ System.err.println(e);
+ }
+ return null;
+ }
+
+ 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)
+ throws JsonParseException {
+ return Instant.ofEpochMilli(json.getAsLong());
+ }
+
+ @Override
+ public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) {
+ return new JsonPrimitive(src.toEpochMilli());
+ }
+
+ }
+
+
+ /**
+ * decoder
+ */
+ @JsonIgnore
+ protected final Gson decoder = new GsonBuilder().disableHtmlEscaping().
+ registerTypeAdapter(ZonedDateTime.class, new GsonUTCAdapter()).
+ create();
+
+ /**
+ * encoder
+ */
+ @JsonIgnore
+ protected final Gson encoder = new GsonBuilder().disableHtmlEscaping().
+ registerTypeAdapter(ZonedDateTime.class, new GsonUTCAdapter()).
+ create();
+
+ /**
+ * Toolset to encode/decode tools associated with a topic
+ *
+ * @param topic topic
+ * @param decodedClass decoded class of an event
+ * @param filter
+ */
+ public GsonProtocolCoderToolset(String topic, String controllerId,
+ String groupId, String artifactId,
+ String decodedClass,
+ JsonProtocolFilter filter,
+ CustomGsonCoder customGsonCoder,
+ int modelClassLoaderHash) {
+ super(topic, controllerId, groupId, artifactId, decodedClass, filter, customGsonCoder, modelClassLoaderHash);
+ }
+
+ /**
+ * gets the Gson decoder
+ *
+ * @return the Gson decoder
+ */
+ @JsonIgnore
+ protected Gson getDecoder() {return decoder;}
+
+ /**
+ * gets the Gson encoder
+ *
+ * @return the Gson encoder
+ */
+ @JsonIgnore
+ protected Gson getEncoder() {return encoder;}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object decode(String json)
+ throws IllegalArgumentException, UnsupportedOperationException, IllegalStateException {
+
+ DroolsController droolsController =
+ DroolsController.factory.get(groupId, artifactId, "");
+ if (droolsController == null) {
+ String error = "NO-DROOLS-CONTROLLER for: " + json + " IN " + this;
+ logger.warn(error);
+ throw new IllegalStateException(error);
+ }
+
+ CoderFilters decoderFilter = filter(json);
+ if (decoderFilter == null) {
+ String error = "NO-DECODER for: " + json + " IN " + this;
+ logger.warn(error);
+ throw new UnsupportedOperationException(error);
+ }
+
+ Class<?> decoderClass;
+ try {
+ decoderClass =
+ droolsController.fetchModelClass(decoderFilter.getCodedClass());
+ if (decoderClass == null) {
+ String error = "DECODE-ERROR FETCHING MODEL CLASS: " + ":" + json + ":" + this;
+ logger.error(error);
+ throw new IllegalStateException(error);
+ }
+ } catch (Exception e) {
+ String error = "DECODE-ERROR FETCHING MODEL CLASS: "+ e.getMessage() + ":" + json + ":" + this;
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e, error);
+ throw new UnsupportedOperationException(error, e);
+ }
+
+ if (this.customCoder != null) {
+ try {
+ Class<?> gsonClassContainer =
+ droolsController.fetchModelClass(this.customCoder.getClassContainer());
+ Field gsonField = gsonClassContainer.getField(this.customCoder.staticCoderField);
+ Object gsonObject = gsonField.get(null);
+ Method fromJsonMethod = gsonObject.getClass().
+ getDeclaredMethod
+ ("fromJson", new Class[]{String.class, Class.class});
+ Object fact = fromJsonMethod.invoke(gsonObject, json, decoderClass);
+ return fact;
+ } catch (NoSuchFieldException | SecurityException | IllegalAccessException |
+ NoSuchMethodException | InvocationTargetException e) {
+ String error = "DECODE-ERROR-FROM-CUSTOM-CODER: " + e.getMessage() + ":" + json + ":" + this;
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
+ throw new UnsupportedOperationException(error, e);
+ }
+ } else {
+ try {
+ Object fact = this.decoder.fromJson(json, decoderClass);
+ return fact;
+ } catch (Exception e) {
+ String error = "DECODE-ERROR FROM PDP-D FRAMEWORK: "+ json + ":" + e.getMessage() + ":" + this;
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
+ throw new UnsupportedOperationException(error, e);
+ }
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String encode(Object event)
+ throws IllegalArgumentException, UnsupportedOperationException {
+
+ DroolsController droolsController =
+ DroolsController.factory.get(groupId, artifactId, "");
+ if (droolsController == null) {
+ String error = "NO-DROOLS-CONTROLLER for: " + event + " IN " + this;
+ logger.warn(error);
+ throw new IllegalStateException(error);
+ }
+
+ if (this.customCoder != null) {
+ try {
+ Class<?> gsonClassContainer =
+ droolsController.fetchModelClass(this.customCoder.getClassContainer());
+ Field gsonField = gsonClassContainer.getField(this.customCoder.staticCoderField);
+ Object gsonObject = gsonField.get(null);
+ Method toJsonMethod = gsonObject.getClass().
+ getDeclaredMethod
+ ("toJson", new Class[]{Object.class});
+ String encodedJson = (String) toJsonMethod.invoke(gsonObject, event);
+ return encodedJson;
+ } catch (NoSuchFieldException | SecurityException | IllegalAccessException |
+ NoSuchMethodException | InvocationTargetException e) {
+ String error = "DECODE-ERROR-FROM-CUSTOM-CODER: " + e.getMessage() + ":" + event + ":" + this;
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
+ throw new UnsupportedOperationException(error, e);
+ }
+ } else {
+ try {
+ String encodedEvent = this.encoder.toJson(event);
+ return encodedEvent;
+ } catch (Exception e) {
+ String error = "ENCODE-ERROR: "+ event + " IN " + this;
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
+ throw new UnsupportedOperationException(error, e);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("GsonProtocolCoderToolset [toString()=").append(super.toString()).append("]");
+ return builder.toString();
+ }
+}
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/protocol/coders/TopicCoderFilterConfiguration.java b/policy-management/src/main/java/org/openecomp/policy/drools/protocol/coders/TopicCoderFilterConfiguration.java
new file mode 100644
index 00000000..3c112573
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/protocol/coders/TopicCoderFilterConfiguration.java
@@ -0,0 +1,309 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-management
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.policy.drools.protocol.coders;
+
+import java.util.List;
+
+public class TopicCoderFilterConfiguration {
+
+ /**
+ * Custom coder, contains class and static field to access parser that the controller
+ * desires to use instead of the framework provided parser
+ */
+ public static abstract class CustomCoder {
+ protected String className;
+ protected String staticCoderField;
+
+ /**
+ * create custom coder from raw string in the following format
+ * (typically embedded in a property file):
+ *
+ * Note this is to support decoding/encoding of partial structures that are
+ * only known by the model.
+ *
+ * @param rawCustomCoder with format: <class-containing-custom-coder>,<static-coder-field>
+ */
+ public CustomCoder(String rawCustomCoder) throws IllegalArgumentException {
+ if (rawCustomCoder != null && !rawCustomCoder.isEmpty()) {
+
+ this.className = rawCustomCoder.substring(0,rawCustomCoder.indexOf(","));
+ if (this.className == null || this.className.isEmpty()) {
+ throw new IllegalArgumentException("No classname to create CustomCoder cannot be created");
+ }
+
+ this.staticCoderField = rawCustomCoder.substring(rawCustomCoder.indexOf(",")+1);
+ if (this.staticCoderField == null || this.staticCoderField.isEmpty()) {
+ throw new IllegalArgumentException
+ ("No staticCoderField to create CustomCoder cannot be created for class " +
+ className);
+ }
+
+ }
+ }
+ /**
+ * @param classContainer
+ * @param staticCoderField
+ */
+ public CustomCoder(String className, String staticCoderField) throws IllegalArgumentException {
+ if (className == null || className.isEmpty()) {
+ throw new IllegalArgumentException("No classname to create CustomCoder cannot be created");
+ }
+
+ if (staticCoderField == null || staticCoderField.isEmpty()) {
+ throw new IllegalArgumentException
+ ("No staticCoderField to create CustomCoder cannot be created for class " +
+ className);
+ }
+
+ this.className = className;
+ this.staticCoderField = staticCoderField;
+ }
+
+ /**
+ * @return the className
+ */
+ public String getClassContainer() {
+ return className;
+ }
+
+ /**
+ * @param className the className to set
+ */
+ public void setClassContainer(String className) {
+ this.className = className;
+ }
+
+ /**
+ * @return the staticCoderField
+ */
+ public String getStaticCoderField() {
+ return staticCoderField;
+ }
+
+ /**
+ * @param staticCoderField the staticGson to set
+ */
+ public void setStaticCoderField(String staticCoderField) {
+ this.staticCoderField = staticCoderField;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("CustomCoder [className=").append(className).append(", staticCoderField=")
+ .append(staticCoderField).append("]");
+ return builder.toString();
+ }
+ }
+
+ public static class CustomGsonCoder extends CustomCoder {
+
+ public CustomGsonCoder(String className, String staticCoderField) {
+ super(className, staticCoderField);
+ }
+
+ public CustomGsonCoder(String customGson) throws IllegalArgumentException {
+ super(customGson);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("CustomGsonCoder [toString()=").append(super.toString()).append("]");
+ return builder.toString();
+ }
+
+ }
+
+ public static class CustomJacksonCoder extends CustomCoder {
+
+ public CustomJacksonCoder(String className, String staticCoderField) {
+ super(className, staticCoderField);
+ }
+
+ public CustomJacksonCoder(String customJackson) throws IllegalArgumentException {
+ super(customJackson);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("CustomJacksonCoder [toString()=").append(super.toString()).append("]");
+ return builder.toString();
+ }
+
+ }
+
+ /**
+ * Coder/Decoder class and Filter container. The decoder class is potential,
+ * in order to be operational needs to be fetched from an available
+ * class loader.
+ *
+ */
+ public static class PotentialCoderFilter {
+
+ /**
+ * decoder class (pending from being able to be fetched and found
+ * in some class loader)
+ */
+ protected String codedClass;
+
+ /**
+ * filters to apply to the selection of the decodedClass;
+ */
+ protected JsonProtocolFilter filter;
+
+ /**
+ * constructor
+ *
+ * @param codedClass decoder class
+ * @param filter filters to apply
+ */
+ public PotentialCoderFilter(String codedClass, JsonProtocolFilter filter) {
+ this.codedClass = codedClass;
+ this.filter = filter;
+ }
+
+ /**
+ * @return the decodedClass
+ */
+ public String getCodedClass() {
+ return codedClass;
+ }
+
+ /**
+ * @param decodedClass the decodedClass to set
+ */
+ public void setCodedClass(String decodedClass) {
+ this.codedClass = decodedClass;
+ }
+
+ /**
+ * @return the filter
+ */
+ public JsonProtocolFilter getFilter() {
+ return filter;
+ }
+
+ /**
+ * @param filter the filter to set
+ */
+ public void setFilter(JsonProtocolFilter filter) {
+ this.filter = filter;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("PotentialCoderFilter [codedClass=").append(codedClass).append(", filter=").append(filter)
+ .append("]");
+ return builder.toString();
+ }
+ }
+
+ /**
+ * the source topic
+ */
+ protected final String topic;
+
+ /**
+ * List of decoder -> filters
+ */
+ protected final List<PotentialCoderFilter> coderFilters;
+
+ /**
+ * custom gson coder that this controller prefers to use instead of the framework ones
+ */
+ protected CustomGsonCoder customGsonCoder;
+
+ /**
+ * custom jackson coder that this controller prefers to use instead of the framework ones
+ */
+ protected CustomJacksonCoder customJacksonCoder;
+
+ /**
+ * Constructor
+ *
+ * @param decoderFilters list of decoders and associated filters
+ * @param topic the topic
+ */
+ public TopicCoderFilterConfiguration(String topic, List<PotentialCoderFilter> decoderFilters,
+ CustomGsonCoder customGsonCoder,
+ CustomJacksonCoder customJacksonCoder) {
+ this.coderFilters = decoderFilters;
+ this.topic = topic;
+ this.customGsonCoder = customGsonCoder;
+ this.customJacksonCoder = customJacksonCoder;
+ }
+
+ /**
+ * @return the topic
+ */
+ public String getTopic() {
+ return topic;
+ }
+
+ /**
+ * @return the decoderFilters
+ */
+ public List<PotentialCoderFilter> getCoderFilters() {
+ return coderFilters;
+ }
+
+ /**
+ * @return the customGsonCoder
+ */
+ public CustomGsonCoder getCustomGsonCoder() {
+ return customGsonCoder;
+ }
+
+ /**
+ * @param customGsonCoder the customGsonCoder to set
+ */
+ public void setCustomGsonCoder(CustomGsonCoder customGsonCoder) {
+ this.customGsonCoder = customGsonCoder;
+ }
+
+ /**
+ * @return the customJacksonCoder
+ */
+ public CustomJacksonCoder getCustomJacksonCoder() {
+ return customJacksonCoder;
+ }
+
+ /**
+ * @param customJacksonCoder the customJacksonCoder to set
+ */
+ public void setCustomJacksonCoder(CustomJacksonCoder customJacksonCoder) {
+ this.customJacksonCoder = customJacksonCoder;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("TopicCoderFilterConfiguration [topic=").append(topic).append(", coderFilters=")
+ .append(coderFilters).append(", customGsonCoder=").append(customGsonCoder)
+ .append(", customJacksonCoder=").append(customJacksonCoder).append("]");
+ return builder.toString();
+ }
+
+
+}
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/protocol/configuration/ControllerConfiguration.java b/policy-management/src/main/java/org/openecomp/policy/drools/protocol/configuration/ControllerConfiguration.java
new file mode 100644
index 00000000..98af02ee
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/protocol/configuration/ControllerConfiguration.java
@@ -0,0 +1,280 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-management
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.policy.drools.protocol.configuration;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+
+/**
+ * Drools Related Information
+ *
+ */
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class ControllerConfiguration {
+
+ public static final String CONFIG_CONTROLLER_OPERATION_CREATE = "create";
+ public static final String CONFIG_CONTROLLER_OPERATION_UPDATE = "update";
+ public static final String CONFIG_CONTROLLER_OPERATION_LOCK = "lock";
+ public static final String CONFIG_CONTROLLER_OPERATION_UNLOCK = "unlock";
+
+ /**
+ *
+ * (Required)
+ *
+ */
+ @JsonProperty("name")
+ private String name;
+ /**
+ * Set of operations that can be applied to a controller: create, lock
+ * (Required)
+ *
+ */
+ @JsonProperty("operation")
+ private String operation;
+ /**
+ * Maven Related Information
+ *
+ */
+ @JsonProperty("drools")
+ private DroolsConfiguration drools;
+ @JsonIgnore
+ private Map<String, Object> additionalProperties = new HashMap<String, Object>();
+ protected final static Object NOT_FOUND_VALUE = new Object();
+
+ /**
+ * No args constructor for use in serialization
+ *
+ */
+ public ControllerConfiguration() {
+ }
+
+ /**
+ *
+ * @param name
+ * @param drools
+ * @param operation
+ */
+ public ControllerConfiguration(String name, String operation, DroolsConfiguration drools) {
+ this.name = name;
+ this.operation = operation;
+ this.drools = drools;
+ }
+
+ /**
+ *
+ * (Required)
+ *
+ * @return
+ * The name
+ */
+ @JsonProperty("name")
+ public String getName() {
+ return name;
+ }
+
+ /**
+ *
+ * (Required)
+ *
+ * @param name
+ * The name
+ */
+ @JsonProperty("name")
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public ControllerConfiguration withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Set of operations that can be applied to a controller: create, lock
+ * (Required)
+ *
+ * @return
+ * The operation
+ */
+ @JsonProperty("operation")
+ public String getOperation() {
+ return operation;
+ }
+
+ /**
+ * Set of operations that can be applied to a controller: create, lock
+ * (Required)
+ *
+ * @param operation
+ * The operation
+ */
+ @JsonProperty("operation")
+ public void setOperation(String operation) {
+ this.operation = operation;
+ }
+
+ public ControllerConfiguration withOperation(String operation) {
+ this.operation = operation;
+ return this;
+ }
+
+ /**
+ * Maven Related Information
+ *
+ * @return
+ * The drools
+ */
+ @JsonProperty("drools")
+ public DroolsConfiguration getDrools() {
+ return drools;
+ }
+
+ /**
+ * Maven Related Information
+ *
+ * @param drools
+ * The drools
+ */
+ @JsonProperty("drools")
+ public void setDrools(DroolsConfiguration drools) {
+ this.drools = drools;
+ }
+
+ public ControllerConfiguration withDrools(DroolsConfiguration drools) {
+ this.drools = drools;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this);
+ }
+
+ @JsonAnyGetter
+ public Map<String, Object> getAdditionalProperties() {
+ return this.additionalProperties;
+ }
+
+ @JsonAnySetter
+ public void setAdditionalProperty(String name, Object value) {
+ this.additionalProperties.put(name, value);
+ }
+
+ public ControllerConfiguration withAdditionalProperty(String name, Object value) {
+ this.additionalProperties.put(name, value);
+ return this;
+ }
+
+ protected boolean declaredProperty(String name, Object value) {
+ switch (name) {
+ case "name":
+ if (value instanceof String) {
+ setName(((String) value));
+ } else {
+ throw new IllegalArgumentException(("property \"name\" is of type \"java.lang.String\", but got "+ value.getClass().toString()));
+ }
+ return true;
+ case "operation":
+ if (value instanceof String) {
+ setOperation(((String) value));
+ } else {
+ throw new IllegalArgumentException(("property \"operation\" is of type \"java.lang.String\", but got "+ value.getClass().toString()));
+ }
+ return true;
+ case "drools":
+ if (value instanceof DroolsConfiguration) {
+ setDrools(((DroolsConfiguration) value));
+ } else {
+ throw new IllegalArgumentException(("property \"drools\" is of type \"org.openecomp.policy.drools.protocol.configuration.Drools\", but got "+ value.getClass().toString()));
+ }
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected Object declaredPropertyOrNotFound(String name, Object notFoundValue) {
+ switch (name) {
+ case "name":
+ return getName();
+ case "operation":
+ return getOperation();
+ case "drools":
+ return getDrools();
+ default:
+ return notFoundValue;
+ }
+ }
+
+ @SuppressWarnings({
+ "unchecked"
+ })
+ public<T >T get(String name) {
+ Object value = declaredPropertyOrNotFound(name, ControllerConfiguration.NOT_FOUND_VALUE);
+ if (ControllerConfiguration.NOT_FOUND_VALUE!= value) {
+ return ((T) value);
+ } else {
+ return ((T) getAdditionalProperties().get(name));
+ }
+ }
+
+ public void set(String name, Object value) {
+ if (!declaredProperty(name, value)) {
+ getAdditionalProperties().put(name, ((Object) value));
+ }
+ }
+
+ public ControllerConfiguration with(String name, Object value) {
+ if (!declaredProperty(name, value)) {
+ getAdditionalProperties().put(name, ((Object) value));
+ }
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder().append(name).append(operation).append(drools).append(additionalProperties).toHashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if ((other instanceof ControllerConfiguration) == false) {
+ return false;
+ }
+ ControllerConfiguration rhs = ((ControllerConfiguration) other);
+ return new EqualsBuilder().append(name, rhs.name).append(operation, rhs.operation).append(drools, rhs.drools).append(additionalProperties, rhs.additionalProperties).isEquals();
+ }
+
+}
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/protocol/configuration/DroolsConfiguration.java b/policy-management/src/main/java/org/openecomp/policy/drools/protocol/configuration/DroolsConfiguration.java
new file mode 100644
index 00000000..87cf2348
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/protocol/configuration/DroolsConfiguration.java
@@ -0,0 +1,278 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-management
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.policy.drools.protocol.configuration;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+
+/**
+ * Maven Related Information
+ *
+ */
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class DroolsConfiguration {
+
+ /**
+ * Maven Artifact ID
+ * (Required)
+ *
+ */
+ @JsonProperty("artifactId")
+ private String artifactId;
+ /**
+ * Maven Group ID
+ * (Required)
+ *
+ */
+ @JsonProperty("groupId")
+ private String groupId;
+ /**
+ * Maven Version
+ * (Required)
+ *
+ */
+ @JsonProperty("version")
+ private String version;
+ @JsonIgnore
+ private Map<String, Object> additionalProperties = new HashMap<String, Object>();
+ protected final static Object NOT_FOUND_VALUE = new Object();
+
+ /**
+ * No args constructor for use in serialization
+ *
+ */
+ public DroolsConfiguration() {
+ }
+
+ /**
+ *
+ * @param groupId
+ * @param artifactId
+ * @param version
+ */
+ public DroolsConfiguration(String artifactId, String groupId, String version) {
+ this.artifactId = artifactId;
+ this.groupId = groupId;
+ this.version = version;
+ }
+
+ /**
+ * Maven Artifact ID
+ * (Required)
+ *
+ * @return
+ * The artifactId
+ */
+ @JsonProperty("artifactId")
+ public String getArtifactId() {
+ return artifactId;
+ }
+
+ /**
+ * Maven Artifact ID
+ * (Required)
+ *
+ * @param artifactId
+ * The artifactId
+ */
+ @JsonProperty("artifactId")
+ public void setArtifactId(String artifactId) {
+ this.artifactId = artifactId;
+ }
+
+ public DroolsConfiguration withArtifactId(String artifactId) {
+ this.artifactId = artifactId;
+ return this;
+ }
+
+ /**
+ * Maven Group ID
+ * (Required)
+ *
+ * @return
+ * The groupId
+ */
+ @JsonProperty("groupId")
+ public String getGroupId() {
+ return groupId;
+ }
+
+ /**
+ * Maven Group ID
+ * (Required)
+ *
+ * @param groupId
+ * The groupId
+ */
+ @JsonProperty("groupId")
+ public void setGroupId(String groupId) {
+ this.groupId = groupId;
+ }
+
+ public DroolsConfiguration withGroupId(String groupId) {
+ this.groupId = groupId;
+ return this;
+ }
+
+ /**
+ * Maven Version
+ * (Required)
+ *
+ * @return
+ * The version
+ */
+ @JsonProperty("version")
+ public String getVersion() {
+ return version;
+ }
+
+ /**
+ * Maven Version
+ * (Required)
+ *
+ * @param version
+ * The version
+ */
+ @JsonProperty("version")
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public DroolsConfiguration withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this);
+ }
+
+ @JsonAnyGetter
+ public Map<String, Object> getAdditionalProperties() {
+ return this.additionalProperties;
+ }
+
+ @JsonAnySetter
+ public void setAdditionalProperty(String name, Object value) {
+ this.additionalProperties.put(name, value);
+ }
+
+ public DroolsConfiguration withAdditionalProperty(String name, Object value) {
+ this.additionalProperties.put(name, value);
+ return this;
+ }
+
+ protected boolean declaredProperty(String name, Object value) {
+ switch (name) {
+ case "artifactId":
+ if (value instanceof String) {
+ setArtifactId(((String) value));
+ } else {
+ throw new IllegalArgumentException(("property \"artifactId\" is of type \"java.lang.String\", but got "+ value.getClass().toString()));
+ }
+ return true;
+ case "groupId":
+ if (value instanceof String) {
+ setGroupId(((String) value));
+ } else {
+ throw new IllegalArgumentException(("property \"groupId\" is of type \"java.lang.String\", but got "+ value.getClass().toString()));
+ }
+ return true;
+ case "version":
+ if (value instanceof String) {
+ setVersion(((String) value));
+ } else {
+ throw new IllegalArgumentException(("property \"version\" is of type \"java.lang.String\", but got "+ value.getClass().toString()));
+ }
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected Object declaredPropertyOrNotFound(String name, Object notFoundValue) {
+ switch (name) {
+ case "artifactId":
+ return getArtifactId();
+ case "groupId":
+ return getGroupId();
+ case "version":
+ return getVersion();
+ default:
+ return notFoundValue;
+ }
+ }
+
+ @SuppressWarnings({
+ "unchecked"
+ })
+ public<T >T get(String name) {
+ Object value = declaredPropertyOrNotFound(name, DroolsConfiguration.NOT_FOUND_VALUE);
+ if (DroolsConfiguration.NOT_FOUND_VALUE!= value) {
+ return ((T) value);
+ } else {
+ return ((T) getAdditionalProperties().get(name));
+ }
+ }
+
+ public void set(String name, Object value) {
+ if (!declaredProperty(name, value)) {
+ getAdditionalProperties().put(name, ((Object) value));
+ }
+ }
+
+ public DroolsConfiguration with(String name, Object value) {
+ if (!declaredProperty(name, value)) {
+ getAdditionalProperties().put(name, ((Object) value));
+ }
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder().append(artifactId).append(groupId).append(version).append(additionalProperties).toHashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if ((other instanceof DroolsConfiguration) == false) {
+ return false;
+ }
+ DroolsConfiguration rhs = ((DroolsConfiguration) other);
+ return new EqualsBuilder().append(artifactId, rhs.artifactId).append(groupId, rhs.groupId).append(version, rhs.version).append(additionalProperties, rhs.additionalProperties).isEquals();
+ }
+
+}
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/protocol/configuration/PdpdConfiguration.java b/policy-management/src/main/java/org/openecomp/policy/drools/protocol/configuration/PdpdConfiguration.java
new file mode 100644
index 00000000..65de6656
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/protocol/configuration/PdpdConfiguration.java
@@ -0,0 +1,283 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-management
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.policy.drools.protocol.configuration;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import com.fasterxml.jackson.annotation.JsonAnyGetter;
+import com.fasterxml.jackson.annotation.JsonAnySetter;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+
+/**
+ * ENGINE-CONFIGURATION
+ * <p>
+ *
+ *
+ */
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class PdpdConfiguration {
+
+ /**
+ * Controller Entity ID
+ */
+ public static final String CONFIG_ENTITY_CONTROLLER = "controller";
+
+ /**
+ * Unique Transaction ID. This is an UUID.
+ * (Required)
+ *
+ */
+ @JsonProperty("requestID")
+ private String requestID;
+ /**
+ * Set of entities on which configuration can be performed: controller
+ * (Required)
+ *
+ */
+ @JsonProperty("entity")
+ private String entity;
+ /**
+ * Controller Information, only applicable when the entity is set to controller
+ *
+ */
+ @JsonProperty("controllers")
+ private List<ControllerConfiguration> controllers = new ArrayList<ControllerConfiguration>();
+ @JsonIgnore
+ private Map<String, Object> additionalProperties = new HashMap<String, Object>();
+ protected final static Object NOT_FOUND_VALUE = new Object();
+
+ /**
+ * No args constructor for use in serialization
+ *
+ */
+ public PdpdConfiguration() {
+ }
+
+ /**
+ *
+ * @param controller
+ * @param requestID
+ * @param entity
+ */
+ public PdpdConfiguration(String requestID, String entity, List<ControllerConfiguration> controllers) {
+ this.requestID = requestID;
+ this.entity = entity;
+ this.controllers = controllers;
+ }
+
+ /**
+ * Unique Transaction ID. This is an UUID.
+ * (Required)
+ *
+ * @return
+ * The requestID
+ */
+ @JsonProperty("requestID")
+ public String getRequestID() {
+ return requestID;
+ }
+
+ /**
+ * Unique Transaction ID. This is an UUID.
+ * (Required)
+ *
+ * @param requestID
+ * The requestID
+ */
+ @JsonProperty("requestID")
+ public void setRequestID(String requestID) {
+ this.requestID = requestID;
+ }
+
+ public PdpdConfiguration withRequestID(String requestID) {
+ this.requestID = requestID;
+ return this;
+ }
+
+ /**
+ * Set of entities on which configuration can be performed: controller
+ * (Required)
+ *
+ * @return
+ * The entity
+ */
+ @JsonProperty("entity")
+ public String getEntity() {
+ return entity;
+ }
+
+ /**
+ * Set of entities on which configuration can be performed: controller
+ * (Required)
+ *
+ * @param entity
+ * The entity
+ */
+ @JsonProperty("entity")
+ public void setEntity(String entity) {
+ this.entity = entity;
+ }
+
+ public PdpdConfiguration withEntity(String entity) {
+ this.entity = entity;
+ return this;
+ }
+
+ /**
+ * Controller Information, only applicable when the entity is set to controller
+ *
+ * @return
+ * The controller
+ */
+ @JsonProperty("controller")
+ public List<ControllerConfiguration> getControllers() {
+ return controllers;
+ }
+
+ /**
+ * Controller Information, only applicable when the entity is set to controller
+ *
+ * @param controller
+ * The controller
+ */
+ @JsonProperty("controller")
+ public void setControllers(List<ControllerConfiguration> controllers) {
+ this.controllers = controllers;
+ }
+
+ public PdpdConfiguration withController(List<ControllerConfiguration> controllers) {
+ this.controllers = controllers;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this);
+ }
+
+ @JsonAnyGetter
+ public Map<String, Object> getAdditionalProperties() {
+ return this.additionalProperties;
+ }
+
+ @JsonAnySetter
+ public void setAdditionalProperty(String name, Object value) {
+ this.additionalProperties.put(name, value);
+ }
+
+ public PdpdConfiguration withAdditionalProperty(String name, Object value) {
+ this.additionalProperties.put(name, value);
+ return this;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected boolean declaredProperty(String name, Object value) {
+ switch (name) {
+ case "requestID":
+ if (value instanceof String) {
+ setRequestID(((String) value));
+ } else {
+ throw new IllegalArgumentException(("property \"requestID\" is of type \"java.lang.String\", but got "+ value.getClass().toString()));
+ }
+ return true;
+ case "entity":
+ if (value instanceof String) {
+ setEntity(((String) value));
+ } else {
+ throw new IllegalArgumentException(("property \"entity\" is of type \"java.lang.String\", but got "+ value.getClass().toString()));
+ }
+ return true;
+ case "controllers":
+ if (value instanceof List) {
+ setControllers(((List<ControllerConfiguration> ) value));
+ } else {
+ throw new IllegalArgumentException(("property \"controllers\" is of type \"java.util.List<org.openecomp.policy.drools.protocol.configuration.Controller>\", but got "+ value.getClass().toString()));
+ }
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ protected Object declaredPropertyOrNotFound(String name, Object notFoundValue) {
+ switch (name) {
+ case "requestID":
+ return getRequestID();
+ case "entity":
+ return getEntity();
+ case "controllers":
+ return getControllers();
+ default:
+ return notFoundValue;
+ }
+ }
+
+ @SuppressWarnings({
+ "unchecked"
+ })
+ public<T >T get(String name) {
+ Object value = declaredPropertyOrNotFound(name, PdpdConfiguration.NOT_FOUND_VALUE);
+ if (PdpdConfiguration.NOT_FOUND_VALUE!= value) {
+ return ((T) value);
+ } else {
+ return ((T) getAdditionalProperties().get(name));
+ }
+ }
+
+ public void set(String name, Object value) {
+ if (!declaredProperty(name, value)) {
+ getAdditionalProperties().put(name, ((Object) value));
+ }
+ }
+
+ public PdpdConfiguration with(String name, Object value) {
+ if (!declaredProperty(name, value)) {
+ getAdditionalProperties().put(name, ((Object) value));
+ }
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder().append(requestID).append(entity).append(controllers).append(additionalProperties).toHashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if ((other instanceof PdpdConfiguration) == false) {
+ return false;
+ }
+ PdpdConfiguration rhs = ((PdpdConfiguration) other);
+ return new EqualsBuilder().append(requestID, rhs.requestID).append(entity, rhs.entity).append(controllers, rhs.controllers).append(additionalProperties, rhs.additionalProperties).isEquals();
+ }
+
+}
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/server/restful/RestManager.java b/policy-management/src/main/java/org/openecomp/policy/drools/server/restful/RestManager.java
new file mode 100644
index 00000000..48e6313a
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/server/restful/RestManager.java
@@ -0,0 +1,1181 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-management
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.policy.drools.server.restful;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.UUID;
+import java.util.regex.Pattern;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.openecomp.policy.common.logging.eelf.MessageCodes;
+import org.openecomp.policy.common.logging.flexlogger.FlexLogger;
+import org.openecomp.policy.common.logging.flexlogger.Logger;
+import org.openecomp.policy.drools.controller.DroolsController;
+import org.openecomp.policy.drools.event.comm.TopicEndpoint;
+import org.openecomp.policy.drools.event.comm.TopicSink;
+import org.openecomp.policy.drools.event.comm.TopicSource;
+import org.openecomp.policy.drools.event.comm.bus.DmaapTopicSink;
+import org.openecomp.policy.drools.event.comm.bus.DmaapTopicSource;
+import org.openecomp.policy.drools.event.comm.bus.UebTopicSink;
+import org.openecomp.policy.drools.event.comm.bus.UebTopicSource;
+import org.openecomp.policy.drools.properties.PolicyProperties;
+import org.openecomp.policy.drools.protocol.coders.EventProtocolCoder;
+import org.openecomp.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters;
+import org.openecomp.policy.drools.protocol.coders.JsonProtocolFilter;
+import org.openecomp.policy.drools.protocol.coders.JsonProtocolFilter.FilterRule;
+import org.openecomp.policy.drools.protocol.coders.ProtocolCoderToolset;
+import org.openecomp.policy.drools.protocol.configuration.ControllerConfiguration;
+import org.openecomp.policy.drools.protocol.configuration.PdpdConfiguration;
+import org.openecomp.policy.drools.system.PolicyController;
+import org.openecomp.policy.drools.system.PolicyEngine;
+
+
+/**
+ * REST Endpoint for management of the Drools PDP
+ */
+@Path("/policy/pdp")
+public class RestManager {
+ /**
+ * Logger
+ */
+ private static Logger logger = FlexLogger.getLogger(RestManager.class);
+
+ /**
+ * gets the Policy Engine
+ *
+ * @return the Policy Engine
+ */
+ @GET
+ @Path("engine")
+ @Produces(MediaType.APPLICATION_JSON)
+ public PolicyEngine engine() {
+ return PolicyEngine.manager;
+ }
+
+
+ /**
+ * Updates the Policy Engine
+ *
+ * @param configuration configuration
+ * @return Policy Engine
+ */
+ @PUT
+ @Path("engine")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response updateEngine(PdpdConfiguration configuration) {
+ PolicyController controller = null;
+ boolean success = true;
+ try {
+ success = PolicyEngine.manager.configure(configuration);
+ } catch (Exception e) {
+ success = false;
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ "PolicyEngine", this.toString());
+ }
+
+ if (!success)
+ return Response.status(Response.Status.NOT_ACCEPTABLE).
+ entity(new Error("cannot perform operation")).build();
+ else
+ return Response.status(Response.Status.OK).entity(controller).build();
+ }
+
+ /**
+ * Activates the Policy Engine
+ *
+ * @param configuration configuration
+ * @return Policy Engine
+ */
+ @PUT
+ @Path("engine/activation")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response activateEngine() {
+ boolean success = true;
+ try {
+ PolicyEngine.manager.activate();
+ } catch (Exception e) {
+ success = false;
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ "PolicyEngine", this.toString());
+ }
+
+ if (!success)
+ return Response.status(Response.Status.NOT_ACCEPTABLE).
+ entity(new Error("cannot perform operation")).build();
+ else
+ return Response.status(Response.Status.OK).entity(PolicyEngine.manager).build();
+ }
+
+ /**
+ * Activates the Policy Engine
+ *
+ * @param configuration configuration
+ * @return Policy Engine
+ */
+ @PUT
+ @Path("engine/deactivation")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response deactivateEngine() {
+ boolean success = true;
+ try {
+ PolicyEngine.manager.deactivate();
+ } catch (Exception e) {
+ success = false;
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ "PolicyEngine", this.toString());
+ }
+
+ if (!success)
+ return Response.status(Response.Status.NOT_ACCEPTABLE).
+ entity(new Error("cannot perform operation")).build();
+ else
+ return Response.status(Response.Status.OK).entity(PolicyEngine.manager).build();
+ }
+
+ @DELETE
+ @Path("engine")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response engineShutdown() {
+ try {
+ PolicyEngine.manager.shutdown();
+ } catch (IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ "shutdown: " + PolicyEngine.manager);
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(PolicyEngine.manager).
+ build();
+ }
+
+ return Response.status(Response.Status.OK).
+ entity(PolicyEngine.manager).
+ build();
+ }
+
+ @PUT
+ @Path("engine/lock")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response lockEngine() {
+ boolean success = PolicyEngine.manager.lock();
+ if (success)
+ return Response.status(Status.OK).
+ entity("Policy Engine is locked").
+ build();
+ else
+ return Response.status(Status.SERVICE_UNAVAILABLE).
+ entity("Policy Engine cannot be locked").
+ build();
+ }
+
+ @DELETE
+ @Path("engine/unlock")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response unlockEngine() {
+ boolean success = PolicyEngine.manager.unlock();
+ if (success)
+ return Response.status(Status.OK).
+ entity("Policy Engine is unlocked").
+ build();
+ else
+ return Response.status(Status.SERVICE_UNAVAILABLE).
+ entity("Policy Engine cannot be unlocked").
+ build();
+ }
+
+ @GET
+ @Path("engine/controllers")
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<PolicyController> controllers() {
+ return PolicyEngine.manager.getPolicyControllers();
+ }
+
+ @POST
+ @Path("engine/controllers")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response addController(Properties config) {
+ if (config == null)
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error("A configuration must be provided")).
+ build();
+
+ String controllerName = config.getProperty(PolicyProperties.PROPERTY_CONTROLLER_NAME);
+ if (controllerName == null || controllerName.isEmpty())
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error
+ ("Configuration must have an entry for " +
+ PolicyProperties.PROPERTY_CONTROLLER_NAME)).
+ build();
+
+ PolicyController controller;
+ try {
+ controller = PolicyController.factory.get(controllerName);
+ if (controller != null)
+ return Response.status(Response.Status.NOT_MODIFIED).
+ entity(controller).
+ build();
+ } catch (IllegalArgumentException e) {
+ // This is OK
+ } catch (IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.NOT_ACCEPTABLE).
+ entity(new Error(controllerName + " not found")).build();
+ }
+
+ try {
+ controller = PolicyEngine.manager.createPolicyController
+ (config.getProperty(PolicyProperties.PROPERTY_CONTROLLER_NAME), config);
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(e.getMessage())).
+ build();
+ }
+
+ try {
+ boolean success = controller.start();
+ if (!success) {
+ logger.warn("Can't start " + controllerName + ": " + controller.toString());
+ return Response.status(Response.Status.PARTIAL_CONTENT).
+ entity(new Error(controllerName + " can't be started")).build();
+ }
+ } catch (IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.PARTIAL_CONTENT).
+ entity(controller).build();
+ }
+
+ return Response.status(Response.Status.CREATED).
+ entity(controller).
+ build();
+ }
+
+ @GET
+ @Path("engine/controllers/{controllerName}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response controller(@PathParam("controllerName") String controllerName) {
+ PolicyController controller = null;
+ try {
+ controller = PolicyController.factory.get(controllerName);
+ } catch (IllegalArgumentException e) {
+ logger.info("Can't retrieve controller " + controllerName +
+ ". Reason: " + e.getMessage());
+ } catch (IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.NOT_ACCEPTABLE).
+ entity(new Error(controllerName + " not acceptable")).build();
+ }
+
+ if (controller != null)
+ return Response.status(Response.Status.OK).
+ entity(controller).build();
+ else
+ return Response.status(Response.Status.NOT_FOUND).
+ entity(new Error(controllerName + " not found")).build();
+ }
+
+ @DELETE
+ @Path("engine/controllers/{controllerName}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response deleteController(@PathParam("controllerName") String controllerName) {
+
+ if (controllerName == null || controllerName.isEmpty())
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity("A controller name must be provided").
+ build();
+
+ PolicyController controller;
+ try {
+ controller =
+ PolicyController.factory.get(controllerName);
+ if (controller == null)
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(controllerName + " does not exist")).
+ build();
+ } catch (IllegalArgumentException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(controllerName + " not found: " + e.getMessage())).
+ build();
+ } catch (IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.NOT_ACCEPTABLE).
+ entity(new Error(controllerName + " not acceptable")).build();
+ }
+
+ try {
+ PolicyEngine.manager.removePolicyController(controllerName);
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName + controller);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).
+ entity(new Error(e.getMessage())).
+ build();
+ }
+
+ return Response.status(Response.Status.OK).
+ entity(controller).
+ build();
+ }
+
+ /**
+ * Updates the Policy Engine
+ *
+ * @param configuration configuration
+ * @return Policy Engine
+ */
+ @PUT
+ @Path("engine/controllers/{controllerName}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response updateController(@PathParam("controllerName") String controllerName,
+ ControllerConfiguration controllerConfiguration) {
+
+ if (controllerName == null || controllerName.isEmpty() ||
+ controllerConfiguration == null ||
+ controllerConfiguration.getName().intern() != controllerName)
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity("A valid or matching controller names must be provided").
+ build();
+
+ PolicyController controller;
+ try {
+ controller = PolicyEngine.manager.updatePolicyController(controllerConfiguration);
+ if (controller == null)
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(controllerName + " does not exist")).
+ build();
+ } catch (IllegalArgumentException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(controllerName + " not found: " + e.getMessage())).
+ build();
+ } catch (Exception e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.NOT_ACCEPTABLE).
+ entity(new Error(controllerName + " not acceptable")).build();
+ }
+
+ return Response.status(Response.Status.OK).
+ entity(controller).
+ build();
+ }
+
+ public DroolsController getDroolsController(String controllerName) throws IllegalArgumentException {
+ PolicyController controller = PolicyController.factory.get(controllerName);
+ if (controller == null)
+ throw new IllegalArgumentException(controllerName + " does not exist");
+
+ DroolsController drools = controller.getDrools();
+ if (drools == null)
+ throw new IllegalArgumentException(controllerName + " has no drools configuration");
+
+ return drools;
+ }
+
+ @GET
+ @Path("engine/controllers/{controllerName}/decoders")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response decoders(@PathParam("controllerName") String controllerName) {
+ try {
+ DroolsController drools = getDroolsController(controllerName);
+ List<ProtocolCoderToolset> decoders = EventProtocolCoder.manager.getDecoders
+ (drools.getGroupId(), drools.getArtifactId());
+ return Response.status(Response.Status.OK).
+ entity(decoders).
+ build();
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(e.getMessage())).
+ build();
+ }
+ }
+
+ @GET
+ @Path("engine/controllers/{controllerName}/decoders/filters")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response decoderFilters(@PathParam("controllerName") String controllerName) {
+ try {
+ DroolsController drools = getDroolsController(controllerName);
+ List<CoderFilters> filters = EventProtocolCoder.manager.getDecoderFilters
+ (drools.getGroupId(), drools.getArtifactId());
+ return Response.status(Response.Status.OK).
+ entity(filters).
+ build();
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(e.getMessage())).
+ build();
+ }
+ }
+
+ @GET
+ @Path("engine/controllers/{controllerName}/decoders/{topicName}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response decoder(@PathParam("controllerName") String controllerName,
+ @PathParam("topicName") String topicName) {
+ try {
+ DroolsController drools = getDroolsController(controllerName);
+ ProtocolCoderToolset decoder = EventProtocolCoder.manager.getDecoders
+ (drools.getGroupId(), drools.getArtifactId(), topicName);
+ return Response.status(Response.Status.OK).
+ entity(decoder).
+ build();
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(e.getMessage())).
+ build();
+ }
+ }
+
+ @GET
+ @Path("engine/controllers/{controllerName}/decoders/{topicName}/filters")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response decoderFilter(@PathParam("controllerName") String controllerName,
+ @PathParam("topicName") String topicName) {
+ try {
+ DroolsController drools = getDroolsController(controllerName);
+ ProtocolCoderToolset decoder = EventProtocolCoder.manager.getDecoders
+ (drools.getGroupId(), drools.getArtifactId(), topicName);
+ if (decoder == null)
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(topicName + " does not exist")).
+ build();
+ else
+ return Response.status(Response.Status.OK).
+ entity(decoder.getCoders()).
+ build();
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(e.getMessage())).
+ build();
+ }
+ }
+
+ @GET
+ @Path("engine/controllers/{controllerName}/decoders/{topicName}/filters/{factClassName}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response decoderFilter(@PathParam("controllerName") String controllerName,
+ @PathParam("topicName") String topicName,
+ @PathParam("factClassName") String factClass) {
+ try {
+ DroolsController drools = getDroolsController(controllerName);
+ ProtocolCoderToolset decoder = EventProtocolCoder.manager.getDecoders
+ (drools.getGroupId(), drools.getArtifactId(), topicName);
+ CoderFilters filters = decoder.getCoder(factClass);
+ if (filters == null)
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(topicName + ":" + factClass + " does not exist")).
+ build();
+ else
+ return Response.status(Response.Status.OK).
+ entity(filters).
+ build();
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(e.getMessage())).
+ build();
+ }
+ }
+
+ @POST
+ @Path("engine/controllers/{controllerName}/decoders/{topicName}/filters/{factClassName}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response decoderFilter(@PathParam("controllerName") String controllerName,
+ @PathParam("topicName") String topicName,
+ @PathParam("factClassName") String factClass,
+ JsonProtocolFilter configFilters) {
+
+ if (configFilters == null) {
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error("Configuration Filters not provided")).
+ build();
+ }
+
+ try {
+ DroolsController drools = getDroolsController(controllerName);
+ ProtocolCoderToolset decoder = EventProtocolCoder.manager.getDecoders
+ (drools.getGroupId(), drools.getArtifactId(), topicName);
+ CoderFilters filters = decoder.getCoder(factClass);
+ if (filters == null)
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(topicName + ":" + factClass + " does not exist")).
+ build();
+ filters.setFilter(configFilters);
+ return Response.status(Response.Status.OK).
+ entity(filters).
+ build();
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(e.getMessage())).
+ build();
+ }
+ }
+
+ @GET
+ @Path("engine/controllers/{controllerName}/decoders/{topicName}/filters/{factClassName}/rules")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response decoderFilterRules(@PathParam("controllerName") String controllerName,
+ @PathParam("topicName") String topicName,
+ @PathParam("factClassName") String factClass) {
+ try {
+ DroolsController drools = getDroolsController(controllerName);
+ ProtocolCoderToolset decoder = EventProtocolCoder.manager.getDecoders
+ (drools.getGroupId(), drools.getArtifactId(), topicName);
+
+ CoderFilters filters = decoder.getCoder(factClass);
+ if (filters == null)
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(controllerName + ":" + topicName + ":" + factClass + " does not exist")).
+ build();
+
+ JsonProtocolFilter filter = filters.getFilter();
+ if (filter == null)
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(controllerName + ":" + topicName + ":" + factClass + " no filters")).
+ build();
+
+ return Response.status(Response.Status.OK).
+ entity(filter.getRules()).
+ build();
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(e.getMessage())).
+ build();
+ }
+ }
+
+ @GET
+ @Path("engine/controllers/{controllerName}/decoders/{topicName}/filters/{factClassName}/rules/{ruleName}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response decoderFilterRules(@PathParam("controllerName") String controllerName,
+ @PathParam("topicName") String topicName,
+ @PathParam("factClassName") String factClass,
+ @PathParam("ruleName") String ruleName) {
+ try {
+ DroolsController drools = getDroolsController(controllerName);
+ ProtocolCoderToolset decoder = EventProtocolCoder.manager.getDecoders
+ (drools.getGroupId(), drools.getArtifactId(), topicName);
+
+ CoderFilters filters = decoder.getCoder(factClass);
+ if (filters == null)
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(controllerName + ":" + topicName + ":" + factClass + " does not exist")).
+ build();
+
+ JsonProtocolFilter filter = filters.getFilter();
+ if (filter == null)
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(controllerName + ":" + topicName + ":" + factClass + " no filters")).
+ build();
+
+ return Response.status(Response.Status.OK).
+ entity(filter.getRules(ruleName)).
+ build();
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(e.getMessage())).
+ build();
+ }
+ }
+
+ @DELETE
+ @Path("engine/controllers/{controllerName}/decoders/{topicName}/filters/{factClassName}/rules/{ruleName}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response deleteDecoderFilterRule(@PathParam("controllerName") String controllerName,
+ @PathParam("topicName") String topicName,
+ @PathParam("factClassName") String factClass,
+ @PathParam("ruleName") String ruleName,
+ FilterRule rule) {
+
+ try {
+ DroolsController drools = getDroolsController(controllerName);
+ ProtocolCoderToolset decoder = EventProtocolCoder.manager.getDecoders
+ (drools.getGroupId(), drools.getArtifactId(), topicName);
+
+ CoderFilters filters = decoder.getCoder(factClass);
+ if (filters == null)
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(controllerName + ":" + topicName + ":" + factClass + " does not exist")).
+ build();
+
+ JsonProtocolFilter filter = filters.getFilter();
+ if (filter == null)
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(controllerName + ":" + topicName + ":" + factClass + " no filters")).
+ build();
+
+ if (rule == null) {
+ filter.deleteRules(ruleName);
+ return Response.status(Response.Status.OK).
+ entity(filter.getRules()).
+ build();
+ }
+
+ if (rule.getName() == null || !rule.getName().equals(ruleName))
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(controllerName + ":" + topicName + ":" + factClass + ":" + ruleName +
+ " rule name request inconsistencies (" + rule.getName() + ")")).
+ build();
+
+ filter.deleteRule(ruleName, rule.getRegex());
+ return Response.status(Response.Status.OK).
+ entity(filter.getRules()).
+ build();
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(e.getMessage())).
+ build();
+ }
+ }
+
+ @PUT
+ @Path("engine/controllers/{controllerName}/decoders/{topicName}/filters/{factClassName}/rules")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response decoderFilterRule(@PathParam("controllerName") String controllerName,
+ @PathParam("topicName") String topicName,
+ @PathParam("factClassName") String factClass,
+ JsonProtocolFilter.FilterRule rule) {
+
+ try {
+ DroolsController drools = getDroolsController(controllerName);
+ ProtocolCoderToolset decoder = EventProtocolCoder.manager.getDecoders
+ (drools.getGroupId(), drools.getArtifactId(), topicName);
+
+ CoderFilters filters = decoder.getCoder(factClass);
+ if (filters == null)
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(controllerName + ":" + topicName + ":" + factClass + " does not exist")).
+ build();
+
+ JsonProtocolFilter filter = filters.getFilter();
+ if (filter == null)
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(controllerName + ":" + topicName + ":" + factClass + " no filters")).
+ build();
+
+ if (rule.getName() == null)
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(controllerName + ":" + topicName + ":" + factClass +
+ " rule name request inconsistencies (" + rule.getName() + ")")).
+ build();
+
+ filter.addRule(rule.getName(), rule.getRegex());
+ return Response.status(Response.Status.OK).
+ entity(filter.getRules()).
+ build();
+ } catch (IllegalArgumentException | IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(e.getMessage())).
+ build();
+ }
+ }
+
+ @GET
+ @Path("engine/controllers/{controllerName}/encoders")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response encoderFilters(@PathParam("controllerName") String controllerName) {
+ List<CoderFilters> encoders;
+ try {
+ PolicyController controller = PolicyController.factory.get(controllerName);
+ if (controller == null)
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(controllerName + " does not exist")).
+ build();
+ DroolsController drools = controller.getDrools();
+ if (drools == null)
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).
+ entity(new Error(controllerName + " has not drools component")).
+ build();
+ encoders = EventProtocolCoder.manager.getEncoderFilters
+ (drools.getGroupId(), drools.getArtifactId());
+ } catch (IllegalArgumentException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(controllerName + " not found: " + e.getMessage())).
+ build();
+ } catch (IllegalStateException e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ controllerName, this.toString());
+ return Response.status(Response.Status.NOT_ACCEPTABLE).
+ entity(new Error(controllerName + " is not accepting the request")).build();
+ }
+
+ return Response.status(Response.Status.OK).
+ entity(encoders).
+ build();
+ }
+
+ @POST
+ @Path("engine/controllers/{controllerName}/decoders/{topic}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response decode(@PathParam("controllerName") String controllerName,
+ @PathParam("topic") String topic,
+ String json) {
+
+ PolicyController policyController = PolicyController.factory.get(controllerName);
+
+ CodingResult result = new CodingResult();
+ result.decoding = false;
+ result.encoding = false;
+ result.jsonEncoding = null;
+
+ Object event;
+ try {
+ event = EventProtocolCoder.manager.decode
+ (policyController.getDrools().getGroupId(),
+ policyController.getDrools().getArtifactId(),
+ topic,
+ json);
+ result.decoding = true;
+ } catch (Exception e) {
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(e.getMessage())).
+ build();
+ }
+
+ try {
+ result.jsonEncoding = EventProtocolCoder.manager.encode(topic, event);
+ result.encoding = true;
+ } catch (Exception e) {
+ return Response.status(Response.Status.OK).
+ entity(result).
+ build();
+ }
+
+ return Response.status(Response.Status.OK).
+ entity(result).
+ build();
+ }
+
+ @GET
+ @Path("engine/topics")
+ @Produces(MediaType.APPLICATION_JSON)
+ public TopicEndpoint topics() {
+ return TopicEndpoint.manager;
+ }
+
+ @SuppressWarnings("unchecked")
+ @GET
+ @Path("engine/topics/sources")
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<TopicSource> sources() {
+ return (List<TopicSource>) TopicEndpoint.manager.getTopicSources();
+ }
+
+ @SuppressWarnings("unchecked")
+ @GET
+ @Path("engine/topics/sinks")
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<TopicSink> sinks() {
+ return (List<TopicSink>) TopicEndpoint.manager.getTopicSinks();
+ }
+
+ @GET
+ @Path("engine/topics/sources/ueb")
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<UebTopicSource> uebSources() {
+ return TopicEndpoint.manager.getUebTopicSources();
+ }
+
+ @GET
+ @Path("engine/topics/sinks/ueb")
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<UebTopicSink> uebSinks() {
+ return (List<UebTopicSink>) TopicEndpoint.manager.getUebTopicSinks();
+ }
+
+ @GET
+ @Path("engine/topics/sources/dmaap")
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<DmaapTopicSource> dmaapSources() {
+ return TopicEndpoint.manager.getDmaapTopicSources();
+ }
+
+ @GET
+ @Path("engine/topics/sinks/dmaap")
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<DmaapTopicSink> dmaapSinks() {
+ return (List<DmaapTopicSink>) TopicEndpoint.manager.getDmaapTopicSinks();
+ }
+
+ @SuppressWarnings("unchecked")
+ @GET
+ @Path("engine/topics/{topic}/sources")
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<TopicSource> sourceTopic(@PathParam("topic") String topic) {
+ List<String> topics = new ArrayList<String>();
+ topics.add(topic);
+
+ return (List<TopicSource>) TopicEndpoint.manager.getTopicSources(topics);
+ }
+
+ @SuppressWarnings("unchecked")
+ @GET
+ @Path("engine/topics/{topic}/sinks")
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<TopicSink> sinkTopic(@PathParam("topic") String topic) {
+ List<String> topics = new ArrayList<String>();
+ topics.add(topic);
+
+ return (List<TopicSink>) TopicEndpoint.manager.getTopicSinks(topics);
+ }
+
+
+ @GET
+ @Path("engine/topics/{topic}/ueb/source")
+ @Produces(MediaType.APPLICATION_JSON)
+ public UebTopicSource uebSourceTopic(@PathParam("topic") String topic) {
+ return TopicEndpoint.manager.getUebTopicSource(topic);
+ }
+
+ @GET
+ @Path("engine/topics/{topic}/ueb/sink")
+ @Produces(MediaType.APPLICATION_JSON)
+ public UebTopicSink uebSinkTopic(@PathParam("topic") String topic) {
+ return TopicEndpoint.manager.getUebTopicSink(topic);
+ }
+
+ @GET
+ @Path("engine/topics/{topic}/dmaap/source")
+ @Produces(MediaType.APPLICATION_JSON)
+ public DmaapTopicSource dmaapSourceTopic(@PathParam("topic") String topic) {
+ return TopicEndpoint.manager.getDmaapTopicSource(topic);
+ }
+
+ @GET
+ @Path("engine/topics/{topic}/dmaap/sink")
+ @Produces(MediaType.APPLICATION_JSON)
+ public DmaapTopicSink dmaapSinkTopic(@PathParam("topic") String topic) {
+ return TopicEndpoint.manager.getDmaapTopicSink(topic);
+ }
+
+ @GET
+ @Path("engine/topics/{topic}/ueb/source/events")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response uebSourceEvent(@PathParam("topic") String topicName) {
+
+ UebTopicSource uebReader = TopicEndpoint.manager.getUebTopicSource(topicName);
+ String[] events = uebReader.getRecentEvents();
+ return Response.status(Status.OK).
+ entity(events).
+ build();
+ }
+
+ @GET
+ @Path("engine/topics/{topic}/ueb/sink/events")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response uebSinkEvent(@PathParam("topic") String topicName) {
+
+ UebTopicSink uebSink = TopicEndpoint.manager.getUebTopicSink(topicName);
+ String[] events = uebSink.getRecentEvents();
+ return Response.status(Status.OK).
+ entity(events).
+ build();
+ }
+
+ @GET
+ @Path("engine/topics/{topic}/dmaap/source/events")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response dmaapSourcevent(@PathParam("topic") String topicName) {
+
+ DmaapTopicSource uebReader = TopicEndpoint.manager.getDmaapTopicSource(topicName);
+ String[] events = uebReader.getRecentEvents();
+ return Response.status(Status.OK).
+ entity(events).
+ build();
+ }
+
+ @GET
+ @Path("engine/topics/{topic}/dmaap/sink/events")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response dmaapSinkEvent(@PathParam("topic") String topicName) {
+
+ DmaapTopicSink uebSink = TopicEndpoint.manager.getDmaapTopicSink(topicName);
+ String[] events = uebSink.getRecentEvents();
+ return Response.status(Status.OK).
+ entity(events).
+ build();
+ }
+
+ @PUT
+ @Path("engine/topics/{topic}/ueb/sources/events")
+ @Consumes(MediaType.TEXT_PLAIN)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response uebOffer(@PathParam("topic") String topicName,
+ String json) {
+ try {
+ UebTopicSource uebReader = TopicEndpoint.manager.getUebTopicSource(topicName);
+ boolean success = uebReader.offer(json);
+ if (success)
+ return Response.status(Status.OK).
+ entity("Successfully injected event over " + topicName).
+ build();
+ else
+ return Response.status(Status.NOT_ACCEPTABLE).
+ entity("Failure to inject event over " + topicName).
+ build();
+ } catch (Exception e) {
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(e.getMessage())).
+ build();
+ }
+ }
+
+ @PUT
+ @Path("engine/topics/{topic}/dmaap/sources/events")
+ @Consumes(MediaType.TEXT_PLAIN)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response dmaapOffer(@PathParam("topic") String topicName,
+ String json) {
+ try {
+ DmaapTopicSource dmaapReader = TopicEndpoint.manager.getDmaapTopicSource(topicName);
+ boolean success = dmaapReader.offer(json);
+ if (success)
+ return Response.status(Status.OK).
+ entity("Successfully injected event over " + topicName).
+ build();
+ else
+ return Response.status(Status.NOT_ACCEPTABLE).
+ entity("Failure to inject event over " + topicName).
+ build();
+ } catch (Exception e) {
+ return Response.status(Response.Status.BAD_REQUEST).
+ entity(new Error(e.getMessage())).
+ build();
+ }
+ }
+
+ @PUT
+ @Path("engine/topics/lock")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response lockTopics() {
+ boolean success = TopicEndpoint.manager.lock();
+ if (success)
+ return Response.status(Status.OK).
+ entity("Endpoints are locked").
+ build();
+ else
+ return Response.status(Status.SERVICE_UNAVAILABLE).
+ entity("Endpoints cannot be locked").
+ build();
+ }
+
+ @DELETE
+ @Path("engine/topics/lock")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response unlockTopics() {
+ boolean success = TopicEndpoint.manager.unlock();
+ if (success)
+ return Response.status(Status.OK).
+ entity("Endpoints are unlocked").
+ build();
+ else
+ return Response.status(Status.SERVICE_UNAVAILABLE).
+ entity("Endpoints cannot be unlocked").
+ build();
+ }
+
+ @PUT
+ @Path("engine/topics/{topic}/ueb/sources/lock")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response lockTopic(@PathParam("topic") String topicName) {
+ UebTopicSource reader = TopicEndpoint.manager.getUebTopicSource(topicName);
+ boolean success = reader.lock();
+ if (success)
+ return Response.status(Status.OK).
+ entity("Endpoints are unlocked").
+ build();
+ else
+ return Response.status(Status.SERVICE_UNAVAILABLE).
+ entity("Endpoints cannot be unlocked").
+ build();
+ }
+
+ @PUT
+ @Path("engine/topics/{topic}/ueb/sources/unlock")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response unlockTopic(@PathParam("topic") String topicName) {
+ UebTopicSource reader = TopicEndpoint.manager.getUebTopicSource(topicName);
+ boolean success = reader.unlock();
+ if (success)
+ return Response.status(Status.OK).
+ entity("Endpoints are unlocked").
+ build();
+ else
+ return Response.status(Status.SERVICE_UNAVAILABLE).
+ entity("Endpoints cannot be unlocked").
+ build();
+ }
+
+ @PUT
+ @Path("engine/controllers/{controllerName}/lock")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response lockController(@PathParam("controllerName") String controllerName) {
+ PolicyController policyController = PolicyController.factory.get(controllerName);
+ boolean success = policyController.lock();
+ if (success)
+ return Response.status(Status.OK).
+ entity("Controller " + controllerName + " is now locked").
+ build();
+ else
+ return Response.status(Status.SERVICE_UNAVAILABLE).
+ entity("Controller " + controllerName + " cannot be locked").
+ build();
+ }
+
+ @DELETE
+ @Path("engine/controllers/{controllerName}/lock")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response unlockController(@PathParam("controllerName") String controllerName) {
+ PolicyController policyController = PolicyController.factory.get(controllerName);
+ boolean success = policyController.unlock();
+ if (success)
+ return Response.status(Status.OK).
+ entity("Controller " + controllerName + " is now unlocked").
+ build();
+ else
+ return Response.status(Status.SERVICE_UNAVAILABLE).
+ entity("Controller " + controllerName + " cannot be unlocked").
+ build();
+ }
+
+ @POST
+ @Path("engine/util/coders/filters/rules/{ruleName}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response rules(@DefaultValue("false") @QueryParam("negate") boolean negate,
+ @PathParam("ruleName") String name,
+ String regex) {
+ String literalRegex = Pattern.quote(regex);
+ if (negate)
+ literalRegex = "^(?!" + literalRegex + "$).*";
+
+ return Response.status(Status.OK).
+ entity(new JsonProtocolFilter.FilterRule(name,literalRegex)).
+ build();
+ }
+
+ @GET
+ @Path("engine/util/uuid")
+ public Response uuid() {
+ return Response.status(Status.OK).
+ entity(UUID.randomUUID().toString()).
+ build();
+ }
+
+ /*
+ * Helper classes for aggregation of results
+ */
+
+
+ public static class Endpoints {
+ public List<TopicSource> sources;
+ public List<TopicSink> sinks;
+
+ public Endpoints(List<TopicSource> sources,
+ List<TopicSink> sinks) {
+ this.sources = sources;
+ this.sinks = sinks;
+ }
+ }
+
+ public static class Endpoint {
+ public TopicSource source;
+ public TopicSink sink;
+
+ public Endpoint(TopicSource source,
+ TopicSink sink) {
+ this.source = source;
+ this.sink = sink;
+ }
+ }
+
+ public static class CodingResult {
+ public String jsonEncoding;
+ public Boolean encoding;
+ public Boolean decoding;
+ }
+
+ public static class Error {
+ public String error;
+
+ /**
+ * @param error
+ */
+ public Error(String error) {
+ this.error = error;
+ }
+ }
+}
+
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/system/Main.java b/policy-management/src/main/java/org/openecomp/policy/drools/system/Main.java
new file mode 100644
index 00000000..108600b2
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/system/Main.java
@@ -0,0 +1,131 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-management
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.policy.drools.system;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Properties;
+
+import org.openecomp.policy.common.logging.flexlogger.FlexLogger;
+import org.openecomp.policy.common.logging.flexlogger.Logger;
+import org.openecomp.policy.drools.core.PolicyContainer;
+import org.openecomp.policy.drools.persistence.SystemPersistence;
+import org.openecomp.policy.drools.utils.PropertyUtil;
+
+/**
+ * Programmatic entry point to the management layer
+ */
+public class Main {
+
+ /**
+ * logger
+ */
+ private static Logger logger = FlexLogger.getLogger(Main.class, true);
+
+ public static void main(String args[]) {
+
+ File configDir = new File(SystemPersistence.CONFIG_DIR_NAME);
+
+ if (!configDir.isDirectory()) {
+ throw new IllegalArgumentException
+ ("config directory: " + configDir.getAbsolutePath() +
+ " not found");
+ }
+
+
+ /* 0. Start the CORE layer first */
+
+ try {
+ PolicyContainer.globalInit(args);
+ } catch (Exception e) {
+ System.out.println("policy-core startup failed");
+ logger.warn("policy-core startup failed");
+ e.printStackTrace();
+ }
+
+ /* 1. Configure the Engine */
+
+ try {
+ Path policyEnginePath = Paths.get(configDir.toPath().toString(), SystemPersistence.PROPERTIES_FILE_ENGINE);
+ Properties properties = PropertyUtil.getProperties(policyEnginePath.toFile());
+ PolicyEngine.manager.configure(properties);
+ } catch (Exception e) {
+ String msg = "Policy Engine cannot be configured with properties: " + e.getMessage() + " : " + PolicyEngine.manager;
+ System.out.println(msg);
+ logger.warn(msg);
+ }
+
+ /* 2. Start the Engine with the basic services only (no Policy Controllers) */
+
+ try {
+ boolean success = PolicyEngine.manager.start();
+ if (!success) {
+ System.out.println("Policy Engine found some problems starting some components: " + PolicyEngine.manager);
+ logger.warn("Policy Engine is in an invalid state: " + PolicyEngine.manager);
+ }
+ } catch (IllegalStateException e) {
+ String msg = "Policy Engine is starting in an unexpected state: " + e.getMessage() + " : " + PolicyEngine.manager;
+ System.out.println(msg);
+ logger.warn(msg);
+ } catch (Exception e) {
+ String msg = "Unexpected Situation. Policy Engine cannot be started: " + e.getMessage() + " : " + PolicyEngine.manager;
+ System.out.println(msg);
+ e.printStackTrace();
+ System.exit(1);
+ }
+
+ /* 3. Create and start the controllers */
+
+ File[] controllerFiles = configDir.listFiles();
+ for (File config : controllerFiles) {
+
+ if (config.getName().endsWith(SystemPersistence.PROPERTIES_FILE_CONTROLLER_SUFFIX)) {
+ int idxSuffix =
+ config.getName().indexOf(SystemPersistence.PROPERTIES_FILE_CONTROLLER_SUFFIX);
+ int lastIdxSuffix =
+ config.getName().lastIndexOf(SystemPersistence.PROPERTIES_FILE_CONTROLLER_SUFFIX);
+ if (idxSuffix != lastIdxSuffix) {
+ throw new IllegalArgumentException
+ ("Improper naming of controller properties file: " +
+ "Expected <controller-name>" +
+ SystemPersistence.PROPERTIES_FILE_CONTROLLER_SUFFIX);
+ }
+
+ String name =
+ config.getName().substring(0, lastIdxSuffix);
+ try {
+ Properties properties = PropertyUtil.getProperties(config);
+ PolicyController controller = PolicyEngine.manager.createPolicyController(name, properties);
+ controller.start();
+ } catch (Exception e) {
+ System.out.println("can't instantiate Policy Controller based on properties file: " +
+ config + " with message " + e.getMessage());
+ e.printStackTrace();
+ } catch (LinkageError le) {
+ System.out.println("can't instantiate Policy Controller based on properties file: " +
+ config + ". A Linkage Error has been encountered: " + le.getMessage());
+ le.printStackTrace();
+ }
+ }
+ }
+ }
+}
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/system/PolicyController.java b/policy-management/src/main/java/org/openecomp/policy/drools/system/PolicyController.java
new file mode 100644
index 00000000..543fd0e3
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/system/PolicyController.java
@@ -0,0 +1,110 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-management
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.policy.drools.system;
+
+import java.util.List;
+import java.util.Properties;
+
+import org.openecomp.policy.drools.controller.DroolsController;
+import org.openecomp.policy.drools.event.comm.TopicSource;
+import org.openecomp.policy.drools.event.comm.Topic.CommInfrastructure;
+import org.openecomp.policy.drools.event.comm.TopicSink;
+import org.openecomp.policy.drools.properties.Lockable;
+import org.openecomp.policy.drools.properties.Startable;
+import org.openecomp.policy.drools.protocol.configuration.DroolsConfiguration;
+
+/**
+ * A Policy Controller is the higher level unit of control. It corresponds to
+ * the ncomp equivalent of a controller. It provides management of underlying
+ * resources associated with the policy controller, which is a) communication
+ * infrastructure, and b) policy-core (drools) session infrastructure
+ *
+ */
+public interface PolicyController extends Startable, Lockable {
+
+ /**
+ * name of this Policy Controller
+ */
+ public String getName();
+
+ /**
+ * Get the topic readers of interest for this controller
+ */
+ public List<? extends TopicSource> getTopicSources();
+
+ /**
+ * Get the topic readers of interest for this controller
+ */
+ public List<? extends TopicSink> getTopicSinks();
+
+ /**
+ * Get the Drools Controller
+ */
+ public DroolsController getDrools();
+
+ /**
+ * update maven configuration
+ *
+ * @param newDroolsConfiguration new drools configuration
+ * @return true if the update was successful, false otherwise
+ */
+ public boolean updateDrools(DroolsConfiguration newDroolsConfiguration);
+
+ /**
+ * Get the Initialization Properties
+ */
+ public Properties getInitializationProperties();
+
+ /**
+ * Attempts delivering of an String over communication
+ * infrastructure "busType"
+ *
+ * @param eventBus Communication infrastructure identifier
+ * @param topic topic
+ * @param event the event object to send
+ *
+ * @return true if successful, false if a failure has occurred.
+ * @throws IllegalArgumentException when invalid or insufficient
+ * properties are provided
+ * @throws IllegalStateException when the engine is in a state where
+ * this operation is not permitted (ie. locked or stopped).
+ * @throws UnsupportedOperationException when the engine cannot deliver due
+ * to the functionality missing (ie. communication infrastructure
+ * not supported.
+ */
+ public boolean deliver(CommInfrastructure busType, String topic,
+ Object event)
+ throws IllegalArgumentException, IllegalStateException,
+ UnsupportedOperationException;
+
+ /**
+ * halts and permanently releases all resources
+ * @throws IllegalStateException
+ */
+ public void halt() throws IllegalStateException;
+
+ /**
+ * Factory that tracks and manages Policy Controllers
+ */
+ public static PolicyControllerFactory factory =
+ new IndexedPolicyControllerFactory();
+
+}
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/system/PolicyControllerFactory.java b/policy-management/src/main/java/org/openecomp/policy/drools/system/PolicyControllerFactory.java
new file mode 100644
index 00000000..e105bbb9
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/system/PolicyControllerFactory.java
@@ -0,0 +1,464 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-management
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.policy.drools.system;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Properties;
+
+import org.openecomp.policy.drools.controller.DroolsController;
+import org.openecomp.policy.common.logging.flexlogger.FlexLogger;
+import org.openecomp.policy.common.logging.flexlogger.Logger;
+import org.openecomp.policy.drools.protocol.configuration.DroolsConfiguration;
+import org.openecomp.policy.drools.system.internal.AggregatedPolicyController;
+
+/**
+ * Policy Controller Factory to manage controller creation, destruction,
+ * and retrieval for management interfaces
+ */
+public interface PolicyControllerFactory {
+ /**
+ * Build a controller from a properties file
+ *
+ * @param name the global name of this controller
+ * @param properties input parameters in form of properties for controller
+ * initialization.
+ *
+ * @return a Policy Controller
+ *
+ * @throws IllegalArgumentException invalid values provided in properties
+ */
+ public PolicyController build(String name, Properties properties)
+ throws IllegalArgumentException;
+
+ /**
+ * patches (updates) a controller from a critical configuration update.
+ *
+ * @param name
+ * @param configController
+ *
+ * @return a Policy Controller
+ */
+ public PolicyController patch(String name, DroolsConfiguration configController);
+
+ /**
+ * rebuilds (updates) a controller from a configuration update.
+ *
+ * @param controller
+ * @param configController
+ *
+ * @return a Policy Controller
+ */
+ public PolicyController patch(PolicyController controller,
+ DroolsConfiguration configController);
+
+ /**
+ * get PolicyController from DroolsController
+ *
+ * @param droolsController
+ * @return
+ * @throws IllegalArgumentException
+ * @throws IllegalStateException
+ */
+ public PolicyController get(DroolsController droolsController)
+ throws IllegalArgumentException, IllegalStateException;
+
+ /**
+ * Makes the Policy Controller identified by controllerName not operational, but
+ * does not delete its associated data
+ *
+ * @param controllerName name of the policy controller
+ * @throws IllegalArgumentException invalid arguments
+ */
+ public void shutdown(String controllerName) throws IllegalArgumentException;;
+
+ /**
+ * Makes the Policy Controller identified by controller not operational, but
+ * does not delete its associated data
+ *
+ * @param controller a Policy Controller
+ * @throws IllegalArgumentException invalid arguments
+ */
+ public void shutdown(PolicyController controller) throws IllegalArgumentException;
+
+ /**
+ * Releases all Policy Controllers from operation
+ */
+ public void shutdown();
+
+ /**
+ * Destroys this Policy Controller
+ *
+ * @param controllerName name of the policy controller
+ * @throws IllegalArgumentException invalid arguments
+ */
+ public void destroy(String controllerName) throws IllegalArgumentException;;
+
+ /**
+ * Destroys this Policy Controller
+ *
+ * @param controller a Policy Controller
+ * @throws IllegalArgumentException invalid arguments
+ */
+ public void destroy(PolicyController controller) throws IllegalArgumentException;
+
+ /**
+ * Releases all Policy Controller resources
+ */
+ public void destroy();
+
+ /**
+ * gets the Policy Controller identified by its name
+ *
+ * @param policyControllerName
+ * @return
+ * @throws IllegalArgumentException
+ * @throws IllegalStateException
+ */
+ public PolicyController get(String policyControllerName)
+ throws IllegalArgumentException, IllegalStateException;
+
+ /**
+ * gets the Policy Controller identified by group and artifact ids
+ *
+ * @param groupId group id
+ * @param artifactId artifact id
+ * @return
+ * @throws IllegalArgumentException
+ * @throws IllegalStateException
+ */
+ public PolicyController get(String groupId, String artifactId)
+ throws IllegalArgumentException, IllegalStateException;
+
+ /**
+ * returns the current inventory of Policy Controllers
+ *
+ * @return a list of Policy Controllers
+ */
+ public 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 Logger logger = FlexLogger.getLogger(PolicyControllerFactory.class);
+
+ /**
+ * Policy Controller Name Index
+ */
+ protected HashMap<String,PolicyController> policyControllers =
+ new HashMap<String,PolicyController>();
+
+ /**
+ * Group/Artifact Ids Index
+ */
+ protected HashMap<String,PolicyController> coordinates2Controller =
+ new HashMap<String,PolicyController>();
+
+ /**
+ * produces key for indexing controller names
+ *
+ * @param group group id
+ * @param artifactId artifact id
+ * @return index key
+ */
+ protected String toKey(String groupId, String artifactId) {
+ return groupId + ":" + artifactId;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized PolicyController build(String name, Properties properties)
+ throws IllegalArgumentException {
+
+ if (this.policyControllers.containsKey(name)) {
+ return this.policyControllers.get(name);
+ }
+
+ /* A PolicyController does not exist */
+
+ PolicyController controller =
+ new AggregatedPolicyController(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)
+ throws IllegalArgumentException {
+
+ if (name == null || name.isEmpty() || !this.policyControllers.containsKey(name)) {
+ throw new IllegalArgumentException("Invalid " + name);
+ }
+
+ if (droolsConfig == null)
+ throw new IllegalArgumentException("Invalid Drools Configuration");
+
+ PolicyController controller = this.get(name);
+
+ if (controller == null) {
+ logger.warn("A POLICY CONTROLLER of name " + name +
+ "does not exist for patch operation: " + droolsConfig);
+
+ throw new IllegalArgumentException("Not a valid controller of name " + name);
+ }
+
+ this.patch(controller, droolsConfig);
+
+ if (logger.isInfoEnabled())
+ logger.info("UPDATED drools configuration: " + droolsConfig + " on " + this);
+
+ return controller;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PolicyController patch(PolicyController controller, DroolsConfiguration droolsConfig)
+ throws IllegalArgumentException {
+
+ if (controller == null)
+ throw new IllegalArgumentException("Not a valid controller: null");
+
+ if (!controller.updateDrools(droolsConfig)) {
+ logger.warn("Cannot update drools configuration: " + droolsConfig + " on " + this);
+ throw new IllegalArgumentException("Cannot update drools configuration Drools Configuration");
+ }
+
+ if (logger.isInfoEnabled())
+ logger.info("UPDATED drools configuration: " + droolsConfig + " on " + this);
+
+ String coordinates = toKey(controller.getDrools().getGroupId(),
+ controller.getDrools().getArtifactId());
+
+ if (controller.getDrools().isBrained())
+ this.coordinates2Controller.put(coordinates, controller);
+
+ return controller;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void shutdown(String controllerName) throws IllegalArgumentException {
+
+ if (controllerName == null || controllerName.isEmpty()) {
+ throw new IllegalArgumentException("Invalid " + 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) throws IllegalArgumentException {
+ 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
+ * @return
+ * @throws IllegalArgumentException
+ */
+ protected void unmanage(PolicyController controller) throws IllegalArgumentException {
+ if (controller == null) {
+ throw new IllegalArgumentException("Invalid Controller");
+ }
+
+ synchronized(this) {
+ if (!this.policyControllers.containsKey(controller.getName())) {
+ return;
+ }
+ controller = this.policyControllers.remove(controller.getName());
+
+ String coordinates = toKey(controller.getDrools().getGroupId(),
+ controller.getDrools().getArtifactId());
+ this.coordinates2Controller.remove(coordinates);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void destroy(String controllerName) throws IllegalArgumentException {
+
+ if (controllerName == null || controllerName.isEmpty()) {
+ throw new IllegalArgumentException("Invalid " + 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) throws IllegalArgumentException {
+ 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) throws IllegalArgumentException, IllegalStateException {
+
+ if (name == null || name.isEmpty()) {
+ throw new IllegalArgumentException("Invalid " + name);
+ }
+
+ synchronized(this) {
+ if (this.policyControllers.containsKey(name)) {
+ return this.policyControllers.get(name);
+ } else {
+ throw new IllegalArgumentException("Invalid " + name);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PolicyController get(String groupId, String artifactId)
+ throws IllegalArgumentException, IllegalStateException {
+
+ 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 new IllegalArgumentException("Invalid " + key);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PolicyController get(DroolsController droolsController)
+ throws IllegalArgumentException, IllegalStateException {
+
+ 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() {
+ List<PolicyController> controllers =
+ new ArrayList<PolicyController>(this.policyControllers.values());
+ return controllers;
+ }
+
+}
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/system/PolicyEngine.java b/policy-management/src/main/java/org/openecomp/policy/drools/system/PolicyEngine.java
new file mode 100644
index 00000000..33f2a098
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/system/PolicyEngine.java
@@ -0,0 +1,1182 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-management
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.policy.drools.system;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.openecomp.policy.common.logging.eelf.MessageCodes;
+import org.openecomp.policy.common.logging.flexlogger.FlexLogger;
+import org.openecomp.policy.common.logging.flexlogger.Logger;
+import org.openecomp.policy.drools.controller.DroolsController;
+import org.openecomp.policy.drools.core.FeatureAPI;
+import org.openecomp.policy.drools.core.jmx.PdpJmxListener;
+import org.openecomp.policy.drools.event.comm.Topic;
+import org.openecomp.policy.drools.event.comm.Topic.CommInfrastructure;
+import org.openecomp.policy.drools.event.comm.TopicEndpoint;
+import org.openecomp.policy.drools.event.comm.TopicListener;
+import org.openecomp.policy.drools.event.comm.TopicSink;
+import org.openecomp.policy.drools.event.comm.TopicSource;
+import org.openecomp.policy.drools.http.server.HttpServletServer;
+import org.openecomp.policy.drools.persistence.SystemPersistence;
+import org.openecomp.policy.drools.properties.Lockable;
+import org.openecomp.policy.drools.properties.PolicyProperties;
+import org.openecomp.policy.drools.properties.Startable;
+import org.openecomp.policy.drools.protocol.coders.EventProtocolCoder;
+import org.openecomp.policy.drools.protocol.configuration.ControllerConfiguration;
+import org.openecomp.policy.drools.protocol.configuration.PdpdConfiguration;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+/**
+ * Policy Engine, the top abstraction for the Drools PDP Policy Engine.
+ * It abstracts away a Drools PDP Engine from management purposes.
+ * This is the best place to looking at the code from a top down approach.
+ * Other managed entities can be obtained from the PolicyEngine, hierarchically.
+ * <br>
+ * PolicyEngine 1 --- * PolicyController 1 --- 1 DroolsController 1 --- 1 PolicyContainer 1 --- * PolicySession
+ * <br>
+ * PolicyEngine 1 --- 1 TopicEndpointManager 1 -- * TopicReader 1 --- 1 UebTopicReader
+ * <br>
+ * PolicyEngine 1 --- 1 TopicEndpointManager 1 -- * TopicReader 1 --- 1 DmaapTopicReader
+ * <br>
+ * PolicyEngine 1 --- 1 TopicEndpointManager 1 -- * TopicWriter 1 --- 1 DmaapTopicWriter
+ * <br>
+ * PolicyEngine 1 --- 1 TopicEndpointManager 1 -- * TopicReader 1 --- 1 RestTopicReader
+ * <br>
+ * PolicyEngine 1 --- 1 TopicEndpointManager 1 -- * TopicWriter 1 --- 1 RestTopicWriter
+ * <br>
+ * PolicyEngine 1 --- 1 ManagementServer
+ */
+public interface PolicyEngine extends Startable, Lockable, TopicListener {
+
+ /**
+ * Default Config Server Port
+ */
+ public static final int CONFIG_SERVER_DEFAULT_PORT = 9696;
+
+ /**
+ * Default Config Server Hostname
+ */
+ public static final String CONFIG_SERVER_DEFAULT_HOST = "localhost";
+
+ /**
+ * configure the policy engine according to the given properties
+ *
+ * @param properties Policy Engine properties
+ * @throws IllegalArgumentException when invalid or insufficient
+ * properties are provided
+ */
+ public void configure(Properties properties) throws IllegalArgumentException;
+
+ /**
+ * registers a new Policy Controller with the Policy Engine
+ * initialized per properties.
+ *
+ * @param controller name
+ * @param properties properties to initialize the Policy Controller
+ * @throws IllegalArgumentException when invalid or insufficient
+ * properties are provided
+ * @throws IllegalStateException when the engine is in a state where
+ * this operation is not permitted.
+ * @return the newly instantiated Policy Controller
+ */
+ public PolicyController createPolicyController(String name, Properties properties)
+ throws IllegalArgumentException, IllegalStateException;
+
+ /**
+ * updates the Policy Engine with the given configuration
+ *
+ * @param configuration the configuration
+ * @return success or failure
+ * @throws IllegalArgumentException if invalid argument provided
+ * @throws IllegalStateException if the system is in an invalid state
+ */
+ public boolean configure(PdpdConfiguration configuration)
+ throws IllegalArgumentException, IllegalStateException;
+
+ /**
+ * updates a set of Policy Controllers with configuration information
+ *
+ * @param configuration
+ * @return
+ * @throws IllegalArgumentException
+ * @throws IllegalStateException
+ */
+ public List<PolicyController> updatePolicyControllers(List<ControllerConfiguration> configuration)
+ throws IllegalArgumentException, IllegalStateException;
+
+ /**
+ * updates an already existing Policy Controller with configuration information
+ *
+ * @param configuration configuration
+ *
+ * @return the updated Policy Controller
+ * @throws IllegalArgumentException in the configuration is invalid
+ * @throws IllegalStateException if the controller is in a bad state
+ * @throws Exception any other reason
+ */
+ public PolicyController updatePolicyController(ControllerConfiguration configuration)
+ throws Exception;
+
+ /**
+ * removes the Policy Controller identified by its name from the Policy Engine
+ *
+ * @param name name of the Policy Controller
+ * @return the removed Policy Controller
+ */
+ public void removePolicyController(String name);
+
+ /**
+ * removes a Policy Controller from the Policy Engine
+ * @param controller the Policy Controller to remove from the Policy Engine
+ */
+ public void removePolicyController(PolicyController controller);
+
+ /**
+ * returns a list of the available Policy Controllers
+ *
+ * @return list of Policy Controllers
+ */
+ public List<PolicyController> getPolicyControllers();
+
+ /**
+ * get unmanaged sources
+ *
+ * @return unmanaged sources
+ */
+ public List<TopicSource> getSources();
+
+ /**
+ * get unmanaged sinks
+ *
+ * @return unmanaged sinks
+ */
+ public List<TopicSink> getSinks();
+
+ /**
+ * get unmmanaged http servers list
+ * @return http servers
+ */
+ public List<HttpServletServer> getHttpServers();
+
+ /**
+ * get properties configuration
+ *
+ * @return properties objects
+ */
+ public Properties getProperties();
+
+ /**
+ * Attempts the dispatching of an "event" object
+ *
+ * @param topic topic
+ * @param event the event object to send
+ *
+ * @return true if successful, false if a failure has occurred.
+ * @throws IllegalArgumentException when invalid or insufficient
+ * properties are provided
+ * @throws IllegalStateException when the engine is in a state where
+ * this operation is not permitted (ie. locked or stopped).
+ */
+ public boolean deliver(String topic, Object event)
+ throws IllegalArgumentException, IllegalStateException;
+
+ /**
+ * Attempts the dispatching of an "event" object over communication
+ * infrastructure "busType"
+ *
+ * @param eventBus Communication infrastructure identifier
+ * @param topic topic
+ * @param event the event object to send
+ *
+ * @return true if successful, false if a failure has occurred.
+ * @throws IllegalArgumentException when invalid or insufficient
+ * properties are provided
+ * @throws IllegalStateException when the engine is in a state where
+ * this operation is not permitted (ie. locked or stopped).
+ * @throws UnsupportedOperationException when the engine cannot deliver due
+ * to the functionality missing (ie. communication infrastructure
+ * not supported.
+ */
+ public boolean deliver(String busType, String topic, Object event)
+ throws IllegalArgumentException, IllegalStateException,
+ UnsupportedOperationException;
+
+ /**
+ * Attempts the dispatching of an "event" object over communication
+ * infrastructure "busType"
+ *
+ * @param eventBus Communication infrastructure enum
+ * @param topic topic
+ * @param event the event object to send
+ *
+ * @return true if successful, false if a failure has occurred.
+ * @throws IllegalArgumentException when invalid or insufficient
+ * properties are provided
+ * @throws IllegalStateException when the engine is in a state where
+ * this operation is not permitted (ie. locked or stopped).
+ * @throws UnsupportedOperationException when the engine cannot deliver due
+ * to the functionality missing (ie. communication infrastructure
+ * not supported.
+ */
+ public boolean deliver(CommInfrastructure busType, String topic, Object event)
+ throws IllegalArgumentException, IllegalStateException,
+ UnsupportedOperationException;
+
+ /**
+ * Attempts delivering of an String over communication
+ * infrastructure "busType"
+ *
+ * @param eventBus Communication infrastructure identifier
+ * @param topic topic
+ * @param event the event object to send
+ *
+ * @return true if successful, false if a failure has occurred.
+ * @throws IllegalArgumentException when invalid or insufficient
+ * properties are provided
+ * @throws IllegalStateException when the engine is in a state where
+ * this operation is not permitted (ie. locked or stopped).
+ * @throws UnsupportedOperationException when the engine cannot deliver due
+ * to the functionality missing (ie. communication infrastructure
+ * not supported.
+ */
+ public boolean deliver(CommInfrastructure busType, String topic,
+ String event)
+ throws IllegalArgumentException, IllegalStateException,
+ UnsupportedOperationException;
+
+ /**
+ * Invoked when the host goes into the active state.
+ */
+ public void activate();
+
+ /**
+ * Invoked when the host goes into the standby state.
+ */
+ public void deactivate();
+
+ /**
+ * get policy controller names
+ *
+ * @return list of controller names
+ */
+ public List<String> getControllers();
+
+ /**
+ * Policy Engine Manager
+ */
+ public final static PolicyEngine manager = new PolicyEngineManager();
+}
+
+/**
+ * Policy Engine Manager Implementation
+ */
+class PolicyEngineManager implements PolicyEngine {
+ /**
+ * logger
+ */
+ private static Logger logger = FlexLogger.getLogger(PolicyEngineManager.class);
+
+ /**
+ * Is the Policy Engine running?
+ */
+ protected boolean alive = false;
+
+ /**
+ * Is the engine locked?
+ */
+ protected boolean locked = false;
+
+ /**
+ * Properties used to initialize the engine
+ */
+ protected Properties properties;
+
+ /**
+ * Policy Engine Sources
+ */
+ protected List<? extends TopicSource> sources = new ArrayList<>();
+
+ /**
+ * Policy Engine Sinks
+ */
+ protected List<? extends TopicSink> sinks = new ArrayList<>();
+
+ /**
+ * Policy Engine HTTP Servers
+ */
+ protected List<HttpServletServer> httpServers = new ArrayList<HttpServletServer>();
+
+ protected Gson decoder = new GsonBuilder().disableHtmlEscaping().create();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void configure(Properties properties) throws IllegalArgumentException {
+
+ if (properties == null) {
+ logger.warn("No properties provided");
+ throw new IllegalArgumentException("No properties provided");
+ }
+
+ this.properties = properties;
+
+ try {
+ this.sources = TopicEndpoint.manager.addTopicSources(properties);
+ for (TopicSource source: this.sources) {
+ source.register(this);
+ }
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, "PolicyEngine", "configure");
+ }
+
+ try {
+ this.sinks = TopicEndpoint.manager.addTopicSinks(properties);
+ } catch (IllegalArgumentException e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, "PolicyEngine", "configure");
+ }
+
+ try {
+ this.httpServers = HttpServletServer.factory.build(properties);
+ } catch (IllegalArgumentException e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, "PolicyEngine", "configure");
+ }
+
+ return;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PolicyController createPolicyController(String name, Properties properties)
+ throws IllegalArgumentException, IllegalStateException {
+
+ // check if a PROPERTY_CONTROLLER_NAME property is present
+ // if so, override the given name
+
+ String propertyControllerName = properties.getProperty(PolicyProperties.PROPERTY_CONTROLLER_NAME);
+ if (propertyControllerName != null && !propertyControllerName.isEmpty()) {
+ if (!propertyControllerName.equals(name)) {
+ throw new IllegalStateException("Proposed name (" + name +
+ ") and properties name (" + propertyControllerName +
+ ") don't match");
+ }
+ name = propertyControllerName;
+ }
+
+ // feature hook
+ for (FeatureAPI feature : FeatureAPI.impl.getList()) {
+ feature.beforeCreateController(name, properties);
+ }
+
+ PolicyController controller = PolicyController.factory.build(name, properties);
+ if (this.isLocked())
+ controller.lock();
+
+ // feature hook
+ for (FeatureAPI feature : FeatureAPI.impl.getList()) {
+ // NOTE: this should change to the actual controller object
+ feature.afterCreateController(name);
+ }
+
+ return controller;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean configure(PdpdConfiguration config) throws IllegalArgumentException, IllegalStateException {
+
+ if (config == null)
+ throw new IllegalArgumentException("No configuration provided");
+
+ String entity = config.getEntity();
+
+ switch (entity) {
+ case PdpdConfiguration.CONFIG_ENTITY_CONTROLLER:
+ /* only this one supported for now */
+ List<ControllerConfiguration> configControllers = config.getControllers();
+ if (configControllers == null || configControllers.isEmpty()) {
+ if (logger.isInfoEnabled())
+ logger.info("No controller configuration provided: " + config);
+ return false;
+ }
+ List<PolicyController> policyControllers = this.updatePolicyControllers(config.getControllers());
+ if (policyControllers == null || policyControllers.isEmpty())
+ return false;
+ else if (policyControllers.size() == configControllers.size())
+ return true;
+
+ return false;
+ default:
+ String msg = "Configuration Entity is not supported: " + entity;
+ logger.warn(msg);
+ throw new IllegalArgumentException(msg);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<PolicyController> updatePolicyControllers(List<ControllerConfiguration> configControllers)
+ throws IllegalArgumentException, IllegalStateException {
+
+ List<PolicyController> policyControllers = new ArrayList<PolicyController>();
+ if (configControllers == null || configControllers.isEmpty()) {
+ if (logger.isInfoEnabled())
+ logger.info("No controller configuration provided: " + configControllers);
+ return policyControllers;
+ }
+
+ for (ControllerConfiguration configController: configControllers) {
+ try {
+ PolicyController policyController = this.updatePolicyController(configController);
+ policyControllers.add(policyController);
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, "PolicyEngine", "updatePolicyControllers");
+ }
+ }
+
+ return policyControllers;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PolicyController updatePolicyController(ControllerConfiguration configController)
+ throws Exception {
+
+ if (configController == null)
+ throw new IllegalArgumentException("No controller configuration has been provided");
+
+ 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 {
+ String operation = configController.getOperation();
+ if (operation == null || operation.isEmpty()) {
+ logger.warn("operation must be provided");
+ throw new IllegalArgumentException("operation must be provided");
+ }
+
+ try {
+ policyController = PolicyController.factory.get(controllerName);
+ } catch (IllegalArgumentException e) {
+ // not found
+ logger.warn("Policy Controller " + controllerName + " not found");
+ }
+
+ 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 " + controllerName + " does not exist. " +
+ "Attempting recovery from disk");
+
+ Properties properties =
+ SystemPersistence.manager.getControllerProperties(controllerName);
+
+ /*
+ * returned properties cannot be null (per implementation)
+ * assert (properties != null)
+ */
+
+ if (properties == null) {
+ throw new IllegalArgumentException(controllerName + " is invalid");
+ }
+
+ logger.warn("controller " + controllerName + " being recovered. " +
+ "Reset controller's bad maven coordinates to brainless");
+
+ /*
+ * try to bring up bad controller in brainless mode,
+ * after having it working, apply the new create/update operation.
+ */
+ properties.setProperty(PolicyProperties.RULES_GROUPID, DroolsController.NO_GROUP_ID);
+ properties.setProperty(PolicyProperties.RULES_ARTIFACTID, DroolsController.NO_ARTIFACT_ID);
+ properties.setProperty(PolicyProperties.RULES_VERSION, DroolsController.NO_VERSION);
+
+ policyController = PolicyEngine.manager.createPolicyController(controllerName, properties);
+
+ /* fall through to do brain update operation*/
+ }
+
+ switch (operation) {
+ case ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_CREATE:
+ PolicyController.factory.patch(policyController, configController.getDrools());
+ break;
+ case ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UPDATE:
+ policyController.unlock();
+ PolicyController.factory.patch(policyController, configController.getDrools());
+ break;
+ case ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_LOCK:
+ policyController.lock();
+ break;
+ case ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UNLOCK:
+ policyController.unlock();
+ break;
+ default:
+ String msg = "Controller Operation Configuration is not supported: " +
+ operation + " for " + controllerName;
+ logger.warn(msg);
+ throw new IllegalArgumentException(msg);
+ }
+
+ return policyController;
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, "PolicyEngine", "updatePolicyController " + e.getMessage());
+ throw e;
+ } catch (LinkageError e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, "PolicyEngine", "updatePolicyController " + e.getMessage());
+ throw new IllegalStateException(e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean start() throws IllegalStateException {
+
+ if (this.locked) {
+ throw new IllegalStateException("Engine is locked");
+ }
+
+ // Features hook
+ for (FeatureAPI feature : FeatureAPI.impl.getList()) {
+ feature.beforeStartEngine();
+ }
+
+ synchronized(this) {
+ this.alive = true;
+ }
+
+ boolean success = true;
+
+ /* Start Policy Engine exclusively-owned (unmanaged) http servers */
+
+ for (HttpServletServer httpServer: this.httpServers) {
+ try {
+ if (!httpServer.start())
+ success = false;
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, httpServer.toString(), this.toString());
+ }
+ }
+ /* Start Policy Engine exclusively-owned (unmanaged) sources */
+
+ for (TopicSource source: this.sources) {
+ try {
+ if (!source.start())
+ success = false;
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, source.toString(), this.toString());
+ }
+ }
+
+ /* Start Policy Engine owned (unmanaged) sinks */
+
+ for (TopicSink sink: this.sinks) {
+ try {
+ if (!sink.start())
+ success = false;
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, sink.toString(), this.toString());
+ }
+ }
+
+ /* Start Policy Controllers */
+
+ List<PolicyController> controllers = PolicyController.factory.inventory();
+ for (PolicyController controller : controllers) {
+ try {
+ if (!controller.start())
+ success = false;
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, controller.toString(), this.toString());
+ success = false;
+ }
+ }
+
+ /* Start managed Topic Endpoints */
+
+ try {
+ if (!TopicEndpoint.manager.start())
+ success = false;
+ } catch (IllegalStateException e) {
+ String msg = "Topic Endpoint Manager is in an invalid state: " + e.getMessage() + " : " + this;
+ logger.warn(msg);
+ }
+
+
+ // Start the JMX listener
+
+ PdpJmxListener.start();
+
+ // Features hook
+ for (FeatureAPI feature : FeatureAPI.impl.getList()) {
+ feature.afterStartEngine();
+ }
+
+ return success;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean stop() {
+
+ /* stop regardless of the lock state */
+
+ synchronized(this) {
+ if (!this.alive)
+ return true;
+
+ this.alive = false;
+ }
+
+ boolean success = true;
+ List<PolicyController> controllers = PolicyController.factory.inventory();
+ for (PolicyController controller : controllers) {
+ try {
+ if (!controller.stop())
+ success = false;
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, controller.toString(), this.toString());
+ success = false;
+ }
+ }
+
+ /* Stop Policy Engine owned (unmanaged) sources */
+ for (TopicSource source: this.sources) {
+ try {
+ if (!source.stop())
+ success = false;
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, source.toString(), this.toString());
+ }
+ }
+
+ /* Stop Policy Engine owned (unmanaged) sinks */
+ for (TopicSink sink: this.sinks) {
+ try {
+ if (!sink.stop())
+ success = false;
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, sink.toString(), this.toString());
+ }
+ }
+
+ /* stop all managed topics sources and sinks */
+ if (!TopicEndpoint.manager.stop())
+ success = false;
+
+ /* stop all unmanaged http servers */
+ for (HttpServletServer httpServer: this.httpServers) {
+ try {
+ if (!httpServer.stop())
+ success = false;
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, httpServer.toString(), this.toString());
+ }
+ }
+
+ return success;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void shutdown() throws IllegalStateException {
+
+ synchronized(this) {
+ this.alive = false;
+ }
+
+ // feature hook reporting that the Policy Engine is being shut down
+ for (FeatureAPI feature : FeatureAPI.impl.getList()) {
+ feature.beforeShutdownEngine();
+ }
+
+ /* Shutdown Policy Engine owned (unmanaged) sources */
+ for (TopicSource source: this.sources) {
+ try {
+ source.shutdown();
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, source.toString(), this.toString());
+ }
+ }
+
+ /* Shutdown Policy Engine owned (unmanaged) sinks */
+ for (TopicSink sink: this.sinks) {
+ try {
+ sink.shutdown();
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, sink.toString(), this.toString());
+ }
+ }
+
+ /* Shutdown managed resources */
+ PolicyController.factory.shutdown();
+ TopicEndpoint.manager.shutdown();
+ HttpServletServer.factory.destroy();
+
+ // Stop the JMX listener
+
+ PdpJmxListener.stop();
+
+ // feature hook reporting that the Policy Engine has being shut down
+ for (FeatureAPI feature : FeatureAPI.impl.getList()) {
+ feature.afterShutdownEngine();
+ }
+
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(5000L);
+ } catch (InterruptedException e) {
+ logger.warn("InterruptedException while shutting down management server: " + this.toString());
+ }
+
+ /* shutdown all unmanaged http servers */
+ for (HttpServletServer httpServer: getHttpServers()) {
+ try {
+ httpServer.shutdown();
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, httpServer.toString(), this.toString());
+ }
+ }
+
+ try {
+ Thread.sleep(5000L);
+ } catch (InterruptedException e) {
+ logger.warn("InterruptedException while shutting down management server: " + this.toString());
+ }
+
+ System.exit(0);
+ }
+ }).start();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAlive() {
+ return this.alive;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean lock() {
+
+ synchronized(this) {
+ if (this.locked)
+ return true;
+
+ this.locked = true;
+ }
+
+ boolean success = true;
+ List<PolicyController> controllers = PolicyController.factory.inventory();
+ for (PolicyController controller : controllers) {
+ try {
+ success = controller.lock() && success;
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, controller.toString(), this.toString());
+ success = false;
+ }
+ }
+
+ success = TopicEndpoint.manager.lock();
+ return success;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean unlock() {
+ synchronized(this) {
+ if (!this.locked)
+ return true;
+
+ this.locked = false;
+ }
+
+ boolean success = true;
+ List<PolicyController> controllers = PolicyController.factory.inventory();
+ for (PolicyController controller : controllers) {
+ try {
+ success = controller.unlock() && success;
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, controller.toString(), this.toString());
+ success = false;
+ }
+ }
+
+ success = TopicEndpoint.manager.unlock();
+ return success;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isLocked() {
+ return this.locked;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void removePolicyController(String name) {
+ PolicyController.factory.destroy(name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void removePolicyController(PolicyController controller) {
+ PolicyController.factory.destroy(controller);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @JsonIgnore
+ @Override
+ public List<PolicyController> getPolicyControllers() {
+ return PolicyController.factory.inventory();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<String> getControllers() {
+ List<String> controllerNames = new ArrayList<String>();
+ for (PolicyController controller: PolicyController.factory.inventory()) {
+ controllerNames.add(controller.getName());
+ }
+ return controllerNames;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Properties getProperties() {
+ return this.properties;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public List<TopicSource> getSources() {
+ return (List<TopicSource>) this.sources;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public List<TopicSink> getSinks() {
+ return (List<TopicSink>) this.sinks;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<HttpServletServer> getHttpServers() {
+ return this.httpServers;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onTopicEvent(CommInfrastructure commType, String topic, String event) {
+ /* configuration request */
+ try {
+ PdpdConfiguration configuration = this.decoder.fromJson(event, PdpdConfiguration.class);
+ this.configure(configuration);
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, "CONFIGURATION ERROR IN PDP-D POLICY ENGINE: "+ event + ":" + e.getMessage() + ":" + this);
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean deliver(String topic, Object event)
+ throws IllegalArgumentException, IllegalStateException {
+
+ /*
+ * Note this entry point is usually from the DRL
+ */
+
+ if (topic == null || topic.isEmpty())
+ throw new IllegalArgumentException("Invalid Topic");
+
+ if (event == null)
+ throw new IllegalArgumentException("Invalid Event");
+
+ if (!this.isAlive())
+ throw new IllegalStateException("Policy Engine is stopped");
+
+ if (this.isLocked())
+ throw new IllegalStateException("Policy Engine is locked");
+
+ List<? extends TopicSink> sinks =
+ TopicEndpoint.manager.getTopicSinks(topic);
+ if (sinks == null || sinks.isEmpty() || sinks.size() > 1)
+ throw new IllegalStateException
+ ("Cannot ensure correct delivery on topic " + topic + ": " + sinks);
+
+ return this.deliver(sinks.get(0).getTopicCommInfrastructure(),
+ topic, event);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean deliver(String busType, String topic, Object event)
+ throws IllegalArgumentException, IllegalStateException,
+ UnsupportedOperationException {
+
+ /*
+ * 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");
+
+ if (event == null)
+ throw new IllegalArgumentException("Invalid Event");
+
+ boolean valid = false;
+ for (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("Policy Engine is stopped");
+
+ if (this.isLocked())
+ throw new IllegalStateException("Policy Engine is locked");
+
+
+ return this.deliver(Topic.CommInfrastructure.valueOf(busType),
+ topic, event);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean deliver(Topic.CommInfrastructure busType,
+ String topic, Object event)
+ throws IllegalArgumentException, IllegalStateException,
+ UnsupportedOperationException {
+
+ if (topic == null || topic.isEmpty())
+ throw new IllegalArgumentException("Invalid Topic");
+
+ if (event == null)
+ throw new IllegalArgumentException("Invalid Event");
+
+ if (!this.isAlive())
+ throw new IllegalStateException("Policy Engine is stopped");
+
+ if (this.isLocked())
+ throw new IllegalStateException("Policy Engine is locked");
+
+ /* Try to send through the controller, this is the
+ * preferred way, since it may want to apply additional
+ * processing
+ */
+ try {
+ DroolsController droolsController =
+ EventProtocolCoder.manager.getDroolsController(topic, event);
+ PolicyController controller = PolicyController.factory.get(droolsController);
+ if (controller != null)
+ return controller.deliver(busType, topic, event);
+ } catch (Exception e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ busType + ":" + topic + " :" + event, this.toString());
+ /* continue (try without routing through the controller) */
+ }
+
+ /*
+ * cannot route through the controller, send directly through
+ * the topic sink
+ */
+ try {
+ String json = EventProtocolCoder.manager.encode(topic, event);
+ return this.deliver(busType, topic, json);
+
+ } catch (Exception e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ busType + ":" + topic + " :" + event, this.toString());
+ throw e;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean deliver(Topic.CommInfrastructure busType,
+ String topic, String event)
+ throws IllegalArgumentException, IllegalStateException,
+ UnsupportedOperationException {
+
+ if (topic == null || topic.isEmpty())
+ throw new IllegalArgumentException("Invalid Topic");
+
+ if (event == null || event.isEmpty())
+ throw new IllegalArgumentException("Invalid Event");
+
+ if (!this.isAlive())
+ throw new IllegalStateException("Policy Engine is stopped");
+
+ if (this.isLocked())
+ throw new IllegalStateException("Policy Engine is locked");
+
+ try {
+ TopicSink sink =
+ TopicEndpoint.manager.getTopicSink
+ (busType, topic);
+
+ if (sink == null)
+ throw new IllegalStateException("Inconsistent State: " + this);
+
+ return sink.send(event);
+
+ } catch (Exception e) {
+ logger.warn(MessageCodes.EXCEPTION_ERROR, e,
+ busType + ":" + topic + " :" + event, this.toString());
+ throw e;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void activate() {
+
+ // activate 'policy-management'
+ for (PolicyController policyController : getPolicyControllers()) {
+ try {
+ policyController.unlock();
+ policyController.start();
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, "PolicyEngine.activate: cannot start " +
+ policyController + " because of " + e.getMessage());
+ } catch (LinkageError e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, "PolicyEngine.activate: cannot start " +
+ policyController + " because of " + e.getMessage());
+ }
+ }
+
+ this.unlock();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void deactivate() {
+
+ this.lock();
+
+ for (PolicyController policyController : getPolicyControllers()) {
+ try {
+ policyController.stop();
+ } catch (Exception e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, "PolicyEngine.deactivate: cannot stop " +
+ policyController + " because of " + e.getMessage());
+ } catch (LinkageError e) {
+ logger.error(MessageCodes.EXCEPTION_ERROR, e, "PolicyEngine.deactivate: cannot start " +
+ policyController + " because of " + e.getMessage());
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("PolicyEngineManager [alive=").append(alive).append(", locked=").append(locked).append("]");
+ return builder.toString();
+ }
+
+}
+
+
diff --git a/policy-management/src/main/java/org/openecomp/policy/drools/system/internal/AggregatedPolicyController.java b/policy-management/src/main/java/org/openecomp/policy/drools/system/internal/AggregatedPolicyController.java
new file mode 100644
index 00000000..96f9e5bf
--- /dev/null
+++ b/policy-management/src/main/java/org/openecomp/policy/drools/system/internal/AggregatedPolicyController.java
@@ -0,0 +1,462 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-management
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.openecomp.policy.drools.system.internal;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Properties;
+
+import org.openecomp.policy.drools.controller.DroolsController;
+import org.openecomp.policy.drools.event.comm.Topic;
+import org.openecomp.policy.drools.event.comm.TopicEndpoint;
+import org.openecomp.policy.drools.event.comm.TopicListener;
+import org.openecomp.policy.drools.event.comm.TopicSink;
+import org.openecomp.policy.drools.event.comm.TopicSource;
+import org.openecomp.policy.common.logging.flexlogger.FlexLogger;
+import org.openecomp.policy.common.logging.flexlogger.Logger;
+import org.openecomp.policy.drools.persistence.SystemPersistence;
+import org.openecomp.policy.drools.properties.PolicyProperties;
+import org.openecomp.policy.drools.protocol.configuration.DroolsConfiguration;
+import org.openecomp.policy.drools.system.PolicyController;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+/**
+ * This implementation of the Policy Controller merely aggregates and tracks for
+ * management purposes all underlying resources that this controller depends upon.
+ */
+public class AggregatedPolicyController implements PolicyController,
+ TopicListener {
+
+ /**
+ * Logger
+ */
+ private static Logger logger = FlexLogger.getLogger(AggregatedPolicyController.class);
+
+ /**
+ * identifier for this policy controller
+ */
+ protected final String name;
+
+ /**
+ * Abstracted Event Sources List regardless communication
+ * technology
+ */
+ protected final List<? extends TopicSource> sources;
+
+ /**
+ * Abstracted Event Sinks List regardless communication
+ * technology
+ */
+ protected final List<? extends TopicSink> sinks;
+
+ /**
+ * Mapping topics to sinks
+ */
+ @JsonIgnore
+ protected final HashMap<String, TopicSink> topic2Sinks =
+ new HashMap<String, TopicSink>();
+
+ /**
+ * Is this Policy Controller running (alive) ?
+ * reflects invocation of start()/stop() only
+ */
+ protected volatile boolean alive;
+
+ /**
+ * Is this Policy Controller locked ?
+ * reflects if i/o controller related operations and start
+ * are permitted,
+ * more specifically: start(), deliver() and onTopicEvent().
+ * It does not affect the ability to stop the
+ * underlying drools infrastructure
+ */
+ protected volatile boolean locked;
+
+ /**
+ * Policy Drools Controller
+ */
+ protected volatile DroolsController droolsController;
+
+ /**
+ * Properties used to initialize controller
+ */
+ protected final Properties properties;
+
+ /**
+ * Constructor version mainly used for bootstrapping at initialization time
+ * a policy engine controller
+ *
+ * @param name controller name
+ * @param properties
+ *
+ * @throws IllegalArgumentException when invalid arguments are provided
+ */
+ public AggregatedPolicyController(String name, Properties properties)
+ throws IllegalArgumentException {
+
+ this.name = name;
+
+ /*
+ * 1. Register read topics with network infrastructure (ueb, dmaap, rest)
+ * 2. Register write topics with network infrastructure (ueb, dmaap, rest)
+ * 3. Register with drools infrastructure
+ */
+
+ // Create/Reuse Readers/Writers for all event sources endpoints
+
+ this.sources = TopicEndpoint.manager.addTopicSources(properties);
+ this.sinks = TopicEndpoint.manager.addTopicSinks(properties);
+
+ initDrools(properties);
+ initSinks();
+
+ /* persist new properties */
+ SystemPersistence.manager.storeController(name, properties);
+ this.properties = properties;
+ }
+
+ /**
+ * initialize drools layer
+ * @throws IllegalArgumentException if invalid parameters are passed in
+ */
+ protected void initDrools(Properties properties) throws IllegalArgumentException {
+ try {
+ // Register with drools infrastructure
+ this.droolsController = DroolsController.factory.build(properties, sources, sinks);
+ } catch (Exception | LinkageError e) {
+ logger.error("BUILD-INIT-DROOLS: " + e.getMessage());
+ e.printStackTrace();
+
+ // throw back exception as input properties cause problems
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ /**
+ * initialize sinks
+ * @throws IllegalArgumentException if invalid parameters are passed in
+ */
+ protected void initSinks() throws IllegalArgumentException {
+ this.topic2Sinks.clear();
+ for (TopicSink sink: sinks) {
+ this.topic2Sinks.put(sink.getTopic(), sink);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean updateDrools(DroolsConfiguration newDroolsConfiguration) {
+
+ DroolsConfiguration oldDroolsConfiguration =
+ new DroolsConfiguration(this.droolsController.getArtifactId(),
+ this.droolsController.getGroupId(),
+ this.droolsController.getVersion());
+
+ if (oldDroolsConfiguration.getGroupId().equalsIgnoreCase(newDroolsConfiguration.getGroupId()) &&
+ oldDroolsConfiguration.getArtifactId().equalsIgnoreCase(newDroolsConfiguration.getArtifactId()) &&
+ oldDroolsConfiguration.getVersion().equalsIgnoreCase(newDroolsConfiguration.getVersion())) {
+ logger.warn("UPDATE-DROOLS: nothing to do: identical configuration: " + oldDroolsConfiguration +
+ " <=> " + newDroolsConfiguration);
+ return true;
+ }
+
+ try {
+ /* Drools Controller created, update initialization properties for restarts */
+
+ this.properties.setProperty(PolicyProperties.RULES_GROUPID, newDroolsConfiguration.getGroupId());
+ this.properties.setProperty(PolicyProperties.RULES_ARTIFACTID, newDroolsConfiguration.getArtifactId());
+ this.properties.setProperty(PolicyProperties.RULES_VERSION, newDroolsConfiguration.getVersion());
+
+ SystemPersistence.manager.storeController(name, this.properties);
+
+ this.initDrools(this.properties);
+
+ /* set drools controller to current locked status */
+
+ if (this.isLocked())
+ this.droolsController.lock();
+ else
+ this.droolsController.unlock();
+
+ /* set drools controller to current alive status */
+
+ if (this.isAlive())
+ this.droolsController.start();
+ else
+ this.droolsController.stop();
+
+ } catch (IllegalArgumentException e) {
+ logger.warn("INIT-DROOLS: " + e.getMessage());
+ e.printStackTrace();
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean start() throws IllegalStateException {
+ if (logger.isInfoEnabled())
+ logger.info("START: " + this);
+
+ if (this.isLocked())
+ throw new IllegalStateException("Policy Controller " + name + " is locked");
+
+ synchronized(this) {
+ if (this.alive)
+ return true;
+
+ this.alive = true;
+ }
+
+ boolean success = this.droolsController.start();
+
+ // register for events
+
+ for (TopicSource source: sources) {
+ source.register(this);
+ }
+
+ for (TopicSink sink: sinks) {
+ try {
+ sink.start();
+ } catch (Exception e) {
+ logger.warn("can't start sink: " + sink + " because of " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ return success;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean stop() {
+
+ logger.info("STOP: " + this);
+
+ /* stop regardless locked state */
+
+ synchronized(this) {
+ if (!this.alive)
+ return true;
+
+ this.alive = false;
+ }
+
+ // 1. Stop registration
+
+ for (TopicSource source: sources) {
+ source.unregister(this);
+ }
+
+ boolean success = this.droolsController.stop();
+ return success;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void shutdown() throws IllegalStateException {
+ if (logger.isInfoEnabled())
+ logger.info(this + "SHUTDOWN");
+
+ this.stop();
+
+ DroolsController.factory.shutdown(this.droolsController);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void halt() throws IllegalStateException {
+ if (logger.isInfoEnabled())
+ logger.info(this + "HALT");
+
+ this.stop();
+ DroolsController.factory.destroy(this.droolsController);
+ SystemPersistence.manager.deleteController(this.name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onTopicEvent(Topic.CommInfrastructure commType,
+ String topic, String event) {
+
+ logger.info("EVENT NOTIFICATION: " + commType + ":" + topic + ":" + event + " INTO " + this);
+
+ if (this.locked)
+ return false;
+
+ if (!this.alive)
+ return true;
+
+ return this.droolsController.offer(topic, event);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean deliver(Topic.CommInfrastructure commType,
+ String topic, Object event)
+ throws IllegalArgumentException, IllegalStateException,
+ UnsupportedOperationException {
+
+ logger.info("DELIVER: " + commType + ":" + topic + ":" + event + " FROM " + this);
+
+ if (topic == null || topic.isEmpty())
+ throw new IllegalArgumentException("Invalid Topic");
+
+ if (event == null)
+ throw new IllegalArgumentException("Invalid Event");
+
+ if (!this.isAlive())
+ throw new IllegalStateException("Policy Engine is stopped");
+
+ if (this.isLocked())
+ throw new IllegalStateException("Policy Engine is locked");
+
+ if (!this.topic2Sinks.containsKey(topic)) {
+ logger.error("UNDELIVERED: " + commType + ":" + topic + ":" + event + " FROM " + this);
+ throw new IllegalArgumentException
+ ("Unsuported topic " + topic + " for delivery");
+ }
+
+ return this.droolsController.deliver
+ (this.topic2Sinks.get(topic), event);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isAlive() {
+ return this.alive;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean lock() {
+ logger.info("LOCK: " + this);
+
+ synchronized(this) {
+ if (this.locked)
+ return true;
+
+ this.locked = true;
+ }
+
+ // it does not affect associated sources/sinks, they are
+ // autonomous entities
+
+ return this.droolsController.lock();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean unlock() {
+ logger.info("UNLOCK: " + this);
+
+ synchronized(this) {
+ if (!this.locked)
+ return true;
+
+ this.locked = false;
+ }
+
+ return this.droolsController.unlock();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isLocked() {
+ return this.locked;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<? extends TopicSource> getTopicSources() {
+ return this.sources;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<? extends TopicSink> getTopicSinks() {
+ return this.sinks;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public DroolsController getDrools() {
+ return this.droolsController;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("AggregatedPolicyController [name=").append(name).append(", sources=").append(sources)
+ .append(", sinks=").append(sinks).append(", alive=").append(alive).append(", locked=").append(locked)
+ .append(", droolsController=").append(droolsController).append("]");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Properties getInitializationProperties() {
+ return this.properties;
+ }
+
+}
+