[rules-users] Dynamic updates of stateful sessions

Esteban Aliverti esteban.aliverti at gmail.com
Tue Apr 19 07:57:27 EDT 2011


There is no need to update your kbase in the listener. The agent will apply
all the changes to the provided kbase. If you already have a stateful
ksession from that kbase, then the changes in the kbase should be
reflected immediately.
Could you please post the log output of the kagent?

Best Regards,

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Esteban Aliverti
- Developer @ http://www.plugtree.com
- Blog @ http://ilesteban.wordpress.com


On Tue, Apr 19, 2011 at 4:24 AM, Mattias Avelin
<mattias.avelin at netlight.se>wrote:

> Hi!
>
> Yes, I have already implemented a KnowledgeAgentEventListener acts on
> KnowledgeBaseUpdatedEvent and ResourceCompilationFailedEvent. I use this to
> update my knowledgebase reference in my class that wraps the KnowledgeBase
> and KnowledgeAgent. Below is the code for this class
> ("DynamicDecisionService"). I suspect that I have missed something vital
> since I'm actually not getting my knowledgebase to update itself either but
> I "do it myself" in the listener when I receive a KnowledgeBaseUpdatedEvent.
>
> Here is the code that handles the KnowledgeAgent and KnowledgeBase (I have
> stripped out some labels and comments since this belongs to the client).
>
> Best regards
>
> Mattias Avelin
>
> package com.x.service;
>
> import java.util.ArrayList;
> import org.apache.log4j.Logger;
> import org.drools.KnowledgeBase;
> import org.drools.KnowledgeBaseConfiguration;
> import org.drools.KnowledgeBaseFactory;
> import org.drools.agent.KnowledgeAgent;
> import org.drools.agent.KnowledgeAgentConfiguration;
> import org.drools.agent.KnowledgeAgentFactory;
> import org.drools.builder.KnowledgeBuilderError;
> import org.drools.builder.KnowledgeBuilderErrors;
> import org.drools.conf.EventProcessingOption;
> import org.drools.event.knowledgeagent.KnowledgeBaseUpdatedEvent;
> import org.drools.event.knowledgeagent.ResourceCompilationFailedEvent;
> import org.drools.event.rule.DefaultKnowledgeAgentEventListener;
> import org.drools.io.ResourceChangeScannerConfiguration;
> import org.drools.io.ResourceFactory;
>
> /**
>  * {@inheritDoc}
>  * <p>
>  * This implementation of DecisionService updates the knowledgebase
> dynamically
>  * when the underlying rules (drl-files) are updated. Therefore each call
> to the
>  * createSession() method will return a session based on the most resent
> version
>  * of the rules (allowing for some lag due to the intervals of the
>  * resource scanner).
>  * </p>
>  * <p>
>  * For statefull sessions the session needs to be updated "manually" when
> the
>  * KnowledgeBase has been updated.
>  * </P>
>  * @author Mattias Avelin - mattias.avelin at netlight.se
>  */
> public class DynamicDecisionService implements DecisionService
> {
>    private static Logger LOG =
> Logger.getLogger(DynamicDecisionService.class);
>
>    // Repository of all the application's knowledge definitions
>    private KnowledgeBase kBase;
>    // The agent that monitores the rule files (resources) for changes
>    private KnowledgeAgent kAgent;
>    // Decision service type
>    private DecisionSessionType type;
>    // When the current knowledgebase was created
>    private long kbaseLastUpdated;
>
>    /**
>     * Create a DynamicDecisionService using a ChangeSet.
>     *
>     * @param changeSet - The ChangeSet referencing the rule-files to use
>     *                    for this service. Expects a classpath reference
>     *
>     * @throws IllegalArgumentException if changeSet is not a valid
> ChangeSet.
>     */
>    public DynamicDecisionService(
>            String changeSet,
>            DecisionSessionType type)
>    {
>        this.kBase = createKnowledgeBase(type);
>        // the method could reference kBase itself but I thought it
>        // more more "clear" if I actually supply it in the method
> invocation
>        this.kAgent = createKnowledgeAgent(changeSet, this.kBase);
>        // This should not be neccesary according to all sources I've found
>        // but without re-assigning the reference it doesn't work!?!?
>        this.kBase = kAgent.getKnowledgeBase();
>        this.kbaseLastUpdated = System.currentTimeMillis();
>    }
>
>
>    /**
>     * {@inheritDoc}
>     */
>    public DecisionSession createSession ()
>    {
>        return new DecisionSession(kBase.newStatefulKnowledgeSession());
>    }
>
>
>    /**
>     * {@inheritDoc}
>     */
>    public DecisionSession updateSession (DecisionSession oldSession)
>    {
>        // Create a new session from the current KnowledgeBase
>        DecisionSession newSession =
>                new DecisionSession(kBase.newStatefulKnowledgeSession());
>
>        if(oldSession != null)
>        {
>            // "Clean" the old session of all stateless facts before update
>            oldSession.removeAllStatelessKnowledge();
>            // Copy all remaining facts to the new session
>            newSession.uploadKnowledge(new
> ArrayList(oldSession.getAllFacts()));
>        }
>        return newSession;
>    }
>
>
>    /**
>     * {@inheritDoc}
>     */
>    public boolean isSessionStale(DecisionSession decisionSession)
>    {
>        if(decisionSession.getCreationTime() < this.kbaseLastUpdated)
>        {
>            return true;
>        }
>        else
>        {
>            return false;
>        }
>    }
>
>
>    /**
>     * Create a KnowledgeBuilder from a ChangeSet.
>     *
>     * @param changeSet - The ChangeSet
>     * @return A KnowledgeBuilder
>     * @throws IllegalArgumentException if changeSet is not a valid
> ChangeSet.
>     */
>    private KnowledgeAgent createKnowledgeAgent(
>            String changeSet,
>            KnowledgeBase kbase)
>    {
>        // Configure KnowledgeAgentFactory
>        configureAgentFactory();
>        // Create the knowledge agent
>        KnowledgeAgent agent = KnowledgeAgentFactory.newKnowledgeAgent(
>                "classificationAgent",
>                kbase);
>
>  agent.applyChangeSet(ResourceFactory.newClassPathResource(changeSet));
>        // Add actionListener for detecting rule updates
>        agent.addEventListener(new xKnowledgeAgentEventListener());
>        // Start the monitoring...
>        ResourceFactory.getResourceChangeNotifierService().start();
>        ResourceFactory.getResourceChangeScannerService().start();
>        return agent;
>    }
>
>
>    /**
>     * Configure the agentFactory.
>     */
>    private void configureAgentFactory()
>    {
>        // Enable incremental knowledge base build - false means that the
>        // old knowledge base is "reused" and updated when there are
>        // updated rules. True means that the old base is discarded and a
> new
>        // one is created
>        KnowledgeAgentConfiguration kagentConf =
>        KnowledgeAgentFactory.newKnowledgeAgentConfiguration();
>        kagentConf.setProperty("drools.agent.newInstance", "false");
>
>        // Configure the scanning interval - default is 60 sec.
>        // (Scanning resources for changes...)
>        ResourceChangeScannerConfiguration sconf =
>
>  ResourceFactory.getResourceChangeScannerService().newResourceChangeScannerConfiguration();
>        sconf.setProperty("drools.resource.scanner.interval", "30");
>        ResourceFactory.getResourceChangeScannerService().configure(sconf);
>    }
>
>
>    /**
>     * Create a KnowledgeBase. For CEP (Drools Fusion) decision service
>     * use type = STATE_FULL otherwise use STATE_LESS.
>     *
>     * @param type - The type KnowledgeBase, state full or state less
>     *
>     * @return A KnowledgeBase
>     */
>    private KnowledgeBase createKnowledgeBase(DecisionSessionType type)
>    {
>        KnowledgeBase kbase;
>        if(type.equals(DecisionSessionType.STATE_FULL)){
>            KnowledgeBaseConfiguration config =
> KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
>            config.setOption( EventProcessingOption.STREAM );
>            kbase = KnowledgeBaseFactory.newKnowledgeBase( config );
>        }
>        else
>        {
>            kbase = KnowledgeBaseFactory.newKnowledgeBase();
>        }
>        return kbase;
>    }
>
>
>    /**
>     * Get the ServiceType of this Service.
>     *
>     * @return The ServiceType of this service (STATE_FULL or STATE_LESS)
>     */
>    public DecisionSessionType getType ()
>    {
>        return type;
>    }
>
>
>    /**
>     * When was the KnowledgeBase last updated.
>     *
>     * @return The timestamp (ms) for last update of KnowledgeBase
>     */
>    public long getLastUpdated()
>    {
>        return this.kbaseLastUpdated;
>    }
>
>
>    /**
>     * Custom event listener for acting on events such as rule updates and
>     * rule loading errors.
>     */
>    private class xKnowledgeAgentEventListener
>            extends DefaultKnowledgeAgentEventListener
>    {
>
>        /**
>         * Update the working copy of the  KnowledgeBase when detecting
>         * a updated one.
>         *
>         * @param event - The event signalling that the rule base has been
> updated
>         */
>        @Override
>        public void knowledgeBaseUpdated (
>                KnowledgeBaseUpdatedEvent event)
>        {
>            LOG.info("Detected updated rules! Rebuilding knowledgeBase!");
>            long startTime = System.currentTimeMillis();
>            kBase = kAgent.getKnowledgeBase();
>            long endTime = System.currentTimeMillis();
>            // Update the timestamp for when the knowledgeBase was updated
>            kbaseLastUpdated = endTime;
>            LOG.info("Rebuilding knowledgebase took " +
>                    (endTime - startTime) + "ms...");
>        }
>
>
>        /**
>         * Log errors when problems updating the knowledge base.
>         *
>         * @param event - the Event signalling failure to load updated rules
>         */
>        @Override
>        public void resourceCompilationFailed (
>                ResourceCompilationFailedEvent event)
>        {
>            KnowledgeBuilderErrors errors =
>                    event.getKnowledgeBuilder().getErrors();
>
>            if (errors.size() > 0)
>            {
>                LOG.error("Errors loading knowledge.");
>                for (KnowledgeBuilderError error : errors)
>                {
>                    LOG.error(error);
>                }
>            }
>        }
>    }
> }
>
>
>
>
>
> ________________________________________
> From: rules-users-bounces at lists.jboss.org [
> rules-users-bounces at lists.jboss.org] On Behalf Of Esteban Aliverti [
> esteban.aliverti at gmail.com]
> Sent: Monday, April 18, 2011 19:18
> To: Rules Users List
> Subject: Re: [rules-users] Dynamic updates of stateful sessions
>
> Maybe the new rules fail to compile. You can add an
> KnowledgeAgentEventListener to the agent to check for compilation errors.
> Could you post some code snippet showing how are you creating the kagent,
> the kbase, etc.
>
> You can take a look at some tests using newInstence=false here:
>
> https://github.com/droolsjbpm/drools/blob/master/drools-compiler/src/test/java/org/drools/agent/KnowledgeAgentIncrementalChangeSetTest.java
>
> Best Regards,
>
> XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
>
> Esteban Aliverti
> - Developer @ http://www.plugtree.com
> - Blog @ http://ilesteban.wordpress.com
>
>
> On Mon, Apr 18, 2011 at 1:31 PM, Mattias Avelin <
> mattias.avelin at netlight.se<mailto:mattias.avelin at netlight.se>> wrote:
> Thanks for the swift reply!
>
> I have configured my Knowledge Agent to reuse the same KnowledgeBase (set
> drools.agent.newInstance property to "false" in my
> KnowledgeAgentConfiguration) as described in the Expert documentation. But
> my stateful session keeps using the old rules event though I can see from
> the log output that the Knowledgebase has been updated.
>
> /Mattias
> ________
> From: rules-users-bounces at lists.jboss.org<mailto:
> rules-users-bounces at lists.jboss.org> [rules-users-bounces at lists.jboss.org
> <mailto:rules-users-bounces at lists.jboss.org>] On Behalf Of Esteban
> Aliverti [esteban.aliverti at gmail.com<mailto:esteban.aliverti at gmail.com>]
> Sent: Monday, April 18, 2011 16:49
> To: Rules Users List
> Subject: Re: [rules-users] Dynamic updates of stateful sessions
>
> By default, the Knowledge Agent creates a new kbase when changes in the
> monitored resources are detected. So the stateful sessions you had are not
> going to "see" the changes because they belong to a different kbase.
> If you want the agent to apply the changes to the current kbase, you need
> to set drools.agent.newInstance property to "false" in knowledge agent
> configuration.
> You can read about this here:
> http://ilesteban.wordpress.com/2010/03/25/knowledge-agent-incremental-change-set-processing-and-binary-diff/
>
> If you are defining the kagent using spring, you can use the "newInstence"
> attribute of <kagent> bean.
>
> Best Regards,
>
> XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
>
> Esteban Aliverti
> - Developer @ http://www.plugtree.com
> - Blog @ http://ilesteban.wordpress.com
>
>
> On Mon, Apr 18, 2011 at 11:41 AM, Mattias Avelin <
> mattias.avelin at netlight.se<mailto:mattias.avelin at netlight.se><mailto:
> mattias.avelin at netlight.se<mailto:mattias.avelin at netlight.se>>> wrote:
> We have a application in which we have both stateless & stateful sessions
> running. We're using the KnowledgeAgent and ChangeSets to automatically
> update our knowledge bases when the rules are changed.
> This works fine for our stateless sessions for which we are creating a new
> session for each request and if the rules have been updated this is
> reflected in next session we create.
>
> But for stateful sessions I'm not quite as sure on how to get it working. I
> can see in the logs that the KnowledgeBase is re-built when the
> KnowledgeAgent detects the updated rule files. But my "long running"
> session
> is does not reflect these changes (which it shouldn't right?). But how do
> we
> apply these changes to the stateful session without loosing all the facts I
> accumulated?
>
> The way I've solved it now is to keep a "last updated" timestamp for the
> knowledgeBase and a "created" timestamp for the session and then compare
> these before using the session. If the knowledgeBase's "update timestamp"
> is
> later that the sessions "creation timestamp" I create a new Session and
> then
> "manually" copy all facts from the former to the latter.
>
> Is this the way you would do this or is there a better way?
>
> Best Regards
>
> Mattias Avelin
>
>
> _______________________________________________
> rules-users mailing list
> rules-users at lists.jboss.org<mailto:rules-users at lists.jboss.org><mailto:
> rules-users at lists.jboss.org<mailto:rules-users at lists.jboss.org>>
> https://lists.jboss.org/mailman/listinfo/rules-users
>
>
> ______________________________________________________________________
> This email has been scanned by the MessageLabs Email Security System.
> For more information please visit http://www.messagelabs.com/email
> ______________________________________________________________________
>
>
>
> _______________________________________________
> rules-users mailing list
> rules-users at lists.jboss.org<mailto:rules-users at lists.jboss.org>
> https://lists.jboss.org/mailman/listinfo/rules-users
>
>
> ______________________________________________________________________
> This email has been scanned by the MessageLabs Email Security System.
> For more information please visit http://www.messagelabs.com/email
> ______________________________________________________________________
>
>
>
> _______________________________________________
> rules-users mailing list
> rules-users at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/rules-users
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/rules-users/attachments/20110419/99a19eff/attachment.html 


More information about the rules-users mailing list