import java.time.Instant;
 import java.util.LinkedList;
 import java.util.Iterator;
-import java.util.HashSet;
-import java.util.Set;
 
 import org.onap.policy.drools.system.PolicyEngine;
 
   controlLoopYaml : String
 end
 
+/*
+ * Used to trigger clean up Params that no longer have associated rules.
+ */
+declare ParamsInitCleaner
+  closedLoopControlName : String    // only used when logging
+end
+
 /*
  * Used to clean up Params that no longer have associated rules.
  */
 declare ParamsCleaner
   closedLoopControlName : String
-  identified : boolean              // true if all active Params have been identified
-  active : Set                      // Params that are still active
+  controlLoopYaml : String
 end
 
 /*
     params.setControlLoopYaml("${controlLoopYaml}");
     insert(params);
     
-    ParamsCleaner cleaner = new ParamsCleaner();
-    cleaner.setClosedLoopControlName("${closedLoopControlName}");
-    cleaner.setIdentified(false);
-    cleaner.setActive(new HashSet());
-    insert(cleaner);
+    ParamsInitCleaner initCleaner = new ParamsInitCleaner();
+    initCleaner.setClosedLoopControlName("${closedLoopControlName}");
+    insert(initCleaner);
 
     // Note: globals have bad behavior when persistence is used,
     //       hence explicitly getting the logger vs using a global
 end
 
 /*
-* Indicates to the cleaner that this Params object is still active.
-* This has a higher salience so that it is fired before processing any events.
+* Creates a cleaner for every Params object.
+* This has a higher salience so that it is fired before PARAMS.FINISHED in ANY policy.
 */
-rule "${policyName}.PARAMS.ACTIVE"
-    salience 4
+rule "${policyName}.PARAMS.CLEANING"
+    salience 2
     when
-        $params: Params( getClosedLoopControlName() == "${closedLoopControlName}",
-                            getControlLoopYaml() == "${controlLoopYaml}" )
-        ParamsCleaner( !identified, $active: active )
+        $params: Params( )
+        ParamsInitCleaner( )
     then
  
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
     logger.info("{}: {} : YAML=[{}]", $params.getClosedLoopControlName(), drools.getRule().getName(),
         $params.getControlLoopYaml());
         
-    $active.add($params);
-    
-    // do NOT update anything at this point
+    ParamsCleaner cleaner = new ParamsCleaner();
+    cleaner.setClosedLoopControlName($params.getClosedLoopControlName());
+    cleaner.setControlLoopYaml($params.getControlLoopYaml());
+    insert(cleaner);
 end
 
 /*
-* Finished identifying active Params objects.  Begin deleting inactive Params.
+* Finished creating cleaner objects, so remove the trigger.
 * This has a higher salience so that it is fired before processing any events.
 */
-rule "${policyName}.PARAMS.IDENTIFIED"
-    salience 3
+rule "${policyName}.PARAMS.FINISHED"
+    salience 1
     when
-        $cleaner: ParamsCleaner( !identified )
+        $initCleaner: ParamsInitCleaner( )
     then
  
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}", $cleaner.getClosedLoopControlName(), drools.getRule().getName());
+    logger.info("{}: {}", $initCleaner.getClosedLoopControlName(), drools.getRule().getName());
     
-    $cleaner.setIdentified(true);
-    update($cleaner);
+    retract($initCleaner);
 end
 
 /*
-* Delete Params objects that have not been identified as being active.
-* This has a higher salience so that it is fired before processing any events.
+* Identifies Params objects that are still active, removing their associated cleaners.
+* This should only leave one active Params object for each policy.
+* This has a higher salience so that it is fired before PARAMS.DELETE in ANY policy.
+*/
+rule "${policyName}.PARAMS.ACTIVE"
+    salience 3
+    when
+        $params: Params( getClosedLoopControlName() == "${closedLoopControlName}",
+                            getControlLoopYaml() == "${controlLoopYaml}" )
+        $cleaner: ParamsCleaner( getClosedLoopControlName() == "${closedLoopControlName}",
+                            getControlLoopYaml() == "${controlLoopYaml}"  )
+    then
+ 
+    Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+    logger.info("{}: {} : YAML=[{}]", $params.getClosedLoopControlName(), drools.getRule().getName(),
+        $params.getControlLoopYaml());
+        
+    retract($cleaner);
+end
+
+/*
+* Delete Params objects that are not active (i.e., those that still have an associated
+* cleaner object).
+* This has a higher salience so that it is fired before PARAMS.CLEANED in ANY policy.
 */
 rule "${policyName}.PARAMS.DELETE"
     salience 2
     when
         $params: Params( )
-        ParamsCleaner( identified, !active.contains($params) )
+        $cleaner: ParamsCleaner( getClosedLoopControlName() == $params.getClosedLoopControlName(),
+                            getControlLoopYaml() == $params.getControlLoopYaml()  )
     then
  
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
         $params.getControlLoopYaml());
         
     retract($params);
-    
-    // do NOT update anything at this point
 end
 
 /*
-* Finished deleting inactive Params objects, so remove the cleaner.
+* Finished clean-up, so delete the cleaner objects.
 * This has a higher salience so that it is fired before processing any events.
 */
 rule "${policyName}.PARAMS.CLEANED"
     salience 1
     when
-        $cleaner: ParamsCleaner( identified )
+        $cleaner: ParamsCleaner( )
     then
  
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}", $cleaner.getClosedLoopControlName(), drools.getRule().getName());
-    
+    logger.info("{}: {} : YAML=[{}]", $cleaner.getClosedLoopControlName(), drools.getRule().getName(),
+        $cleaner.getControlLoopYaml());
+        
     retract($cleaner);
 end
 
 import java.time.Instant;
 import java.util.LinkedList;
 import java.util.Iterator;
-import java.util.HashSet;
-import java.util.Set;
 
 import org.onap.policy.drools.system.PolicyEngine;
 
   controlLoopYaml : String
 end
 
+/*
+ * Used to trigger clean up Params that no longer have associated rules.
+ */
+declare ParamsInitCleaner
+  closedLoopControlName : String    // only used when logging
+end
+
 /*
  * Used to clean up Params that no longer have associated rules.
  */
 declare ParamsCleaner
   closedLoopControlName : String
-  identified : boolean              // true if all active Params have been identified
-  active : Set                      // Params that are still active
+  controlLoopYaml : String
 end
 
 
     params.setControlLoopYaml("${controlLoopYaml}");
     insert(params);
     
-    ParamsCleaner cleaner = new ParamsCleaner();
-    cleaner.setClosedLoopControlName("${closedLoopControlName}");
-    cleaner.setIdentified(false);
-    cleaner.setActive(new HashSet());
-    insert(cleaner);
+    ParamsInitCleaner initCleaner = new ParamsInitCleaner();
+    initCleaner.setClosedLoopControlName("${closedLoopControlName}");
+    insert(initCleaner);
 
     // Note: globals have bad behavior when persistence is used,
     //       hence explicitly getting the logger vs using a global
 end
 
 /*
-* Indicates to the cleaner that this Params object is still active.
-* This has a higher salience so that it is fired before processing any events.
+* Creates a cleaner for every Params object.
+* This has a higher salience so that it is fired before PARAMS.FINISHED in ANY policy.
 */
-rule "${policyName}.PARAMS.ACTIVE"
-    salience 4
+rule "${policyName}.PARAMS.CLEANING"
+    salience 2
     when
-        $params: Params( getClosedLoopControlName() == "${closedLoopControlName}",
-                            getControlLoopYaml() == "${controlLoopYaml}" )
-        ParamsCleaner( !identified, $active: active )
+        $params: Params( )
+        ParamsInitCleaner( )
     then
  
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
     logger.info("{}: {} : YAML=[{}]", $params.getClosedLoopControlName(), drools.getRule().getName(),
         $params.getControlLoopYaml());
         
-    $active.add($params);
-    
-    // do NOT update anything at this point
+    ParamsCleaner cleaner = new ParamsCleaner();
+    cleaner.setClosedLoopControlName($params.getClosedLoopControlName());
+    cleaner.setControlLoopYaml($params.getControlLoopYaml());
+    insert(cleaner);
 end
 
 /*
-* Finished identifying active Params objects.  Begin deleting inactive Params.
+* Finished creating cleaner objects, so remove the trigger.
 * This has a higher salience so that it is fired before processing any events.
 */
-rule "${policyName}.PARAMS.IDENTIFIED"
-    salience 3
+rule "${policyName}.PARAMS.FINISHED"
+    salience 1
     when
-        $cleaner: ParamsCleaner( !identified )
+        $initCleaner: ParamsInitCleaner( )
     then
  
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}", $cleaner.getClosedLoopControlName(), drools.getRule().getName());
+    logger.info("{}: {}", $initCleaner.getClosedLoopControlName(), drools.getRule().getName());
     
-    $cleaner.setIdentified(true);
-    update($cleaner);
+    retract($initCleaner);
 end
 
 /*
-* Delete Params objects that have not been identified as being active.
-* This has a higher salience so that it is fired before processing any events.
+* Identifies Params objects that are still active, removing their associated cleaners.
+* This should only leave one active Params object for each policy.
+* This has a higher salience so that it is fired before PARAMS.DELETE in ANY policy.
+*/
+rule "${policyName}.PARAMS.ACTIVE"
+    salience 3
+    when
+        $params: Params( getClosedLoopControlName() == "${closedLoopControlName}",
+                            getControlLoopYaml() == "${controlLoopYaml}" )
+        $cleaner: ParamsCleaner( getClosedLoopControlName() == "${closedLoopControlName}",
+                            getControlLoopYaml() == "${controlLoopYaml}"  )
+    then
+ 
+    Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
+    logger.info("{}: {} : YAML=[{}]", $params.getClosedLoopControlName(), drools.getRule().getName(),
+        $params.getControlLoopYaml());
+        
+    retract($cleaner);
+end
+
+/*
+* Delete Params objects that are not active (i.e., those that still have an associated
+* cleaner object).
+* This has a higher salience so that it is fired before PARAMS.CLEANED in ANY policy.
 */
 rule "${policyName}.PARAMS.DELETE"
     salience 2
     when
         $params: Params( )
-        ParamsCleaner( identified, !active.contains($params) )
+        $cleaner: ParamsCleaner( getClosedLoopControlName() == $params.getClosedLoopControlName(),
+                            getControlLoopYaml() == $params.getControlLoopYaml()  )
     then
  
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
         $params.getControlLoopYaml());
         
     retract($params);
-    
-    // do NOT update anything at this point
 end
 
 /*
-* Finished deleting inactive Params objects, so remove the cleaner.
+* Finished clean-up, so delete the cleaner objects.
 * This has a higher salience so that it is fired before processing any events.
 */
 rule "${policyName}.PARAMS.CLEANED"
     salience 1
     when
-        $cleaner: ParamsCleaner( identified )
+        $cleaner: ParamsCleaner( )
     then
  
     Logger logger = LoggerFactory.getLogger(drools.getRule().getPackage());
-    logger.info("{}: {}", $cleaner.getClosedLoopControlName(), drools.getRule().getName());
-    
+    logger.info("{}: {} : YAML=[{}]", $cleaner.getClosedLoopControlName(), drools.getRule().getName(),
+        $cleaner.getControlLoopYaml());
+        
     retract($cleaner);
 end
 
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * demo
+ * ================================================================================
+ * Copyright (C) 2018 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.template.demo.clc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+import java.util.UUID;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.kie.api.runtime.KieSession;
+import org.onap.policy.common.endpoints.event.comm.TopicEndpoint;
+import org.onap.policy.common.endpoints.event.comm.TopicSink;
+import org.onap.policy.common.endpoints.http.server.HttpServletServer;
+import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
+import org.onap.policy.controlloop.ControlLoopEventStatus;
+import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager;
+import org.onap.policy.controlloop.policy.ControlLoopPolicy;
+import org.onap.policy.drools.protocol.coders.EventProtocolCoder;
+import org.onap.policy.drools.protocol.coders.JsonProtocolFilter;
+import org.onap.policy.drools.system.PolicyController;
+import org.onap.policy.drools.system.PolicyEngine;
+import org.onap.policy.drools.utils.logging.LoggerUtil;
+import org.onap.policy.template.demo.clc.Util.Pair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Verifies that event objects are cleaned up when rules are updated. This loads
+ * <b>two</b> copies of the rule set into a single policy to ensure that the two copies
+ * interact appropriately with each other's event objects.
+ */
+public class ControlLoopEventCleanupTest {
+    private static final Logger logger = LoggerFactory.getLogger(ControlLoopEventCleanupTest.class);
+
+    /**
+     * Number of objects per control loop, including the Params object.
+     */
+    private static int CL_OBJECTS = 7;
+
+    private static final String YAML = "src/test/resources/yaml/policy_ControlLoop_EventCleanup-test.yaml";
+
+    /**
+     * YAML to be used when the first rule set is updated.
+     */
+    private static final String YAML2 = "src/test/resources/yaml/policy_ControlLoop_EventCleanup-test2.yaml";
+
+    private static final String POLICY_VERSION = "v2.0";
+
+    private static final String POLICY_NAME = "CL_CleanupTest";
+
+    private static final String POLICY_SCOPE = "type=operational";
+
+    private static final String CONTROL_LOOP_NAME = "ControlLoop-Event-Cleanup-Test";
+
+    private static final String DROOLS_TEMPLATE = "src/main/resources/__closedLoopControlName__.drl";
+
+    // values specific to the second copy of the rules
+
+    private static final String YAML_B = "src/test/resources/yaml/policy_ControlLoop_EventCleanup-test-B.yaml";
+    private static final String POLICY_NAME_B = "CL_CleanupTest_B";
+    private static final String CONTROL_LOOP_NAME_B = "ControlLoop-Event-Cleanup-Test-B";
+
+    private static final String GUARD_DISABLED = "guard.disabled";
+
+    private static String saveGuardFlag;
+
+    private static KieSession kieSession;
+    private static Util.RuleSpec[] specifications;
+
+    /**
+     * Setup the simulator.
+     */
+    @BeforeClass
+    public static void setUpSimulator() {
+        LoggerUtil.setLevel(LoggerUtil.ROOT_LOGGER, "INFO");
+
+        saveGuardFlag = PolicyEngine.manager.getEnvironmentProperty(GUARD_DISABLED);
+        PolicyEngine.manager.getEnvironment().setProperty(GUARD_DISABLED, "true");
+
+        Util.setAaiProps();
+
+        PolicyEngine.manager.configure(new Properties());
+        assertTrue(PolicyEngine.manager.start());
+        Properties noopSinkProperties = new Properties();
+        noopSinkProperties.put(PolicyEndPointProperties.PROPERTY_NOOP_SINK_TOPICS, "APPC-CL,POLICY-CL-MGT");
+        noopSinkProperties.put("noop.sink.topics.APPC-CL.events", "org.onap.policy.appc.Response");
+        noopSinkProperties.put("noop.sink.topics.APPC-CL.events.custom.gson",
+                        "org.onap.policy.appc.util.Serialization,gsonPretty");
+        noopSinkProperties.put("noop.sink.topics.POLICY-CL-MGT.events",
+                        "org.onap.policy.controlloop.VirtualControlLoopNotification");
+        noopSinkProperties.put("noop.sink.topics.POLICY-CL-MGT.events.custom.gson",
+                        "org.onap.policy.controlloop.util.Serialization,gsonPretty");
+        final List<TopicSink> noopTopics = TopicEndpoint.manager.addTopicSinks(noopSinkProperties);
+
+        EventProtocolCoder.manager.addEncoder("junit.groupId", "junit.artifactId", "POLICY-CL-MGT",
+                        "org.onap.policy.controlloop.VirtualControlLoopNotification", new JsonProtocolFilter(), null,
+                        null, 1111);
+        EventProtocolCoder.manager.addEncoder("junit.groupId", "junit.artifactId", "APPC-CL",
+                        "org.onap.policy.appc.Request", new JsonProtocolFilter(), null, null, 1111);
+
+        try {
+            Util.buildAaiSim();
+
+        } catch (Exception e) {
+            logger.error("Could not create simulator", e);
+            fail("Could not create simulator");
+        }
+
+        for (TopicSink sink : noopTopics) {
+            assertTrue(sink.start());
+        }
+
+        try {
+            specifications = new Util.RuleSpec[2];
+
+            specifications[0] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME, POLICY_SCOPE, POLICY_NAME,
+                            POLICY_VERSION, loadYaml(YAML));
+
+            specifications[1] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME_B, POLICY_SCOPE, POLICY_NAME_B,
+                            POLICY_VERSION, loadYaml(YAML_B));
+
+            kieSession = Util.buildContainer(POLICY_VERSION, specifications);
+
+        } catch (IOException e) {
+            logger.error("Could not create kieSession", e);
+            fail("Could not create kieSession");
+        }
+    }
+
+    /**
+     * Tear down.
+     */
+    @AfterClass
+    public static void tearDown() {
+        kieSession.dispose();
+
+        PolicyEngine.manager.stop();
+        HttpServletServer.factory.destroy();
+        PolicyController.factory.shutdown();
+        TopicEndpoint.manager.shutdown();
+
+        if (saveGuardFlag == null) {
+            PolicyEngine.manager.getEnvironment().remove(GUARD_DISABLED);
+
+        } else {
+            PolicyEngine.manager.getEnvironment().setProperty(GUARD_DISABLED, saveGuardFlag);
+        }
+    }
+
+    @Test
+    public void test() throws IOException {
+
+        /*
+         * Let rules create Params objects.
+         */
+        kieSession.fireAllRules();
+
+        injectEvent(CONTROL_LOOP_NAME);
+        injectEvent(CONTROL_LOOP_NAME_B);
+
+        kieSession.fireAllRules();
+        List<Object> facts = getSessionObjects();
+        
+        // should have events for both control loops
+        assertEquals(2 * CL_OBJECTS, facts.size());
+        assertTrue(hasEvent(facts, CONTROL_LOOP_NAME));
+        assertTrue(hasEvent(facts, CONTROL_LOOP_NAME_B));
+
+        logger.info("UPDATING VERSION TO v3.0");
+        updatePolicy(YAML2, "v3.0");
+
+        /*
+         * Let rules update Params objects. The Params for the first set of rules should
+         * now be deleted and replaced with a new one, while the Params for the second set
+         * should be unchanged.
+         */
+        kieSession.fireAllRules();
+        facts = getSessionObjects();
+
+        // should only have event for second control loop + 1 Params for first control loop
+        assertEquals(CL_OBJECTS + 1, facts.size());
+        assertTrue(hasEvent(facts, CONTROL_LOOP_NAME_B));
+
+        // add event for first control loop again
+        injectEvent(CONTROL_LOOP_NAME);
+        kieSession.fireAllRules();
+
+        logger.info("UPDATING VERSION TO v4.0");
+        updatePolicy(YAML, "v4.0");
+
+        /*
+         * Let rules update Params objects. The Params for the first set of rules should
+         * now be deleted and replaced with a new one, while the Params for the second set
+         * should be unchanged.
+         */
+        kieSession.fireAllRules();
+        facts = getSessionObjects();
+
+        // should only have event for second control loop + 1 Params for first control loop
+        assertEquals(CL_OBJECTS + 1, facts.size());
+        assertTrue(hasEvent(facts, CONTROL_LOOP_NAME_B));
+
+        // add event for first control loop again
+        injectEvent(CONTROL_LOOP_NAME);
+        kieSession.fireAllRules();
+
+        logger.info("UPDATING VERSION TO v4.0 (i.e., unchanged)");
+        updatePolicy(YAML, "v4.0");
+
+        /*
+         * Let rules update Params objects. As the version (and YAML) are unchanged for
+         * either rule set, both Params objects should be unchanged.
+         */
+        kieSession.fireAllRules();
+        facts = getSessionObjects();
+
+        // should have events for both control loops
+        assertEquals(2 * CL_OBJECTS, facts.size());
+        assertTrue(hasEvent(facts, CONTROL_LOOP_NAME));
+        assertTrue(hasEvent(facts, CONTROL_LOOP_NAME_B));
+
+        /*
+         * Now we'll delete the first rule set. That won't actually have any immediate
+         * effect, so then we'll update the second rule set, which should trigger a
+         * clean-up of both.
+         */
+        Util.RuleSpec[] specs = new Util.RuleSpec[1];
+        specs[0] = specifications[1];
+
+        logger.info("UPDATING VERSION TO v5.0 - DELETED RULE SET");
+        Util.updateContainer("v5.0", specs);
+
+        specs[0] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME_B, POLICY_SCOPE, POLICY_NAME_B, POLICY_VERSION,
+                        loadYaml(YAML));
+
+        logger.info("UPDATING VERSION TO v6.0 - UPDATED SECOND RULE SET");
+        Util.updateContainer("v6.0", specs);
+
+        kieSession.fireAllRules();
+        facts = getSessionObjects();
+
+        // only 1 Params should remain, for second rule set, but events should be gone
+        assertEquals(1, facts.size());
+        assertTrue(facts.stream().anyMatch(obj -> obj.toString().startsWith("Params( ")));
+    }
+
+    /**
+     * Updates the policy, changing the YAML associated with the first rule set.
+     *
+     * @param yamlFile name of the YAML file
+     * @param policyVersion policy version
+     * @throws IOException if an error occurs
+     */
+    private static void updatePolicy(String yamlFile, String policyVersion) throws IOException {
+
+        specifications[0] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME, POLICY_SCOPE, POLICY_NAME,
+                        policyVersion, loadYaml(yamlFile));
+
+        /*
+         * Update the policy within the container.
+         */
+        Util.updateContainer(policyVersion, specifications);
+    }
+
+    /**
+     * Loads a YAML file and URL-encodes it.
+     *
+     * @param yamlFile name of the YAML file
+     * @return the contents of the specified file, URL-encoded
+     * @throws UnsupportedEncodingException if an error occurs
+     */
+    private static String loadYaml(String yamlFile) throws UnsupportedEncodingException {
+        Pair<ControlLoopPolicy, String> pair = Util.loadYaml(yamlFile);
+        assertNotNull(pair);
+        assertNotNull(pair.first);
+        assertNotNull(pair.first.getControlLoop());
+        assertNotNull(pair.first.getControlLoop().getControlLoopName());
+        assertTrue(pair.first.getControlLoop().getControlLoopName().length() > 0);
+
+        return URLEncoder.encode(pair.second, "UTF-8");
+    }
+
+    /**
+     * Gets the session objects.
+     *
+     * @return the session objects
+     */
+    private static List<Object> getSessionObjects() {
+        // sort the objects so we know the order
+        LinkedList<Object> lst = new LinkedList<>(kieSession.getObjects());
+        lst.sort((left, right) -> left.toString().compareTo(right.toString()));
+
+        lst.forEach(obj -> logger.info("obj={}", obj));
+
+        return lst;
+    }
+
+    /**
+     * Injects an ONSET event into the rule engine.
+     *
+     * @param controlLoopName the control loop name
+     */
+    private void injectEvent(String controlLoopName) {
+        VirtualControlLoopEvent event = new VirtualControlLoopEvent();
+
+        event.setClosedLoopControlName(controlLoopName);
+
+        UUID reqid = UUID.randomUUID();
+        event.setRequestId(reqid);
+
+        event.setTarget("generic-vnf.vnf-id");
+        event.setClosedLoopAlarmStart(Instant.now());
+        event.setAai(new HashMap<>());
+        event.getAai().put("generic-vnf.vnf-id", "vnf-" + reqid.toString());
+        event.getAai().put(ControlLoopEventManager.GENERIC_VNF_IS_CLOSED_LOOP_DISABLED, "false");
+        event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET);
+
+        kieSession.insert(event);
+    }
+
+    /**
+     * Determines if the facts contain an event for the given control loop.
+     * 
+     * @param facts session facts to be checked
+     * @param controlLoopName name of the control loop of interest
+     * @return {@code true} if the facts contain an event for the given control loop,
+     *         {@code false} otherwise
+     */
+    private boolean hasEvent(List<Object> facts, String controlLoopName) {
+        return (facts.stream().anyMatch(obj -> obj instanceof VirtualControlLoopEvent
+                        && controlLoopName.equals(((VirtualControlLoopEvent) obj).getClosedLoopControlName())));
+    }
+}
 
--- /dev/null
+# Copyright 2018 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.
+controlLoop:
+  version: 2.0.0
+  controlLoopName: ControlLoop-Event-Cleanup-Test-B
+  services:
+    - serviceInvariantUUID: 5cfe6f4a-41bc-4247-8674-ebd4b98e35cc
+      serviceUUID: 0f40bba5-986e-4b3c-803f-ddd1b7b25f24
+      serviceName: 57e66ea7-0ed6-45c7-970f
+  trigger_policy: unique-policy-id-1-modifyConfig
+  timeout: 60
+  abatement: true
+ 
+policies:
+  - id: unique-policy-id-1-modifyConfig
+    name: modify packet gen config
+    description:
+    actor: APPC
+    recipe: ModifyConfig
+    target:
+      resourceID: Eace933104d443b496b8.nodes.heat.vpg
+      type: VNF
+    retry: 0
+    timeout: 30
+    success: final_success
+    failure: final_failure
+    failure_timeout: final_failure_timeout
+    failure_retries: final_failure_retries
+    failure_exception: final_failure_exception
+    failure_guard: final_failure_guard
\ No newline at end of file
 
--- /dev/null
+# Copyright 2018 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.
+controlLoop:
+  version: 2.0.0
+  controlLoopName: ControlLoop-Event-Cleanup-Test
+  services:
+    - serviceInvariantUUID: 5cfe6f4a-41bc-4247-8674-ebd4b98e35cc
+      serviceUUID: 0f40bba5-986e-4b3c-803f-ddd1b7b25f24
+      serviceName: 57e66ea7-0ed6-45c7-970f
+  trigger_policy: unique-policy-id-1-modifyConfig
+  timeout: 60
+  abatement: true
+ 
+policies:
+  - id: unique-policy-id-1-modifyConfig
+    name: modify packet gen config
+    description:
+    actor: APPC
+    recipe: ModifyConfig
+    target:
+      resourceID: Eace933104d443b496b8.nodes.heat.vpg
+      type: VNF
+    retry: 0
+    timeout: 30
+    success: final_success
+    failure: final_failure
+    failure_timeout: final_failure_timeout
+    failure_retries: final_failure_retries
+    failure_exception: final_failure_exception
+    failure_guard: final_failure_guard
\ No newline at end of file
 
--- /dev/null
+# Copyright 2018 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.
+
+#
+# This YAML must be slightly different from test.yaml.
+#
+controlLoop:
+  version: 3.0.0
+  controlLoopName: ControlLoop-Event-Cleanup-Test
+  services:
+    - serviceInvariantUUID: 5cfe6f4a-41bc-4247-8674-ebd4b98e35cc
+      serviceUUID: 0f40bba5-986e-4b3c-803f-ddd1b7b25f24
+      serviceName: 57e66ea7-0ed6-45c7-970f
+  trigger_policy: unique-policy-id-1-modifyConfig
+  timeout: 60
+  abatement: true
+ 
+policies:
+  - id: unique-policy-id-1-modifyConfig
+    name: modify packet gen config
+    description:
+    actor: APPC
+    recipe: ModifyConfig
+    target:
+      resourceID: Eace933104d443b496b8.nodes.heat.vpg
+      type: VNF
+    retry: 0
+    timeout: 30
+    success: final_success
+    failure: final_failure
+    failure_timeout: final_failure_timeout
+    failure_retries: final_failure_retries
+    failure_exception: final_failure_exception
+    failure_guard: final_failure_guard
\ No newline at end of file
 
--- /dev/null
+/*-
+ * ============LICENSE_START=======================================================
+ * demo
+ * ================================================================================
+ * Copyright (C) 2018 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.template.demo;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+import java.util.UUID;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.kie.api.runtime.KieSession;
+import org.onap.policy.common.endpoints.event.comm.TopicEndpoint;
+import org.onap.policy.common.endpoints.event.comm.TopicSink;
+import org.onap.policy.common.endpoints.http.server.HttpServletServer;
+import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
+import org.onap.policy.controlloop.ControlLoopEventStatus;
+import org.onap.policy.controlloop.VirtualControlLoopEvent;
+import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager;
+import org.onap.policy.controlloop.policy.ControlLoopPolicy;
+import org.onap.policy.drools.protocol.coders.EventProtocolCoder;
+import org.onap.policy.drools.protocol.coders.JsonProtocolFilter;
+import org.onap.policy.drools.system.PolicyController;
+import org.onap.policy.drools.system.PolicyEngine;
+import org.onap.policy.drools.utils.logging.LoggerUtil;
+import org.onap.policy.template.demo.Util.Pair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Verifies that event objects are cleaned up when rules are updated. This loads
+ * <b>two</b> copies of the rule set into a single policy to ensure that the two copies
+ * interact appropriately with each other's event objects.
+ */
+public class ControlLoopEventCleanupTest {
+    private static final Logger logger = LoggerFactory.getLogger(ControlLoopEventCleanupTest.class);
+
+    /**
+     * Number of objects per control loop, including the Params object.
+     */
+    private static int CL_OBJECTS = 7;
+
+    private static final String YAML = "src/test/resources/yaml/policy_ControlLoop_EventCleanup-test.yaml";
+
+    /**
+     * YAML to be used when the first rule set is updated.
+     */
+    private static final String YAML2 = "src/test/resources/yaml/policy_ControlLoop_EventCleanup-test2.yaml";
+
+    private static final String POLICY_VERSION = "v2.0";
+
+    private static final String POLICY_NAME = "CL_CleanupTest";
+
+    private static final String POLICY_SCOPE = "type=operational";
+
+    private static final String CONTROL_LOOP_NAME = "ControlLoop-Event-Cleanup-Test";
+
+    private static final String DROOLS_TEMPLATE = "../archetype-cl-amsterdam/src/main/resources/archetype-resources/"
+                    + "src/main/resources/__closedLoopControlName__.drl";
+
+    // values specific to the second copy of the rules
+
+    private static final String YAML_B = "src/test/resources/yaml/policy_ControlLoop_EventCleanup-test-B.yaml";
+    private static final String POLICY_NAME_B = "CL_CleanupTest_B";
+    private static final String CONTROL_LOOP_NAME_B = "ControlLoop-Event-Cleanup-Test-B";
+
+    private static final String GUARD_DISABLED = "guard.disabled";
+
+    private static String saveGuardFlag;
+
+    private static KieSession kieSession;
+    private static Util.RuleSpec[] specifications;
+
+    /**
+     * Setup the simulator.
+     */
+    @BeforeClass
+    public static void setUpSimulator() {
+        LoggerUtil.setLevel(LoggerUtil.ROOT_LOGGER, "INFO");
+
+        saveGuardFlag = PolicyEngine.manager.getEnvironmentProperty(GUARD_DISABLED);
+        PolicyEngine.manager.getEnvironment().setProperty(GUARD_DISABLED, "true");
+
+        Util.setAaiProps();
+
+        PolicyEngine.manager.configure(new Properties());
+        assertTrue(PolicyEngine.manager.start());
+        Properties noopSinkProperties = new Properties();
+        noopSinkProperties.put(PolicyEndPointProperties.PROPERTY_NOOP_SINK_TOPICS, "APPC-CL,POLICY-CL-MGT");
+        noopSinkProperties.put("noop.sink.topics.APPC-CL.events", "org.onap.policy.appc.Response");
+        noopSinkProperties.put("noop.sink.topics.APPC-CL.events.custom.gson",
+                        "org.onap.policy.appc.util.Serialization,gsonPretty");
+        noopSinkProperties.put("noop.sink.topics.POLICY-CL-MGT.events",
+                        "org.onap.policy.controlloop.VirtualControlLoopNotification");
+        noopSinkProperties.put("noop.sink.topics.POLICY-CL-MGT.events.custom.gson",
+                        "org.onap.policy.controlloop.util.Serialization,gsonPretty");
+        final List<TopicSink> noopTopics = TopicEndpoint.manager.addTopicSinks(noopSinkProperties);
+
+        EventProtocolCoder.manager.addEncoder("junit.groupId", "junit.artifactId", "POLICY-CL-MGT",
+                        "org.onap.policy.controlloop.VirtualControlLoopNotification", new JsonProtocolFilter(), null,
+                        null, 1111);
+        EventProtocolCoder.manager.addEncoder("junit.groupId", "junit.artifactId", "APPC-CL",
+                        "org.onap.policy.appc.Request", new JsonProtocolFilter(), null, null, 1111);
+
+        try {
+            Util.buildAaiSim();
+
+        } catch (Exception e) {
+            logger.error("Could not create simulator", e);
+            fail("Could not create simulator");
+        }
+
+        for (TopicSink sink : noopTopics) {
+            assertTrue(sink.start());
+        }
+
+        try {
+            specifications = new Util.RuleSpec[2];
+
+            specifications[0] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME, POLICY_SCOPE, POLICY_NAME,
+                            POLICY_VERSION, loadYaml(YAML));
+
+            specifications[1] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME_B, POLICY_SCOPE, POLICY_NAME_B,
+                            POLICY_VERSION, loadYaml(YAML_B));
+
+            kieSession = Util.buildContainer(POLICY_VERSION, specifications);
+
+        } catch (IOException e) {
+            logger.error("Could not create kieSession", e);
+            fail("Could not create kieSession");
+        }
+    }
+
+    /**
+     * Tear down.
+     */
+    @AfterClass
+    public static void tearDown() {
+        kieSession.dispose();
+
+        PolicyEngine.manager.stop();
+        HttpServletServer.factory.destroy();
+        PolicyController.factory.shutdown();
+        TopicEndpoint.manager.shutdown();
+
+        if (saveGuardFlag == null) {
+            PolicyEngine.manager.getEnvironment().remove(GUARD_DISABLED);
+
+        } else {
+            PolicyEngine.manager.getEnvironment().setProperty(GUARD_DISABLED, saveGuardFlag);
+        }
+    }
+
+    @Test
+    public void test() throws IOException {
+
+        /*
+         * Let rules create Params objects.
+         */
+        kieSession.fireAllRules();
+
+        injectEvent(CONTROL_LOOP_NAME);
+        injectEvent(CONTROL_LOOP_NAME_B);
+
+        kieSession.fireAllRules();
+        List<Object> facts = getSessionObjects();
+        
+        // should have events for both control loops
+        assertEquals(2 * CL_OBJECTS, facts.size());
+        assertTrue(hasEvent(facts, CONTROL_LOOP_NAME));
+        assertTrue(hasEvent(facts, CONTROL_LOOP_NAME_B));
+
+        logger.info("UPDATING VERSION TO v3.0");
+        updatePolicy(YAML2, "v3.0");
+
+        /*
+         * Let rules update Params objects. The Params for the first set of rules should
+         * now be deleted and replaced with a new one, while the Params for the second set
+         * should be unchanged.
+         */
+        kieSession.fireAllRules();
+        facts = getSessionObjects();
+
+        // should only have event for second control loop + 1 Params for first control loop
+        assertEquals(CL_OBJECTS + 1, facts.size());
+        assertTrue(hasEvent(facts, CONTROL_LOOP_NAME_B));
+
+        // add event for first control loop again
+        injectEvent(CONTROL_LOOP_NAME);
+        kieSession.fireAllRules();
+
+        logger.info("UPDATING VERSION TO v4.0");
+        updatePolicy(YAML, "v4.0");
+
+        /*
+         * Let rules update Params objects. The Params for the first set of rules should
+         * now be deleted and replaced with a new one, while the Params for the second set
+         * should be unchanged.
+         */
+        kieSession.fireAllRules();
+        facts = getSessionObjects();
+
+        // should only have event for second control loop + 1 Params for first control loop
+        assertEquals(CL_OBJECTS + 1, facts.size());
+        assertTrue(hasEvent(facts, CONTROL_LOOP_NAME_B));
+
+        // add event for first control loop again
+        injectEvent(CONTROL_LOOP_NAME);
+        kieSession.fireAllRules();
+
+        logger.info("UPDATING VERSION TO v4.0 (i.e., unchanged)");
+        updatePolicy(YAML, "v4.0");
+
+        /*
+         * Let rules update Params objects. As the version (and YAML) are unchanged for
+         * either rule set, both Params objects should be unchanged.
+         */
+        kieSession.fireAllRules();
+        facts = getSessionObjects();
+
+        // should have events for both control loops
+        assertEquals(2 * CL_OBJECTS, facts.size());
+        assertTrue(hasEvent(facts, CONTROL_LOOP_NAME));
+        assertTrue(hasEvent(facts, CONTROL_LOOP_NAME_B));
+
+        /*
+         * Now we'll delete the first rule set. That won't actually have any immediate
+         * effect, so then we'll update the second rule set, which should trigger a
+         * clean-up of both.
+         */
+        Util.RuleSpec[] specs = new Util.RuleSpec[1];
+        specs[0] = specifications[1];
+
+        logger.info("UPDATING VERSION TO v5.0 - DELETED RULE SET");
+        Util.updateContainer("v5.0", specs);
+
+        specs[0] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME_B, POLICY_SCOPE, POLICY_NAME_B, POLICY_VERSION,
+                        loadYaml(YAML));
+
+        logger.info("UPDATING VERSION TO v6.0 - UPDATED SECOND RULE SET");
+        Util.updateContainer("v6.0", specs);
+
+        kieSession.fireAllRules();
+        facts = getSessionObjects();
+
+        // only 1 Params should remain, for second rule set, but events should be gone
+        assertEquals(1, facts.size());
+        assertTrue(facts.stream().anyMatch(obj -> obj.toString().startsWith("Params( ")));
+    }
+
+    /**
+     * Updates the policy, changing the YAML associated with the first rule set.
+     *
+     * @param yamlFile name of the YAML file
+     * @param policyVersion policy version
+     * @throws IOException if an error occurs
+     */
+    private static void updatePolicy(String yamlFile, String policyVersion) throws IOException {
+
+        specifications[0] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME, POLICY_SCOPE, POLICY_NAME,
+                        policyVersion, loadYaml(yamlFile));
+
+        /*
+         * Update the policy within the container.
+         */
+        Util.updateContainer(policyVersion, specifications);
+    }
+
+    /**
+     * Loads a YAML file and URL-encodes it.
+     *
+     * @param yamlFile name of the YAML file
+     * @return the contents of the specified file, URL-encoded
+     * @throws UnsupportedEncodingException if an error occurs
+     */
+    private static String loadYaml(String yamlFile) throws UnsupportedEncodingException {
+        Pair<ControlLoopPolicy, String> pair = Util.loadYaml(yamlFile);
+        assertNotNull(pair);
+        assertNotNull(pair.first);
+        assertNotNull(pair.first.getControlLoop());
+        assertNotNull(pair.first.getControlLoop().getControlLoopName());
+        assertTrue(pair.first.getControlLoop().getControlLoopName().length() > 0);
+
+        return URLEncoder.encode(pair.second, "UTF-8");
+    }
+
+    /**
+     * Gets the session objects.
+     *
+     * @return the session objects
+     */
+    private static List<Object> getSessionObjects() {
+        // sort the objects so we know the order
+        LinkedList<Object> lst = new LinkedList<>(kieSession.getObjects());
+        lst.sort((left, right) -> left.toString().compareTo(right.toString()));
+
+        lst.forEach(obj -> logger.info("obj={}", obj));
+
+        return lst;
+    }
+
+    /**
+     * Injects an ONSET event into the rule engine.
+     *
+     * @param controlLoopName the control loop name
+     */
+    private void injectEvent(String controlLoopName) {
+        VirtualControlLoopEvent event = new VirtualControlLoopEvent();
+
+        event.setClosedLoopControlName(controlLoopName);
+
+        UUID reqid = UUID.randomUUID();
+        event.setRequestId(reqid);
+
+        event.setTarget("generic-vnf.vnf-id");
+        event.setClosedLoopAlarmStart(Instant.now());
+        event.setAai(new HashMap<>());
+        event.getAai().put("generic-vnf.vnf-id", "vnf-" + reqid.toString());
+        event.getAai().put(ControlLoopEventManager.GENERIC_VNF_IS_CLOSED_LOOP_DISABLED, "false");
+        event.setClosedLoopEventStatus(ControlLoopEventStatus.ONSET);
+
+        kieSession.insert(event);
+    }
+
+    /**
+     * Determines if the facts contain an event for the given control loop.
+     * 
+     * @param facts session facts to be checked
+     * @param controlLoopName name of the control loop of interest
+     * @return {@code true} if the facts contain an event for the given control loop,
+     *         {@code false} otherwise
+     */
+    private boolean hasEvent(List<Object> facts, String controlLoopName) {
+        return (facts.stream().anyMatch(obj -> obj instanceof VirtualControlLoopEvent
+                        && controlLoopName.equals(((VirtualControlLoopEvent) obj).getClosedLoopControlName())));
+    }
+}
 
         iter = facts.iterator();
         assertTrue(iter.next() == fact3);
         assertTrue(iter.next() == fact1b);
+        
+        /*
+         * Now we'll delete the first rule set.  That won't actually have any immediate
+         * effect, so then we'll update the second rule set, which should trigger a
+         * clean-up of both.
+         */
+        Util.RuleSpec[] specs = new Util.RuleSpec[1];
+        specs[0] = specifications[1];
+
+        logger.info("UPDATING VERSION TO v5.0 - DELETED RULE SET");
+        Util.updateContainer("v5.0", specs);
+
+        specs[0] = new Util.RuleSpec(DROOLS_TEMPLATE, CONTROL_LOOP_NAME_B, POLICY_SCOPE, POLICY_NAME_B,
+                        POLICY_VERSION, loadYaml(YAML));
+        
+        logger.info("UPDATING VERSION TO v6.0 - UPDATED SECOND RULE SET");
+        Util.updateContainer("v6.0", specs);
+        
+        kieSession.fireAllRules();
+        facts = getSessionObjects();
+        assertEquals(specs.length, facts.size());
+        iter = facts.iterator();
+        assertTrue(iter.next().toString().contains(CONTROL_LOOP_NAME_B));
     }
 
     /**
 
--- /dev/null
+# Copyright 2018 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.
+controlLoop:
+  version: 2.0.0
+  controlLoopName: ControlLoop-Event-Cleanup-Test-B
+  services:
+    - serviceInvariantUUID: 5cfe6f4a-41bc-4247-8674-ebd4b98e35cc
+      serviceUUID: 0f40bba5-986e-4b3c-803f-ddd1b7b25f24
+      serviceName: 57e66ea7-0ed6-45c7-970f
+  trigger_policy: unique-policy-id-1-modifyConfig
+  timeout: 60
+  abatement: true
+ 
+policies:
+  - id: unique-policy-id-1-modifyConfig
+    name: modify packet gen config
+    description:
+    actor: APPC
+    recipe: ModifyConfig
+    target:
+      resourceID: Eace933104d443b496b8.nodes.heat.vpg
+      type: VNF
+    retry: 0
+    timeout: 30
+    success: final_success
+    failure: final_failure
+    failure_timeout: final_failure_timeout
+    failure_retries: final_failure_retries
+    failure_exception: final_failure_exception
+    failure_guard: final_failure_guard
\ No newline at end of file
 
--- /dev/null
+# Copyright 2018 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.
+controlLoop:
+  version: 2.0.0
+  controlLoopName: ControlLoop-Event-Cleanup-Test
+  services:
+    - serviceInvariantUUID: 5cfe6f4a-41bc-4247-8674-ebd4b98e35cc
+      serviceUUID: 0f40bba5-986e-4b3c-803f-ddd1b7b25f24
+      serviceName: 57e66ea7-0ed6-45c7-970f
+  trigger_policy: unique-policy-id-1-modifyConfig
+  timeout: 60
+  abatement: true
+ 
+policies:
+  - id: unique-policy-id-1-modifyConfig
+    name: modify packet gen config
+    description:
+    actor: APPC
+    recipe: ModifyConfig
+    target:
+      resourceID: Eace933104d443b496b8.nodes.heat.vpg
+      type: VNF
+    retry: 0
+    timeout: 30
+    success: final_success
+    failure: final_failure
+    failure_timeout: final_failure_timeout
+    failure_retries: final_failure_retries
+    failure_exception: final_failure_exception
+    failure_guard: final_failure_guard
\ No newline at end of file
 
--- /dev/null
+# Copyright 2018 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.
+
+#
+# This YAML must be slightly different from test.yaml.
+#
+controlLoop:
+  version: 3.0.0
+  controlLoopName: ControlLoop-Event-Cleanup-Test
+  services:
+    - serviceInvariantUUID: 5cfe6f4a-41bc-4247-8674-ebd4b98e35cc
+      serviceUUID: 0f40bba5-986e-4b3c-803f-ddd1b7b25f24
+      serviceName: 57e66ea7-0ed6-45c7-970f
+  trigger_policy: unique-policy-id-1-modifyConfig
+  timeout: 60
+  abatement: true
+ 
+policies:
+  - id: unique-policy-id-1-modifyConfig
+    name: modify packet gen config
+    description:
+    actor: APPC
+    recipe: ModifyConfig
+    target:
+      resourceID: Eace933104d443b496b8.nodes.heat.vpg
+      type: VNF
+    retry: 0
+    timeout: 30
+    success: final_success
+    failure: final_failure
+    failure_timeout: final_failure_timeout
+    failure_retries: final_failure_retries
+    failure_exception: final_failure_exception
+    failure_guard: final_failure_guard
\ No newline at end of file