2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.policy.drools.controller;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Properties;
28 import org.onap.policy.common.endpoints.event.comm.Topic;
29 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
30 import org.onap.policy.common.endpoints.event.comm.TopicSink;
31 import org.onap.policy.common.endpoints.event.comm.TopicSource;
32 import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
33 import org.onap.policy.drools.controller.internal.MavenDroolsController;
34 import org.onap.policy.drools.controller.internal.NullDroolsController;
35 import org.onap.policy.drools.properties.DroolsProperties;
36 import org.onap.policy.drools.protocol.coders.JsonProtocolFilter;
37 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration;
38 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
39 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.PotentialCoderFilter;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
44 * Factory of Drools Controllers indexed by the Maven coordinates.
46 class IndexedDroolsControllerFactory implements DroolsControllerFactory {
51 private static Logger logger = LoggerFactory.getLogger(MavenDroolsController.class);
54 * Policy Controller Name Index.
56 protected HashMap<String, DroolsController> droolsControllers = new HashMap<>();
59 * Null Drools Controller.
61 protected NullDroolsController nullDroolsController = new NullDroolsController();
64 * Constructs the object.
66 public IndexedDroolsControllerFactory() {
68 /* Add a NULL controller which will always be present in the hash */
70 DroolsController controller = new NullDroolsController();
71 String controllerId = controller.getGroupId() + ":" + controller.getArtifactId();
74 droolsControllers.put(controllerId, controller);
79 public DroolsController build(Properties properties, List<? extends TopicSource> eventSources,
80 List<? extends TopicSink> eventSinks) throws LinkageError {
82 String groupId = properties.getProperty(DroolsProperties.RULES_GROUPID);
83 if (groupId == null || groupId.isEmpty()) {
84 groupId = DroolsController.NO_GROUP_ID;
87 String artifactId = properties.getProperty(DroolsProperties.RULES_ARTIFACTID);
88 if (artifactId == null || artifactId.isEmpty()) {
89 artifactId = DroolsController.NO_ARTIFACT_ID;
92 String version = properties.getProperty(DroolsProperties.RULES_VERSION);
93 if (version == null || version.isEmpty()) {
94 version = DroolsController.NO_VERSION;
97 List<TopicCoderFilterConfiguration> topics2DecodedClasses2Filters = codersAndFilters(properties, eventSources);
99 List<TopicCoderFilterConfiguration> topics2EncodedClasses2Filters = codersAndFilters(properties, eventSinks);
101 return this.build(groupId, artifactId, version, topics2DecodedClasses2Filters, topics2EncodedClasses2Filters);
105 public DroolsController build(String newGroupId, String newArtifactId, String newVersion,
106 List<TopicCoderFilterConfiguration> decoderConfigurations,
107 List<TopicCoderFilterConfiguration> encoderConfigurations) throws LinkageError {
109 if (newGroupId == null || newGroupId.isEmpty()) {
110 throw new IllegalArgumentException("Missing maven group-id coordinate");
113 if (newArtifactId == null || newArtifactId.isEmpty()) {
114 throw new IllegalArgumentException("Missing maven artifact-id coordinate");
117 if (newVersion == null || newVersion.isEmpty()) {
118 throw new IllegalArgumentException("Missing maven version coordinate");
121 String controllerId = newGroupId + ":" + newArtifactId;
122 DroolsController controllerCopy = null;
123 synchronized (this) {
125 * The Null Drools Controller for no maven coordinates is always here so when no
126 * coordinates present, this is the return point
128 * assert (controllerCopy instanceof NullDroolsController)
130 if (droolsControllers.containsKey(controllerId)) {
131 controllerCopy = droolsControllers.get(controllerId);
132 if (controllerCopy.getVersion().equalsIgnoreCase(newVersion)) {
133 return controllerCopy;
138 if (controllerCopy != null) {
140 * a controller keyed by group id + artifact id exists but with different version =>
141 * version upgrade/downgrade
144 controllerCopy.updateToVersion(newGroupId, newArtifactId, newVersion, decoderConfigurations,
145 encoderConfigurations);
147 return controllerCopy;
150 /* new drools controller */
152 DroolsController controller = new MavenDroolsController(newGroupId, newArtifactId, newVersion,
153 decoderConfigurations, encoderConfigurations);
155 synchronized (this) {
156 droolsControllers.put(controllerId, controller);
163 * find out decoder classes and filters.
165 * @param properties properties with information about decoders
166 * @param topicEntities topic sources
167 * @return list of topics, each with associated decoder classes, each with a list of associated
169 * @throws IllegalArgumentException invalid input data
171 protected List<TopicCoderFilterConfiguration> codersAndFilters(Properties properties,
172 List<? extends Topic> topicEntities) {
174 String propertyTopicEntityPrefix;
176 List<TopicCoderFilterConfiguration> topics2DecodedClasses2Filters = new ArrayList<>();
178 if (topicEntities == null || topicEntities.isEmpty()) {
179 return topics2DecodedClasses2Filters;
182 for (Topic topic : topicEntities) {
184 /* source or sink ? ueb or dmaap? */
185 boolean isSource = topic instanceof TopicSource;
186 CommInfrastructure commInfra = topic.getTopicCommInfrastructure();
187 if (commInfra == CommInfrastructure.UEB) {
189 propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_UEB_SOURCE_TOPICS + ".";
191 propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_UEB_SINK_TOPICS + ".";
193 } else if (commInfra == CommInfrastructure.DMAAP) {
195 propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_DMAAP_SOURCE_TOPICS + ".";
197 propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_DMAAP_SINK_TOPICS + ".";
199 } else if (commInfra == CommInfrastructure.NOOP) {
201 propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_NOOP_SOURCE_TOPICS + ".";
203 propertyTopicEntityPrefix = PolicyEndPointProperties.PROPERTY_NOOP_SINK_TOPICS + ".";
206 throw new IllegalArgumentException("Invalid Communication Infrastructure: " + commInfra);
209 // 1. first the topic
211 String firstTopic = topic.getTopic();
213 // 2. check if there is a custom decoder for this topic that the user prefers to use
214 // instead of the ones provided in the platform
216 String customGson = properties.getProperty(propertyTopicEntityPrefix + firstTopic
217 + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_CUSTOM_MODEL_CODER_GSON_SUFFIX);
219 CustomGsonCoder customGsonCoder = null;
220 if (customGson != null && !customGson.isEmpty()) {
222 customGsonCoder = new CustomGsonCoder(customGson);
223 } catch (IllegalArgumentException e) {
224 logger.warn("{}: cannot create custom-gson-coder {} because of {}", this, customGson,
229 // 3. second the list of classes associated with each topic
231 String eventClasses = properties
232 .getProperty(propertyTopicEntityPrefix + firstTopic
233 + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_SUFFIX);
235 if (eventClasses == null || eventClasses.isEmpty()) {
236 logger.warn("There are no event classes for topic {}", firstTopic);
240 List<PotentialCoderFilter> classes2Filters = new ArrayList<>();
242 List<String> topicClasses = new ArrayList<>(Arrays.asList(eventClasses.split("\\s*,\\s*")));
244 for (String theClass : topicClasses) {
247 // 4. third, for each coder class, get the filter expression
249 String filter = properties
250 .getProperty(propertyTopicEntityPrefix + firstTopic
251 + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_SUFFIX
252 + "." + theClass + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_FILTER_SUFFIX);
254 JsonProtocolFilter protocolFilter = new JsonProtocolFilter(filter);
255 PotentialCoderFilter class2Filters = new PotentialCoderFilter(theClass, protocolFilter);
256 classes2Filters.add(class2Filters);
259 TopicCoderFilterConfiguration topic2Classes2Filters =
260 new TopicCoderFilterConfiguration(firstTopic, classes2Filters, customGsonCoder);
261 topics2DecodedClasses2Filters.add(topic2Classes2Filters);
264 return topics2DecodedClasses2Filters;
268 public void destroy(DroolsController controller) {
269 unmanage(controller);
274 public void destroy() {
275 List<DroolsController> controllers = this.inventory();
276 for (DroolsController controller : controllers) {
280 synchronized (this) {
281 this.droolsControllers.clear();
286 * unmanage the drools controller.
288 * @param controller the controller
290 protected void unmanage(DroolsController controller) {
291 if (controller == null) {
292 throw new IllegalArgumentException("No controller provided");
295 if (!controller.isBrained()) {
296 logger.info("Drools Controller is NOT OPERATIONAL - nothing to destroy");
300 String controllerId = controller.getGroupId() + ":" + controller.getArtifactId();
301 synchronized (this) {
302 if (!this.droolsControllers.containsKey(controllerId)) {
306 droolsControllers.remove(controllerId);
311 public void shutdown(DroolsController controller) {
312 this.unmanage(controller);
313 controller.shutdown();
317 public void shutdown() {
318 List<DroolsController> controllers = this.inventory();
319 for (DroolsController controller : controllers) {
320 controller.shutdown();
323 synchronized (this) {
324 this.droolsControllers.clear();
329 public DroolsController get(String groupId, String artifactId, String version) {
331 if (groupId == null || artifactId == null || groupId.isEmpty() || artifactId.isEmpty()) {
332 throw new IllegalArgumentException("Missing maven coordinates: " + groupId + ":" + artifactId);
335 String controllerId = groupId + ":" + artifactId;
337 synchronized (this) {
338 if (this.droolsControllers.containsKey(controllerId)) {
339 return droolsControllers.get(controllerId);
341 throw new IllegalStateException("DroolController for " + controllerId + " not found");
347 public List<DroolsController> inventory() {
348 return new ArrayList<>(this.droolsControllers.values());
352 public String toString() {
353 StringBuilder builder = new StringBuilder();
354 builder.append("IndexedDroolsControllerFactory [#droolsControllers=").append(droolsControllers.size())
356 return builder.toString();