diff options
author | 2020-08-31 20:53:44 +0000 | |
---|---|---|
committer | 2020-08-31 20:53:44 +0000 | |
commit | a588736799d94747f70ed648d3d821210993c5c4 (patch) | |
tree | f54ea646db61861985fe6ae87b76d87e8a9369a2 /controlloop/common/controller-tdjam | |
parent | 1c5cb8a0d740ccd92d2b3fdce8eb192cd20b147f (diff) | |
parent | a6d4077e3639a0f3478f0cbf51e06ef46517a10d (diff) |
Merge "Add tdjam-controller"
Diffstat (limited to 'controlloop/common/controller-tdjam')
15 files changed, 2855 insertions, 0 deletions
diff --git a/controlloop/common/controller-tdjam/pom.xml b/controlloop/common/controller-tdjam/pom.xml new file mode 100644 index 000000000..02483d340 --- /dev/null +++ b/controlloop/common/controller-tdjam/pom.xml @@ -0,0 +1,245 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ============LICENSE_START======================================================= + ONAP + ================================================================================ + Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + ================================================================================ + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + ============LICENSE_END========================================================= + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onap.policy.drools-applications.controlloop.common</groupId> + <artifactId>drools-applications-common</artifactId> + <version>1.7.1-SNAPSHOT</version> + </parent> + + <artifactId>controller-tdjam</artifactId> + <packaging>kjar</packaging> + + <name>${project.artifactId}</name> + + <build> + <plugins> + <plugin> + <groupId>org.kie</groupId> + <artifactId>kie-maven-plugin</artifactId> + <extensions>true</extensions> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId> + <artifactId>events</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId> + <artifactId>aai</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId> + <artifactId>appc</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId> + <artifactId>appclcm</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId> + <artifactId>cds</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.drools-applications.controlloop.common</groupId> + <artifactId>guard</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId> + <artifactId>sdc</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId> + <artifactId>sdnc</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId> + <artifactId>sdnr</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId> + <artifactId>so</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-impl</groupId> + <artifactId>vfc</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.drools-applications.controlloop.common</groupId> + <artifactId>eventmanager</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId> + <artifactId>actorServiceProvider</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId> + <artifactId>actor.aai</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId> + <artifactId>actor.appc</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId> + <artifactId>actor.appclcm</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId> + <artifactId>actor.cds</artifactId> + <version>${policy.models.version}</version> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId> + <artifactId>actor.guard</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId> + <artifactId>actor.sdnc</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId> + <artifactId>actor.sdnr</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId> + <artifactId>actor.so</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions.model-actors</groupId> + <artifactId>actor.vfc</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.models.policy-models-interactions</groupId> + <artifactId>model-yaml</artifactId> + <version>${policy.models.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.onap.policy.drools-pdp</groupId> + <artifactId>policy-management</artifactId> + <version>${version.policy.drools-pdp}</version> + <scope>provided</scope> + <!-- <optional>true</optional> --> + </dependency> + <dependency> + <groupId>org.onap.policy.drools-applications.controlloop.common</groupId> + <artifactId>rules-test</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <profiles> + <profile> + <!--This profile is used to store Eclipse m2e settings only. It has no + influence on the Maven build itself. --> + <id>only-eclipse</id> + <activation> + <property> + <name>m2e.version</name> + </property> + </activation> + <build> + <pluginManagement> + <plugins> + <plugin> + <groupId>org.eclipse.m2e</groupId> + <artifactId>lifecycle-mapping</artifactId> + <version>1.0.0</version> + <configuration> + <lifecycleMappingMetadata> + <pluginExecutions> + <pluginExecution> + <pluginExecutionFilter> + <groupId>org.kie</groupId> + <artifactId>kie-maven-plugin</artifactId> + <goals> + <goal>build</goal> + </goals> + </pluginExecutionFilter> + <action> + <ignore /> + </action> + </pluginExecution> + </pluginExecutions> + </lifecycleMappingMetadata> + </configuration> + </plugin> + </plugins> + </pluginManagement> + </build> + </profile> + </profiles> +</project> diff --git a/controlloop/common/controller-tdjam/src/main/java/org/onap/policy/controlloop/tdjam/SerialWorkQueue.java b/controlloop/common/controller-tdjam/src/main/java/org/onap/policy/controlloop/tdjam/SerialWorkQueue.java new file mode 100644 index 000000000..7d83765a3 --- /dev/null +++ b/controlloop/common/controller-tdjam/src/main/java/org/onap/policy/controlloop/tdjam/SerialWorkQueue.java @@ -0,0 +1,123 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop.tdjam; + +import java.util.LinkedList; +import lombok.Getter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class provides a way to handle synchronization, with minimal blocking. Requests + * are queued until {@link #start()} is invoked. + */ +public class SerialWorkQueue { + private static Logger logger = LoggerFactory.getLogger(SerialWorkQueue.class); + + // current work list + private LinkedList<Runnable> workQueue; + + @Getter + private boolean running = false; + + /** + * Constructor - no initial Runnable. + */ + public SerialWorkQueue() { + workQueue = new LinkedList<>(); + } + + /** + * Constructor - initial 'Runnable' is specified. + * + * @param runnable an initial 'Runnnable' to run + */ + public SerialWorkQueue(Runnable runnable) { + workQueue = new LinkedList<>(); + workQueue.add(runnable); + } + + /** + * Starts the queue. If the current thread is the first to start it, then the current + * thread will process any requests in the queue before returning. + */ + public void start() { + Runnable item; + + synchronized (this) { + if (running) { + // already running + return; + } + + running = true; + item = workQueue.peekFirst(); + } + + if (item != null) { + processQueue(item); + } + } + + /** + * Called to add a 'Runnable' to the work queue. If the queue was empty, the current + * thread is used to process the queue. + * + * @param work the Runnable to be queued, and eventually run + */ + public void queueAndRun(Runnable work) { + synchronized (this) { + workQueue.add(work); + if (!running || workQueue.size() > 1) { + // there was already work in the queue, so presumably there is + // already an associated thread running + return; + } + // if we reach this point, the queue was empty when this method was + // called, so this thread will process the queue + } + + processQueue(work); + } + + /** + * Internal method to process the work queue until it is empty. Note that entries + * could be added by this thread or another one while we are working. + * + * @param firstItem the first item in the queue + */ + private void processQueue(Runnable firstItem) { + Runnable next = firstItem; + while (next != null) { + try { + next.run(); + } catch (Exception e) { + logger.error("SerialWorkQueue.processQueue exception", e); + } + + synchronized (this) { + // remove the job we just ran + workQueue.removeFirst(); + next = workQueue.peekFirst(); + } + } + } +} diff --git a/controlloop/common/controller-tdjam/src/main/java/org/onap/policy/controlloop/tdjam/TdjamController.java b/controlloop/common/controller-tdjam/src/main/java/org/onap/policy/controlloop/tdjam/TdjamController.java new file mode 100644 index 000000000..0b17f196f --- /dev/null +++ b/controlloop/common/controller-tdjam/src/main/java/org/onap/policy/controlloop/tdjam/TdjamController.java @@ -0,0 +1,833 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop.tdjam; + +import static org.onap.policy.drools.properties.DroolsPropertyConstants.PROPERTY_CONTROLLER_TYPE; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import org.onap.policy.common.endpoints.event.comm.Topic; +import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager; +import org.onap.policy.common.endpoints.event.comm.TopicListener; +import org.onap.policy.common.endpoints.event.comm.TopicSource; +import org.onap.policy.controlloop.CanonicalOnset; +import org.onap.policy.controlloop.ControlLoopEvent; +import org.onap.policy.controlloop.ControlLoopException; +import org.onap.policy.controlloop.ControlLoopNotificationType; +import org.onap.policy.controlloop.ControlLoopResponse; +import org.onap.policy.controlloop.VirtualControlLoopEvent; +import org.onap.policy.controlloop.VirtualControlLoopNotification; +import org.onap.policy.controlloop.drl.legacy.ControlLoopParams; +import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager2; +import org.onap.policy.controlloop.utils.ControlLoopUtils; +import org.onap.policy.drools.controller.DroolsController; +import org.onap.policy.drools.features.DroolsControllerFeatureApi; +import org.onap.policy.drools.features.PolicyControllerFeatureApi; +import org.onap.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters; +import org.onap.policy.drools.protocol.coders.EventProtocolCoderConstants; +import org.onap.policy.drools.protocol.coders.ProtocolCoderToolset; +import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration; +import org.onap.policy.drools.system.PolicyController; +import org.onap.policy.drools.system.PolicyEngineConstants; +import org.onap.policy.extension.system.NonDroolsPolicyController; +import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This replaces a Drools session with Java code. Although Drools memory + * is simulated when running the Junit tests, there is no actual use of + * Drools here. + */ +public class TdjamController extends NonDroolsPolicyController { + private static Logger logger = LoggerFactory.getLogger(TdjamController.class); + + // the 'controller.type' property is set to this value + private static final String TDJAM_CONTROLLER_BUILDER_TAG = "tdjam"; + + // additional data associated with session + private final String groupId; + private final String artifactId; + + // top-level tosca policy table (first key = name, second key = version) + private final Map<String, Map<String, ToscaPolicy>> toscaPolicies = new HashMap<>(); + + // maps 'controlLoopControlName' to 'ControlLoopParams' + private final Map<String, ControlLoopParams> controlLoopParams = new HashMap<>(); + + // maps 'requestId' to 'ControlLoopEventManager' + private final Map<UUID, ControlLoopEventManager> eventManagers = new ConcurrentHashMap<>(); + + // maps onset to 'ControlLoopEventManager' + private final Map<VirtualControlLoopEvent, ControlLoopEventManager> onsetToEventManager = new ConcurrentHashMap<>(); + + // maps 'topic' to 'TopicData' + private final Map<String, TopicData> topicDataTable = new ConcurrentHashMap<>(); + + /* ============================================================ */ + + /** + * Initialize a new 'TdjamController'. + * + * @param name the controller name + * @param properties properties defining the controller + */ + public TdjamController(String name, Properties properties) { + super(name, properties); + + this.groupId = getGroupId(); + this.artifactId = getArtifactId(); + + init(); + } + + private void init() { + // go through all of the incoming message decoders associated + // with this controller + for (ProtocolCoderToolset pct : + EventProtocolCoderConstants.getManager() + .getDecoders(groupId, artifactId)) { + // go through the 'CoderFilters' instances, and see if there are + // any that we are interested in + for (CoderFilters cf : pct.getCoders()) { + try { + Class<?> clazz = Class.forName(cf.getCodedClass()); + if (ControlLoopEvent.class.isAssignableFrom(clazz)) { + // this one is of interest + logger.debug("TdjamController using CoderFilters: {}", cf); + getTopicData(pct.getTopic()); + } + } catch (ClassNotFoundException e) { + logger.error("CoderFilter refers to unknown class: {}", + cf.getCodedClass(), e); + } + } + } + + // start all 'TopicData' instances + for (TopicData topicData : topicDataTable.values()) { + topicData.start(); + } + } + + @Override + public <T> boolean offer(T object) { + if (object instanceof ToscaPolicy) { + addToscaPolicy((ToscaPolicy) object); + return true; + } + return false; + } + + /** + * Add or replace a ToscaPolicy instance. The policy is keyed by name and + * version. + * + * @param toscaPolicy the ToscaPolicy being added + * @return if a ToscaPolicy with this name/version previously existed within + * this TdjamController, it is returned; otherwise, 'null' is returned. + */ + public synchronized ToscaPolicy addToscaPolicy(ToscaPolicy toscaPolicy) { + Map<String, ToscaPolicy> level2 = + toscaPolicies.computeIfAbsent(toscaPolicy.getName(), + key -> new HashMap<String, ToscaPolicy>()); + ToscaPolicy prev = level2.put(toscaPolicy.getVersion(), toscaPolicy); + if (prev != null) { + // update 'ControlLoopParams' entries + for (ControlLoopParams clp : controlLoopParams.values()) { + if (clp.getToscaPolicy() == prev) { + clp.setToscaPolicy(toscaPolicy); + } + } + } + logger.debug("ToscaPolicy name={}, version={}, count={}, prev={}", + toscaPolicy.getName(), toscaPolicy.getVersion(), toscaPolicies.size(), (prev != null)); + dumpTables(); + + // attempt to create a 'ControlLoopParams' instance from this object + ControlLoopParams params = + ControlLoopUtils.toControlLoopParams(toscaPolicy); + if (params != null) { + addControlLoopParams(params); + } + return prev; + } + + /** + * Remove a ToscaPolicy instance associated with the specified name and + * version. + * + * @param name the name of the ToscaPolicy to remove + * @param version the version of the ToscaPolicy to remove + * @return the ToscaPolicy that was removed, or 'null' if not found + */ + public synchronized ToscaPolicy removeToscaPolicy(String name, String version) { + ToscaPolicy prev = null; + Map<String, ToscaPolicy> level2 = toscaPolicies.get(name); + + if (level2 != null && (prev = level2.remove(version)) != null) { + // remove all 'ControlLoopParams' entries referencing this policy + for (ControlLoopParams clp : + new ArrayList<>(controlLoopParams.values())) { + if (clp.getToscaPolicy() == prev) { + controlLoopParams.remove(clp.getClosedLoopControlName()); + } + } + } + return prev; + } + + /** + * Fetch a ToscaPolicy instance associated with the specified name and + * version. + * + * @param name the name of the ToscaPolicy + * @param version the version of the ToscaPolicy + * @return the ToscaPolicy, or 'null' if not found + */ + public synchronized ToscaPolicy getToscaPolicy(String name, String version) { + Map<String, ToscaPolicy> level2 = toscaPolicies.get(name); + return (level2 == null ? null : level2.get(version)); + } + + /** + * Return a collection of all ToscaPolicy instances. + * + * @return all ToscaPolicy instances + */ + public synchronized Collection<ToscaPolicy> getAllToscaPolicies() { + HashSet<ToscaPolicy> rval = new HashSet<>(); + for (Map<String, ToscaPolicy> map : toscaPolicies.values()) { + rval.addAll(map.values()); + } + return rval; + } + + /** + * Add a new 'ControlLoopParams' instance -- they are keyed by + * 'closedLoopControlName'. + * + * @param clp the 'ControlLoopParams' instance to add + * @return the 'ControlLoopParams' instance previously associated with the + * 'closedLoopControlName' ('null' if it didn't exist) + */ + public synchronized ControlLoopParams addControlLoopParams(ControlLoopParams clp) { + ToscaPolicy toscaPolicy = + getToscaPolicy(clp.getPolicyName(), clp.getPolicyVersion()); + if (toscaPolicy == null) { + // there needs to be a 'ToscaPolicy' instance with a matching + // name/version + logger.debug("Missing ToscaPolicy, name={}, version={}", + clp.getPolicyName(), clp.getPolicyVersion()); + return clp; + } + + clp.setToscaPolicy(toscaPolicy); + ControlLoopParams prev = + controlLoopParams.put(clp.getClosedLoopControlName(), clp); + + logger.debug("ControlLoopParams name={}, version={}, closedLoopControlName={}, count={}, prev={}", + clp.getPolicyName(), clp.getPolicyVersion(), + clp.getClosedLoopControlName(), controlLoopParams.size(), (prev != null)); + dumpTables(); + return prev; + } + + /** + * Return a collection of all ControlLoopParams instances. + * + * @return all ControlLoopParams instances + */ + public synchronized Collection<ControlLoopParams> getAllControlLoopParams() { + return new ArrayList<>(controlLoopParams.values()); + } + + /** + * Return a collection of all EventManager instances. + * + * @return all EventManager instances + * + */ + public synchronized Collection<ControlLoopEventManager> getAllEventManagers() { + return new ArrayList<>(eventManagers.values()); + } + + /** + * Return a collection of all onsetToEventManager instances. + * + * @return all onsetToEventManager instances + * + */ + public synchronized Collection<ControlLoopEventManager> getAllOnsetToEventManager() { + return new ArrayList<>(onsetToEventManager.values()); + } + + /** + * Reset the controller. + * + */ + public synchronized void reset() { + toscaPolicies.clear(); + controlLoopParams.clear(); + eventManagers.clear(); + onsetToEventManager.clear(); + } + + @Override + public boolean stop() { + super.stop(); + + // stop all 'TopicData' instances + for (TopicData topicData : topicDataTable.values()) { + topicData.stop(); + } + return true; + } + + /** + * Remove a ControlLoopParams instance associated with the specified + * 'closedLoopControlName'. + * + * @param closedLoopControlName the closedLoopControlName identifying the + * ControlLoopParams instance + * @return the 'ControlLoopParams' instance, 'null' if not found + */ + public synchronized ControlLoopParams removeControlLoopParams(String closedLoopControlName) { + return controlLoopParams.remove(closedLoopControlName); + } + + /** + * Dump out the ToscaPolicy and ControlLoopParams tables in + * human-readable form. + */ + private void dumpTables() { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(bos, true); + + // name(25) version(10) closedLoopControlName(...) + + String format = "%-25s %-10s %s\n"; + out.println("ToscaPolicy Table"); + out.format(format, "Name", "Version", ""); + out.format(format, "----", "-------", ""); + + for (Map<String, ToscaPolicy> level2 : toscaPolicies.values()) { + for (ToscaPolicy tp : level2.values()) { + out.format(format, tp.getName(), tp.getVersion(), ""); + } + } + + out.println("\nControlLoopParams Table"); + out.format(format, "Name", "Version", "ClosedLoopControlName"); + out.format(format, "----", "-------", "---------------------"); + for (ControlLoopParams cp : controlLoopParams.values()) { + out.format(format, cp.getPolicyName(), cp.getPolicyVersion(), + cp.getClosedLoopControlName()); + } + + logger.debug(new String(bos.toByteArray())); + } + + /** + * Find or create a 'TopicData' instance associated with the specified + * topic name. + * + * @param name the topic name + * @return the new or existing 'TopicData' instance associated with 'name' + */ + private TopicData getTopicData(String name) { + return topicDataTable.computeIfAbsent(name, key -> new TopicData(name)); + } + + /* ============================================================ */ + + /** + * Process an incoming 'ControlLoopEvent'. + * + * @param event the incoming 'ControlLoopEvent' + */ + private void processEvent(ControlLoopEvent event) { + String clName = event.getClosedLoopControlName(); + ControlLoopParams params = controlLoopParams.get(clName); + if (params == null) { + logger.debug("No ControlLoopParams for event: {}", event); + return; + } + + UUID requestId = event.getRequestId(); + if (event instanceof CanonicalOnset) { + CanonicalOnset coEvent = (CanonicalOnset) event; + + if (requestId == null) { + // the requestId should not be 'null' + handleNullRequestId(coEvent, params); + return; + } + + ControlLoopEventManager manager = onsetToEventManager.computeIfAbsent(coEvent, key -> { + // a ControlLoopEventManager does not yet exist for this + // 'event' -- create one, with the initial event + try { + ControlLoopEventManager mgr = new ControlLoopEventManager(params, coEvent); + eventManagers.put(requestId, mgr); + return mgr; + } catch (ControlLoopException e) { + logger.error("Exception creating ControlLoopEventManager", e); + return null; + } + }); + + if (manager != null && !manager.getSerialWorkQueue().isRunning()) { + // new manager - start it by processing the initial event + manager.getSerialWorkQueue().start(); + return; + } + } + + if (event instanceof VirtualControlLoopEvent) { + ControlLoopEventManager manager = eventManagers.get(requestId); + if (manager != null) { + manager.getSerialWorkQueue() + .queueAndRun(() -> manager.subsequentEvent((VirtualControlLoopEvent) event)); + return; + } + } + + // this block of code originally appeared in the 'EVENT.CLEANUP' + // Drools rule + String ruleName = "EVENT.CLEANUP"; + + logger.info("{}: {}", clName, ruleName); + logger.debug("{}: {}: orphan event={}", clName, ruleName, event); + } + + /** + * Generate and send a notification message in response to a 'CanonicalOnset' + * with a null 'requestId'. + * + * @param event the CanonicalOnset event + * @param params the associated ControlLoopParams + */ + private void handleNullRequestId(CanonicalOnset event, + ControlLoopParams params) { + // this block of code originally appeared in the 'EVENT' Drools rule + String ruleName = "EVENT"; + String clName = event.getClosedLoopControlName(); + + VirtualControlLoopNotification notification = + new VirtualControlLoopNotification(event); + notification.setNotification(ControlLoopNotificationType.REJECTED); + notification.setFrom("policy"); + notification.setMessage("Missing requestId"); + notification.setPolicyName(params.getPolicyName() + "." + ruleName); + notification.setPolicyScope(params.getPolicyScope()); + notification.setPolicyVersion(params.getPolicyVersion()); + + // + // Generate notification + // + try { + PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); + + } catch (RuntimeException e) { + logger.warn("{}: {}.{}: event={} exception generating notification", + clName, params.getPolicyName(), ruleName, + event, e); + } + } + + /* ============================================================ */ + + /** + * This nested class corresponds to a single topic name. At present, the + * only topics that are directly handled by this class are + * 'ControlLoopEvent', and subclasses (hence, the call to 'processEvent'). + * If other event types later need to be directly handled, this may need to + * become an abstract class, with subclasses for the various event types. + */ + private class TopicData implements TopicListener { + // topic name + private String name; + + // set of 'TopicSource' instances associated with this topic + // (probably only one, but the underlying APIs support a list) + private List<TopicSource> topicSources = null; + + /** + * Constructor -- initialize the 'TopicData' instance. + * + * @param name the topic name + */ + private TopicData(String name) { + this.name = name; + } + + /** + * Register all of the 'TopicSource' instances associated with this + * topic, and start the listeners. + */ + private void start() { + if (topicSources == null) { + // locate topic sources + ArrayList<String> topics = new ArrayList<>(); + topics.add(name); + topicSources = TopicEndpointManager.getManager().getTopicSources(topics); + } + + for (TopicSource consumer : topicSources) { + consumer.register(this); + consumer.start(); + } + } + + /** + * Unregister all of the 'TopicSource' instances associated with this + * topic, and stop the listeners. + */ + private void stop() { + if (topicSources != null) { + for (TopicSource consumer : topicSources) { + consumer.unregister(this); + consumer.stop(); + } + } + } + + /*===========================*/ + /* 'TopicListener' interface */ + /*===========================*/ + + @Override + public void onTopicEvent(Topic.CommInfrastructure commType, String topic, String event) { + logger.debug("TopicData.onTopicEvent: {}", event); + Object decodedObject = + EventProtocolCoderConstants.getManager().decode(groupId, artifactId, topic, event); + if (decodedObject != null) { + logger.debug("Decoded to object of {}", decodedObject.getClass()); + if (decodedObject instanceof ControlLoopEvent) { + PolicyEngineConstants.getManager().getExecutorService().execute(() -> + processEvent((ControlLoopEvent) decodedObject)); + } + } + } + } + + /* ============================================================ */ + + /** + * This is a 'ControlLoopEventManager2' variant designed to run under + * 'TdjamController'. + */ + private class ControlLoopEventManager extends ControlLoopEventManager2 { + private static final long serialVersionUID = 1L; + + // used to serialize method calls from multiple threads, which avoids the + // need for additional synchronization + private final SerialWorkQueue serialWorkQueue; + + private final ControlLoopParams params; + + // onset event + private final CanonicalOnset event; + + /** + * Constructor - initialize a ControlLoopEventManager. + * + * @param params the 'ControlLoopParam's instance associated with the + * 'closedLoopControlName' + * @param event the initial ControlLoopEvent + */ + private ControlLoopEventManager(ControlLoopParams params, CanonicalOnset event) + throws ControlLoopException { + + super(params, event); + this.params = params; + this.event = event; + this.serialWorkQueue = new SerialWorkQueue(this::initialEvent); + } + + /** + * Return the SerialWorkQueue. + * + * @return the SerialWorkQueue + */ + private SerialWorkQueue getSerialWorkQueue() { + return serialWorkQueue; + } + + /** + * This is a notification from the base class that a state transition + * has occurred. + */ + @Override + protected void notifyUpdate() { + update(); + } + + /** + * Process the initial event from DCAE that caused the + * 'ControlLoopEventManager' to be created. + */ + private void initialEvent() { + // this block of code originally appeared in the 'EVENT' Drools rule + String ruleName = "EVENT"; + UUID requestId = event.getRequestId(); + String clName = event.getClosedLoopControlName(); + + VirtualControlLoopNotification notification; + + try { + // + // Check the event, because we need it to not be null when + // we create the ControlLoopEventManager. The ControlLoopEventManager + // will do extra syntax checking as well as check if the closed loop is disabled. + // + try { + start(); + } catch (Exception e) { + eventManagers.remove(requestId, this); + onsetToEventManager.remove(event, this); + throw e; + } + notification = makeNotification(); + notification.setNotification(ControlLoopNotificationType.ACTIVE); + notification.setPolicyName(params.getPolicyName() + "." + ruleName); + } catch (Exception e) { + logger.warn("{}: {}.{}", clName, params.getPolicyName(), ruleName, e); + notification = new VirtualControlLoopNotification(event); + notification.setNotification(ControlLoopNotificationType.REJECTED); + notification.setMessage("Exception occurred: " + e.getMessage()); + notification.setPolicyName(params.getPolicyName() + "." + ruleName); + notification.setPolicyScope(params.getPolicyScope()); + notification.setPolicyVersion(params.getPolicyVersion()); + } + // + // Generate notification + // + try { + PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); + + } catch (RuntimeException e) { + logger.warn("{}: {}.{}: event={} exception generating notification", + clName, params.getPolicyName(), ruleName, + event, e); + } + } + + /** + * Process a subsequent event from DCAE. + * + * @param event the VirtualControlLoopEvent event + */ + private void subsequentEvent(VirtualControlLoopEvent event) { + // this block of code originally appeared in the + // 'EVENT.MANAGER>NEW.EVENT' Drools rule + String ruleName = "EVENT.MANAGER.NEW.EVENT"; + + // + // Check what kind of event this is + // + switch (onNewEvent(event)) { + case SYNTAX_ERROR: + // + // Ignore any bad syntax events + // + logger.warn("{}: {}.{}: syntax error", + getClosedLoopControlName(), getPolicyName(), ruleName); + break; + + case FIRST_ABATEMENT: + case SUBSEQUENT_ABATEMENT: + // + // TODO: handle the abatement. Currently, it's just discarded. + // + break; + + case FIRST_ONSET: + case SUBSEQUENT_ONSET: + default: + // + // We don't care about subsequent onsets + // + logger.warn("{}: {}.{}: subsequent onset", + getClosedLoopControlName(), getPolicyName(), ruleName); + break; + } + } + + /** + * Called when a state transition occurs. + */ + private void update() { + // handle synchronization by running it under the SerialWorkQueue + getSerialWorkQueue().queueAndRun(() -> { + if (isActive()) { + updateActive(); + } else { + updateInactive(); + } + }); + } + + /** + * Called when a state transition occurs, and we are in the active state. + */ + private void updateActive() { + if (!isUpdated()) { + // no notification needed + return; + } + + // this block of code originally appeared in the + // 'EVENT.MANAGER.PROCESSING' Drools rule + String ruleName = "EVENT.MANAGER.PROCESSING"; + VirtualControlLoopNotification notification = + getNotification(); + + logger.info("{}: {}.{}: manager={}", + getClosedLoopControlName(), getPolicyName(), ruleName, + this); + // + // Generate notification + // + try { + notification.setPolicyName(getPolicyName() + "." + ruleName); + PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); + + } catch (RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception generating notification", + getClosedLoopControlName(), getPolicyName(), ruleName, + this, e); + } + // + // Generate Response notification + // + try { + ControlLoopResponse clResponse = getControlLoopResponse(); + if (clResponse != null) { + PolicyEngineConstants.getManager().deliver("DCAE_CL_RSP", clResponse); + } + + } catch (RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception generating Response notification", + getClosedLoopControlName(), getPolicyName(), ruleName, + this, e); + } + // + // Discard this message and wait for the next response. + // + nextStep(); + update(); + } + + /** + * Called when a state transition has occurred, and we are not in the + * active state. + */ + private void updateInactive() { + // this block of code originally appeared in the 'EVENT.MANAGER.FINAL' + // Drools rule + String ruleName = "EVENT.MANAGER.FINAL"; + VirtualControlLoopNotification notification = + getNotification(); + + logger.info("{}: {}.{}: manager={}", + getClosedLoopControlName(), getPolicyName(), ruleName, + this); + // + // Generate notification + // + try { + notification.setPolicyName(getPolicyName() + "." + ruleName); + PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification); + } catch (RuntimeException e) { + logger.warn("{}: {}.{}: manager={} exception generating notification", + getClosedLoopControlName(), getPolicyName(), ruleName, + this, e); + } + // + // Destroy the manager + // + destroy(); + + // Remove the entry from the table + eventManagers.remove(getRequestId(), this); + onsetToEventManager.remove(event, this); + } + } + + /* ============================================================ */ + + /** + * An instance of this class is called by 'IndexedPolicyControllerFactory'. + * It does the build operation when the value of the 'controller.type' + * property matches the value of TDJAM_CONTROLLER_BUILDER_TAG. + */ + public static class PolicyBuilder implements PolicyControllerFeatureApi { + @Override + public int getSequenceNumber() { + return 1; + } + + @Override + public PolicyController beforeInstance(String name, Properties properties) { + if (TDJAM_CONTROLLER_BUILDER_TAG.equals(properties.getProperty(PROPERTY_CONTROLLER_TYPE))) { + return new TdjamController(name, properties); + } + return null; + } + } + + /* ============================================================ */ + + /** + * An instance of this class is called by 'IndexedDroolsControllerFactory'. + * It does the build operation when the value of the 'controller.type' + * property matches the value of TDJAM_CONTROLLER_BUILDER_TAG. + */ + public static class DroolsBuilder implements DroolsControllerFeatureApi { + @Override + public int getSequenceNumber() { + return 1; + } + + @Override + public DroolsController beforeInstance(Properties properties, + String groupId, String artifactId, String version, + List<TopicCoderFilterConfiguration> decoderConfigurations, + List<TopicCoderFilterConfiguration> encoderConfigurations) throws LinkageError { + + if (TDJAM_CONTROLLER_BUILDER_TAG.equals(properties.getProperty(PROPERTY_CONTROLLER_TYPE))) { + return TdjamController.getBuildInProgress(); + } + return null; + } + } +} diff --git a/controlloop/common/controller-tdjam/src/main/java/org/onap/policy/extension/system/NonDroolsPolicyController.java b/controlloop/common/controller-tdjam/src/main/java/org/onap/policy/extension/system/NonDroolsPolicyController.java new file mode 100644 index 000000000..d876bee96 --- /dev/null +++ b/controlloop/common/controller-tdjam/src/main/java/org/onap/policy/extension/system/NonDroolsPolicyController.java @@ -0,0 +1,668 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.extension.system; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import org.apache.commons.collections4.queue.CircularFifoQueue; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.onap.policy.common.endpoints.event.comm.Topic; +import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure; +import org.onap.policy.common.endpoints.event.comm.TopicSink; +import org.onap.policy.common.endpoints.event.comm.TopicSource; +import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties; +import org.onap.policy.common.utils.services.OrderedServiceImpl; +import org.onap.policy.drools.controller.DroolsController; +import org.onap.policy.drools.core.PolicyContainer; +import org.onap.policy.drools.features.DroolsControllerFeatureApi; +import org.onap.policy.drools.features.DroolsControllerFeatureApiConstants; +import org.onap.policy.drools.protocol.coders.EventProtocolCoder; +import org.onap.policy.drools.protocol.coders.EventProtocolCoderConstants; +import org.onap.policy.drools.protocol.coders.EventProtocolParams; +import org.onap.policy.drools.protocol.coders.JsonProtocolFilter; +import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration; +import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder; +import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.PotentialCoderFilter; +import org.onap.policy.drools.system.internal.AggregatedPolicyController; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class combines the 'PolicyController' and 'DroolsController' + * interfaces, and provides a controller that does not have Drools running + * underneath. It also contains some code copied from 'MavenDroolsController' + * and 'NullDroolsController'. The goal is to have it look like other + * controllers, use the same style property file, and provide access to + * UEB/DMAAP message streams associated with the controller. + */ +public class NonDroolsPolicyController extends AggregatedPolicyController implements DroolsController { + /** + * Logger. + */ + private static final Logger logger = LoggerFactory.getLogger(NonDroolsPolicyController.class); + + /** + * The PolicyController and DroolsController factories assume that the + * controllers are separate objects, but in this case, the same object + * is used for both. We want the DroolsController 'build' method to + * return the same object; however, at the point the DroolsController + * build is taking place, the PolicyController hasn't yet been placed + * in any tables. The following variable is used to pass this information + * from one stack frame to another within the same thread. + */ + private static ThreadLocal<NonDroolsPolicyController> buildInProgress = new ThreadLocal<>(); + + /** + * 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 sink events processed. + */ + protected final CircularFifoQueue<String> recentSinkEvents = new CircularFifoQueue<>(10); + + // this is used to avoid infinite recursion in a shutdown or halt operation + private boolean shutdownInProgress = false; + + private static Properties convert(String name, Properties properties) { + + Properties newProperties = new Properties(); + for (String pname : properties.stringPropertyNames()) { + newProperties.setProperty(pname, properties.getProperty(pname)); + } + + newProperties.setProperty("rules.groupId", "NonDroolsPolicyController"); + newProperties.setProperty("rules.artifactId", name); + newProperties.setProperty("rules.version", "1.0"); + return newProperties; + } + + /** + * constructor -- pass parameters to superclass. + * @param name controller name + * @param properties contents of controller properties file + */ + public NonDroolsPolicyController(String name, Properties properties) { + super(name, convert(name, properties)); + } + + /** + * This is used to pass the 'NonDroolsPolicyController' object to the + * 'DroolsPolicyBuilder' object, as the same object is used for both + * 'PolicyController' and 'DroolsController'. + * + * @return the NonDroolsPolicyController object ('null' if not available) + */ + public static NonDroolsPolicyController getBuildInProgress() { + return buildInProgress.get(); + } + + protected void initDrools(Properties properties) { + try { + // Register with drools factory + buildInProgress.set(this); + this.droolsController.set(getDroolsFactory().build(properties, sources, sinks)); + buildInProgress.set(null); + } catch (Exception | LinkageError e) { + logger.error("{}: cannot init-drools", this); + throw new IllegalArgumentException(e); + } + + decoderConfigurations = codersAndFilters(properties, sources); + encoderConfigurations = codersAndFilters(properties, sinks); + + // add to 'EventProtocolCoderConstants.getManager()' table + for (TopicCoderFilterConfiguration tcfc : decoderConfigurations) { + for (PotentialCoderFilter pcf : tcfc.getCoderFilters()) { + getCoderManager().addDecoder( + EventProtocolParams.builder() + .groupId(getGroupId()) + .artifactId(getArtifactId()) + .topic(tcfc.getTopic()) + .eventClass(pcf.getCodedClass()) + .protocolFilter(pcf.getFilter()) + .customGsonCoder(tcfc.getCustomGsonCoder()) + .modelClassLoaderHash(NonDroolsPolicyController.class.getClassLoader().hashCode())); + } + } + for (TopicCoderFilterConfiguration tcfc : encoderConfigurations) { + for (PotentialCoderFilter pcf : tcfc.getCoderFilters()) { + getCoderManager().addEncoder( + EventProtocolParams.builder() + .groupId(getGroupId()) + .artifactId(getArtifactId()) + .topic(tcfc.getTopic()) + .eventClass(pcf.getCodedClass()) + .protocolFilter(pcf.getFilter()) + .customGsonCoder(tcfc.getCustomGsonCoder()) + .modelClassLoaderHash(NonDroolsPolicyController.class.getClassLoader().hashCode())); + } + } + } + + /*==============================*/ + /* 'DroolsController' interface */ + /*==============================*/ + + // methods copied from 'MavenDroolsController' and 'NullDroolsController' + + @Override + public boolean start() { + + logger.info("START: {}", this); + + synchronized (this) { + if (this.alive) { + return true; + } + this.alive = true; + } + + return true; + } + + @Override + public boolean stop() { + + logger.info("STOP: {}", this); + + synchronized (this) { + if (!this.alive) { + return true; + } + this.alive = false; + } + + return true; + } + + @Override + public void shutdown() { + if (shutdownInProgress) { + // avoid infinite recursion + return; + } + logger.info("{}: SHUTDOWN", this); + + try { + this.stop(); + this.removeCoders(); + shutdownInProgress = true; + + // the following method calls 'this.shutdown' recursively + getDroolsFactory().shutdown(this); + } catch (Exception e) { + logger.error("{} SHUTDOWN FAILED because of {}", this, e.getMessage(), e); + } finally { + shutdownInProgress = false; + } + } + + @Override + public void halt() { + if (shutdownInProgress) { + // avoid infinite recursion + return; + } + logger.info("{}: HALT", this); + + try { + this.stop(); + this.removeCoders(); + shutdownInProgress = true; + + // the following method calls 'this.halt' recursively + getDroolsFactory().destroy(this); + } catch (Exception e) { + logger.error("{} HALT FAILED because of {}", this, e.getMessage(), e); + } finally { + shutdownInProgress = false; + } + } + + @Override + public boolean isAlive() { + return this.alive; + } + + @Override + public boolean lock() { + logger.info("LOCK: {}", this); + + this.locked = true; + return true; + } + + @Override + public boolean unlock() { + logger.info("UNLOCK: {}", this); + + this.locked = false; + return true; + } + + @Override + public boolean isLocked() { + return this.locked; + } + + @Override + public String getGroupId() { + return "NonDroolsPolicyController"; + } + + @Override + public String getArtifactId() { + return getName(); + } + + @Override + public String getVersion() { + return "1.0"; + } + + @Override + public List<String> getSessionNames() { + return new ArrayList<>(); + } + + @Override + public List<String> getCanonicalSessionNames() { + return new ArrayList<>(); + } + + @Override + public List<String> getBaseDomainNames() { + return Collections.emptyList(); + } + + @Override + public boolean offer(String topic, String event) { + return false; + } + + @Override + public <T> boolean offer(T event) { + return false; + } + + @Override + public boolean deliver(TopicSink sink, Object event) { + + // this one is from 'MavenDroolsController' + + logger.info("{} DELIVER: {} FROM {} TO {}", this, event, this, sink); + + for (DroolsControllerFeatureApi feature : getDroolsProviders().getList()) { + try { + if (feature.beforeDeliver(this, sink, event)) { + return true; + } + } catch (Exception e) { + logger.error("{}: feature {} before-deliver failure because of {}", this, feature.getClass().getName(), + e.getMessage(), e); + } + } + + 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 = + getCoderManager().encode(sink.getTopic(), event, this); + + synchronized (this.recentSinkEvents) { + this.recentSinkEvents.add(json); + } + + boolean success = sink.send(json); + + for (DroolsControllerFeatureApi feature : getDroolsProviders().getList()) { + try { + if (feature.afterDeliver(this, sink, event, json, success)) { + return true; + } + } catch (Exception e) { + logger.error("{}: feature {} after-deliver failure because of {}", this, feature.getClass().getName(), + e.getMessage(), e); + } + } + + return success; + + } + + @Override + public Object[] getRecentSourceEvents() { + return new String[0]; + } + + @Override + public PolicyContainer getContainer() { + return null; + } + + @Override + public String[] getRecentSinkEvents() { + synchronized (this.recentSinkEvents) { + String[] events = new String[recentSinkEvents.size()]; + return recentSinkEvents.toArray(events); + } + } + + @Override + public boolean ownsCoder(Class<?> coderClass, int modelHash) { + //throw new IllegalStateException(makeInvokeMsg()); + return true; + } + + @Override + public Class<?> fetchModelClass(String className) { + try { + return Class.forName(className); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException(makeInvokeMsg()); + } + } + + @Override + public boolean isBrained() { + return true; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("NonDroolsPolicyController []"); + return builder.toString(); + } + + @Override + public void updateToVersion(String newGroupId, String newArtifactId, String newVersion, + List<TopicCoderFilterConfiguration> decoderConfigurations, + List<TopicCoderFilterConfiguration> encoderConfigurations) + throws LinkageError { + throw new IllegalStateException(makeInvokeMsg()); + } + + @Override + public Map<String, Integer> factClassNames(String sessionName) { + return new HashMap<>(); + } + + @Override + public long factCount(String sessionName) { + return 0; + } + + @Override + public List<Object> facts(String sessionName, String className, boolean delete) { + return new ArrayList<>(); + } + + @Override + public <T> List<T> facts(@NonNull String sessionName, @NonNull Class<T> clazz) { + return new ArrayList<>(); + } + + @Override + public List<Object> factQuery(String sessionName, String queryName, + String queriedEntity, + boolean delete, Object... queryParams) { + return new ArrayList<>(); + } + + @Override + public <T> boolean delete(@NonNull String sessionName, @NonNull T fact) { + return false; + } + + @Override + public <T> boolean delete(@NonNull T fact) { + return false; + } + + @Override + public <T> boolean delete(@NonNull String sessionName, @NonNull Class<T> fact) { + return false; + } + + @Override + public <T> boolean delete(@NonNull Class<T> fact) { + return false; + } + + private String makeInvokeMsg() { + return this.getClass().getName() + " invoked"; + } + + /** + * remove decoders. + */ + protected void removeDecoders() { + logger.info("REMOVE-DECODERS: {}", this); + + if (this.decoderConfigurations == null) { + return; + } + + + for (TopicCoderFilterConfiguration coderConfig: decoderConfigurations) { + String topic = coderConfig.getTopic(); + getCoderManager().removeDecoders(this.getGroupId(), this.getArtifactId(), topic); + } + } + + /** + * remove decoders. + */ + protected void removeEncoders() { + + logger.info("REMOVE-ENCODERS: {}", this); + + if (this.encoderConfigurations == null) { + return; + } + + for (TopicCoderFilterConfiguration coderConfig: encoderConfigurations) { + String topic = coderConfig.getTopic(); + getCoderManager().removeEncoders(this.getGroupId(), this.getArtifactId(), topic); + } + } + + /** + * removes this drools controllers and encoders and decoders from operation. + */ + protected void removeCoders() { + logger.info("{}: REMOVE-CODERS", this); + + try { + this.removeDecoders(); + } catch (IllegalArgumentException e) { + logger.error("{} REMOVE-DECODERS FAILED because of {}", this, e.getMessage(), e); + } + + try { + this.removeEncoders(); + } catch (IllegalArgumentException e) { + logger.error("{} REMOVE-ENCODERS FAILED because of {}", this, e.getMessage(), e); + } + } + + protected List<TopicCoderFilterConfiguration> codersAndFilters(Properties properties, + List<? extends Topic> topicEntities) { + + List<TopicCoderFilterConfiguration> topics2DecodedClasses2Filters = new ArrayList<>(); + + if (topicEntities == null || topicEntities.isEmpty()) { + return topics2DecodedClasses2Filters; + } + + for (Topic topic : topicEntities) { + + // 1. first the topic + + String firstTopic = topic.getTopic(); + + String propertyTopicEntityPrefix = getPropertyTopicPrefix(topic) + firstTopic; + + // 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 + + CustomGsonCoder customGsonCoder = getCustomCoder(properties, propertyTopicEntityPrefix); + + // 3. second the list of classes associated with each topic + + String eventClasses = properties + .getProperty(propertyTopicEntityPrefix + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_SUFFIX); + + if (eventClasses == null || eventClasses.isEmpty()) { + logger.warn("There are no event classes for topic {}", firstTopic); + continue; + } + + List<PotentialCoderFilter> classes2Filters = + getFilterExpressions(properties, propertyTopicEntityPrefix, eventClasses); + + TopicCoderFilterConfiguration topic2Classes2Filters = + new TopicCoderFilterConfiguration(firstTopic, classes2Filters, customGsonCoder); + topics2DecodedClasses2Filters.add(topic2Classes2Filters); + } + + return topics2DecodedClasses2Filters; + } + + private String getPropertyTopicPrefix(Topic topic) { + boolean isSource = topic instanceof TopicSource; + CommInfrastructure commInfra = topic.getTopicCommInfrastructure(); + if (commInfra == CommInfrastructure.UEB) { + if (isSource) { + return PolicyEndPointProperties.PROPERTY_UEB_SOURCE_TOPICS + "."; + } else { + return PolicyEndPointProperties.PROPERTY_UEB_SINK_TOPICS + "."; + } + } else if (commInfra == CommInfrastructure.DMAAP) { + if (isSource) { + return PolicyEndPointProperties.PROPERTY_DMAAP_SOURCE_TOPICS + "."; + } else { + return PolicyEndPointProperties.PROPERTY_DMAAP_SINK_TOPICS + "."; + } + } else if (commInfra == CommInfrastructure.NOOP) { + if (isSource) { + return PolicyEndPointProperties.PROPERTY_NOOP_SOURCE_TOPICS + "."; + } else { + return PolicyEndPointProperties.PROPERTY_NOOP_SINK_TOPICS + "."; + } + } else { + throw new IllegalArgumentException("Invalid Communication Infrastructure: " + commInfra); + } + } + + private CustomGsonCoder getCustomCoder(Properties properties, String propertyPrefix) { + String customGson = properties.getProperty(propertyPrefix + + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_CUSTOM_MODEL_CODER_GSON_SUFFIX); + + CustomGsonCoder customGsonCoder = null; + if (customGson != null && !customGson.isEmpty()) { + try { + customGsonCoder = new CustomGsonCoder(customGson); + } catch (IllegalArgumentException e) { + logger.warn("{}: cannot create custom-gson-coder {} because of {}", this, customGson, + e.getMessage(), e); + } + } + return customGsonCoder; + } + + private List<PotentialCoderFilter> getFilterExpressions(Properties properties, String propertyPrefix, + String eventClasses) { + + List<PotentialCoderFilter> classes2Filters = new ArrayList<>(); + + List<String> topicClasses = new ArrayList<>(Arrays.asList(eventClasses.split("\\s*,\\s*"))); + + for (String theClass : topicClasses) { + + // 4. for each coder class, get the filter expression + + String filter = properties + .getProperty(propertyPrefix + + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_SUFFIX + + "." + theClass + PolicyEndPointProperties.PROPERTY_TOPIC_EVENTS_FILTER_SUFFIX); + + JsonProtocolFilter protocolFilter = new JsonProtocolFilter(filter); + PotentialCoderFilter class2Filters = new PotentialCoderFilter(theClass, protocolFilter); + classes2Filters.add(class2Filters); + } + + return classes2Filters; + } + + // these may be overridden by junit tests + + protected EventProtocolCoder getCoderManager() { + return EventProtocolCoderConstants.getManager(); + } + + protected OrderedServiceImpl<DroolsControllerFeatureApi> getDroolsProviders() { + return DroolsControllerFeatureApiConstants.getProviders(); + } +} diff --git a/controlloop/common/controller-tdjam/src/main/resources/META-INF/services/org.onap.policy.drools.features.DroolsControllerFeatureApi b/controlloop/common/controller-tdjam/src/main/resources/META-INF/services/org.onap.policy.drools.features.DroolsControllerFeatureApi new file mode 100644 index 000000000..09a087ee6 --- /dev/null +++ b/controlloop/common/controller-tdjam/src/main/resources/META-INF/services/org.onap.policy.drools.features.DroolsControllerFeatureApi @@ -0,0 +1 @@ +org.onap.policy.controlloop.tdjam.TdjamController$DroolsBuilder diff --git a/controlloop/common/controller-tdjam/src/main/resources/META-INF/services/org.onap.policy.drools.features.PolicyControllerFeatureApi b/controlloop/common/controller-tdjam/src/main/resources/META-INF/services/org.onap.policy.drools.features.PolicyControllerFeatureApi new file mode 100644 index 000000000..dad467869 --- /dev/null +++ b/controlloop/common/controller-tdjam/src/main/resources/META-INF/services/org.onap.policy.drools.features.PolicyControllerFeatureApi @@ -0,0 +1 @@ +org.onap.policy.controlloop.tdjam.TdjamController$PolicyBuilder diff --git a/controlloop/common/controller-tdjam/src/test/java/org/onap/policy/controlloop/TdjamTest.java b/controlloop/common/controller-tdjam/src/test/java/org/onap/policy/controlloop/TdjamTest.java new file mode 100644 index 000000000..cf40ba6d4 --- /dev/null +++ b/controlloop/common/controller-tdjam/src/test/java/org/onap/policy/controlloop/TdjamTest.java @@ -0,0 +1,159 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop; + +import java.util.Properties; +import lombok.Getter; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.controlloop.common.rules.test.BaseTest; +import org.onap.policy.controlloop.common.rules.test.Listener; +import org.onap.policy.controlloop.common.rules.test.NamedRunner; +import org.onap.policy.controlloop.common.rules.test.Rules; +import org.onap.policy.controlloop.common.rules.test.TestNames; +import org.onap.policy.drools.persistence.SystemPersistence; +import org.onap.policy.drools.persistence.SystemPersistenceConstants; +import org.onap.policy.drools.system.PolicyController; +import org.onap.policy.drools.system.PolicyControllerConstants; +import org.onap.policy.drools.system.PolicyEngine; +import org.onap.policy.drools.system.PolicyEngineConstants; +import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; +import org.onap.policy.simulators.Util; + + + +/** + * Tests use cases using BaseTest Set. + * + * <p/> + * Note: this runs ALL tests (i.e., any whose names start with "test"). + */ +@RunWith(NamedRunner.class) +@TestNames(prefixes = {"test"}) + +public class TdjamTest extends BaseTest { + protected static final String CONTROLLER_NAME = "tdjam"; + protected static PolicyController controller; + + @Getter + private static final PolicyEngine pdpd = makeEngine(); + + @Getter + private static final SystemPersistence pdpdRepo = makePdpdRepo(); + + /** + * Sets up statics. + */ + @BeforeClass + public static void setUpBeforeClass() { + initStatics(); + pdpdRepo.setConfigurationDir("src/test/resources/config"); + pdpd.configure(new Properties()); + controller = pdpd.createPolicyController(CONTROLLER_NAME, pdpdRepo.getControllerProperties(CONTROLLER_NAME)); + pdpd.start(); + httpClients.addClients("tdjam"); + simulators.start(Util::buildAaiSim, Util::buildSoSim, Util::buildVfcSim, Util::buildGuardSim, + Util::buildSdncSim); + } + + /** + * Cleans up statics. + */ + @AfterClass + public static void tearDownAfterClass() { + finishStatics(); + PolicyControllerConstants.getFactory().shutdown(controller); + pdpd.stop(); + } + + /** + * Sets up. + */ + @Before + public void setUp() { + topics = topicMaker.get(); + } + + /** + * Tears down. + */ + @After + public void tearDown() { + topics.destroy(); + } + + protected static PolicyEngine makeEngine() { + return PolicyEngineConstants.getManager(); + } + + protected static SystemPersistence makePdpdRepo() { + return SystemPersistenceConstants.getManager(); + } + + @Override + protected void waitForLockAndPermit(ToscaPolicy policy, Listener<VirtualControlLoopNotification> policyClMgt) { + String policyName = policy.getIdentifier().getName(); + + policyClMgt.await(notif -> notif.getNotification() == ControlLoopNotificationType.ACTIVE + && (policyName + ".EVENT").equals(notif.getPolicyName())); + + policyClMgt.await(notif -> notif.getNotification() == ControlLoopNotificationType.OPERATION + && (policyName + ".EVENT.MANAGER.PROCESSING").equals(notif.getPolicyName()) + && notif.getMessage().startsWith("Sending guard query")); + + policyClMgt.await(notif -> notif.getNotification() == ControlLoopNotificationType.OPERATION + && (policyName + ".EVENT.MANAGER.PROCESSING").equals(notif.getPolicyName()) + && notif.getMessage().startsWith("Guard result") && notif.getMessage().endsWith("Permit")); + + policyClMgt.await(notif -> notif.getNotification() == ControlLoopNotificationType.OPERATION + && (policyName + ".EVENT.MANAGER.PROCESSING").equals(notif.getPolicyName()) + && notif.getMessage().startsWith("actor=")); + } + + @Override + protected VirtualControlLoopNotification waitForFinal(ToscaPolicy policy, + Listener<VirtualControlLoopNotification> policyClMgt, ControlLoopNotificationType finalType) { + + return policyClMgt.await(notif -> notif.getNotification() == finalType + && (policy.getIdentifier().getName() + ".EVENT.MANAGER.FINAL").equals(notif.getPolicyName())); + } + + @Override + protected ToscaPolicy checkPolicy(String fileName) { + try { + policy = Rules.getPolicyFromFile(fileName); + } catch (CoderException e) { + throw new IllegalArgumentException(fileName, e); + } + controller.getDrools().offer(policy); + return policy; + } + + @Override + protected Listener<VirtualControlLoopNotification> createNoficationTopicListener() { + return topics.createListener(POLICY_CL_MGT_TOPIC, + VirtualControlLoopNotification.class, controller); + } +} diff --git a/controlloop/common/controller-tdjam/src/test/java/org/onap/policy/controlloop/tdjam/TdjamControllerTest.java b/controlloop/common/controller-tdjam/src/test/java/org/onap/policy/controlloop/tdjam/TdjamControllerTest.java new file mode 100644 index 000000000..5edba8701 --- /dev/null +++ b/controlloop/common/controller-tdjam/src/test/java/org/onap/policy/controlloop/tdjam/TdjamControllerTest.java @@ -0,0 +1,240 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.controlloop.tdjam; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.onap.policy.drools.properties.DroolsPropertyConstants.PROPERTY_CONTROLLER_TYPE; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; +import java.util.HashSet; +import java.util.Properties; +import java.util.UUID; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.onap.policy.controlloop.CanonicalOnset; +import org.onap.policy.controlloop.VirtualControlLoopNotification; +import org.onap.policy.controlloop.common.rules.test.Listener; +import org.onap.policy.controlloop.common.rules.test.Topics; +import org.onap.policy.controlloop.drl.legacy.ControlLoopParams; +import org.onap.policy.drools.controller.DroolsControllerConstants; +import org.onap.policy.drools.system.PolicyControllerConstants; +import org.onap.policy.drools.system.PolicyEngineConstants; +import org.onap.policy.drools.utils.PropertyUtil; +import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy; +import org.powermock.reflect.Whitebox; +import org.slf4j.LoggerFactory; + +public class TdjamControllerTest { + private static Properties prop; + private static Logger logger = (Logger) LoggerFactory.getLogger(TdjamController.class); + private static ListAppender<ILoggingEvent> appender = new ListAppender<ILoggingEvent>(); + + /** + * Setup appender, and initialize properties. + */ + @BeforeClass + public static void setupClass() throws Exception { + logger.setLevel(Level.DEBUG); + logger.addAppender(appender); + + prop = PropertyUtil.getProperties("src/test/resources/config/tdjam-controller.properties"); + prop.setProperty(PROPERTY_CONTROLLER_TYPE, "tdjam"); + + PolicyEngineConstants.getManager().configure(new Properties()); + PolicyEngineConstants.getManager().start(); + + } + + /** + * Remove appender. + */ + @AfterClass + public static void cleanupClass() { + + PolicyEngineConstants.getManager().stop(); + PolicyEngineConstants.getManager().getExecutorService().shutdown(); + + appender.stop(); + System.out.println("APPENDER:"); + for (ILoggingEvent event : appender.list) { + System.out.println(" " + event); + } + logger.detachAppender(appender); + } + + @Test + public void toscaPolicyTests() { + TdjamController tc = (TdjamController) PolicyControllerConstants.getFactory().build("tc", prop); + assertTrue(PolicyControllerConstants.getFactory().inventory().contains(tc)); + assertTrue(DroolsControllerConstants.getFactory().inventory().contains(tc)); + + final HashSet<ToscaPolicy> toscaPolicies = new HashSet<>(); + final HashSet<ControlLoopParams> controlLoopParams = new HashSet<>(); + + ToscaPolicy a1 = buildToscaPolicy("a", "1", tc); + ToscaPolicy a2 = buildToscaPolicy("a", "2", tc); + ToscaPolicy b1 = buildToscaPolicy("b", "1", tc); + + toscaPolicies.add(a1); + toscaPolicies.add(a2); + toscaPolicies.add(b1); + + assertSame(a1, tc.getToscaPolicy("a", "1")); + assertSame(a2, tc.getToscaPolicy("a", "2")); + assertSame(b1, tc.getToscaPolicy("b", "1")); + assertEquals(toscaPolicies, tc.getAllToscaPolicies()); + + // create associated ControlLoopParams + final ControlLoopParams clpa1 = buildControlLoopParams("a", "1", "clpa1", tc); + final ControlLoopParams clpa2 = buildControlLoopParams("a", "2", "clpa2", tc); + final ControlLoopParams clpb1 = buildControlLoopParams("b", "1", "clpb1", tc); + final ControlLoopParams clpb3 = buildControlLoopParams("b", "3", "clpb3", null); + + // the add for 'clpb3' should fail, because there is no ToscaPolicy + startLog(); + assertSame(clpb3, tc.addControlLoopParams(clpb3)); + stopLog(); + assertLog(".*Missing ToscaPolicy, name=b, version=3.*"); + assertNull(tc.removeControlLoopParams("clpb3")); + + controlLoopParams.add(clpa1); + controlLoopParams.add(clpa2); + controlLoopParams.add(clpb1); + assertEquals(controlLoopParams, new HashSet<>(tc.getAllControlLoopParams())); + + // manually remove a ControlLoopParams + assertSame(clpa1, tc.removeControlLoopParams("clpa1")); + assertTrue(controlLoopParams.remove(clpa1)); + assertEquals(controlLoopParams, new HashSet<>(tc.getAllControlLoopParams())); + + // tests of nonexistent policies + assertNull(tc.getToscaPolicy("c", "1")); // non-existent name + assertNull(tc.removeToscaPolicy("c", "1")); + assertNull(tc.getToscaPolicy("b", "3")); // non-existent version + assertNull(tc.removeToscaPolicy("b", "3")); + + assertSame(a1, tc.removeToscaPolicy("a", "1")); + assertTrue(toscaPolicies.remove(a1)); + assertEquals(toscaPolicies, tc.getAllToscaPolicies()); + assertSame(a2, tc.removeToscaPolicy("a", "2")); + assertTrue(toscaPolicies.remove(a2)); + assertEquals(toscaPolicies, tc.getAllToscaPolicies()); + + // ControlLoopParams removal should be automatic + assertTrue(controlLoopParams.remove(clpa2)); + assertEquals(controlLoopParams, new HashSet<>(tc.getAllControlLoopParams())); + + // test reset method + tc.reset(); + assertTrue(tc.getAllToscaPolicies().isEmpty()); + assertTrue(tc.getAllControlLoopParams().isEmpty()); + assertTrue(tc.getAllEventManagers().isEmpty()); + assertTrue(tc.getAllOnsetToEventManager().isEmpty()); + + PolicyControllerConstants.getFactory().shutdown(tc); + assertFalse(PolicyControllerConstants.getFactory().inventory().contains(tc)); + assertFalse(DroolsControllerConstants.getFactory().inventory().contains(tc)); + } + + @Test + public void onsetErrors() throws Exception { + TdjamController tc = (TdjamController) PolicyControllerConstants.getFactory().build("tc", prop); + assertTrue(PolicyControllerConstants.getFactory().inventory().contains(tc)); + assertTrue(DroolsControllerConstants.getFactory().inventory().contains(tc)); + tc.start(); + + buildToscaPolicy("a", "1", tc); + final ControlLoopParams clpa1 = buildControlLoopParams("a", "1", "clpa1", tc); + assertTrue(tc.getAllControlLoopParams().contains(clpa1)); + + CanonicalOnset canonicalOnset = new CanonicalOnset(); + startLog(); + Whitebox.invokeMethod(tc, "processEvent", canonicalOnset); + stopLog(); + assertLog(".*No ControlLoopParams for event: CanonicalOnset.*"); + + // set Name with new canonicalOnset + CanonicalOnset canonicalOnset2 = new CanonicalOnset(); + canonicalOnset2.setClosedLoopControlName("clpa1"); + // try with a non-null requestID, but missing target + canonicalOnset2.setRequestId(UUID.randomUUID()); + startLog(); + Whitebox.invokeMethod(tc, "processEvent", canonicalOnset2); + stopLog(); + assertLog(".*Exception creating ControlLoopEventManager.*"); + + PolicyControllerConstants.getFactory().shutdown(tc); + assertFalse(PolicyControllerConstants.getFactory().inventory().contains(tc)); + } + + private ToscaPolicy buildToscaPolicy(String name, String version, TdjamController tc) { + ToscaPolicy tp = new ToscaPolicy(); + tp.setName(name); + tp.setVersion(version); + + if (tc != null) { + tc.addToscaPolicy(tp); + } + return tp; + } + + private ControlLoopParams buildControlLoopParams(String name, String version, + String closedLoopControlName, TdjamController tc) { + + ControlLoopParams clp = new ControlLoopParams(); + clp.setPolicyName(name); + clp.setPolicyVersion(version); + clp.setClosedLoopControlName(closedLoopControlName); + + if (tc != null) { + assertTrue(tc.addControlLoopParams(clp) != clp); + } + + return clp; + } + + private void startLog() { + appender.list.clear(); + appender.start(); + } + + private void stopLog() { + appender.stop(); + } + + private void assertLog(String regexp) { + for (ILoggingEvent event : appender.list) { + if (event.toString().matches(regexp)) { + return; + } + } + fail("Missing log entry: " + regexp); + } +} diff --git a/controlloop/common/controller-tdjam/src/test/java/org/onap/policy/extension/system/NonDroolsPolicyControllerTest.java b/controlloop/common/controller-tdjam/src/test/java/org/onap/policy/extension/system/NonDroolsPolicyControllerTest.java new file mode 100644 index 000000000..ee96cb893 --- /dev/null +++ b/controlloop/common/controller-tdjam/src/test/java/org/onap/policy/extension/system/NonDroolsPolicyControllerTest.java @@ -0,0 +1,343 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.extension.system; + +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.onap.policy.drools.properties.DroolsPropertyConstants.PROPERTY_CONTROLLER_TYPE; + +import java.util.List; +import java.util.Properties; +import java.util.function.Function; +import org.junit.BeforeClass; +import org.junit.Test; +import org.onap.policy.common.endpoints.event.comm.TopicSink; +import org.onap.policy.controlloop.VirtualControlLoopNotification; +import org.onap.policy.drools.controller.DroolsController; +import org.onap.policy.drools.features.DroolsControllerFeatureApi; +import org.onap.policy.drools.features.PolicyControllerFeatureApi; +import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration; +import org.onap.policy.drools.system.PolicyController; +import org.onap.policy.drools.system.PolicyControllerConstants; +import org.onap.policy.drools.utils.PropertyUtil; + +public class NonDroolsPolicyControllerTest { + //public static boolean loop = true; + private static Properties prop; + + @BeforeClass + public static void setupClass() throws Exception { + prop = PropertyUtil.getProperties("src/test/resources/config/tdjam-controller.properties"); + } + + @Test + public void testState() { + NonDroolsPolicyController controller = buildController("tdjam"); + + assertEquals("nondrools", controller.getName()); + assertEquals("NonDroolsPolicyController", controller.getGroupId()); + assertEquals("nondrools", controller.getArtifactId()); + assertEquals("1.0", controller.getVersion()); + assertTrue(controller.isBrained()); + + assertFalse(controller.isAlive()); + assertFalse(controller.isLocked()); + + // first 'start()' + controller.start(); + assertTrue(controller.isAlive()); + assertFalse(controller.isLocked()); + + // second 'start()' + controller.start(); + assertTrue(controller.isAlive()); + assertFalse(controller.isLocked()); + + // test a few stubbed-off methods + assertTrue(controller.getSessionNames().isEmpty()); + assertTrue(controller.getCanonicalSessionNames().isEmpty()); + assertTrue(controller.getBaseDomainNames().isEmpty()); + assertFalse(controller.offer("topic", "event")); + assertFalse(controller.offer("event")); + assertEquals(0, controller.getRecentSourceEvents().length); + assertEquals(0, controller.getRecentSinkEvents().length); + assertNull(controller.getContainer()); + assertThatIllegalArgumentException().isThrownBy( + () -> controller.fetchModelClass("NoSuchClass")); + assertThatIllegalStateException().isThrownBy( + () -> controller.updateToVersion(null, null, null, null, null)); + assertTrue(controller.factClassNames(null).isEmpty()); + assertEquals(0, controller.factCount(null)); + assertTrue(controller.facts(null, null, false).isEmpty()); + assertTrue(controller.facts("sessionName", String.class).isEmpty()); + assertTrue(controller.factQuery(null, null, null, false).isEmpty()); + assertFalse(controller.delete("sessionName", "fact")); + assertFalse(controller.delete("fact")); + assertFalse(controller.delete("sessionName", String.class)); + assertFalse(controller.delete(String.class)); + + controller.lock(); + assertTrue(controller.isAlive()); + assertTrue(controller.isLocked()); + + controller.stop(); + assertFalse(controller.isAlive()); + assertTrue(controller.isLocked()); + + controller.unlock(); + assertFalse(controller.isAlive()); + assertFalse(controller.isLocked()); + + destroy(controller); + } + + @Test + public void deliverTest() { + DroolsControllerFeatureHandler.resetStats(); + final NonDroolsPolicyController controller = buildController("tdjam"); + + final TopicSink topicSink = mock(TopicSink.class); + when(topicSink.getTopic()).thenReturn("POLICY-CL-MGT"); + when(topicSink.send(any())).thenReturn(false); + + final VirtualControlLoopNotification msg = new VirtualControlLoopNotification(null); + + controller.lock(); + + // invalid sink + try { + controller.deliver(null, null); + fail("Expected IllegalArgumentException did not occur"); + } catch (IllegalArgumentException ex) { + assertTrue(ex.getMessage(), + ex.getMessage().endsWith(" invalid sink")); + } + + // invalid event + try { + controller.deliver(topicSink, null); + fail("Expected IllegalArgumentException did not occur"); + } catch (IllegalArgumentException ex) { + assertTrue(ex.getMessage(), + ex.getMessage().endsWith(" invalid event")); + } + + // is locked + try { + controller.deliver(topicSink, "event"); + fail("Expected IllegalStateException did not occur"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage(), + ex.getMessage().endsWith(" is locked")); + } + controller.unlock(); + + // is stopped + try { + controller.deliver(topicSink, "event"); + fail("Expected IllegalStateException did not occur"); + } catch (IllegalStateException ex) { + assertTrue(ex.getMessage(), + ex.getMessage().endsWith(" is stopped")); + } + + // there should have been 4 'beforeDeliver' calls up to this point + assertEquals(4, DroolsControllerFeatureHandler.beforeDeliverFalse); + + Function<String, Boolean> signal = (sig) -> { + msg.getAai().put("signal", sig); + return controller.deliver(topicSink, msg); + }; + + controller.start(); + + // 'beforeDeliver' intercepts + DroolsControllerFeatureHandler.resetStats(); + + assertTrue(signal.apply("beforeDeliverTrue")); + assertEquals(1, DroolsControllerFeatureHandler.beforeDeliverTrue); + assertEquals(0, DroolsControllerFeatureHandler.afterDeliverFalse); + + assertFalse(signal.apply("beforeDeliverException")); + assertEquals(1, DroolsControllerFeatureHandler.beforeDeliverException); + assertEquals(1, DroolsControllerFeatureHandler.afterDeliverFalse); + // it would be nice to check the log message at this point + + // 'afterDeliver' intercepts + DroolsControllerFeatureHandler.resetStats(); + + assertTrue(signal.apply("afterDeliverTrue")); + assertEquals(1, DroolsControllerFeatureHandler.afterDeliverTrue); + + assertFalse(signal.apply("afterDeliverException")); + assertEquals(1, DroolsControllerFeatureHandler.afterDeliverException); + + assertFalse(signal.apply("nothing in particular")); + assertEquals(1, DroolsControllerFeatureHandler.afterDeliverFalse); + + destroy(controller); + } + + private NonDroolsPolicyController buildController(String type) { + prop.setProperty(PROPERTY_CONTROLLER_TYPE, type); + PolicyController controller = + PolicyControllerConstants.getFactory().build("nondrools", prop); + assertTrue(controller instanceof NonDroolsPolicyController); + return (NonDroolsPolicyController) controller; + } + + private void destroy(PolicyController controller) { + String name = controller.getName(); + assertSame(controller, PolicyControllerConstants.getFactory().get(name)); + PolicyControllerConstants.getFactory().destroy(controller); + assertThatIllegalArgumentException().isThrownBy( + () -> PolicyControllerConstants.getFactory().get(name)); + } + + /* ============================================================ */ + + /** + * An instance of this class is called by 'IndexedPolicyControllerFactory'. + * It does the build operation when the value of the 'controller.type' + * property matches the value of TDJAM_CONTROLLER_BUILDER_TAG. + */ + public static class PolicyBuilder implements PolicyControllerFeatureApi { + @Override + public int getSequenceNumber() { + return 1; + } + + @Override + public PolicyController beforeInstance(String name, Properties properties) { + if ("nondrools".equals(properties.getProperty(PROPERTY_CONTROLLER_TYPE))) { + return new NonDroolsPolicyController(name, properties); + } + return null; + } + } + + /* ============================================================ */ + + /** + * An instance of this class is called by 'IndexedDroolsControllerFactory'. + * It does the build operation when the value of the 'controller.type' + * property matches the value of TDJAM_CONTROLLER_BUILDER_TAG. + */ + public static class DroolsBuilder implements DroolsControllerFeatureApi { + @Override + public int getSequenceNumber() { + return 1; + } + + @Override + public DroolsController beforeInstance(Properties properties, + String groupId, String artifactId, String version, + List<TopicCoderFilterConfiguration> decoderConfigurations, + List<TopicCoderFilterConfiguration> encoderConfigurations) throws LinkageError { + + if ("nondrools".equals(properties.getProperty(PROPERTY_CONTROLLER_TYPE))) { + return NonDroolsPolicyController.getBuildInProgress(); + } + return null; + } + } + + /* ============================================================ */ + + public static class DroolsControllerFeatureHandler implements DroolsControllerFeatureApi { + static int beforeDeliverFalse = 0; + static int beforeDeliverTrue = 0; + static int beforeDeliverException = 0; + static int afterDeliverFalse = 0; + static int afterDeliverTrue = 0; + static int afterDeliverException = 0; + + private static void resetStats() { + beforeDeliverFalse = 0; + beforeDeliverTrue = 0; + beforeDeliverException = 0; + afterDeliverFalse = 0; + afterDeliverTrue = 0; + afterDeliverException = 0; + } + + @Override + public int getSequenceNumber() { + return 1; + } + + @Override + public boolean beforeDeliver(DroolsController controller, TopicSink sink, Object fact) { + if (fact instanceof VirtualControlLoopNotification) { + String factString = ((VirtualControlLoopNotification) fact).getAai().get("signal"); + if (factString == null) { + // this hook is run during 'FrankfurtTest' as well + return false; + } + if (factString.contains("beforeDeliverTrue")) { + beforeDeliverTrue += 1; + return true; + } + if (factString.contains("beforeDeliverException")) { + beforeDeliverException += 1; + RuntimeException ex = new RuntimeException("beforeDeliver"); + ex.printStackTrace(); + throw ex; + } + } + beforeDeliverFalse += 1; + return false; + } + + + @Override + public boolean afterDeliver(DroolsController controller, TopicSink sink, Object fact, + String json, boolean success) { + + if (fact instanceof VirtualControlLoopNotification) { + String factString = ((VirtualControlLoopNotification) fact).getAai().get("signal"); + if (factString == null) { + // this hook is run during 'FrankfurtTest' as well + return false; + } + if (factString.contains("afterDeliverTrue")) { + afterDeliverTrue += 1; + return true; + } + if (factString.contains("afterDeliverException")) { + afterDeliverException += 1; + throw new RuntimeException("afterDeliver"); + } + } + afterDeliverFalse += 1; + return false; + } + } +} diff --git a/controlloop/common/controller-tdjam/src/test/resources/META-INF/services/org.onap.policy.drools.features.DroolsControllerFeatureApi b/controlloop/common/controller-tdjam/src/test/resources/META-INF/services/org.onap.policy.drools.features.DroolsControllerFeatureApi new file mode 100644 index 000000000..bb7cf8e3f --- /dev/null +++ b/controlloop/common/controller-tdjam/src/test/resources/META-INF/services/org.onap.policy.drools.features.DroolsControllerFeatureApi @@ -0,0 +1,3 @@ +org.onap.policy.extension.system.NonDroolsPolicyControllerTest$DroolsControllerFeatureHandler +org.onap.policy.extension.system.NonDroolsPolicyControllerTest$DroolsBuilder +org.onap.policy.controlloop.tdjam.TdjamController$DroolsBuilder diff --git a/controlloop/common/controller-tdjam/src/test/resources/META-INF/services/org.onap.policy.drools.features.PolicyControllerFeatureApi b/controlloop/common/controller-tdjam/src/test/resources/META-INF/services/org.onap.policy.drools.features.PolicyControllerFeatureApi new file mode 100644 index 000000000..4f2764376 --- /dev/null +++ b/controlloop/common/controller-tdjam/src/test/resources/META-INF/services/org.onap.policy.drools.features.PolicyControllerFeatureApi @@ -0,0 +1,2 @@ +org.onap.policy.controlloop.tdjam.TdjamController$PolicyBuilder +org.onap.policy.extension.system.NonDroolsPolicyControllerTest$PolicyBuilder diff --git a/controlloop/common/controller-tdjam/src/test/resources/config/event-manager.properties b/controlloop/common/controller-tdjam/src/test/resources/config/event-manager.properties new file mode 100644 index 000000000..f5be41c35 --- /dev/null +++ b/controlloop/common/controller-tdjam/src/test/resources/config/event-manager.properties @@ -0,0 +1,83 @@ +# +# ============LICENSE_START====================================================== +# ONAP +# =============================================================================== +# Copyright (C) 2020 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======================================================== +# + +# DB parameters +operation.history.url=jdbc:h2:mem:Tdjam +operation.history.userName=sa +operation.history.password= + +# Actor parameters +# +# Note: every operation must have at least one entry, otherwise it will not be +# configured and started. Thus some of them have a "placeholder" property. +# + +#actor.service.GUARD.disabled=true +actor.service.GUARD.clientName=GUARD +actor.service.GUARD.onapName=my-onap-name +actor.service.GUARD.onapComponent=my-onap-component +actor.service.GUARD.onapInstance=my-onap-instance +actor.service.GUARD.operations.Decision.path=decision + +actor.service.AAI.clientName=AAI +actor.service.AAI.operations.CustomQuery.path=aai/v16/query +actor.service.AAI.operations.Pnf.path=aai/v16/network/pnfs/pnf +actor.service.AAI.operations.Tenant.path=aai/v16/search/nodes-query + +actor.service.APPC.sinkTopic=APPC-LCM-READ +actor.service.APPC.sourceTopic=APPC-LCM-WRITE +actor.service.APPC.operations.ConfigModify.placeholder= +actor.service.APPC.operations.Migrate.placeholder= +actor.service.APPC.operations.Restart.placeholder= +actor.service.APPC.operations.Rebuild.placeholder= + +# legacy APPC - must specify sink and source for each operation +actor.service.APPC.operations.ModifyConfig.sinkTopic=APPC-CL +actor.service.APPC.operations.ModifyConfig.sourceTopic=APPC-CL + +actor.service.CDS.operations.any.host=localhost +actor.service.CDS.operations.any.port=7878 +actor.service.CDS.operations.any.username=grpc-username +actor.service.CDS.operations.any.password=grpc-password +actor.service.CDS.operations.any.timeout=10 + +actor.service.SDNC.clientName=SDNC +actor.service.SDNC.operations.BandwidthOnDemand.path=\ + GENERIC-RESOURCE-API:vf-module-topology-operation +actor.service.SDNC.operations.Reroute.path=\ + GENERIC-RESOURCE-API:network-topology-operation + +actor.service.SDNR.sinkTopic=SDNR-CL +actor.service.SDNR.sourceTopic=SDNR-CL-RSP +actor.service.SDNR.operations.any.placeholder= + +actor.service.SO.clientName=SO +actor.service.SO.pollPath=orchestrationRequests/v5/ +actor.service.SO.maxPolls=20 +actor.service.SO.pollWaitSec=20 +actor.service.SO.operations.VF\ Module\ Create.path=serviceInstantiation/v7/serviceInstances +actor.service.SO.operations.VF\ Module\ Delete.path=serviceInstances/v7 + +actor.service.VFC.clientName=VFC +actor.service.VFC.pollPath=jobs +actor.service.VFC.maxPolls=20 +actor.service.VFC.pollWaitSec=20 +actor.service.VFC.operations.Restart.path=ns +actor.service.VFC.operations.Restart.timeoutSec=60 diff --git a/controlloop/common/controller-tdjam/src/test/resources/config/tdjam-controller.properties b/controlloop/common/controller-tdjam/src/test/resources/config/tdjam-controller.properties new file mode 100644 index 000000000..41db06c51 --- /dev/null +++ b/controlloop/common/controller-tdjam/src/test/resources/config/tdjam-controller.properties @@ -0,0 +1,64 @@ +# +# ============LICENSE_START======================================================= +# ONAP +# ================================================================================ +# Copyright (C) 2020 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========================================================= +# + +controller.name=tdjam +controller.type=tdjam + +rules.groupId=NonDroolsPolicyController +rules.artifactId=tdjam +rules.version=1.0.0 + +noop.source.topics=DCAE_TOPIC,APPC-CL,APPC-LCM-WRITE,SDNR-CL-RSP,POLICY-CL-MGT,APPC-LCM-READ + +noop.source.topics.DCAE_TOPIC.events=\ + org.onap.policy.controlloop.CanonicalOnset,org.onap.policy.controlloop.CanonicalAbated +noop.source.topics.DCAE_TOPIC.events.org.onap.policy.controlloop.CanonicalOnset.\ + filter=[?($.closedLoopEventStatus == 'ONSET')] +noop.source.topics.DCAE_TOPIC.events.org.onap.policy.controlloop.CanonicalAbated.\ + filter=[?($.closedLoopEventStatus == 'ABATED')] +noop.source.topics.DCAE_TOPIC.events.custom.gson=org.onap.policy.controlloop.util.Serialization,gson + +noop.source.topics.APPC-CL.events=org.onap.policy.appc.Response,org.onap.policy.appc.Request +noop.source.topics.APPC-CL.events.org.onap.policy.appc.Response.filter=[?($.CommonHeader && $.Status)] +noop.source.topics.APPC-CL.events.org.onap.policy.appc.Request.filter=[?($.CommonHeader && $.Action)] +noop.source.topics.APPC-CL.events.custom.gson=org.onap.policy.appc.util.Serialization,gsonPretty + +noop.source.topics.APPC-LCM-WRITE.events=org.onap.policy.appclcm.AppcLcmDmaapWrapper +noop.source.topics.APPC-LCM-WRITE.events.org.onap.policy.appclcm.AppcLcmDmaapWrapper.filter=[?($.type == 'response')] +noop.source.topics.APPC-LCM-WRITE.events.custom.gson=org.onap.policy.appclcm.util.Serialization,gson + +noop.source.topics.SDNR-CL-RSP.events=org.onap.policy.sdnr.PciResponseWrapper +noop.source.topics.SDNR-CL-RSP.events.org.onap.policy.sdnr.PciResponseWrapper.filter=[?($.type == 'response')] +noop.source.topics.SDNR-CL-RSP.events.custom.gson=org.onap.policy.sdnr.util.Serialization,gson + +noop.source.topics.POLICY-CL-MGT.events=org.onap.policy.controlloop.VirtualControlLoopNotification +noop.source.topics.POLICY-CL-MGT.events.custom.gson=org.onap.policy.controlloop.util.Serialization,gsonPretty + +noop.source.topics.APPC-LCM-READ.events=org.onap.policy.appclcm.AppcLcmDmaapWrapper +noop.source.topics.APPC-LCM-READ.events.custom.gson=org.onap.policy.appclcm.util.Serialization,gson + +noop.sink.topics=APPC-CL,APPC-LCM-READ,POLICY-CL-MGT,SDNR-CL,DCAE_CL_RSP + +noop.sink.topics.POLICY-CL-MGT.events=org.onap.policy.controlloop.VirtualControlLoopNotification +noop.sink.topics.POLICY-CL-MGT.events.custom.gson=org.onap.policy.controlloop.util.Serialization,gsonPretty + +noop.sink.topics.DCAE_CL_RSP.events=org.onap.policy.controlloop.ControlLoopResponse +noop.sink.topics.DCAE_CL_RSP.events.custom.gson=org.onap.policy.controlloop.util.Serialization,gsonPretty + diff --git a/controlloop/common/controller-tdjam/src/test/resources/config/tdjam-http-client.properties b/controlloop/common/controller-tdjam/src/test/resources/config/tdjam-http-client.properties new file mode 100644 index 000000000..1e3e88cec --- /dev/null +++ b/controlloop/common/controller-tdjam/src/test/resources/config/tdjam-http-client.properties @@ -0,0 +1,52 @@ +# +# ============LICENSE_START======================================================= +# ONAP +# ================================================================================ +# Copyright (C) 2020 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========================================================= +# + +http.client.services=GUARD,AAI,SDNC,SO,VFC + +http.client.services.GUARD.managed=true +http.client.services.GUARD.host=localhost +http.client.services.GUARD.port=6669 +http.client.services.GUARD.userName=pdpx +http.client.services.GUARD.password=pdpx +http.client.services.GUARD.contextUriPath=policy/pdpx/v1/ + +http.client.services.AAI.managed=true +http.client.services.AAI.host=localhost +http.client.services.AAI.port=6666 +http.client.services.AAI.contextUriPath= + +http.client.services.SDNC.managed=true +http.client.services.SDNC.host=localhost +http.client.services.SDNC.port=6665 +http.client.services.SDNC.userName=sdnc +http.client.services.SDNC.password=sdnc +http.client.services.SDNC.contextUriPath= + +http.client.services.SO.managed=true +http.client.services.SO.host=localhost +http.client.services.SO.port=6667 +http.client.services.SO.contextUriPath= + +http.client.services.VFC.managed=true +http.client.services.VFC.host=localhost +http.client.services.VFC.port=6668 +http.client.services.VFC.userName=VFC +http.client.services.VFC.password=VFC +http.client.services.VFC.contextUriPath=api/nslcm/v1 diff --git a/controlloop/common/controller-tdjam/src/test/resources/logback-test.xml b/controlloop/common/controller-tdjam/src/test/resources/logback-test.xml new file mode 100644 index 000000000..656cb8136 --- /dev/null +++ b/controlloop/common/controller-tdjam/src/test/resources/logback-test.xml @@ -0,0 +1,38 @@ +<!-- + ============LICENSE_START======================================================= + controller-tdjam + ================================================================================ + Copyright (C) 2020 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========================================================= + --> + +<configuration> + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <!-- encoders are assigned the type + ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> + <encoder> + <pattern>%d{dd-MMM-yyyy HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n</pattern> + </encoder> + </appender> + + <!-- the following line doesn't seem necessary, but it is needed for some reason --> + <logger name="org.onap.policy.controlloop.tdjam" level="debug" additivity="false"> + <appender-ref ref="STDOUT" /> + </logger> + + <root level="debug"> + <appender-ref ref="STDOUT" /> + </root> +</configuration> |