Package org.drools.core.phreak

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

package org.drools.core.phreak;

import org.drools.core.base.SalienceInteger;
import org.drools.core.common.AgendaItem;
import org.drools.core.common.DefaultAgenda;
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.common.Memory;
import org.drools.core.common.StreamTupleEntryQueue;
import org.drools.core.common.TupleEntryQueue;
import org.drools.core.definitions.rule.impl.RuleImpl;
import org.drools.core.reteoo.BetaMemory;
import org.drools.core.reteoo.LeftTuple;
import org.drools.core.reteoo.PathMemory;
import org.drools.core.reteoo.RightTuple;
import org.drools.core.reteoo.RuleTerminalNode;
import org.drools.core.reteoo.RuleTerminalNodeLeftTuple;
import org.drools.core.reteoo.SegmentMemory;
import org.drools.core.spi.Activation;
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.kie.api.runtime.rule.AgendaFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class RuleExecutor {

    protected static final transient Logger   log               = LoggerFactory.getLogger(RuleExecutor.class);
    private static final RuleNetworkEvaluator NETWORK_EVALUATOR = new RuleNetworkEvaluator();
    private final PathMemory                  pmem;
    private final RuleAgendaItem              ruleAgendaItem;
    private final LeftTupleList               tupleList;
    private BinaryHeapQueue                   queue;
    private volatile boolean                  dirty;
    private final 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 gcStreamQueue() {
        List<TupleEntry> nonNormalizedDeletes = flushStreamQueue();
        for (TupleEntry tupleEntry : nonNormalizedDeletes) {
            if (tupleEntry.getLeftTuple() != null) {
                LeftTuple leftTuple = tupleEntry.getLeftTuple();
                if (leftTuple.getMemory() != null) {
                    BetaMemory betaMemory = getBetaMemory(tupleEntry.getNodeMemory());
                    if (betaMemory != null) {
                        betaMemory.getLeftTupleMemory().remove(leftTuple);
                    }
                }
            } else {
                RightTuple rightTuple = tupleEntry.getRightTuple();
                if (rightTuple.getMemory() != null) {
                    BetaMemory betaMemory = getBetaMemory(tupleEntry.getNodeMemory());
                    if (betaMemory != null) {
                        betaMemory.getRightTupleMemory().remove(rightTuple);
                    }
                }
            }
        }
    }

    private BetaMemory getBetaMemory(Memory memory) {
        return memory == null ? null :
               memory instanceof BetaMemory ? (BetaMemory)memory :
               getBetaMemory(memory.getNext());
    }

    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()) {
            if (!fireExitedEarly && isDeclarativeAgendaEnabled()) {
                // Network Evaluation can notify meta rules, which should be given a chance to fire first
                RuleAgendaItem nextRule = agenda.peekNextRule();
                if (isLowerSalience(nextRule, ruleAgendaItem.getSalience())) {
                    fireExitedEarly = true;
                    return localFireCount;
                }
            }

            RuleTerminalNode rtn = (RuleTerminalNode) pmem.getNetworkNode();
            RuleImpl rule = rtn.getRule();
            LeftTuple leftTuple = getNextLeftTuple();
           
            if (rule.isAllMatches()) {
                agenda.fireConsequenceEvent((AgendaItem) leftTuple, DefaultAgenda.ON_BEFORE_ALL_FIRES_CONSEQUENCE_NAME);
            }

            LeftTuple lastLeftTuple = null;
            for (; leftTuple != null; lastLeftTuple = leftTuple, leftTuple = getNextLeftTuple()) {

                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();
                }

                if (!rule.isAllMatches()) { // if firing rule is @All don't give way to other rules
                    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);
                }
            }

            if (rule.isAllMatches()) {
                agenda.fireConsequenceEvent((AgendaItem) lastLeftTuple, DefaultAgenda.ON_AFTER_ALL_FIRES_CONSEQUENCE_NAME);
            }
        }

        removeRuleAgendaItemWhenEmpty(wm);

        fireExitedEarly = false;
        return localFireCount;
    }

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

    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().isQuery() ) {
                        ((InternalAgenda)wm.getAgenda()).removeQueryAgendaItem( ruleAgendaItem );
                    } else if ( ruleAgendaItem.getRule().isEager() ) {
                        ((InternalAgenda) wm.getAgenda()).removeEagerRuleAgendaItem(ruleAgendaItem);
                    }
                }
            }
        }
    }

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

            if ( queue == null || queue.isEmpty() ) {
                NETWORK_EVALUATOR.evaluateNetwork(pmem, outerStack, this, wm);
            } else {
                while (!queue.isEmpty()) {
                    removeQueuedTupleEntry( queue );
                    NETWORK_EVALUATOR.evaluateNetwork(pmem, outerStack, this, wm);
                }
            }
        }
    }

    private static void removeQueuedTupleEntry( TupleEntryQueue tupleQueue ) {
        TupleEntry tupleEntry = tupleQueue.remove();
        PropagationContext originalPctx = tupleEntry.getPropagationContext();

        while (true) {
            processStreamTupleEntry(tupleQueue, tupleEntry);
            if (tupleQueue.isEmpty()) {
                return;
            }
            tupleEntry = tupleQueue.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()) {
                break;
            }
            tupleEntry = tupleQueue.remove();
        }
    }

    private List<TupleEntry> flushStreamQueue() {
        TupleEntryQueue tupleQueue = pmem.getStreamQueue().takeAllForFlushing();
        if (tupleQueue == null || tupleQueue.isEmpty()) {
            return Collections.emptyList();
        }
        List<TupleEntry> nonNormalizedDeletes = new ArrayList<TupleEntry>();
        while (!tupleQueue.isEmpty()) {
            TupleEntry tupleEntry = tupleQueue.remove();
            if ( processStreamTupleEntry( tupleQueue, tupleEntry ) ) {
                nonNormalizedDeletes.add(tupleEntry);
            }
        }
        return nonNormalizedDeletes;
    }

    public static void flushTupleQueue( StreamTupleEntryQueue streamQueue ) {
        if ( streamQueue != null ) {
            TupleEntryQueue tupleQueue = streamQueue.takeAllForFlushing();
            while (!tupleQueue.isEmpty()) {
                processStreamTupleEntry( tupleQueue, tupleQueue.remove() );
            }
        }
    }

    private static boolean processStreamTupleEntry(TupleEntryQueue tupleQueue, TupleEntry tupleEntry) {
        boolean isNonNormalizedDelete = false;
        if (log.isTraceEnabled()) {
            log.trace("Stream removed entry {} {} size {}", System.identityHashCode(tupleQueue), tupleEntry, tupleQueue.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:
                    isNonNormalizedDelete = tupleEntry.getLeftTuple().getStagedType() == LeftTuple.NONE;
                    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:
                    isNonNormalizedDelete = tupleEntry.getRightTuple().getStagedType() == LeftTuple.NONE;
                    bm.getStagedRightTuples().addDelete(tupleEntry.getRightTuple());
                    break;
            }
        }
        return isNonNormalizedDelete;
    }

    public RuleAgendaItem getRuleAgendaItem() {
        return ruleAgendaItem;
    }

    private boolean cancelAndContinue(InternalWorkingMemory wm,
            RuleTerminalNode rtn,
            RuleImpl 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) ) {
            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.getRule().getAgendaGroup() ) || isLowerSalience(nextRule, salience)) )
               || (fireLimit >= 0 && (localFireCount + fireCount >= fireLimit));
    }

    private boolean isLowerSalience(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));
        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);
            }

            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.