Package net.fp.rp.drools

Source Code of net.fp.rp.drools.SpringDecisionTableLoader

package net.fp.rp.drools;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.StringReader;

import net.fp.rp.common.exception.RpException;

import org.apache.log4j.Logger;
import org.drools.FactHandle;
import org.drools.IntegrationException;
import org.drools.RuleBase;
import org.drools.RuleBaseFactory;
import org.drools.StatefulSession;
import org.drools.StatelessSession;
import org.drools.WorkingMemory;
import org.drools.compiler.DroolsParserException;
import org.drools.compiler.PackageBuilder;
import org.drools.decisiontable.InputType;
import org.drools.decisiontable.SpreadsheetCompiler;
import org.drools.rule.Package;
import org.springframework.core.io.ClassPathResource;
import org.xml.sax.SAXException;

/**
* Wrapper Class around the the JBoss Rules (Drools) Decision Table. This can be
* called by Spring
*
* This Class also caches the rules that are compiled from the Decision Table, both in
* the intermediate (drl) rule format , and as a serialised Java class.
*
* @see SpringDecisionTableLoaderTest for a work example of this class in action
* @author Paul Browne
*
* Copyright @link www.firstpartners.net/red
*/
public class SpringDecisionTableLoader {

  private String drlFileLocation = null;

  /**
   * the name of the excel file that is our decision table
   */
  private String excelFileLocation = null;

  // Handle to the ExcelDecisionTable
  private RuleBase excelRules = null;

  /** Logger for this class and subclasses */
  protected final Logger log = Logger.getLogger(getClass());

  // Where serialised files are stored
  private String serialFileLocation = null;

  // The working Memory for a rule
  private StatefulSession statefulSession = null;

  /**
   * executeDecisionTable
   *
   * @param javaBean -
   *            this should be same as the JavaBean expected by the Decision
   *            Table or an RuleCompileException will be thrown.
   * @param useNewWorkingMemory -
   *            whether or not to setup a new working memory , otherwise use
   *            shared.Get a new working memory if requested. We do this as
   *            Spring by default makes the bean act as a singleton. Using the
   *            Shared working memory allows us to re-use previous results
   *            (faster) and preserves state
   * @return javaBean - same Bean (Type and object reference) as the input,
   *         but with values updated as per the decision Table
   * @throws RpException
   */
  public Object executeDecisionTable(Object javaBean,
      boolean useNewWorkingMemory) throws RpException {

    // Local Variables
    WorkingMemory localWorkingMemory = null;

    // Make sure our ruleset is loaded
    initialize();

    // Get a new working memory if requested. We do this as Spring by
    // default
    // makes the bean act as a singleton. Using the Shared working memory
    // allows us to
    // re-use previous results (faster) and preserves state
    if (useNewWorkingMemory) {
      localWorkingMemory = getRuleBase().newStatefulSession();
    } else {
      localWorkingMemory = this.getSharedWorkingMemory();
    }

    // Place the Java Bean in the working memory , keep a handle to it
    FactHandle currentHandle = localWorkingMemory.insert(javaBean);

    // use Drools *without* any Java intervention
    localWorkingMemory.fireAllRules();

    // Remove the reference to this as part of the cleanup for later
    localWorkingMemory.retract(currentHandle);

    return javaBean;
  }

  /**
   * Get the filename of the DRL Rules file , if any (optional)
   *
   * @return
   */
  public String getDrlFileLocation() {
    return drlFileLocation;
  }

  /**
   * Set the filename of the DRL Rules file , if any (optional)
   *
   * @return
   */
  public String getExcelFileLocation() {
    return excelFileLocation;
  }

  /**
   * @return Returns the excelFileName containing the Decision table , if any
   *         (optional)
   */
  public String getExcelFileName() {
    return excelFileLocation;
  }

  /**
   * @return the Rulebase, if it has been loaded, null if it has not
   */
  public RuleBase getRuleBase() {
    return excelRules;
  }

  /**
   * Searches for a file via the local filesystem , then via the Classpath
   *
   * @param fileName
   *            to search for
   * @return InputStream containing the opened file
   */
  private InputStream getFileIfExists(String fileName) throws RpException {
    // Local Variables
    InputStream rIStream = null;

    log.debug("Searching for File name:" + fileName);

    // The next line creates one execution of the process definition.
    // After construction the process execution has one main path
    // of execution (the root token) that is positioned in the
    // startstate.-
    try {
      rIStream = new FileInputStream(fileName);

    } catch (FileNotFoundException fnfe1) {
      log
          .debug("Could not find resource as file - attempting the classpath");

      try {
        ClassPathResource myClassPathReader = new ClassPathResource(
            fileName);
        rIStream = myClassPathReader.getInputStream();
        log.debug("Found File via Classpath");

      } catch (Exception fnfe2) {
        log.debug("Returning Null as could not find file"+fileName+ "locally or on classpath");
        //log.info("Could not find resource as file", fnfe1);
        //log.info("Could not find resource on classpath", fnfe2);
       
       
        // we will return null
      }
    }

    return rIStream;

  }

  /**
   * @return the serialFileLocation, where the Serialized (compiled) rulebase
   *         is stored, if it exists
   */
  public String getSerialFileLocation() {
    return serialFileLocation;
  }

  /**
   * Load and setup the rule engine , if we have not already done so It is <b>
   * not mandatory to call this before the executeCalculation() method, but it
   * does ensure that rules have been loaded and compiled before we try to do
   * the first
   *
   * @throws RPException
   */
  public void initialize() throws RpException {

    if (getRuleBase() == null) {
      setRuleBase(loadRules());
    }

    if (getSharedWorkingMemory() == null) {
      setSharedWorkingMemory(getRuleBase().newStatefulSession());
    }
  }

  /**
   * Overloaded method, calls load Rules , using the excel,drl and serial file
   * locations as previously set using the accessor methods
   *
   * @return
   * @throws RpException
   */
  public RuleBase loadRules() throws RpException {

    return loadRules(this.excelFileLocation, this.drlFileLocation,
        this.serialFileLocation);

  }

  /**
   * Loads a rule base , searching for the rules in the following order:
   * <ol>
   * <li>A serialised (pre compiled RuleBase), as specified in serialPath.
   * This will be generated if it does not exist</li>
   * <li>A set of rules from a .drl file, as specified in drlPath</li>
   * <li>An Excel Decision Table, from a .xls file , as specified in
   * excelPath. This is then compiled, saved as a serialised file and as a
   * <i>sample</i> drl file under the name <i>drlPath-tmp</i> (sample file
   * not used by program)</li>
   * </ol>
   * The first rules file found (from the above order) is the one used and the
   * search terminated.
   *
   * @param excelPath
   * @param drlPath
   * @param serialPath
   * @return RuleBase, using the Rules file that has been found
   * @throws RpException
   */
  public RuleBase loadRules(String excelPath, String drlPath,
      String serialPath) throws RpException {

    // Local Variables
    RuleBase returnRules = null;
    String drlPathTmp = drlPath + "-tmp";

    // Try to use the Serialized Version
    log.debug("Attempting to load pre-serialised Rules from " + serialPath);
    returnRules = loadSerializedRulesFromFile(serialPath);

    if (returnRules != null) {
      log.info("Use pre-serialised Rulebase from file:" + serialPath);
    } else {

      // Try to use the Drl Version
      log.debug("Attempting to load drl Rules from " + drlPath);
      returnRules = loadDrlRulesFromFile(drlPath);
      if (returnRules != null) {
        log.info("Use drl Rulebase from file:" + drlPath);
        log.info("Serializing rules to file " + serialPath
            + " for later use");
        serializeRulesToFile(returnRules, serialPath);
      } else {

        // Use the Excel Version
        log.debug("Attempting to load excel Rules from " + excelPath);
        returnRules = loadRulesFromExcelDecisionTable(excelPath,
            drlPathTmp);

        if (returnRules != null) {
          log.info("Use excel Rulebase from file:" + excelPath);

          log.info("Serializing rules to file " + serialPath
              + " for later use");
          serializeRulesToFile(returnRules, serialPath);

        } else {
          throw new RpException(
              "Unable to find Rules in any form (Serial,Drl,Excel) at any location (file,classpath)");
        }

      }
    }

    return returnRules;

  }

  /**
   * Load the Rules from an Excel Decision Table
   *
   * @param fileName
   * @param drlPathTmp
   * @return
   * @throws RpException
   */
  public RuleBase loadRulesFromExcelDecisionTable(String fileName,
      String drlPathTmp) throws RpException {

    try {

      SpreadsheetCompiler converter = new SpreadsheetCompiler();

      // get the file exists (via file or classpath)
      InputStream iStream = getFileIfExists(excelFileLocation);

      // null check
      if (iStream == null) {
        return null;
      }

      // Compile the rules
      String drl = converter.compile(iStream, InputType.XLS);

      if (drl != null) {
        PackageBuilder builder = new PackageBuilder();
        builder.addPackageFromDrl(new StringReader(drl));

        Package pkg = builder.getPackage();

        if (pkg != null) {
          setRuleBase(RuleBaseFactory.newRuleBase());
          getRuleBase().addPackage(pkg);

          log.info("Saving tmpCopy to " + drlPathTmp);
          outputDrlToFile(drl, drlPathTmp);

        } else {
          throw new RpException(
              "Problem creating new rule base from Excel");
        }
      } else {
        throw new RpException("Problem loading Excel Decision Tables");
      }

    } catch (IOException ie) {
      // Wrap Exception and throw it
      log.debug("Rethrowing", ie);
      throw new RpException(ie);
    } catch (SAXException saxe) {
      // Wrap Exception and throw it
      throw new RpException(saxe);
    } catch (IntegrationException ie) {
      // Wrap Exception and throw it
      throw new RpException(ie);
    } catch (DroolsParserException dpe) {
      // Wrap Exception and throw it
      throw new RpException(dpe);
    } catch (RpException rpe) {
      // just rethrow
      throw rpe;
    } catch (Exception e) {
      // Handling generic 'Exception' is forced upon us by
      // RuleBase.addPackage
      // Wrap Exception and throw it
      log.warn(e);
      throw new RpException(e);
    }

    // Return the excel rules that we have set
    return this.getRuleBase();

  }

  /**
   * Saves the drl string to the given file name
   *
   * @param drl
   * @param fileName
   * @throws RpException
   */
  public void outputDrlToFile(String drl, String fileName) throws RpException {

    // Open the file , overwrite any existing contents
    try {
      FileWriter out = new FileWriter(fileName, false);
      out.write(drl);
      out.close();
      log.debug("Put copy of DRL in file:" + fileName);
    } catch (IOException ie) {
      throw new RpException(ie);
    }

  }

  /**
   * Loads a Rule base from a given DRL file
   *
   * @param fileName
   *            to load
   * @return RuleBase form of the drl file
   * @throws RpException
   */
  public RuleBase loadDrlRulesFromFile(String fileName) throws RpException {

    // Local Variables
    RuleBase returnRuleBase = null;

    // get the file exists (via file or classpath)
    InputStream iStream = getFileIfExists(fileName);

    // null check
    if (iStream == null) {
      return null;
    }

    // Convert the Stream into a reader that Drools uses to parse files
    InputStreamReader drl = new InputStreamReader(iStream);

    try {
      log.info("Reading Rules from drl:" + fileName);

      PackageBuilder builder = new PackageBuilder();
      // builder.addPackageFromDrl( new StringReader(drl) );
      builder.addPackageFromDrl(drl);

      Package pkg = builder.getPackage();

      if (pkg != null) {
        returnRuleBase = RuleBaseFactory.newRuleBase();
        returnRuleBase.addPackage(pkg);
      }
    } catch (IOException ie) {
      throw new RpException(ie);
    } catch (DroolsParserException dpe) {
      throw new RpException(dpe);
    } catch (Exception e) {
      throw new RpException(e);
    }

    return returnRuleBase;

  }

  /**
   * Loads a pre-compiled (serialised) RuleBase from a file
   *
   * @param fileName
   * @return
   * @throws RpException
   */
  public RuleBase loadSerializedRulesFromFile(String fileName)
      throws RpException {

    RuleBase returnRuleBase = null;

    try {
      InputStream inStream = getFileIfExists(fileName);

      // null check
      if (inStream == null) {
        return null;
      }

      ObjectInputStream in = new ObjectInputStream(inStream);
      returnRuleBase = (RuleBase) in.readObject();
      in.close();
    } catch (Exception e) {
      log.info("Exception when loading serialised RuleBase", e);
    }

    return returnRuleBase;

  }

  /**
   * Serialise a rulebase to a file
   *
   * @param output -
   *            the RuleBase to save
   * @param fileName -
   *            to save the Rulebase as
   */
  public void serializeRulesToFile(RuleBase output, String fileName) {
    try {
      ObjectOutputStream out = new ObjectOutputStream(
          new FileOutputStream(fileName));
      out.writeObject(output);
      out.close();
    } catch (Exception e) {
      log.warn("Exception when serialising RuleBase", e);
    }

  }

  /**
   * Where the Drl Rules file is located (if any)
   *
   * @param drlFileLocation
   */
  public void setDrlFileLocation(String drlFileLocation) {
    this.drlFileLocation = drlFileLocation;
  }

  /**
   * Where the Excel Decision file table is located (if any)
   *
   * @param excelFileLocation
   */
  public void setExcelFileLocation(String excelFileLocation) {
    this.excelFileLocation = excelFileLocation;
  }

  /**
   * Set the rulebase that we will use
   *
   * @param Rulebase
   *            the excelRules to set
   */
  public void setRuleBase(RuleBase excelRules) {
    this.excelRules = excelRules;
  }

  /**
   * @param serialFileLocation
   *            the serialFileLocation to set
   */
  public void setSerialFileLocation(String serialFileLocation) {
    this.serialFileLocation = serialFileLocation;
  }

  /**
   * @param sharedWorkingMemory the sharedWorkingMemory to set
   */
  public void setSharedWorkingMemory(StatefulSession sharedWorkingMemory) {
    this.statefulSession = sharedWorkingMemory;
  }

  /**
   * @return the sharedWorkingMemory
   */
  public StatefulSession getSharedWorkingMemory() {
    return statefulSession;
  }

}
TOP

Related Classes of net.fp.rp.drools.SpringDecisionTableLoader

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.