Package org.jboss.internal.soa.esb.services.rules

Source Code of org.jboss.internal.soa.esb.services.rules.DroolsRuleBaseState

/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.internal.soa.esb.services.rules;

import static org.jboss.internal.soa.esb.services.rules.RuleServiceCallHelper.isFireUntilHalt;
import static org.jboss.soa.esb.services.rules.RuleServicePropertiesNames.RULE_AUDIT_TYPE;
import static org.jboss.soa.esb.services.rules.RuleServicePropertiesNames.RULE_CLOCK_TYPE;
import static org.jboss.soa.esb.services.rules.RuleServicePropertiesNames.StringValue.CONSOLE;
import static org.jboss.soa.esb.services.rules.RuleServicePropertiesNames.StringValue.FILE;
import static org.jboss.soa.esb.services.rules.RuleServicePropertiesNames.StringValue.PSEUDO;
import static org.jboss.soa.esb.services.rules.RuleServicePropertiesNames.StringValue.REALTIME;
import static org.jboss.soa.esb.services.rules.RuleServicePropertiesNames.StringValue.THREADED_FILE;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.log4j.Logger;
import org.drools.ClockType;
import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.common.EventFactHandle;
import org.drools.common.InternalFactHandle;
import org.drools.event.KnowledgeRuntimeEventManager;
import org.drools.impl.EnvironmentFactory;
import org.drools.logger.KnowledgeRuntimeLogger;
import org.drools.logger.KnowledgeRuntimeLoggerFactory;
import org.drools.runtime.Channel;
import org.drools.runtime.Globals;
import org.drools.runtime.KnowledgeRuntime;
import org.drools.runtime.KnowledgeSessionConfiguration;
import org.drools.runtime.StatefulKnowledgeSession;
import org.drools.runtime.StatelessKnowledgeSession;
import org.drools.runtime.conf.ClockTypeOption;
import org.drools.runtime.rule.WorkingMemoryEntryPoint;
import org.jboss.internal.soa.esb.services.rules.util.RulesContext;
import org.jboss.soa.esb.message.Message;
import org.jboss.soa.esb.services.rules.RuleInfo;
import org.jboss.soa.esb.services.rules.StatefulRuleInfo;
import org.jboss.soa.esb.services.rules.RuleServicePropertiesNames.StringValue;

/**
* Drools rule base state used to execute and cleanup rule bases.  Some parts have been extracted from the DroolsRuleService.
*
* @author <a href='mailto:Kevin.Conner@jboss.com'>Kevin Conner</a>
* @author jdelong@redhat.com
* @author <a href="mailto:dbevenius@redhat.com">Daniel Bevenius</a>
* @author dward at jboss.org
*/
class DroolsRuleBaseState
{
  /**
   * The logger for this state.
   */
  private static Logger LOGGER = Logger.getLogger(DroolsRuleBaseState.class);

  /**
   * The rule base associated with this state.
   */
  private final KnowledgeBase ruleBase;
  /**
   * The disposed flag.  If set, stateful sessions are automatically disposed.
   */
  private transient boolean disposed;

  /**
   * The stateful session.
   */
  private StatefulKnowledgeSession statefulSession;
  /**
   * The stateful runtime logger
   */
  private KnowledgeRuntimeLogger statefulRuntimeLogger;
  /**
   * The stateful fireUntilHalt thread.
   */
  private Thread statefulFireUntilHaltThread;
  /**
   * The stateful session lock.
   */
  private final Lock statefulSessionLock = new ReentrantLock();
  /**
   * The stateful session count lock.
   */
  private final Lock statefulSessionCountLock = new ReentrantLock();
  /**
   * The number of sessions queued for the stateful session.
   */
  private int statefulSessionCount;
  /**
   * The stateless sessions.
   */
  private final ConcurrentLinkedQueue<StatelessKnowledgeSession> statelessSessions = new ConcurrentLinkedQueue<StatelessKnowledgeSession>();

  /**
   * Construct the rule base state.
   * @param ruleBase The associated rule base.
   */
  DroolsRuleBaseState(final KnowledgeBase ruleBase)
  {
    this.ruleBase = ruleBase;
  }

  /**
   * Get the rule base associated with this state.
   * @return The rule base.
   */
  KnowledgeBase getRuleBase()
  {
    return ruleBase;
  }

  /**
   * Execute rules using using the Stateless API
   *
   * @param ruleInfo - Stateless holder containing execution parameters.
   * @param message - Message that is updated with the results.
   *
   * @return Message - with updated objects.
   */
  Message executeStatelessRules(
      final RuleInfo ruleInfo,
      final Message message)
  {
    final Map<String,Object> globals = ruleInfo.getGlobals();
    final List<Object> objectList = ruleInfo.getDefaultFacts();

    final String sid;
    final StatelessKnowledgeSession head = statelessSessions.poll();
    final StatelessKnowledgeSession statelessSession;
    if (head != null)
    {
      statelessSession = head;
      sid = getId(statelessSession);
      if (LOGGER.isDebugEnabled())
      {
        LOGGER.debug("reusing old stateles session [" + sid + "]");
      }
    }
    else
    {
      statelessSession = ruleBase.newStatelessKnowledgeSession();
      sid = getId(statelessSession);
      if (LOGGER.isDebugEnabled())
      {
        LOGGER.debug("created new stateless session [" + sid + "]");
      }
    }
    RulesContext.clearContext();
    try
    {
      final List<Object> facts = new ArrayList<Object>();
      if (LOGGER.isTraceEnabled())
      {
        LOGGER.trace("adding Message into fact list for stateless session [" + sid + "]");
      }
      facts.add(message);
      if (objectList != null)
      {
        if (LOGGER.isTraceEnabled())
        {
          LOGGER.trace("adding default facts into fact list for stateless session [" + sid + "]");
        }
        facts.addAll(objectList);
      }
      KnowledgeRuntimeLogger statelessRuntimeLogger = getRuntimeLogger(ruleInfo, statelessSession);
      if (statelessRuntimeLogger != null && LOGGER.isDebugEnabled())
      {
        LOGGER.debug("created new runtime logger [" + getId(statelessRuntimeLogger) + "]");
      }
      if (LOGGER.isTraceEnabled())
      {
        LOGGER.trace("setting globals delegate for stateless session [" + sid + "]");
      }
      statelessSession.getGlobals().setDelegate(new StatelessGlobals(globals));
      try
      {
        if (LOGGER.isDebugEnabled())
        {
          LOGGER.debug("calling execute(Iterable) on stateless session [" + sid + "]");
        }
        statelessSession.execute(facts);
      }
      finally
      {
        statelessSession.getGlobals().setDelegate(null);
        if (statelessRuntimeLogger != null)
        {
          if (LOGGER.isDebugEnabled())
          {
            LOGGER.debug("calling close() on runtime logger [" + getId(statelessRuntimeLogger) + "]");
          }
          statelessRuntimeLogger.close();
        }
      }
    }
    finally
    {
      RulesContext.clearContext();
      statelessSessions.add(statelessSession);
    }
    return message;
  }

  /**
   * Execute rules using using the Stateful API
   *
   * @param ruleInfo - Stateful holder containing execution parameters.
   * @param message - Message that is updated with the results.
   *
   * @return Message - with updated objects.
   */
  Message executeStatefulRules(
      final StatefulRuleInfo ruleInfo,
      final Message message ) throws RuleServiceException
  {
    final boolean fireUntilHalt = isFireUntilHalt(ruleInfo);
    final boolean dispose = ruleInfo.dispose();
    final boolean continueState = ruleInfo.continueState();

    RulesContext.clearContext();
    statefulSessionCountLock.lock();
    statefulSessionCount++;
    statefulSessionCountLock.unlock();

    try
    {
      statefulSessionLock.lock();
      try
      {
        if (statefulSession != null && !continueState)
        {
          final String ssid = getId(statefulSession, statefulSessionCount);
          final StatefulKnowledgeSession disposedStatefulSession = statefulSession;
          final Thread haltedStatefulFireUntilHaltThread = statefulFireUntilHaltThread;
          final KnowledgeRuntimeLogger closedStatefulRuntimeLogger = statefulRuntimeLogger;
          statefulSession = null;
          statefulFireUntilHaltThread = null;
          statefulRuntimeLogger = null;
          // Maybe halt the session
          if (haltedStatefulFireUntilHaltThread != null)
          {
            if (LOGGER.isDebugEnabled())
            {
              LOGGER.debug("calling halt on stateful session [" + ssid + "] - no continue set");
            }
            disposedStatefulSession.halt();
            if (LOGGER.isDebugEnabled())
            {
              LOGGER.debug("joining thread [" + haltedStatefulFireUntilHaltThread.getName() + "] for stateful session [" + ssid + "]");
            }
            try
            {
              haltedStatefulFireUntilHaltThread.join();
            }
            catch (InterruptedException ie)
            {
              LOGGER.error("interrupted thread [" + haltedStatefulFireUntilHaltThread.getName() + "] for stateful session [" + ssid + "]", ie);
            }
          }
          // Always dispose the session
          if (LOGGER.isDebugEnabled())
          {
            LOGGER.debug("calling dispose() on stateful session [" + ssid + "] - no continue set");
          }
          disposedStatefulSession.dispose();
          // Maybe close the logger
          if (closedStatefulRuntimeLogger != null)
          {
            if (LOGGER.isDebugEnabled())
            {
              LOGGER.debug("calling close() on runtime logger [" + getId(closedStatefulRuntimeLogger, statefulSessionCount) + "] - no continue set");
            }
            closedStatefulRuntimeLogger.close();
          }
        }

        final String ssid;
        final boolean isStatefulSessionNew;
        if (statefulSession == null)
        {
          isStatefulSessionNew = true;
          KnowledgeSessionConfiguration statefulSessionConfiguration = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
          setClockType(ruleInfo, statefulSessionConfiguration);
          statefulSession = ruleBase.newStatefulKnowledgeSession(statefulSessionConfiguration, EnvironmentFactory.newEnvironment());
          ssid = getId(statefulSession, statefulSessionCount);
          if (LOGGER.isDebugEnabled())
          {
            LOGGER.debug("created new stateful session [" + ssid + "]");
          }
          setChannels(ruleInfo, statefulSession);
          statefulRuntimeLogger = getRuntimeLogger(ruleInfo, statefulSession);
          if (statefulRuntimeLogger != null && LOGGER.isDebugEnabled())
          {
            LOGGER.debug("created new runtime logger [" + getId(statefulRuntimeLogger, statefulSessionCount) + "]");
          }
        }
        else
        {
          isStatefulSessionNew = false;
          ssid = getId(statefulSession, statefulSessionCount);
          if (LOGGER.isDebugEnabled())
          {
            LOGGER.debug("reusing old stateful session [" + ssid + "]");
          }
        }

        try
        {
          final Map<String, Object> globals = ruleInfo.getGlobals();
          if (globals != null)
          {
            if (LOGGER.isTraceEnabled())
            {
              LOGGER.trace("calling setGlobal(String,Object) on stateful session [" + ssid + "] for each global");
            }
            final Set<Entry<String, Object>> entrySet = globals.entrySet();
            for(Entry<String, Object> entry : entrySet)
            {
              statefulSession.setGlobal( entry.getKey(), entry.getValue() );
            }
          }

          if (LOGGER.isTraceEnabled())
          {
            LOGGER.trace("inserting Message on stateful session [" + ssid + "]");
          }
          // Always insert the ESB Message object.
          InternalFactHandle handle = (InternalFactHandle)statefulSession.insert(message);
          if (handle.isEvent() && LOGGER.isDebugEnabled())
          {
            EventFactHandle ef = (EventFactHandle)handle;
            LOGGER.debug("event [" + ef.getObject().getClass().getName() + "], startTimeStamp [" + ef.getStartTimestamp() + "]");
          }

          // Always insert the default facts (into the main WorkingMemory; no entry-point specified)
          final List<Object> defaultFacts = ruleInfo.getDefaultFacts();
          if (defaultFacts != null)
          {
            if (LOGGER.isTraceEnabled())
            {
              LOGGER.trace("calling insert(Object) on stateful session [" + ssid + "] for each default fact");
            }
            for(Object object : defaultFacts)
            {
              statefulSession.insert(object);
            }
          }

          // Maybe insert entry point facts (into a named WorkingMemoryEntryPoint)
          final Map<String,List<Object>> facts = ruleInfo.getFacts();
          if (facts != null)
          {
            if (LOGGER.isTraceEnabled())
            {
              LOGGER.trace("calling insert(Object) on stateful session [" + ssid + "] for each entry point fact");
            }
            for(Entry<String, List<Object>> entry : facts.entrySet())
            {
              String entryPointName = entry.getKey();
              // Insert objects that have explicitly specified an entry-point.
              WorkingMemoryEntryPoint wmep = statefulSession.getWorkingMemoryEntryPoint(entryPointName);
              if (wmep == null)
              {
                throw new RuleServiceException("The entry-point '" + entryPointName + "' was not found in the current stateful session. Please double check your rules source");
              }
              for(Object fact : entry.getValue())
              {
                wmep.insert(fact);
              }
            }
          }

          // Fire stateful rules.
          if (!fireUntilHalt)
          {
            if (LOGGER.isDebugEnabled())
            {
              LOGGER.debug("calling fireAllRules() on stateful session [" + ssid + "]");
            }
            statefulSession.fireAllRules();
          }
          else if (isStatefulSessionNew)
          {
            final String threadName = new StringBuilder()
              .append(getClass().getSimpleName())
              .append(":fireUntilHalt(")
              .append(ssid)
              .append(")")
              .toString();
            if (LOGGER.isDebugEnabled())
            {
              LOGGER.debug("spawning fireUntilHalt() on stateful session [" + ssid + "] in thread [" + threadName + "]");
            }
            final ClassLoader goodClassLoader = Thread.currentThread().getContextClassLoader();
            statefulFireUntilHaltThread = new Thread(new Runnable() {
              public void run() {
                Thread thread = Thread.currentThread();
                ClassLoader origClassLoader = thread.getContextClassLoader();
                thread.setContextClassLoader(goodClassLoader);
                try {
                  if (LOGGER.isDebugEnabled())
                  {
                    LOGGER.debug("calling fireUntilHalt() on stateful session [" + ssid + "] in thread [" + threadName + "]");
                  }
                  statefulSession.fireUntilHalt();
                } catch (NullPointerException npe) {
                  LOGGER.warn("fireUntilHalt() not called on stateful session [" + ssid + "] in thread [" + threadName + "] - already halt()ed and dispose()d: " + npe.getMessage());
                } finally {
                  thread.setContextClassLoader(origClassLoader);
                }
              }
            });
            statefulFireUntilHaltThread.setName(threadName);
            statefulFireUntilHaltThread.setDaemon(true);
            statefulFireUntilHaltThread.start();
          }
          else if (LOGGER.isDebugEnabled())
          {
            LOGGER.debug("rule firing unnecessary on stateful session [" + ssid + "] - was initially fireUntilHalt()");
          }
        }
        finally
        {
          if (dispose)
          {
            final StatefulKnowledgeSession disposedStatefulSession = statefulSession;
            final Thread haltedStatefulFireUntilHaltThread = statefulFireUntilHaltThread;
            final KnowledgeRuntimeLogger closedStatefulRuntimeLogger = statefulRuntimeLogger;
            statefulSession = null;
            statefulFireUntilHaltThread = null;
            statefulRuntimeLogger = null;
            // Maybe halt the session
            if (haltedStatefulFireUntilHaltThread != null)
            {
              if (LOGGER.isDebugEnabled())
              {
                LOGGER.debug("calling halt() on stateful session [" + ssid + "]");
              }
              disposedStatefulSession.halt();
              if (LOGGER.isDebugEnabled())
              {
                LOGGER.debug("joining thread [" + haltedStatefulFireUntilHaltThread.getName() + "] for stateful session [" + ssid + "]");
              }
              try
              {
                haltedStatefulFireUntilHaltThread.join();
              }
              catch (InterruptedException ie)
              {
                LOGGER.error("interrupted thread [" + haltedStatefulFireUntilHaltThread.getName() + "] for stateful session [" + ssid + "]", ie);
              }
            }
            // Always dispose the session
            if (LOGGER.isDebugEnabled())
            {
              LOGGER.debug("calling dispose() on stateful session [" + ssid + "]");
            }
            disposedStatefulSession.dispose();
            // Maybe close the logger
            if (closedStatefulRuntimeLogger != null)
            {
              if (LOGGER.isDebugEnabled())
              {
                LOGGER.debug("calling close() on runtime logger [" + getId(closedStatefulRuntimeLogger, statefulSessionCount) + "]");
              }
              closedStatefulRuntimeLogger.close();
            }
          }
        }
      }
      finally
      {
        statefulSessionLock.unlock();
      }
    }
    finally
    {
      RulesContext.clearContext();
      statefulSessionCountLock.lock();
      statefulSessionCount--;
      if (disposed && statefulSessionCount == 0)
      {
        dispose();
      }
      statefulSessionCountLock.unlock();
    }
    return message;
  }

  void dispose()
  {
    statefulSessionCountLock.lock();
    try
    {
      disposed = true;
      if ((statefulSessionCount == 0) && (statefulSession != null))
      {
        final String ssid = getId(statefulSession, statefulSessionCount);
        final StatefulKnowledgeSession disposedStatefulSession = statefulSession;
        final Thread haltedStatefulFireUntilHaltThread = statefulFireUntilHaltThread;
        final KnowledgeRuntimeLogger closedStatefulRuntimeLogger = statefulRuntimeLogger;
        statefulSession = null;
        statefulFireUntilHaltThread = null;
        statefulRuntimeLogger = null;
        // Maybe halt the session
        if (haltedStatefulFireUntilHaltThread != null)
        {
          if (LOGGER.isDebugEnabled())
          {
            LOGGER.debug("calling halt() on stateful session [" + ssid + "]");
          }
          disposedStatefulSession.halt();
          if (LOGGER.isDebugEnabled())
          {
            LOGGER.debug("joining thread [" + haltedStatefulFireUntilHaltThread.getName() + "] for stateful session [" + ssid + "]");
          }
          try
          {
            haltedStatefulFireUntilHaltThread.join();
          }
          catch (InterruptedException ie)
          {
            LOGGER.error("interrupted thread [" + haltedStatefulFireUntilHaltThread.getName() + "] for stateful session [" + ssid + "]", ie);
          }
        }
        // Always dispose the session
        if (LOGGER.isDebugEnabled())
        {
          LOGGER.debug("calling dispose() on stateful session [" + ssid + "]");
        }
        disposedStatefulSession.dispose();
        // Maybe close the logger
        if (closedStatefulRuntimeLogger != null)
        {
          if (LOGGER.isDebugEnabled())
          {
            LOGGER.debug("calling close() on runtime logger [" + getId(closedStatefulRuntimeLogger, statefulSessionCount) + "]");
          }
          closedStatefulRuntimeLogger.close();
        }
      }
      statelessSessions.clear();
    }
    finally
    {
      statefulSessionCountLock.unlock();
    }
  }

  private void setClockType(RuleInfo ruleInfo, KnowledgeSessionConfiguration statefulSessionConfiguration)
  {
    if (ruleInfo != null)
    {
      StringValue clockType = RULE_CLOCK_TYPE.getStringValue(ruleInfo.getClockType());
      if (REALTIME.equals(clockType))
      {
        statefulSessionConfiguration.setOption(ClockTypeOption.get(ClockType.REALTIME_CLOCK.getId()));
      }
      else if (PSEUDO.equals(clockType))
      {
        statefulSessionConfiguration.setOption(ClockTypeOption.get(ClockType.PSEUDO_CLOCK.getId()));
      }
    }
  }

  private void setChannels(RuleInfo ruleInfo, KnowledgeRuntime session)
  {
    Map<String,Channel> channel_map = ruleInfo.getChannels();
    if (channel_map != null)
    {
      for (Entry<String,Channel> channel_entry : channel_map.entrySet())
      {
        String channel_name = channel_entry.getKey();
        Channel channel = channel_entry.getValue();
        if (channel_name != null && channel != null)
        {
          session.registerChannel(channel_name, channel);
        }
      }
    }
  }

  private KnowledgeRuntimeLogger getRuntimeLogger(RuleInfo ruleInfo, KnowledgeRuntimeEventManager session)
  {
    if (ruleInfo != null)
    {
      StringValue auditType = RULE_AUDIT_TYPE.getStringValue(ruleInfo.getAuditType());
      if (CONSOLE.equals(auditType))
      {
        return KnowledgeRuntimeLoggerFactory.newConsoleLogger(session);
      }
      // If auditType is not defined and neither is ruleAuditFile, no auditing is done.
      // If auditType is not defined but ruleAuditFile is, the assumption is THREADED_FILE.
      boolean isFile = FILE.equals(auditType);
      boolean isThreadedFile = THREADED_FILE.equals(auditType);
      String auditFile = ruleInfo.getAuditFile();
      if (isFile || isThreadedFile || auditFile != null)
      {
        if (auditFile == null)
        {
          auditFile = "event";
        }
        if (isFile)
        {
          return KnowledgeRuntimeLoggerFactory.newFileLogger(session, auditFile);
        }
        Integer auditInterval = ruleInfo.getAuditInterval();
        if (auditInterval == null)
        {
          auditInterval = Integer.valueOf(1000);
        }
        return KnowledgeRuntimeLoggerFactory.newThreadedFileLogger(session, auditFile, auditInterval.intValue());
      }
    }
    return null;
  }

  private final String getId(final Object object)
  {
    return String.valueOf(System.identityHashCode(object));
  }

  private final String getId(final Object object, final int count)
  {
    return new StringBuilder()
      .append(getId(object))
      .append(":")
      .append(count)
      .toString();
  }

  private static final class StatelessGlobals implements Globals
  {

    private final Map<String, Object> globals;

    public StatelessGlobals(Map<String, Object> globals)
    {
      this.globals = new HashMap<String, Object>(globals);
    }

    public Object get(String identifier)
    {
      return globals.get(identifier);
    }

    public void set(String identifier, Object value)
    {
      this.globals.put(identifier, value);
    }

    public void setDelegate(Globals delegate) {}

  }
}
TOP

Related Classes of org.jboss.internal.soa.esb.services.rules.DroolsRuleBaseState

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.