Package org.drools.core.phreak

Source Code of org.drools.core.phreak.RuleExecutor$SalienceComparator

package org.drools.core.phreak;

import java.util.Comparator;

import org.drools.core.base.SalienceInteger;
import org.drools.core.common.AgendaItem;
import org.drools.core.common.EventSupport;
import org.drools.core.common.InternalAgenda;
import org.drools.core.common.InternalWorkingMemory;
import org.drools.core.common.LeftTupleSets;
import org.drools.core.reteoo.BetaMemory;
import org.drools.core.reteoo.LeftTuple;
import org.drools.core.reteoo.PathMemory;
import org.drools.core.reteoo.RuleTerminalNode;
import org.drools.core.reteoo.RuleTerminalNodeLeftTuple;
import org.drools.core.reteoo.SegmentMemory;
import org.drools.core.rule.Rule;
import org.drools.core.spi.Activation;
import org.drools.core.spi.AgendaFilter;
import org.drools.core.spi.PropagationContext;
import org.drools.core.util.BinaryHeapQueue;
import org.drools.core.util.LinkedList;
import org.drools.core.util.index.LeftTupleList;
import org.kie.api.event.rule.MatchCancelledCause;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RuleExecutor {

    protected static transient Logger         log               = LoggerFactory.getLogger(RuleExecutor.class);
    private static final RuleNetworkEvaluator NETWORK_EVALUATOR = new RuleNetworkEvaluator();
    private final PathMemory                  pmem;
    private RuleAgendaItem                    ruleAgendaItem;
    private LeftTupleList                     tupleList;
    private BinaryHeapQueue                   queue;
    private volatile boolean                  dirty;
    private boolean                           declarativeAgendaEnabled;
    private boolean                           fireExitedEarly;

    public RuleExecutor(final PathMemory pmem,
            RuleAgendaItem ruleAgendaItem,
            boolean declarativeAgendaEnabled) {
        this.pmem = pmem;
        this.ruleAgendaItem = ruleAgendaItem;
        this.tupleList = new LeftTupleList();
        this.declarativeAgendaEnabled = declarativeAgendaEnabled;
        if (ruleAgendaItem.getRule().getSalience().isDynamic()) {
            queue = new BinaryHeapQueue(SalienceComparator.INSTANCE);
        }
    }

    public synchronized void evaluateNetwork(InternalWorkingMemory wm) {
        NETWORK_EVALUATOR.evaluateNetwork(pmem, null, this, wm);
        setDirty(false);
        wm.executeQueuedActions();
    }

    public synchronized int evaluateNetworkAndFire(InternalWorkingMemory wm,
            final AgendaFilter filter,
            int fireCount,
            int fireLimit) {
        LinkedList<StackEntry> outerStack = new LinkedList<StackEntry>();

        InternalAgenda agenda = (InternalAgenda) wm.getAgenda();
        boolean fireUntilHalt = agenda.isFireUntilHalt();

        reEvaluateNetwork(wm, outerStack, fireUntilHalt);
        wm.executeQueuedActions();

        return fire(wm, filter, fireCount, fireLimit, outerStack, agenda, fireUntilHalt);
    }

    public synchronized void fire(InternalWorkingMemory wm, LinkedList<StackEntry> outerStack) {
        InternalAgenda agenda = (InternalAgenda) wm.getAgenda();
        boolean fireUntilHalt = agenda.isFireUntilHalt();
        fire(wm, null, 0, Integer.MAX_VALUE, outerStack, agenda, fireUntilHalt);
    }

    private int fire(InternalWorkingMemory wm,
            AgendaFilter filter,
            int fireCount,
            int fireLimit,
            LinkedList<StackEntry> outerStack,
            InternalAgenda agenda,
            boolean fireUntilHalt) {
        int localFireCount = 0;
        if (!tupleList.isEmpty()) {
            RuleTerminalNode rtn = (RuleTerminalNode) pmem.getNetworkNode();

            if (!fireExitedEarly && isDeclarativeAgendaEnabled()) {
                // Network Evaluation can notify meta rules, which should be given a chance to fire first
                RuleAgendaItem nextRule = agenda.peekNextRule();
                if (!isHighestSalience(nextRule, ruleAgendaItem.getSalience())) {
                    fireExitedEarly = true;
                    return localFireCount;
                }
            }

            while (!tupleList.isEmpty()) {
                LeftTuple leftTuple;
                if (queue != null) {
                    leftTuple = (LeftTuple) queue.dequeue();
                    tupleList.remove(leftTuple);
                } else {
                    leftTuple = tupleList.removeFirst();
                    ((Activation) leftTuple).setQueued(false);
                }

                rtn = (RuleTerminalNode) leftTuple.getSink(); // branches result in multiple RTN's for a given rule, so unwrap per LeftTuple
                Rule rule = rtn.getRule();

                PropagationContext pctx = leftTuple.getPropagationContext();
                pctx = RuleTerminalNode.findMostRecentPropagationContext(leftTuple,
                        pctx);

                //check if the rule is not effective or
                // if the current Rule is no-loop and the origin rule is the same then return
                if (cancelAndContinue(wm, rtn, rule, leftTuple, pctx, filter)) {
                    continue;
                }

                AgendaItem item = (AgendaItem) leftTuple;
                if (agenda.getActivationsFilter() != null && !agenda.getActivationsFilter().accept(item, wm, rtn)) {
                    // only relevant for seralization, to not refire Matches already fired
                    continue;
                }

                agenda.fireActivation(item);
                localFireCount++;

                if (rtn.getLeftTupleSource() == null) {
                    break; // The activation firing removed this rule from the rule base
                }

                int salience = ruleAgendaItem.getSalience(); // dyanmic salience may have updated it, so get again.
                if (queue != null && !queue.isEmpty() && salience != queue.peek().getSalience()) {
                    ruleAgendaItem.dequeue();
                    ruleAgendaItem.setSalience(queue.peek().getSalience());
                    ruleAgendaItem.getAgendaGroup().add(ruleAgendaItem);
                    salience = ruleAgendaItem.getSalience();
                }

                RuleAgendaItem nextRule = agenda.peekNextRule();
                if (haltRuleFiring(nextRule, fireCount, fireLimit, localFireCount, agenda, salience)) {
                    break; // another rule has high priority and is on the agenda, so evaluate it first
                }
                reEvaluateNetwork(wm, outerStack, fireUntilHalt);
                wm.executeQueuedActions();

                if (tupleList.isEmpty() && !outerStack.isEmpty()) {
                    // the outer stack is nodes needing evaluation, once all rule firing is done
                    // such as window expiration, which must be done serially
                    StackEntry entry = outerStack.removeFirst();
                    NETWORK_EVALUATOR.evalStackEntry(entry, outerStack, outerStack, this, wm);
                }
            }
        }

        removeRuleAgendaItemWhenEmpty(wm);

        fireExitedEarly = false;
        return localFireCount;
    }

    public PathMemory getPathMemory() {
        return pmem;
    }

    public void removeRuleAgendaItemWhenEmpty(InternalWorkingMemory wm) {
        if (!dirty && tupleList.isEmpty()) {
            // dirty check, before doing the synced check and removal
            synchronized (ruleAgendaItem) {
                if (!dirty && tupleList.isEmpty()) {
                    if (log.isTraceEnabled()) {
                        log.trace("Removing RuleAgendaItem " + ruleAgendaItem);
                    }
                    ruleAgendaItem.remove();
                    if (ruleAgendaItem.getRule().isEager()) {
                        ((InternalAgenda) wm.getAgenda()).removeEagerRuleAgendaItem(ruleAgendaItem);
                    }
                }
            }
        }
    }

    public synchronized void reEvaluateNetwork(InternalWorkingMemory wm, LinkedList<StackEntry> outerStack, boolean fireUntilHalt) {
        if (isDirty() || (pmem.getTupleQueue() != null && !pmem.getTupleQueue().isEmpty())) {
            setDirty(false);

            boolean evaled = false;
            if (pmem.getTupleQueue() != null) {
                while (!pmem.getTupleQueue().isEmpty()) {
                    removeQueuedTupleEntry();
                    NETWORK_EVALUATOR.evaluateNetwork(pmem, outerStack, this, wm);
                    evaled = true;
                }
            }

            if (!evaled) {
                NETWORK_EVALUATOR.evaluateNetwork(pmem, outerStack, this, wm);
            }
        }
    }

    private void removeQueuedTupleEntry() {
        TupleEntry tupleEntry = pmem.getTupleQueue().remove();
        PropagationContext originalPctx = tupleEntry.getPropagationContext();

        boolean repeat = true;
        while (repeat) {
            if (log.isTraceEnabled()) {
                log.trace("Stream removed entry {} {} size {}", System.identityHashCode(pmem.getTupleQueue()), tupleEntry, pmem.getTupleQueue().size());
            }
            if (tupleEntry.getLeftTuple() != null) {
                SegmentMemory sm = tupleEntry.getNodeMemory().getSegmentMemory();
                LeftTupleSets tuples = sm.getStagedLeftTuples();
                tupleEntry.getLeftTuple().setPropagationContext(tupleEntry.getPropagationContext());
                switch (tupleEntry.getPropagationType()) {
                    case PropagationContext.INSERTION:
                    case PropagationContext.RULE_ADDITION:
                        tuples.addInsert(tupleEntry.getLeftTuple());
                        break;
                    case PropagationContext.MODIFICATION:
                        tuples.addUpdate(tupleEntry.getLeftTuple());
                        break;
                    case PropagationContext.DELETION:
                    case PropagationContext.EXPIRATION:
                    case PropagationContext.RULE_REMOVAL:
                        tuples.addDelete(tupleEntry.getLeftTuple());
                        break;
                }
            } else {
                BetaMemory bm = (BetaMemory) tupleEntry.getNodeMemory();
                tupleEntry.getRightTuple().setPropagationContext(tupleEntry.getPropagationContext());
                switch (tupleEntry.getPropagationType()) {
                    case PropagationContext.INSERTION:
                    case PropagationContext.RULE_ADDITION:
                        bm.getStagedRightTuples().addInsert(tupleEntry.getRightTuple());
                        break;
                    case PropagationContext.MODIFICATION:
                        bm.getStagedRightTuples().addUpdate(tupleEntry.getRightTuple());
                        break;
                    case PropagationContext.DELETION:
                    case PropagationContext.EXPIRATION:
                    case PropagationContext.RULE_REMOVAL:
                        bm.getStagedRightTuples().addDelete(tupleEntry.getRightTuple());
                        break;
                }
            }
            if (!pmem.getTupleQueue().isEmpty()) {
                tupleEntry = pmem.getTupleQueue().peek();
                PropagationContext pctx = tupleEntry.getPropagationContext();

                // repeat if either the pctx number is the same, or the event time is the same or before
                if (pctx.getPropagationNumber() == originalPctx.getPropagationNumber()) {
                    repeat = true;
                } else {
                    repeat = false;
                }

                //                if ( tupleEntry.getLeftTuple() != null ) {
                //                    evFh2 = ( EventFactHandle ) tupleEntry.getLeftTuple().getLastHandle();
                //                }  else {
                //                    evFh2 = ( EventFactHandle ) tupleEntry.getRightTuple().getFactHandle();
                //                }
                //
                //                if ( !repeat && evFh2.getStartTimestamp() <= evFh1.getStartTimestamp() ) {
                //                    repeat = true;
                //                } else {
                //                    repeat = false;
                //                }
            } else {
                repeat = false;
            }
            if (repeat) {
                tupleEntry = pmem.getTupleQueue().remove();
            }
        }
    }

    //    private void removeQueuedTupleEntry(boolean fireUntilHalt) {
    //        TupleEntry tupleEntry = pmem.getTupleQueue().remove();
    //        TupleEntry originalTupleEntry = tupleEntry;
    //        PropagationContext originalPctx = tupleEntry.getPropagationContext();
    //        PropagationContext pctx = originalPctx;
    //
    //        EventFactHandle  evFh1 = null;
    //        EventFactHandle  evFh2 = null;
    //        if ( tupleEntry.getLeftTuple() != null ) {
    //            evFh1 = ( EventFactHandle ) tupleEntry.getLeftTuple().getLastHandle();
    //        }  else {
    //            evFh1 = ( EventFactHandle ) tupleEntry.getRightTuple().getFactHandle();
    //        }
    //
    //        boolean repeat = true;
    //        while ( repeat ) {
    //            if ( log.isTraceEnabled() ) {
    //                log.trace( "Stream removed entry {} {} size {}",  System.identityHashCode(  pmem.getTupleQueue() ), tupleEntry, pmem.getTupleQueue().size() );
    //            }
    //            if (tupleEntry.getLeftTuple() != null) {
    //                SegmentMemory sm = (SegmentMemory) tupleEntry.getNodeMemory().getSegmentMemory();
    //                LeftTupleSets tuples = sm.getStagedLeftTuples();
    //                tupleEntry.getLeftTuple().setPropagationContext(tupleEntry.getPropagationContext());
    //                switch (tupleEntry.getPropagationContext().getType()) {
    //                    case PropagationContext.INSERTION:
    //                    case PropagationContext.RULE_ADDITION:
    //                        tuples.addInsert(tupleEntry.getLeftTuple());
    //                        break;
    //                    case PropagationContext.MODIFICATION:
    //                        tuples.addUpdate(tupleEntry.getLeftTuple());
    //                        break;
    //                    case PropagationContext.DELETION:
    //                    case PropagationContext.EXPIRATION:
    //                    case PropagationContext.RULE_REMOVAL:
    //                        tuples.addDelete(tupleEntry.getLeftTuple());
    //                        break;
    //                }
    //            } else {
    //                BetaMemory bm = (BetaMemory) tupleEntry.getNodeMemory();
    //                switch (tupleEntry.getPropagationContext().getType()) {
    //                    case PropagationContext.INSERTION:
    //                    case PropagationContext.RULE_ADDITION:
    //                        bm.getStagedRightTuples().addInsert(tupleEntry.getRightTuple());
    //                        break;
    //                    case PropagationContext.MODIFICATION:
    //                        bm.getStagedRightTuples().addUpdate(tupleEntry.getRightTuple());
    //                        break;
    //                    case PropagationContext.DELETION:
    //                    case PropagationContext.EXPIRATION:
    //                    case PropagationContext.RULE_REMOVAL:
    //                        bm.getStagedRightTuples().addDelete(tupleEntry.getRightTuple());
    //                        break;
    //                }
    //            }
    //            if ( !pmem.getTupleQueue().isEmpty() ) {
    //                tupleEntry = pmem.getTupleQueue().peek();
    //                pctx = tupleEntry.getPropagationContext();
    //
    //                if ( fireUntilHalt ) {
    //                    // repeat if either the pctx number is the same, or the event time is the same or before
    //                    if ( pctx.getPropagationNumber() != originalPctx.getPropagationNumber() ) {
    //                        repeat = false;
    //                    }
    //
    //                    if ( tupleEntry.getLeftTuple() != null ) {
    //                        evFh2 = ( EventFactHandle ) tupleEntry.getLeftTuple().getLastHandle();
    //                    }  else {
    //                        evFh2 = ( EventFactHandle ) tupleEntry.getRightTuple().getFactHandle();
    //                    }
    //
    //                    if ( evFh2.getStartTimestamp() <= evFh1.getStartTimestamp() ) {
    //                        repeat = true;
    //                    }
    //                } else {
    //                    // fireAllRules must drain all events, unless it's already staged
    //                    // it may be staged, if it tries to expire something not yet propagated
    //                    if ( tupleEntry.getLeftTuple() != null && tupleEntry.getLeftTuple().getStagedType() == LeftTuple.NONE) {
    //                        repeat = true;
    //                    } else if ( tupleEntry.getRightTuple() != null && tupleEntry.getRightTuple().getStagedType() == LeftTuple.NONE) {
    //                        repeat = true;
    //                    } else {
    //                        repeat = false;
    //                    }
    //                }
    //            } else {
    //                repeat = false;
    //            }
    //            if ( repeat ) {
    //                tupleEntry = pmem.getTupleQueue().remove();
    //            }
    //        }
    //    }

    public RuleAgendaItem getRuleAgendaItem() {
        return ruleAgendaItem;
    }

    private boolean cancelAndContinue(InternalWorkingMemory wm,
            RuleTerminalNode rtn,
            Rule rule,
            LeftTuple leftTuple,
            PropagationContext pctx, AgendaFilter filter) {
        // NB. stopped setting the LT.object to Boolean.TRUE, that Reteoo did.
        if ((!rule.isEffective(leftTuple, rtn, wm)) ||
                (rule.isNoLoop() && rtn.equals(pctx.getTerminalNodeOrigin()))) {
            return true;
        }

        if (rule.getCalendars() != null) {
            long timestamp = wm.getSessionClock().getCurrentTime();
            for (String cal : rule.getCalendars()) {
                if (!wm.getCalendars().get(cal).isTimeIncluded(timestamp)) {
                    return true;
                }
            }
        }

        return filter != null && !filter.accept((Activation) leftTuple);
    }

    private boolean haltRuleFiring(RuleAgendaItem nextRule,
                                   int fireCount,
                                   int fireLimit,
                                   int localFireCount,
                                   InternalAgenda agenda,
                                   int salience) {


        return !agenda.continueFiring(0) ||
               ( (nextRule != null) && (!ruleAgendaItem.getRule().getAgendaGroup().equals( nextRule.getAgendaGroup() ) || !isHighestSalience(nextRule, salience)) )
               || (fireLimit >= 0 && (localFireCount + fireCount >= fireLimit));
    }

    public boolean isHighestSalience(RuleAgendaItem nextRule,
                                     int currentSalience) {
        return nextRule.getSalience() <= currentSalience;
    }

    public LeftTupleList getLeftTupleList() {
        return tupleList;
    }

    public void addLeftTuple(LeftTuple leftTuple) {
        ((AgendaItem) leftTuple).setQueued(true);
        this.tupleList.add(leftTuple);
        if (queue != null) {
            addQueuedLeftTuple(leftTuple);
        }
    }

    public void addQueuedLeftTuple(LeftTuple leftTuple) {
        int currentSalience = queue.isEmpty() ? 0 : queue.peek().getSalience();
        queue.enqueue((Activation) leftTuple);
        updateSalience(currentSalience);
    }

    public void removeLeftTuple(LeftTuple leftTuple) {
        ((AgendaItem) leftTuple).setQueued(false);
        this.tupleList.remove(leftTuple);
        if (queue != null) {
            removeQueuedLeftTuple(leftTuple);
        }
    }

    private void removeQueuedLeftTuple(LeftTuple leftTuple) {
        int currentSalience = queue.isEmpty() ? 0 : queue.peek().getSalience();
        queue.dequeue(((Activation) leftTuple).getQueueIndex());
        updateSalience(currentSalience);
    }

    private void updateSalience(int currentSalience) {
        // the queue may be emtpy if no more matches are left, so reset it to default salience 0
        int newSalience = queue.isEmpty() ? SalienceInteger.DEFAULT_SALIENCE.getValue() : queue.peek().getSalience();
        if (currentSalience != newSalience) {
            // salience changed, so the RuleAgendaItem needs to be removed and re-added, for sorting
            ruleAgendaItem.remove();
        }
        if (!ruleAgendaItem.isQueued()) {
            ruleAgendaItem.setSalience(newSalience);
            ruleAgendaItem.getAgendaGroup().add(ruleAgendaItem);
        }
    }

    public void cancel(InternalWorkingMemory wm, EventSupport es) {
        while (!tupleList.isEmpty()) {
            RuleTerminalNodeLeftTuple rtnLt = (RuleTerminalNodeLeftTuple) tupleList.removeFirst();
            if (queue != null) {
                queue.dequeue(rtnLt.getQueueIndex());
            }

            es.getAgendaEventSupport().fireActivationCancelled(rtnLt, wm, MatchCancelledCause.CLEAR);
        }
    }

    public boolean isDirty() {
        return dirty;
    }

    public void setDirty(final boolean dirty) {
        this.dirty = dirty;
    }

    public boolean isDeclarativeAgendaEnabled() {
        return this.declarativeAgendaEnabled;
    }

    public static class SalienceComparator implements Comparator {

        public static final SalienceComparator INSTANCE = new SalienceComparator();

        public int compare(Object existing, Object adding) {
            RuleTerminalNodeLeftTuple rtnLt1 = (RuleTerminalNodeLeftTuple) existing;
            RuleTerminalNodeLeftTuple rtnLt2 = (RuleTerminalNodeLeftTuple) adding;

            final int s1 = rtnLt1.getSalience();
            final int s2 = rtnLt2.getSalience();

            // highest goes first
            if (s1 > s2) {
                return 1;
            } else if (s1 < s2) {
                return -1;
            }

            final int l1 = rtnLt1.getRule().getLoadOrder();
            final int l2 = rtnLt2.getRule().getLoadOrder();

            // lowest goes first
            if (l1 < l2) {
                return 1;
            } else if (l1 > l2) {
                return -1;
            } else {
                return 0;
            }
        }
    }

}
TOP

Related Classes of org.drools.core.phreak.RuleExecutor$SalienceComparator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.