Merge "native policy use engine for top-down processing"
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFeature.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFeature.java
index d27172b..d920597 100644
--- a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFeature.java
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFeature.java
@@ -38,7 +38,7 @@
 
     @Override
     public int getSequenceNumber() {
-        return 10;
+        return 1;
     }
 
     @Override
@@ -52,6 +52,11 @@
     }
 
     @Override
+    public boolean afterPatch(PolicyController controller, boolean success) {
+        return fsmPatch(controller);
+    }
+
+    @Override
     public boolean beforeStop(PolicyEngine engine) {
         return fsmStop();
     }
@@ -63,8 +68,7 @@
 
     @Override
     public boolean beforeShutdown(PolicyEngine engine) {
-        fsm.shutdown();
-        return false;
+        return fsmShutdown(engine);
     }
 
     @Override
@@ -101,4 +105,14 @@
         fsm.stop(controller);
         return false;
     }
+
+    private boolean fsmPatch(PolicyController controller) {
+        fsm.patch(controller);
+        return false;
+    }
+
+    private boolean fsmShutdown(PolicyEngine engine) {
+        fsm.shutdown();
+        return false;
+    }
 }
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFsm.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFsm.java
index 6953d83..3ecb4b3 100644
--- a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFsm.java
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFsm.java
@@ -179,6 +179,11 @@
      */
     public synchronized void start(@NonNull PolicyController controller) {
         logger.info("lifecycle event: start controller: {}", controller.getName());
+        if (!controller.getDrools().isBrained()) {
+            logger.warn("ignoring lifecycle event: start controller: {}", controller);
+            return;
+        }
+
         for (ToscaPolicyTypeIdentifier id : controller.getPolicyTypes()) {
             if (isToscaPolicyType(id.getName())) {
                 PolicyTypeDroolsController ptDroolsController = (PolicyTypeDroolsController) policyTypesMap.get(id);
@@ -191,6 +196,18 @@
         }
     }
 
+    /**
+     * Patch a controller event.
+     */
+    public synchronized void patch(@NonNull PolicyController controller) {
+        logger.info("lifecycle event: patch controller: {}", controller.getName());
+        if (controller.getDrools().isBrained()) {
+            this.start(controller);
+        } else {
+            this.stop(controller);
+        }
+    }
+
     @Override
     public synchronized boolean stop() {
         logger.info("lifecycle event: stop engine");
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeArtifactController.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeArtifactController.java
index d91eccc..113bb88 100644
--- a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeArtifactController.java
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeArtifactController.java
@@ -26,9 +26,11 @@
 import org.onap.policy.common.utils.coder.CoderException;
 import org.onap.policy.drools.controller.DroolsControllerConstants;
 import org.onap.policy.drools.domain.models.artifact.NativeArtifactPolicy;
+import org.onap.policy.drools.protocol.configuration.ControllerConfiguration;
 import org.onap.policy.drools.protocol.configuration.DroolsConfiguration;
 import org.onap.policy.drools.system.PolicyController;
 import org.onap.policy.drools.system.PolicyControllerConstants;
+import org.onap.policy.drools.system.PolicyEngineConstants;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
 import org.slf4j.Logger;
@@ -55,42 +57,47 @@
         PolicyController controller;
         try {
             nativePolicy = fsm.getDomainMaker().convertTo(policy, NativeArtifactPolicy.class);
+            DroolsConfiguration droolsConfig =
+                    new DroolsConfiguration(
+                            nativePolicy.getProperties().getRulesArtifact().getArtifactId(),
+                            nativePolicy.getProperties().getRulesArtifact().getGroupId(),
+                            nativePolicy.getProperties().getRulesArtifact().getVersion());
+
             controller =
                     PolicyControllerConstants.getFactory().get(nativePolicy.getProperties().getController().getName());
+            if (controller.getDrools().isBrained()) {
+                logger.warn("upgrade of a live controller is strongly discouraged (undeploy first): {} -> {}",
+                        controller, droolsConfig);
+            }
+
+            return update(nativePolicy, droolsConfig);
         } catch (CoderException | RuntimeException e) {
             logger.warn("Invalid Policy: {}", policy);
             return false;
         }
-
-        DroolsConfiguration newConfig =
-                new DroolsConfiguration(
-                        nativePolicy.getProperties().getRulesArtifact().getArtifactId(),
-                        nativePolicy.getProperties().getRulesArtifact().getGroupId(),
-                        nativePolicy.getProperties().getRulesArtifact().getVersion());
-
-        PolicyControllerConstants.getFactory().patch(controller, newConfig);
-        return true;
     }
 
     @Override
     public boolean undeploy(ToscaPolicy policy) {
-        PolicyController controller;
         try {
             NativeArtifactPolicy nativePolicy = fsm.getDomainMaker().convertTo(policy, NativeArtifactPolicy.class);
-            controller =
-                    PolicyControllerConstants.getFactory().get(nativePolicy.getProperties().getController().getName());
+            DroolsConfiguration noConfig =
+                    new DroolsConfiguration(
+                            DroolsControllerConstants.NO_ARTIFACT_ID,
+                            DroolsControllerConstants.NO_GROUP_ID,
+                            DroolsControllerConstants.NO_VERSION);
+
+            return update(nativePolicy, noConfig);
         } catch (RuntimeException | CoderException e) {
             logger.warn("Invalid Policy: {}", policy);
             return false;
         }
+    }
 
-        DroolsConfiguration noConfig =
-                new DroolsConfiguration(
-                        DroolsControllerConstants.NO_ARTIFACT_ID,
-                        DroolsControllerConstants.NO_GROUP_ID,
-                        DroolsControllerConstants.NO_VERSION);
-
-        PolicyControllerConstants.getFactory().patch(controller, noConfig);
-        return true;
+    private boolean update(NativeArtifactPolicy nativePolicy, DroolsConfiguration droolsConfig) {
+        ControllerConfiguration controllerConfig =
+                new ControllerConfiguration(nativePolicy.getProperties().getController().getName(),
+                        ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UPDATE, droolsConfig);
+        return PolicyEngineConstants.getManager().updatePolicyController(controllerConfig) != null;
     }
 }
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeDroolsController.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeDroolsController.java
index 2a417dc..8255c02 100644
--- a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeDroolsController.java
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/PolicyTypeNativeDroolsController.java
@@ -41,6 +41,7 @@
 import org.onap.policy.drools.properties.DroolsPropertyConstants;
 import org.onap.policy.drools.system.PolicyController;
 import org.onap.policy.drools.system.PolicyControllerConstants;
+import org.onap.policy.drools.system.PolicyEngineConstants;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
 import org.slf4j.Logger;
@@ -83,9 +84,8 @@
 
         PolicyController controller;
         try {
-            controller =
-                PolicyControllerConstants.getFactory().build(
-                    controllerConfig.getControllerName(), controllerProps);
+            controller = PolicyEngineConstants.getManager()
+                                 .createPolicyController(controllerConfig.getControllerName(), controllerProps);
         } catch (RuntimeException e) {
             logger.warn("failed deploy (cannot create controller) for policy: {}", policy);
             return false;
@@ -98,8 +98,8 @@
     public boolean undeploy(ToscaPolicy policy) {
         try {
             ControllerPolicy nativePolicy = fsm.getDomainMaker().convertTo(policy, ControllerPolicy.class);
-            PolicyControllerConstants.getFactory()
-                    .destroy(nativePolicy.getProperties().getControllerName());
+            PolicyEngineConstants.getManager()
+                    .removePolicyController(nativePolicy.getProperties().getControllerName());
             return true;
         } catch (RuntimeException | CoderException e) {
             logger.warn("failed undeploy of policy: {}", policy);
diff --git a/policy-management/src/main/java/org/onap/policy/drools/features/PolicyControllerFeatureApi.java b/policy-management/src/main/java/org/onap/policy/drools/features/PolicyControllerFeatureApi.java
index 9c6ac22..4928778 100644
--- a/policy-management/src/main/java/org/onap/policy/drools/features/PolicyControllerFeatureApi.java
+++ b/policy-management/src/main/java/org/onap/policy/drools/features/PolicyControllerFeatureApi.java
@@ -1,8 +1,8 @@
 /*
  * ============LICENSE_START=======================================================
- * policy-management
+ * ONAP
  * ================================================================================
- * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2017-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.
@@ -23,6 +23,7 @@
 import java.util.Properties;
 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
 import org.onap.policy.common.utils.services.OrderedService;
+import org.onap.policy.drools.protocol.configuration.DroolsConfiguration;
 import org.onap.policy.drools.system.PolicyController;
 
 public interface PolicyControllerFeatureApi extends OrderedService {
@@ -83,7 +84,7 @@
      *
      * @return true if this feature intercepts and takes ownership
      *     of the operation preventing the invocation of
-     *     lower priority features.   False, otherwise..
+     *     lower priority features.   False, otherwise.
      */
     default boolean beforeStop(PolicyController controller) {
         return false;
@@ -101,6 +102,28 @@
     }
 
     /**
+     * intercept before the Policy Controller is patched.
+     *
+     * @return true if this feature intercepts and takes ownership
+     *     of the operation preventing the invocation of
+     *     lower priority features.   False, otherwise.
+     */
+    default boolean beforePatch(PolicyController controller, DroolsConfiguration configuration) {
+        return false;
+    }
+
+    /**
+     * intercept after the Policy Controller is patched.
+     *
+     * @return true if this feature intercepts and takes ownership
+     *     of the operation preventing the invocation of
+     *     lower priority features.   False, otherwise.
+     */
+    default boolean afterPatch(PolicyController controller, boolean success) {
+        return false;
+    }
+
+    /**
      * intercept before the Policy Controller is locked.
      *
      * @return true if this feature intercepts and takes ownership
diff --git a/policy-management/src/main/java/org/onap/policy/drools/system/internal/AggregatedPolicyController.java b/policy-management/src/main/java/org/onap/policy/drools/system/internal/AggregatedPolicyController.java
index 5685ff6..b80f4c8 100644
--- a/policy-management/src/main/java/org/onap/policy/drools/system/internal/AggregatedPolicyController.java
+++ b/policy-management/src/main/java/org/onap/policy/drools/system/internal/AggregatedPolicyController.java
@@ -215,7 +215,6 @@
      */
     @Override
     public boolean updateDrools(DroolsConfiguration newDroolsConfiguration) {
-
         DroolsConfiguration oldDroolsConfiguration = new DroolsConfiguration(this.droolsController.getArtifactId(),
                 this.droolsController.getGroupId(), this.droolsController.getVersion());
 
@@ -227,46 +226,53 @@
             return true;
         }
 
+        if (FeatureApiUtils.apply(getProviders(),
+            feature -> feature.beforePatch(this, newDroolsConfiguration),
+            (feature, ex) -> logger.error("{}: feature {} before-patch failure because of {}", this,
+                        feature.getClass().getName(), ex.getMessage(), ex))) {
+            return true;
+        }
+
         if (droolsController.isBrained()
             && (newDroolsConfiguration.getArtifactId() == null
                 || DroolsControllerConstants.NO_ARTIFACT_ID.equals(newDroolsConfiguration.getArtifactId()))) {
+            // detach maven artifact
             DroolsControllerConstants.getFactory().destroy(this.droolsController);
         }
 
+        boolean success = true;
         try {
-            /* Drools Controller created, update initialization properties for restarts */
-
             this.properties.setProperty(DroolsPropertyConstants.RULES_GROUPID, newDroolsConfiguration.getGroupId());
             this.properties.setProperty(DroolsPropertyConstants.RULES_ARTIFACTID,
                             newDroolsConfiguration.getArtifactId());
             this.properties.setProperty(DroolsPropertyConstants.RULES_VERSION, newDroolsConfiguration.getVersion());
-
             getPersistenceManager().storeController(name, this.properties);
 
             this.initDrools(this.properties);
 
-            /* set drools controller to current locked status */
-
-            if (this.isLocked()) {
-                this.droolsController.lock();
+            if (isLocked()) {
+                droolsController.lock();
             } else {
-                this.droolsController.unlock();
+                droolsController.unlock();
             }
 
-            /* set drools controller to current alive status */
-
-            if (this.isAlive()) {
-                this.droolsController.start();
+            if (isAlive()) {
+                droolsController.start();
             } else {
-                this.droolsController.stop();
+                droolsController.stop();
             }
-
-        } catch (IllegalArgumentException e) {
+        } catch (RuntimeException e) {
             logger.error("{}: cannot update-drools because of {}", this, e.getMessage(), e);
-            return false;
+            success = false;
         }
 
-        return true;
+        boolean finalSuccess = success;
+        FeatureApiUtils.apply(getProviders(),
+            feature -> feature.afterPatch(this, finalSuccess),
+            (feature, ex) -> logger.error("{}: feature {} after-patch failure because of {}", this,
+                        feature.getClass().getName(), ex.getMessage(), ex));
+
+        return finalSuccess;
     }
 
     /**