summaryrefslogtreecommitdiffstats
BranchCommit messageAuthorAge
masterUpdate snapshot and/or references of policy/clamp to latest snapshotswaynedunican11 days
osloUpdate gitreview for oslo in clamprameshiyer273 months
newdelhiupdate references for newdelhi branchadheli.tavares9 months
montrealSet Montreal default branchliamfallon15 months
java-17Fix updating the state in Participant IntermediaryFrancescoFioraEst17 months
londonFix State change result of AC instances after timeoutrameshiyer2719 months
istanbulFix configuration to use configured propertiesliamfallon2 years
kohnUpdate snapshot and/or references of policy/clamp to latest snapshotsliamfallon2 years
jakartaAllow HTTPS to be configured in ACMliamfallon2 years
honoluluPut a note on the CLAMP/PF merge on Honolululiamfallon3 years
tosca-pocMove tosca-controlloop src directries to new modulesliamfallon4 years
 
TagDownloadAuthorAge
8.1.0commit ae3648a59d...jenkins-releng11 days
8.0.1commit 95fe61375f...jenkins-releng3 months
8.0.0commit 1b64b694bb...jenkins-releng7 months
7.1.3commit 2358a28812...jenkins-releng9 months
7.1.2commit 61cb911206...jenkins-releng10 months
7.1.1commit 6b3a118290...jenkins-releng12 months
7.1.0commit 61bd417422...jenkins-releng14 months
7.0.3commit e21f8d46e2...jenkins-releng15 months
7.0.2commit 8e2223231f...jenkins-releng16 months
7.0.1commit 484fc2db2d...jenkins-releng16 months
6.4.3commit 0d3517002d...jenkins-releng21 months
6.4.2commit 235cf1b7d3...jenkins-releng22 months
6.4.1commit 8753f2332b...jenkins-releng23 months
6.4.0commit 892a732def...jenkins-releng2 years
6.3.2commit a80b48bf0b...jenkins-releng2 years
6.3.1commit cad65dd863...jenkins-releng2 years
10.0.1-PF-I1tag dd1577ceed...Liam Fallon2 years
10.0.0-ONAPtag 32affd8910...Liam Fallon2 years
8.0.2-PF-I1tag 60d9e4da81...Liam Fallon2 years
6.3.0commit 42e415de86...jenkins-releng2 years
6.2.4commit 40dc6e0944...jenkins-releng2 years
6.2.3commit 60635fabbf...jenkins-releng3 years
6.2.2commit 16b95134dc...jenkins-releng3 years
6.2.1commit f68b6b5635...jenkins-releng3 years
9.0.1-ONAPtag e59dfd0a6e...Liam Fallon3 years
6.1.4commit 1ddbecc7f2...jenkins-releng3 years
9.0.0-ONAPcommit 906fa908e6...Liam Fallon3 years
6.2.0commit 14936e0e40...jenkins-releng3 years
6.1.3commit 7fe8dbd42b...jenkins-releng3 years
6.1.2commit 71f99db0f3...jenkins-releng3 years
8.0.0-ONAPcommit 9384cd4cf0...Jim Hahn4 years
6.1.1commit b005432494...jenkins-releng4 years
6.1.0commit 430cb714a9...jenkins-releng4 years
6.0.2commit c29c8bef7f...jenkins-releng4 years
6.0.1commit 83fb82c865...jenkins-releng4 years
6.0.0commit 80118d5373...jenkins-releng4 years
/* Literal.String.Backtick */ .highlight .sc { color: #e6db74 } /* Literal.String.Char */ .highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */ .highlight .sd { color: #e6db74 } /* Literal.String.Doc */ .highlight .s2 { color: #e6db74 } /* Literal.String.Double */ .highlight .se { color: #ae81ff } /* Literal.String.Escape */ .highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */ .highlight .si { color: #e6db74 } /* Literal.String.Interpol */ .highlight .sx { color: #e6db74 } /* Literal.String.Other */ .highlight .sr { color: #e6db74 } /* Literal.String.Regex */ .highlight .s1 { color: #e6db74 } /* Literal.String.Single */ .highlight .ss { color: #e6db74 } /* Literal.String.Symbol */ .highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #a6e22e } /* Name.Function.Magic */ .highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */ .highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */ .highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ .highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */ .highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ } @media (prefers-color-scheme: light) { .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ }
/*
 * ============LICENSE_START=======================================================
 * policy-management
 * ================================================================================
 * Copyright (C) 2017-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.drools.system.internal;

import com.fasterxml.jackson.annotation.JsonIgnore;

import java.util.HashMap;
import java.util.List;
import java.util.Properties;

import org.onap.policy.common.endpoints.event.comm.Topic;
import org.onap.policy.common.endpoints.event.comm.TopicEndpoint;
import org.onap.policy.common.endpoints.event.comm.TopicListener;
import org.onap.policy.common.endpoints.event.comm.TopicSink;
import org.onap.policy.common.endpoints.event.comm.TopicSource;
import org.onap.policy.drools.controller.DroolsController;
import org.onap.policy.drools.controller.DroolsControllerFactory;
import org.onap.policy.drools.features.PolicyControllerFeatureAPI;
import org.onap.policy.drools.persistence.SystemPersistence;
import org.onap.policy.drools.properties.DroolsProperties;
import org.onap.policy.drools.protocol.configuration.DroolsConfiguration;
import org.onap.policy.drools.system.PolicyController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This implementation of the Policy Controller merely aggregates and tracks for management purposes
 * all underlying resources that this controller depends upon.
 */
public class AggregatedPolicyController implements PolicyController, TopicListener {

    /**
     * Logger.
     */
    private static final Logger logger = LoggerFactory.getLogger(AggregatedPolicyController.class);

    /**
     * identifier for this policy controller.
     */
    private final String name;

    /**
     * Abstracted Event Sources List regardless communication technology.
     */
    private final List<? extends TopicSource> sources;

    /**
     * Abstracted Event Sinks List regardless communication technology.
     */
    private final List<? extends TopicSink> sinks;

    /**
     * Mapping topics to sinks.
     */
    @JsonIgnore
    private final HashMap<String, TopicSink> topic2Sinks = new HashMap<>();

    /**
     * Is this Policy Controller running (alive) ? reflects invocation of start()/stop() only.
     */
    private volatile boolean alive;

    /**
     * Is this Policy Controller locked ? reflects if i/o controller related operations and start
     * are permitted, more specifically: start(), deliver() and onTopicEvent(). It does not affect
     * the ability to stop the underlying drools infrastructure
     */
    private volatile boolean locked;

    /**
     * Policy Drools Controller.
     */
    private volatile DroolsController droolsController;

    /**
     * Properties used to initialize controller.
     */
    private final Properties properties;

    /**
     * Constructor version mainly used for bootstrapping at initialization time a policy engine
     * controller.
     * 
     * @param name controller name
     * @param properties
     * 
     * @throws IllegalArgumentException when invalid arguments are provided
     */
    public AggregatedPolicyController(String name, Properties properties) {

        this.name = name;

        /*
         * 1. Register read topics with network infrastructure (ueb, dmaap, rest) 2. Register write
         * topics with network infrastructure (ueb, dmaap, rest) 3. Register with drools
         * infrastructure
         */

        // Create/Reuse Readers/Writers for all event sources endpoints

        this.sources = getEndpointManager().addTopicSources(properties);
        this.sinks = getEndpointManager().addTopicSinks(properties);

        initDrools(properties);
        initSinks();

        /* persist new properties */
        getPersistenceManager().storeController(name, properties);
        this.properties = properties;
    }

    /**
     * initialize drools layer.
     * 
     * @throws IllegalArgumentException if invalid parameters are passed in
     */
    private void initDrools(Properties properties) {
        try {
            // Register with drools infrastructure
            this.droolsController = getDroolsFactory().build(properties, sources, sinks);
        } catch (Exception | LinkageError e) {
            logger.error("{}: cannot init-drools because of {}", this, e.getMessage(), e);
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * initialize sinks.
     * 
     * @throws IllegalArgumentException if invalid parameters are passed in
     */
    private void initSinks() {
        this.topic2Sinks.clear();
        for (TopicSink sink : sinks) {
            this.topic2Sinks.put(sink.getTopic(), sink);
        }
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public boolean updateDrools(DroolsConfiguration newDroolsConfiguration) {

        DroolsConfiguration oldDroolsConfiguration = new DroolsConfiguration(this.droolsController.getArtifactId(),
                this.droolsController.getGroupId(), this.droolsController.getVersion());

        if (oldDroolsConfiguration.getGroupId().equalsIgnoreCase(newDroolsConfiguration.getGroupId())
                && oldDroolsConfiguration.getArtifactId().equalsIgnoreCase(newDroolsConfiguration.getArtifactId())
                && oldDroolsConfiguration.getVersion().equalsIgnoreCase(newDroolsConfiguration.getVersion())) {
            logger.warn("{}: cannot update-drools: identical configuration {} vs {}", this, oldDroolsConfiguration,
                    newDroolsConfiguration);
            return true;
        }

        try {
            /* Drools Controller created, update initialization properties for restarts */

            this.properties.setProperty(DroolsProperties.RULES_GROUPID, newDroolsConfiguration.getGroupId());
            this.properties.setProperty(DroolsProperties.RULES_ARTIFACTID, newDroolsConfiguration.getArtifactId());
            this.properties.setProperty(DroolsProperties.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();
            } else {
                this.droolsController.unlock();
            }

            /* set drools controller to current alive status */

            if (this.isAlive()) {
                this.droolsController.start();
            } else {
                this.droolsController.stop();
            }

        } catch (IllegalArgumentException e) {
            logger.error("{}: cannot update-drools because of {}", this, e.getMessage(), e);
            return false;
        }

        return true;
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public String getName() {
        return this.name;
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public boolean start() {
        logger.info("{}: start", this);

        for (PolicyControllerFeatureAPI feature : getProviders()) {
            try {
                if (feature.beforeStart(this)) {
                    return true;
                }
            } catch (Exception e) {
                logger.error("{}: feature {} before-start failure because of {}", this, feature.getClass().getName(),
                        e.getMessage(), e);
            }
        }

        if (this.isLocked()) {
            throw new IllegalStateException("Policy Controller " + name + " is locked");
        }

        synchronized (this) {
            if (this.alive) {
                return true;
            }

            this.alive = true;
        }

        final boolean success = this.droolsController.start();

        // register for events

        for (TopicSource source : sources) {
            source.register(this);
        }

        for (TopicSink sink : sinks) {
            try {
                sink.start();
            } catch (Exception e) {
                logger.error("{}: cannot start {} because of {}", this, sink, e.getMessage(), e);
            }
        }

        for (PolicyControllerFeatureAPI feature : getProviders()) {
            try {
                if (feature.afterStart(this)) {
                    return true;
                }
            } catch (Exception e) {
                logger.error("{}: feature {} after-start failure because of {}", this, feature.getClass().getName(),
                        e.getMessage(), e);
            }
        }

        return success;
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public boolean stop() {
        logger.info("{}: stop", this);

        for (PolicyControllerFeatureAPI feature : getProviders()) {
            try {
                if (feature.beforeStop(this)) {
                    return true;
                }
            } catch (Exception e) {
                logger.error("{}: feature {} before-stop failure because of {}", this, feature.getClass().getName(),
                        e.getMessage(), e);
            }
        }

        /* stop regardless locked state */

        synchronized (this) {
            if (!this.alive) {
                return true;
            }

            this.alive = false;
        }

        // 1. Stop registration

        for (TopicSource source : sources) {
            source.unregister(this);
        }

        boolean success = this.droolsController.stop();

        for (PolicyControllerFeatureAPI feature : getProviders()) {
            try {
                if (feature.afterStop(this)) {
                    return true;
                }
            } catch (Exception e) {
                logger.error("{}: feature {} after-stop failure because of {}", this, feature.getClass().getName(),
                        e.getMessage(), e);
            }
        }

        return success;
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public void shutdown() {
        logger.info("{}: shutdown", this);

        for (PolicyControllerFeatureAPI feature : getProviders()) {
            try {
                if (feature.beforeShutdown(this)) {
                    return;
                }
            } catch (Exception e) {
                logger.error("{}: feature {} before-shutdown failure because of {}", this, feature.getClass().getName(),
                        e.getMessage(), e);
            }
        }

        this.stop();

        getDroolsFactory().shutdown(this.droolsController);

        for (PolicyControllerFeatureAPI feature : getProviders()) {
            try {
                if (feature.afterShutdown(this)) {
                    return;
                }
            } catch (Exception e) {
                logger.error("{}: feature {} after-shutdown failure because of {}", this, feature.getClass().getName(),
                        e.getMessage(), e);
            }
        }
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public void halt() {
        logger.info("{}: halt", this);

        for (PolicyControllerFeatureAPI feature : getProviders()) {
            try {
                if (feature.beforeHalt(this)) {
                    return;
                }
            } catch (Exception e) {
                logger.error("{}: feature {} before-halt failure because of {}", this, feature.getClass().getName(),
                        e.getMessage(), e);
            }
        }

        this.stop();
        getDroolsFactory().destroy(this.droolsController);
        getPersistenceManager().deleteController(this.name);

        for (PolicyControllerFeatureAPI feature : getProviders()) {
            try {
                if (feature.afterHalt(this)) {
                    return;
                }
            } catch (Exception e) {
                logger.error("{}: feature {} after-halt failure because of {}", this, feature.getClass().getName(),
                        e.getMessage(), e);
            }
        }
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public void onTopicEvent(Topic.CommInfrastructure commType, String topic, String event) {

        logger.debug("{}: event offered from {}:{}: {}", this, commType, topic, event);

        for (PolicyControllerFeatureAPI feature : getProviders()) {
            try {
                if (feature.beforeOffer(this, commType, topic, event)) {
                    return;
                }
            } catch (Exception e) {
                logger.error("{}: feature {} before-offer failure because of {}", this, feature.getClass().getName(),
                        e.getMessage(), e);
            }
        }

        if (this.locked) {
            return;
        }

        if (!this.alive) {
            return;
        }

        boolean success = this.droolsController.offer(topic, event);

        for (PolicyControllerFeatureAPI feature : getProviders()) {
            try {
                if (feature.afterOffer(this, commType, topic, event, success)) {
                    return;
                }
            } catch (Exception e) {
                logger.error("{}: feature {} after-offer failure because of {}", this, feature.getClass().getName(),
                        e.getMessage(), e);
            }
        }
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public boolean deliver(Topic.CommInfrastructure commType, String topic, Object event) {

        logger.debug("{}: deliver event to {}:{}: {}", this, commType, topic, event);

        for (PolicyControllerFeatureAPI feature : getProviders()) {
            try {
                if (feature.beforeDeliver(this, commType, topic, event)) {
                    return true;
                }
            } catch (Exception e) {
                logger.error("{}: feature {} before-deliver failure because of {}", this, feature.getClass().getName(),
                        e.getMessage(), e);
            }
        }

        if (topic == null || topic.isEmpty()) {
            throw new IllegalArgumentException("Invalid Topic");
        }

        if (event == null) {
            throw new IllegalArgumentException("Invalid Event");
        }

        if (!this.isAlive()) {
            throw new IllegalStateException("Policy Engine is stopped");
        }

        if (this.isLocked()) {
            throw new IllegalStateException("Policy Engine is locked");
        }

        if (!this.topic2Sinks.containsKey(topic)) {
            logger.warn("{}: cannot deliver event because the sink {}:{} is not registered: {}", this, commType, topic,
                    event);
            throw new IllegalArgumentException("Unsupported topic " + topic + " for delivery");
        }

        boolean success = this.droolsController.deliver(this.topic2Sinks.get(topic), event);

        for (PolicyControllerFeatureAPI feature : getProviders()) {
            try {
                if (feature.afterDeliver(this, commType, topic, event, success)) {
                    return success;
                }
            } catch (Exception e) {
                logger.error("{}: feature {} after-deliver failure because of {}", this, feature.getClass().getName(),
                        e.getMessage(), e);
            }
        }

        return success;
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public boolean isAlive() {
        return this.alive;
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public boolean lock() {
        logger.info("{}: lock", this);

        for (PolicyControllerFeatureAPI feature : getProviders()) {
            try {
                if (feature.beforeLock(this)) {
                    return true;
                }
            } catch (Exception e) {
                logger.error("{}: feature {} before-lock failure because of {}", this, feature.getClass().getName(),
                        e.getMessage(), e);
            }
        }

        synchronized (this) {
            if (this.locked) {
                return true;
            }

            this.locked = true;
        }

        // it does not affect associated sources/sinks, they are
        // autonomous entities

        boolean success = this.droolsController.lock();

        for (PolicyControllerFeatureAPI feature : getProviders()) {
            try {
                if (feature.afterLock(this)) {
                    return true;
                }
            } catch (Exception e) {
                logger.error("{}: feature {} after-lock failure because of {}", this, feature.getClass().getName(),
                        e.getMessage(), e);
            }
        }

        return success;
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public boolean unlock() {

        logger.info("{}: unlock", this);

        for (PolicyControllerFeatureAPI feature : getProviders()) {
            try {
                if (feature.beforeUnlock(this)) {
                    return true;
                }
            } catch (Exception e) {
                logger.error("{}: feature {} before-unlock failure because of {}", this, feature.getClass().getName(),
                        e.getMessage(), e);
            }
        }

        synchronized (this) {
            if (!this.locked) {
                return true;
            }

            this.locked = false;
        }

        boolean success = this.droolsController.unlock();

        for (PolicyControllerFeatureAPI feature : getProviders()) {
            try {
                if (feature.afterUnlock(this)) {
                    return true;
                }
            } catch (Exception e) {
                logger.error("{}: feature {} after-unlock failure because of {}", this, feature.getClass().getName(),
                        e.getMessage(), e);
            }
        }

        return success;
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public boolean isLocked() {
        return this.locked;
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public List<? extends TopicSource> getTopicSources() {
        return this.sources;
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public List<? extends TopicSink> getTopicSinks() {
        return this.sinks;
    }

    /**
     * {@inheritDoc}.
     */
    @Override
    public DroolsController getDrools() {
        return this.droolsController;
    }


    /**
     * {@inheritDoc}.
     */
    @Override
    @JsonIgnore
    public Properties getProperties() {
        return this.properties;
    }

    @Override
    public String toString() {
        return "AggregatedPolicyController [name=" + name + ", alive=" + alive
                + ", locked=" + locked + ", droolsController=" + droolsController + "]";
    }

    // the following methods may be overridden by junit tests
    
    protected SystemPersistence getPersistenceManager() {
        return SystemPersistence.manager;
    }

    protected TopicEndpoint getEndpointManager() {
        return TopicEndpoint.manager;
    }

    protected DroolsControllerFactory getDroolsFactory() {
        return DroolsController.factory;
    }

    protected List<PolicyControllerFeatureAPI> getProviders() {
        return PolicyControllerFeatureAPI.providers.getList();
    }
}