You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hop.apache.org by ha...@apache.org on 2022/04/28 08:04:14 UTC

[hop] branch master updated: HOP-2754 Porting of Rule Executor and Rule Accumulator steps to Hop

This is an automated email from the ASF dual-hosted git repository.

hansva pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/master by this push:
     new 2e757440c4 HOP-2754 Porting of Rule Executor and Rule Accumulator steps to Hop
     new 0bed20c75e Merge pull request #1469 from sramazzina/HOP-2754
2e757440c4 is described below

commit 2e757440c4654b58299324229a4c6b51d1e3ca38
Author: Sergio Ramazzina <se...@serasoft.it>
AuthorDate: Wed Apr 27 23:27:15 2022 +0200

    HOP-2754 Porting of Rule Executor and Rule Accumulator steps to Hop
---
 assemblies/plugins/dist/pom.xml                    |  12 +
 assemblies/plugins/transforms/drools/pom.xml       | 135 +++++++
 .../transforms/drools/src/assembly/assembly.xml    |  69 ++++
 .../drools/src/main/resources/version.xml          |  20 +
 assemblies/plugins/transforms/pom.xml              |   1 +
 .../pipeline/transforms/rulesaccumulator.adoc      | 117 ++++++
 .../pages/pipeline/transforms/rulesexecutor.adoc   | 106 +++++
 plugins/transforms/drools/pom.xml                  | 134 +++++++
 .../pipeline/transforms/drools/RuleResultItem.java |  68 ++++
 .../hop/pipeline/transforms/drools/Rules.java      | 123 ++++++
 .../transforms/drools/RulesAccumulator.java        | 112 ++++++
 .../transforms/drools/RulesAccumulatorData.java    | 177 +++++++++
 .../transforms/drools/RulesAccumulatorDialog.java  | 418 ++++++++++++++++++++
 .../transforms/drools/RulesAccumulatorMeta.java    | 131 +++++++
 .../pipeline/transforms/drools/RulesExecutor.java  | 114 ++++++
 .../transforms/drools/RulesExecutorData.java       | 192 +++++++++
 .../transforms/drools/RulesExecutorDialog.java     | 415 ++++++++++++++++++++
 .../transforms/drools/RulesExecutorMeta.java       | 138 +++++++
 .../drools/messages/messages_en_US.properties      |  45 +++
 .../drools/messages/messages_it_IT.properties      |  45 +++
 .../drools/src/main/resources/rules_acc.svg        |  49 +++
 .../drools/src/main/resources/rules_exec.svg       |  49 +++
 .../rules-accumulator-solve-golfer-rule.hpl        | 432 +++++++++++++++++++++
 plugins/transforms/pom.xml                         |   1 +
 24 files changed, 3103 insertions(+)

diff --git a/assemblies/plugins/dist/pom.xml b/assemblies/plugins/dist/pom.xml
index e7c76f129a..9bbebb8108 100644
--- a/assemblies/plugins/dist/pom.xml
+++ b/assemblies/plugins/dist/pom.xml
@@ -1154,6 +1154,18 @@
       </exclusions>
     </dependency>
 
+    <dependency>
+      <groupId>org.apache.hop</groupId>
+      <artifactId>hop-assemblies-plugins-transforms-drools</artifactId>
+      <version>${hop-plugins-transforms.version}</version>
+      <type>zip</type>
+      <exclusions>
+        <exclusion>
+          <groupId>*</groupId>
+          <artifactId>*</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
 
     <dependency>
       <groupId>org.apache.hop</groupId>
diff --git a/assemblies/plugins/transforms/drools/pom.xml b/assemblies/plugins/transforms/drools/pom.xml
new file mode 100644
index 0000000000..e94e76f729
--- /dev/null
+++ b/assemblies/plugins/transforms/drools/pom.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~       http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  ~
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.hop</groupId>
+        <artifactId>hop-assemblies-plugins-transforms</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+    </parent>
+
+
+    <artifactId>hop-assemblies-plugins-transforms-drools</artifactId>
+    <version>2.0.0-SNAPSHOT</version>
+    <packaging>pom</packaging>
+
+    <name>Hop Assemblies Plugins Transforms for Drools</name>
+
+    <properties>
+        <drools.version>7.68.0.Final</drools.version>
+        <kie.version>7.68.0.Final</kie.version>
+        <antlr.version>3.5.3</antlr.version>
+        <mvel2.version>2.4.14.Final</mvel2.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.hop</groupId>
+            <artifactId>hop-transform-drools</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-core</artifactId>
+            <version>${drools.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.kie</groupId>
+            <artifactId>kie-internal</artifactId>
+            <version>${kie.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.kie</groupId>
+            <artifactId>kie-api</artifactId>
+            <version>${kie.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-core-reflective</artifactId>
+            <version>${drools.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-core-dynamic</artifactId>
+            <version>${drools.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-compiler</artifactId>
+            <version>${drools.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-templates</artifactId>
+            <version>${drools.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-decisiontables</artifactId>
+            <version>${drools.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-mvel</artifactId>
+            <version>${drools.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.kie.soup</groupId>
+            <artifactId>kie-soup-maven-support</artifactId>
+            <version>${kie.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.kie</groupId>
+            <artifactId>kie-memory-compiler</artifactId>
+            <version>${kie.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.kie.soup</groupId>
+            <artifactId>kie-soup-project-datamodel-commons</artifactId>
+            <version>${kie.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.antlr</groupId>
+            <artifactId>antlr-runtime</artifactId>
+            <version>3.5.3</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mvel</groupId>
+            <artifactId>mvel2</artifactId>
+            <version>2.4.14.Final</version>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/assemblies/plugins/transforms/drools/src/assembly/assembly.xml b/assemblies/plugins/transforms/drools/src/assembly/assembly.xml
new file mode 100644
index 0000000000..037bd06462
--- /dev/null
+++ b/assemblies/plugins/transforms/drools/src/assembly/assembly.xml
@@ -0,0 +1,69 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~       http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
+          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
+  <id>hop-assemblies-plugins-transforms-drools</id>
+  <formats>
+    <format>zip</format>
+  </formats>
+  <baseDirectory>transforms/drools</baseDirectory>
+  <files>
+    <file>
+      <source>${project.basedir}/src/main/resources/version.xml</source>
+      <outputDirectory>.</outputDirectory>
+      <filtered>true</filtered>
+    </file>
+  </files>
+  <fileSets>
+    <fileSet>
+      <outputDirectory>lib</outputDirectory>
+      <excludes>
+        <exclude>**/*</exclude>
+      </excludes>
+    </fileSet>
+  </fileSets>
+  <dependencySets>
+    <dependencySet>
+      <useProjectArtifact>false</useProjectArtifact>
+      <includes>
+        <include>org.apache.hop:hop-transform-drools:jar</include>
+      </includes>
+    </dependencySet>
+    <dependencySet>
+      <useProjectArtifact>false</useProjectArtifact>
+      <outputDirectory>lib</outputDirectory>
+      <includes>
+        <include>org.drools:drools-core:jar</include>
+        <include>org.drools:drools-core-reflective:jar</include>
+        <include>org.drools:drools-core-dynamic:jar</include>
+        <include>org.drools:drools-compiler:jar</include>
+        <include>org.drools:drools-templates:jar</include>
+        <include>org.drools:drools-decisiontables:jar</include>
+        <include>org.drools:drools-mvel:jar</include>
+        <include>org.kie:kie-internal:jar</include>
+        <include>org.kie.soup:kie-soup-maven-support:jar</include>
+        <include>org.kie:kie-api:jar</include>
+        <include>org.kie:kie-memory-compiler:jar</include>
+        <include>org.kie.soup:kie-soup-project-datamodel-commons:jar</include>
+        <include>org.antlr:antlr-runtime:jar</include>
+        <include>org.mvel:mvel2:jar</include>
+      </includes>
+    </dependencySet>
+  </dependencySets>
+</assembly>
\ No newline at end of file
diff --git a/assemblies/plugins/transforms/drools/src/main/resources/version.xml b/assemblies/plugins/transforms/drools/src/main/resources/version.xml
new file mode 100644
index 0000000000..6be576acae
--- /dev/null
+++ b/assemblies/plugins/transforms/drools/src/main/resources/version.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~       http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  ~
+  -->
+
+<version>${project.version}</version>
\ No newline at end of file
diff --git a/assemblies/plugins/transforms/pom.xml b/assemblies/plugins/transforms/pom.xml
index e353d744bd..b0d375df99 100644
--- a/assemblies/plugins/transforms/pom.xml
+++ b/assemblies/plugins/transforms/pom.xml
@@ -62,6 +62,7 @@
     <module>detectemptystream</module>
     <module>detectlastrow</module>
     <module>dimensionlookup</module>
+    <module>drools</module>
     <module>dynamicsqlrow</module>
     <module>edi2xml</module>
     <module>excelinput</module>
diff --git a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/rulesaccumulator.adoc b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/rulesaccumulator.adoc
new file mode 100644
index 0000000000..86f18f6ed6
--- /dev/null
+++ b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/rulesaccumulator.adoc
@@ -0,0 +1,117 @@
+////
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+  http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+////
+:documentationPath: /pipeline/transforms/
+:language: en_US
+:description: The REST Client transform enables you to consume RESTfull services.
+
+= Rules accumulator
+
+== Description
+
+The Rules Accumulator collects incoming rows and executes them against a rule set. This may be useful to determine the answer to a question or otherwise analyze a dataset.
+
+https://www.drools.org/[Drools] is the present rule engine implementation and its https://docs.drools.org/7.68.0.Final/drools-docs/html_single/index.html#_droolslanguagereferencechapter[rule language] can be referenced for use by this step. https://docs.drools.org/7.68.0.Final/drools-docs/html_single/index.html#_welcome[Drools documentation]
+
+== Details
+Once all incoming rows have been collected by the Rules Accumulator step (e.g. - the previous step shuts down) the rows are transformed into Rules.Row objects and passed into the rules engine to be executed against the given rule set.
+
+Rules.Row is defined as a key / value Map of fields where key is the name of the field and value is the value of the field; as well as the externalSource boolean property to indicate whether the Rules.Row object was created in the rule set or injected from an external source.
+
+Fields of a row are accessed as "Row (column["<fieldname>"])".
+
+=== Rules Tab
+
+The _Rules_ tab is where you enter your rule definition or a reference to the rule file..
+
+[width="90%",options="header"]
+|===
+|Option|Description
+|Rules filename|
+|Rules script to execute|
+|===
+
+=== Rules result Tab
+
+Rules result tab defines the layout of the Rules output fields.
+
+
+[width="90%",options="header"]
+|===
+|Option|Description
+|Result column name|
+|Result column type|
+|===
+
+== Example
+All Rule Definitions should contain "import org.pentaho.di.trans.steps.rules.Rules.Row;" to give access to the Rules.Row class.
+
+For the input with a row meta: name (String), position (Integer), color (String); a Rules.Row object will be created for each row with a Map containing those fields.
+
+[width="90%",options="header"]
+|===
+|Rules.Row->row (Map)|Name|Position|Color
+||Fred|1|Blue
+||Fred|2|Red
+||Bob|1|Blue
+||Bob|1|Red
+|===
+
+Rules can be defined and applied:
+[source,drools]
+----
+rule "Golfers problem"
+    dialect "mvel"
+    when
+
+    # Define Fred
+    $fred : Row ( externalSource == true,
+        column["name"] == "Fred"
+    )
+
+    # Define Bob
+    $bob : Row ( externalSource == true,
+        column["name"] == "Bob",
+        column["position"] != $fred.column["position"],
+        column["color"] == "blue",
+        column["color"] != $fred.column["color"],
+    )
+
+    then
+
+      Row fredRow = new Row();
+      Row bobRow = new Row();
+
+      fredRow.addColumn("name", "Fred");
+      fredRow.addColumn("position", $fred.column["position"]);
+      fredRow.addColumn("color", $fred.column["color"]);
+
+      bobRow.addColumn("name", "Bob");
+      bobRow.addColumn("position", $bob.column["position"]);
+      bobRow.addColumn("color", $bob.column["color"]);
+
+
+      insert(fredRow);
+      insert(bobRow);
+
+end
+----
+
+Rules.Row objects can be checked as in the above's left hand side.
+
+Rules.Row objects can be created for the data stream as in the above's right hand side.
+
+The step can be told what fields to pickup from the generated row objects by defining the field Map's name in the steps Results tab. Type conversions can be applied by setting the Result column type as well.
\ No newline at end of file
diff --git a/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/rulesexecutor.adoc b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/rulesexecutor.adoc
new file mode 100644
index 0000000000..e0346c68d2
--- /dev/null
+++ b/docs/hop-user-manual/modules/ROOT/pages/pipeline/transforms/rulesexecutor.adoc
@@ -0,0 +1,106 @@
+////
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+  http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+////
+:documentationPath: /pipeline/transforms/
+:language: en_US
+:description: The REST Client transform enables you to consume RESTfull services.
+
+= Rules executor
+
+== Description
+
+The Rules Executor allows fields of incoming rows to be executed against a rule set. This may be useful to determine additional information or route rows onto another step.
+
+https://www.drools.org/[Drools] is the present rule engine implementation and its https://docs.drools.org/7.68.0.Final/drools-docs/html_single/index.html#_droolslanguagereferencechapter[rule language] can be referenced for use by this step. https://docs.drools.org/7.68.0.Final/drools-docs/html_single/index.html#_welcome[Drools documentation]
+
+== Details
+For each row processed by the Rules Executor step each column is converted to a _Rules.Column_ then injected into the rule engine and executed against the rules set. _Rules.Column_ contains four properties:
+
+_externalSource_: boolean property indicating if this Rules.Column was injected from an external source (a field from a processed row)
+
+_name_: name of this field
+
+_type_: type of data stored in payload (one of ValueMetaInterface.typeCodes - Number, String, Integer, Date, Boolean, etc...)
+
+_payload_: value of this field
+
+Results are generated by creating Rules.Column objects. The step is configured to pick up these generated objects by name.
+
+=== Rules Tab
+
+The _Rules_ tab is where you enter your rule definition or a reference to the rule file..
+
+[width="90%",options="header"]
+|===
+|Option|Description
+|Rules filename|
+|Rules script to execute|
+|===
+
+=== Rules result Tab
+
+Rules result tab defines the layout of the Rules output fields.
+
+[width="90%",options="header"]
+|===
+|Option|Description
+|Result column name|
+|Result column type|
+|===
+
+== Example
+All Rule Definitions should contain "import org.pentaho.di.trans.steps.rules.Rules.Column;" to give access to the Rules.Column class.
+
+For the input row: fica_seed (Integer), hi (String), fica (Integer); three Rules.Column objects will be created in the Rules Engine:
+
+[width="90%",options="header"]
+|===
+|Rules.Row->row (Map)|Type|Payload
+|fica_seed|Integer|-3561151667
+|hi|String|test
+|fica|Integer|bad
+|===
+
+Rules can be defined and applied:
+[source,drools]
+----
+rule "Decline"
+    dialect "mvel"
+    when
+        $fica : Column(name == "fica", payload < 550)
+
+    then
+        Column approvalStatus = new Column();
+        approvalStatus.name = "approvalStatus"
+        approvalStatus.type = String.class
+        approvalStatus.payload = "declined"
+
+        Column approvalClassification = new Column();
+        approvalClassification.name = "approvalClass"
+        approvalClassification.type = String.class
+        approvalClassification.payload = "Declined: fica too low"
+
+        insert(approvalStatus);
+        insert(approvalClassification);
+
+        System.out.println("Declined");
+end
+----
+Rules.Column properties can be checked as in the above's left hand side.
+
+Rules.Column objects can be created for pickup as in the above's right hand side.
+
+The step can be told to pickup a generated Rules.Column object by defining the Rules.Column name in the steps Results tab. Type conversions can be applied by setting the Result column type as well.
\ No newline at end of file
diff --git a/plugins/transforms/drools/pom.xml b/plugins/transforms/drools/pom.xml
new file mode 100644
index 0000000000..ced03cde4c
--- /dev/null
+++ b/plugins/transforms/drools/pom.xml
@@ -0,0 +1,134 @@
+<?xml version="1.0"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~       http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  ~
+  -->
+
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+         xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.hop</groupId>
+        <artifactId>hop-plugins-transforms</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>hop-transform-drools</artifactId>
+    <packaging>jar</packaging>
+
+    <name>Hop Plugins Transforms for Drools</name>
+
+    <properties>
+        <drools.version>7.68.0.Final</drools.version>
+        <kie.version>7.68.0.Final</kie.version>
+        <antlr.version>3.5.3</antlr.version>
+        <mvel2.version>2.4.14.Final</mvel2.version>
+    </properties>
+
+
+    <dependencies>
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-core</artifactId>
+            <version>${drools.version}</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.kie</groupId>
+            <artifactId>kie-internal</artifactId>
+            <version>${kie.version}</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.kie</groupId>
+            <artifactId>kie-api</artifactId>
+            <version>${kie.version}</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-core-reflective</artifactId>
+            <version>${drools.version}</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-core-dynamic</artifactId>
+            <version>${drools.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-compiler</artifactId>
+            <version>${drools.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-templates</artifactId>
+            <version>${drools.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-decisiontables</artifactId>
+            <version>${drools.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-mvel</artifactId>
+            <version>${drools.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.kie.soup</groupId>
+            <artifactId>kie-soup-maven-support</artifactId>
+            <version>${kie.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.kie</groupId>
+            <artifactId>kie-memory-compiler</artifactId>
+            <version>${kie.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.kie.soup</groupId>
+            <artifactId>kie-soup-project-datamodel-commons</artifactId>
+            <version>${kie.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.antlr</groupId>
+            <artifactId>antlr-runtime</artifactId>
+            <version>${antlr.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mvel</groupId>
+            <artifactId>mvel2</artifactId>
+            <version>${mvel2.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RuleResultItem.java b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RuleResultItem.java
new file mode 100644
index 0000000000..1af9e85ea0
--- /dev/null
+++ b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RuleResultItem.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.drools;
+
+import org.apache.hop.metadata.api.HopMetadataProperty;
+
+import java.util.Objects;
+
+public class RuleResultItem {
+
+    @HopMetadataProperty(key = "column-name")
+    private String name;
+
+    @HopMetadataProperty(key = "column-type")
+    private String type;
+
+    public RuleResultItem() {
+    }
+
+    public RuleResultItem(String name, String type) {
+        this.name = name;
+        this.type = type;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        RuleResultItem that = (RuleResultItem) o;
+        return name.equals(that.name) && type.equals(that.type);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name, type);
+    }
+}
diff --git a/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/Rules.java b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/Rules.java
new file mode 100644
index 0000000000..bbe08fe031
--- /dev/null
+++ b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/Rules.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.drools;
+
+import java.util.Hashtable;
+import java.util.Map;
+
+public class Rules {
+
+  public static class Row {
+    private Map<String, Object> row;
+
+    private Boolean external;
+
+    public Row() {
+      this( new Hashtable<String, Object>(), false );
+    }
+
+    public Row( Map<String, Object> row ) {
+      this( row, false );
+    }
+
+    public Row( boolean external ) {
+      this( new Hashtable<String, Object>(), external );
+    }
+
+    public Row( Map<String, Object> row, boolean external ) {
+      this.row = row;
+      this.external = external;
+    }
+
+    public Map<String, Object> getColumn() {
+      return row;
+    }
+
+    public boolean isExternalSource() {
+      return external;
+    }
+
+    public void addColumn( String columnName, Object value ) {
+      row.put( columnName, value );
+    }
+  }
+
+  public static class Column {
+    private String name;
+
+    private String type;
+
+    private Object payload;
+
+    private Boolean external;
+
+    public Column() {
+      this.external = false;
+    }
+
+    public Column( Boolean external ) {
+      this.external = external;
+    }
+
+    public Column( String name, String type, Object payload  ) {
+      this();
+      this.name = name;
+      this.type = type;
+      this.payload = payload;
+    }
+
+    public Column( Boolean external, String name, String type, Object payload  ) {
+      this( external );
+      this.name = name;
+      this.type = type;
+      this.payload = payload;
+    }
+
+    public String getName() {
+      return name;
+    }
+
+    public void setName( String name ) {
+      this.name = name;
+    }
+
+    public String getType() {
+      return type;
+    }
+
+    public void setType( String type ) {
+      this.type = type;
+    }
+
+    public Object getPayload() {
+      return payload;
+    }
+
+    public void setPayload( Object payload ) {
+      this.payload = payload;
+    }
+
+    public void setExternalSource( Boolean external ) {
+      this.external = external;
+    }
+
+    public Boolean isExternalSource() {
+      return external;
+    }
+  }
+}
diff --git a/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesAccumulator.java b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesAccumulator.java
new file mode 100644
index 0000000000..702d3fe991
--- /dev/null
+++ b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesAccumulator.java
@@ -0,0 +1,112 @@
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.drools;
+
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.exception.HopTransformException;
+import org.apache.hop.pipeline.Pipeline;
+import org.apache.hop.pipeline.PipelineMeta;
+import org.apache.hop.pipeline.transform.BaseTransform;
+import org.apache.hop.pipeline.transform.TransformMeta;
+
+public class RulesAccumulator  extends BaseTransform<RulesAccumulatorMeta, RulesAccumulatorData> {
+  // private static Class<?> PKG = Rules.class; // for i18n purposes
+
+  public RulesAccumulator(
+          TransformMeta transformMeta,
+          RulesAccumulatorMeta meta,
+          RulesAccumulatorData data,
+          int copyNr,
+          PipelineMeta pipelineMeta,
+          Pipeline pipeline) {
+    super(transformMeta, meta, data, copyNr, pipelineMeta, pipeline);
+  }
+
+  public boolean init() {
+
+    if ( super.init() ) {
+      return true;
+    }
+    return false;
+  }
+
+  public boolean runtimeInit() throws HopTransformException {
+    try {
+      data.setOutputRowMeta( getInputRowMeta().clone() );
+      meta.setKeepInputFields( false );
+      meta.getFields( data.getOutputRowMeta(), getTransformName(), null, null, this, null );
+
+      data.setRuleFilePath( meta.getRuleFile() );
+      data.setRuleString( meta.getRuleDefinition() );
+
+      data.initializeRules();
+      data.initializeInput( getInputRowMeta() );
+
+      return true;
+    } catch ( Exception e ) {
+      throw new HopTransformException( e );
+    }
+  }
+
+  public void dispose( ) {
+     super.dispose();
+  }
+
+  public boolean processRow() throws HopException {
+    try {
+      Object[] r = getRow(); // get row, set busy!
+
+      if ( r == null ) { // no more input to be expected...
+
+        data.execute();
+
+        Object[] outputRow;
+
+        String[] expectedResults = meta.getExpectedResultList();
+
+        for ( Rules.Row resultRow : data.getResultRows() ) {
+          outputRow = new Object[expectedResults.length];
+          for ( String columnName : expectedResults ) {
+            outputRow[data.getOutputRowMeta().indexOfValue( columnName )] = resultRow.getColumn().get( columnName );
+          }
+          putRow( data.getOutputRowMeta(), outputRow );
+        }
+
+        data.shutdown();
+        setOutputDone();
+        return false;
+      }
+
+      if ( first ) {
+        if ( !runtimeInit() ) {
+          return false;
+        }
+
+        first = false;
+      }
+
+      // Store the row for processing
+      data.loadRow( r );
+
+      return true;
+    } catch ( Exception e ) {
+      throw new HopException( e );
+    }
+  }
+}
diff --git a/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesAccumulatorData.java b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesAccumulatorData.java
new file mode 100644
index 0000000000..12fb0c0293
--- /dev/null
+++ b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesAccumulatorData.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.drools;
+
+import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.pipeline.transform.BaseTransformData;
+import org.apache.hop.pipeline.transform.ITransformData;
+import org.kie.api.KieServices;
+import org.kie.api.builder.*;
+import org.kie.api.io.Resource;
+import org.kie.api.io.ResourceType;
+import org.kie.api.runtime.KieContainer;
+import org.kie.api.runtime.KieSession;
+import org.kie.api.runtime.ObjectFilter;
+import org.kie.internal.io.ResourceFactory;
+import org.kie.internal.utils.KieHelper;
+
+import java.io.StringReader;
+import java.util.*;
+
+public class RulesAccumulatorData extends BaseTransformData implements ITransformData {
+  private static Class<?> PKG = RulesAccumulator.class; // for i18n purposes
+
+  private IRowMeta outputRowMeta;
+  private IRowMeta inputRowMeta;
+
+  private List<Object[]> results;
+
+  private String ruleString;
+
+  private List<Rules.Row> rowList = new ArrayList<>();
+  private List<Rules.Row> resultList = new ArrayList<>();
+
+  private KieHelper kieHelper;
+
+  public String getRuleString() {
+    return ruleString;
+  }
+
+  public void setRuleString(String ruleString) {
+    this.ruleString = ruleString;
+  }
+
+  public String getRuleFilePath() {
+    return ruleFilePath;
+  }
+
+  public void setRuleFilePath(String ruleFilePath) {
+    this.ruleFilePath = ruleFilePath;
+  }
+
+  private String ruleFilePath;
+
+  public void setOutputRowMeta(IRowMeta outputRowMeta) {
+    this.outputRowMeta = outputRowMeta;
+  }
+
+  public IRowMeta getOutputRowMeta() {
+    return outputRowMeta;
+  }
+
+  public void initializeRules() {
+
+    // To ensure the plugin classloader use for dependency resolution
+    ClassLoader orig = Thread.currentThread().getContextClassLoader();
+    ClassLoader loader = getClass().getClassLoader();
+    Thread.currentThread().setContextClassLoader(loader);
+
+    Resource ruleSet = null;
+
+    if (ruleString != null) {
+      ruleSet = ResourceFactory.newReaderResource(new StringReader(ruleString));
+    } else {
+      ruleSet = ResourceFactory.newFileResource(ruleFilePath);
+    }
+
+    kieHelper = new KieHelper();
+    kieHelper.addResource(ruleSet, ResourceType.DRL);
+
+    Results results1 = kieHelper.verify();
+    if (results1.hasMessages(Message.Level.ERROR)) {
+      System.out.println(results1.getMessages());
+      throw new RuntimeException(BaseMessages.getString(PKG, "RulesData.Error.CompileDRL"));
+    }
+
+   // reset classloader back to original
+    Thread.currentThread().setContextClassLoader(orig);
+  }
+
+  public void initializeInput(IRowMeta _inputRowMeta) {
+    if (_inputRowMeta == null) {
+      BaseMessages.getString(PKG, "RulesData.InitializeColumns.InputRowMetaIsNull");
+      return;
+    }
+
+    this.inputRowMeta = _inputRowMeta;
+  }
+
+  public void loadRow(Object[] r) throws Exception {
+    // Store rows for processing
+    Map<String, Object> columns = new Hashtable<>();
+    for (String field : inputRowMeta.getFieldNames()) {
+      columns.put(field, r[inputRowMeta.indexOfValue(field)]);
+    }
+
+    rowList.add(new Rules.Row(columns, true));
+  }
+
+  public List<Rules.Row> getResultRows() {
+    return resultList;
+  }
+
+  public void execute() {
+
+    // To ensure the plugin classloader use for dependency resolution
+    ClassLoader orig = Thread.currentThread().getContextClassLoader();
+    ClassLoader loader = getClass().getClassLoader();
+    Thread.currentThread().setContextClassLoader(loader);
+
+    if (kieHelper != null) {
+      KieSession session = kieHelper.getKieContainer().newKieSession();
+      for (Rules.Row fact : rowList) {
+        session.insert(fact);
+      }
+
+      int fh = session.fireAllRules();
+
+      Collection<Object> oList =
+          (Collection<Object>)
+              session.getObjects(
+                  new ObjectFilter() {
+                    @Override
+                    public boolean accept(Object o) {
+                      if (o instanceof Rules.Row && !((Rules.Row) o).isExternalSource()) {
+                        return true;
+                      }
+                      return false;
+                    }
+                  });
+
+      for (Object o : oList) {
+        resultList.add((Rules.Row) o);
+      }
+
+      session.dispose();
+    }
+
+    Thread.currentThread().setContextClassLoader(orig);
+  }
+
+  /**
+   * Get the list of rows generated by the Rules execution
+   *
+   * @return List of rows generated
+   */
+  public List<Object[]> fetchResults() {
+    return results;
+  }
+
+  public void shutdown() {}
+}
diff --git a/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesAccumulatorDialog.java b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesAccumulatorDialog.java
new file mode 100644
index 0000000000..45942eba62
--- /dev/null
+++ b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesAccumulatorDialog.java
@@ -0,0 +1,418 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.drools;
+
+import org.apache.hop.core.Const;
+import org.apache.hop.core.Props;
+import org.apache.hop.core.row.value.ValueMetaFactory;
+import org.apache.hop.core.util.Utils;
+import org.apache.hop.core.variables.IVariables;
+import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.pipeline.PipelineMeta;
+import org.apache.hop.pipeline.transform.BaseTransformMeta;
+import org.apache.hop.pipeline.transform.ITransformDialog;
+import org.apache.hop.ui.core.dialog.BaseDialog;
+import org.apache.hop.ui.core.widget.*;
+import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.widgets.*;
+
+public class RulesAccumulatorDialog  extends BaseTransformDialog implements ITransformDialog {
+
+    private static final Class<?> PKG = Rules.class;
+
+    private RulesAccumulatorMeta input;
+
+    private Label wlRuleFilePath;
+    private Button wbBrowse;
+    private Button wbRulesInEditor;
+    private TextVar wRuleFilePath;
+    private StyledTextComp wRulesEditor;
+    private Label wlPosition;
+    private TableView wResultColumnsFields;
+
+    public RulesAccumulatorDialog(
+            Shell parent, IVariables variables, Object in, PipelineMeta pipelineMeta, String sname) {
+        super(parent, variables, (BaseTransformMeta) in, pipelineMeta, sname);
+        input = (RulesAccumulatorMeta) in;
+    }
+
+    @Override
+    public String open() {
+        Shell parent = getParent();
+
+        shell = new Shell(parent, SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MAX | SWT.MIN);
+        props.setLook(shell);
+        setShellImage(shell, input);
+
+        FormLayout formLayout = new FormLayout();
+        formLayout.marginWidth = Const.FORM_MARGIN;
+        formLayout.marginHeight = Const.FORM_MARGIN;
+
+        shell.setLayout(formLayout);
+        shell.setText(BaseMessages.getString(PKG, "RulesAccumulator.Shell.Title"));
+
+        int middle = props.getMiddlePct();
+        int margin = props.getMargin();
+
+        // THE BUTTONS
+        wOk = new Button(shell, SWT.PUSH);
+        wOk.setText(BaseMessages.getString(PKG, "System.Button.OK"));
+        wCancel = new Button(shell, SWT.PUSH);
+        wCancel.setText(BaseMessages.getString(PKG, "System.Button.Cancel"));
+        setButtonPositions(new Button[] {wOk, wCancel}, margin, null);
+
+        // TransformName line
+        wlTransformName = new Label(shell, SWT.RIGHT);
+        wlTransformName.setText(BaseMessages.getString(PKG, "RulesDialog.TransformName.Label"));
+        props.setLook(wlTransformName);
+        fdlTransformName = new FormData();
+        fdlTransformName.left = new FormAttachment(0, 0);
+        fdlTransformName.right = new FormAttachment(middle, -margin);
+        fdlTransformName.top = new FormAttachment(0, margin);
+        wlTransformName.setLayoutData(fdlTransformName);
+        wTransformName = new Text(shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+        wTransformName.setText(transformName);
+        props.setLook(wTransformName);
+        fdTransformName = new FormData();
+        fdTransformName.left = new FormAttachment(middle, 0);
+        fdTransformName.top = new FormAttachment(0, margin);
+        fdTransformName.right = new FormAttachment(100, 0);
+        wTransformName.setLayoutData(fdTransformName);
+
+        CTabFolder wTabFolder = new CTabFolder(shell, SWT.BORDER);
+        props.setLook(wTabFolder, Props.WIDGET_STYLE_TAB);
+        wTabFolder.setUnselectedCloseVisible(true);
+
+        FormData fdTabFolder = new FormData();
+        fdTabFolder.left = new FormAttachment(0, 0);
+        fdTabFolder.top = new FormAttachment(wTransformName, 20);
+        fdTabFolder.right = new FormAttachment(100, 0);
+        fdTabFolder.bottom = new FormAttachment(100, 0);
+        wTabFolder.setLayoutData(fdTabFolder);
+
+        addRulesTab(wTabFolder, margin);
+        addRulesResultsTab(wTabFolder, margin);
+
+        FormData fdAgg = new FormData();
+        fdAgg.left = new FormAttachment(0, 0);
+        fdAgg.bottom = new FormAttachment(wOk, -margin);
+
+        // Add listeners
+        wOk.addListener(SWT.Selection, e -> ok());
+        wCancel.addListener(SWT.Selection, e -> cancel());
+
+        wTabFolder.setSelection(0);
+
+        getData();
+
+        activeRuleFilenameField();
+        input.setChanged(changed);
+
+        BaseDialog.defaultShellHandling(shell, c -> ok(), c -> cancel());
+
+        return transformName;
+    }
+
+    private void addRulesTab(CTabFolder wTabFolder, int margin) {
+
+        ModifyListener lsMod =
+                e -> {
+                    // changedInDialog = true;
+                    input.setChanged();
+                };
+
+        CTabItem wRulesTab = new CTabItem(wTabFolder, SWT.NONE);
+        wRulesTab.setText(BaseMessages.getString(PKG, "RulesDialog.Tabs.RuleDefinition"));
+
+        Composite wRulesComp = new Composite(wTabFolder, SWT.NONE);
+        props.setLook(wRulesComp);
+
+        FormLayout rulesLayout = new FormLayout();
+        rulesLayout.marginWidth = 3;
+        rulesLayout.marginHeight = 3;
+        wRulesComp.setLayout(rulesLayout);
+
+        wlRuleFilePath = new Label(wRulesComp, SWT.LEFT);
+        props.setLook(wlRuleFilePath);
+        wlRuleFilePath.setText(BaseMessages.getString(PKG, "RulesDialog.RulesFile.Label"));
+        FormData fdlTransformation = new FormData();
+        fdlTransformation.left = new FormAttachment(0, 0);
+        fdlTransformation.top = new FormAttachment(0, 20);
+        fdlTransformation.right = new FormAttachment(50, 0);
+        wlRuleFilePath.setLayoutData(fdlTransformation);
+
+        wbBrowse = new Button(wRulesComp, SWT.PUSH);
+        props.setLook(wbBrowse);
+        wbBrowse.setText(BaseMessages.getString(PKG, "RulesDialog.Browse.Label"));
+        FormData fdBrowse = new FormData();
+        fdBrowse.right = new FormAttachment(100, 0);
+        fdBrowse.top = new FormAttachment(wlRuleFilePath, Const.isOSX() ? 0 : 5);
+        wbBrowse.setLayoutData(fdBrowse);
+        wbBrowse.addListener(
+                SWT.Selection,
+                e ->
+                        BaseDialog.presentFileDialog(
+                                shell,
+                                wRuleFilePath,
+                                variables,
+                                new String[] {"*"},
+                                new String[] {BaseMessages.getString(PKG, "System.FileType.AllFiles")},
+                                true));
+
+        wRuleFilePath = new TextVar(variables, wRulesComp, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+        FormData fdRuleFilePath = new FormData();
+        fdRuleFilePath.left = new FormAttachment(0, 0);
+        fdRuleFilePath.top = new FormAttachment(wlRuleFilePath, 5);
+        fdRuleFilePath.right = new FormAttachment(wbBrowse, -props.getMargin());
+        wRuleFilePath.setLayoutData(fdRuleFilePath);
+
+        wbRulesInEditor = new Button(wRulesComp, SWT.CHECK);
+        props.setLook(wbRulesInEditor);
+        wbRulesInEditor.setText(
+                BaseMessages.getString(PKG, "RulesDialog.RuleDefinition.EnableScriptEditor.Label"));
+        FormData fdPipelineNameInField = new FormData();
+        fdPipelineNameInField.left = new FormAttachment(0, 0);
+        fdPipelineNameInField.top = new FormAttachment(wRuleFilePath, margin);
+        wbRulesInEditor.setLayoutData(fdPipelineNameInField);
+        wbRulesInEditor.addSelectionListener(
+                new SelectionAdapter() {
+                    @Override
+                    public void widgetSelected(SelectionEvent e) {
+                        input.setChanged();
+                        activeRuleFilenameField();
+                    }
+                });
+
+        wRulesEditor =
+                new StyledTextComp(
+                        variables, wRulesComp, SWT.MULTI | SWT.LEFT | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
+        props.setLook(wRulesEditor, Props.WIDGET_STYLE_FIXED);
+
+        FormData fdRulesEditor = new FormData();
+        fdRulesEditor.left = new FormAttachment(0, 0);
+        fdRulesEditor.top = new FormAttachment(wbRulesInEditor, 5);
+        fdRulesEditor.right = new FormAttachment(100, -2 * margin);
+        fdRulesEditor.bottom = new FormAttachment(100, -12 * margin);
+        wRulesEditor.setLayoutData(fdRulesEditor);
+
+        wRulesEditor.addModifyListener(lsMod);
+        wRulesEditor.addModifyListener(arg0 -> setPosition());
+
+        wRulesEditor.addKeyListener(
+                new KeyAdapter() {
+                    @Override
+                    public void keyPressed(KeyEvent e) {
+                        setPosition();
+                    }
+
+                    @Override
+                    public void keyReleased(KeyEvent e) {
+                        setPosition();
+                    }
+                });
+        wRulesEditor.addFocusListener(
+                new FocusAdapter() {
+                    @Override
+                    public void focusGained(FocusEvent e) {
+                        setPosition();
+                    }
+
+                    @Override
+                    public void focusLost(FocusEvent e) {
+                        setPosition();
+                    }
+                });
+        wRulesEditor.addMouseListener(
+                new MouseAdapter() {
+                    @Override
+                    public void mouseDoubleClick(MouseEvent e) {
+                        setPosition();
+                    }
+
+                    @Override
+                    public void mouseDown(MouseEvent e) {
+                        setPosition();
+                    }
+
+                    @Override
+                    public void mouseUp(MouseEvent e) {
+                        setPosition();
+                    }
+                });
+
+        // Position label under the SQL editor
+        //
+        wlPosition = new Label(wRulesComp, SWT.NONE);
+        props.setLook(wlPosition);
+        FormData fdlPosition = new FormData();
+        fdlPosition.left = new FormAttachment(0, 0);
+        fdlPosition.top =
+                new FormAttachment(wRulesEditor, margin); // 2 times since we deal with bottom instead of
+        fdlPosition.right = new FormAttachment(100, 0);
+        // top
+        wlPosition.setLayoutData(fdlPosition);
+
+        FormData fdRulesComp = new FormData();
+        fdRulesComp.left = new FormAttachment(0, 0);
+        fdRulesComp.top = new FormAttachment(0, 0);
+        fdRulesComp.right = new FormAttachment(100, 0);
+        fdRulesComp.bottom = new FormAttachment(100, 0);
+        wRulesComp.setLayoutData(fdRulesComp);
+
+        wRulesComp.layout();
+        wRulesTab.setControl(wRulesComp);
+    }
+
+    private void activeRuleFilenameField() {
+        wlRuleFilePath.setEnabled(!wbRulesInEditor.getSelection());
+        wRuleFilePath.setEnabled(!wbRulesInEditor.getSelection());
+
+        wRulesEditor.setEnabled(wbRulesInEditor.getSelection());
+    }
+
+    private void setPosition() {
+        int lineNumber = wRulesEditor.getLineNumber();
+        int columnNumber = wRulesEditor.getColumnNumber();
+        wlPosition.setText(
+                BaseMessages.getString(
+                        PKG, "RulesDialog.Position.Label", "" + lineNumber, "" + columnNumber));
+    }
+
+    private void addRulesResultsTab(CTabFolder wTabFolder, int margin) {
+
+        CTabItem wRulesResultsTab = new CTabItem(wTabFolder, SWT.NONE);
+        wRulesResultsTab.setText(BaseMessages.getString(PKG, "RulesDialog.Tabs.ColumnSelection"));
+
+        Composite wRulesResultsComp = new Composite(wTabFolder, SWT.NONE);
+        props.setLook(wRulesResultsComp);
+
+        FormLayout rulesResultsLayout = new FormLayout();
+        rulesResultsLayout.marginWidth = 3;
+        rulesResultsLayout.marginHeight = 3;
+        wRulesResultsComp.setLayout(rulesResultsLayout);
+
+        int nrRows =
+                (input.getRuleResultColumns() != null
+                        ? input.getRuleResultColumns().size()
+                        : 1);
+
+
+        ColumnInfo[] ciResultFields =
+                new ColumnInfo[] {
+                        new ColumnInfo(
+                                BaseMessages.getString(PKG, "RulesDialog.ColumnSelection.ColumnName"),
+                                ColumnInfo.COLUMN_TYPE_TEXT,
+                                false,
+                                false),
+                        new ColumnInfo(
+                                BaseMessages.getString(PKG, "RulesDialog.ColumnSelection.ColumnType"),
+                                ColumnInfo.COLUMN_TYPE_CCOMBO,
+                                ValueMetaFactory.getValueMetaNames()),
+                };
+
+        wResultColumnsFields =
+                new TableView(
+                        variables,
+                        wRulesResultsComp,
+                        SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL,
+                        ciResultFields,
+                        nrRows,
+                        false,
+                        null,
+                        props,
+                        false);
+
+        FormData fdResultFields = new FormData();
+        fdResultFields.left = new FormAttachment(0, 0);
+        fdResultFields.top = new FormAttachment(0, 5);
+        fdResultFields.right = new FormAttachment(100, 0);
+        fdResultFields.bottom = new FormAttachment(100, -margin * 8);
+        wResultColumnsFields.setLayoutData(fdResultFields);
+        wResultColumnsFields.getTable().addListener(SWT.Resize, new ColumnsResizer(0, 25, 25));
+
+        FormData fdRulesResultsComp = new FormData();
+        fdRulesResultsComp.left = new FormAttachment(0, 0);
+        fdRulesResultsComp.top = new FormAttachment(0, 0);
+        fdRulesResultsComp.right = new FormAttachment(100, 0);
+        fdRulesResultsComp.bottom = new FormAttachment(100, 0);
+        wRulesResultsComp.setLayoutData(fdRulesResultsComp);
+
+        wRulesResultsComp.layout();
+        wRulesResultsTab.setControl(wRulesResultsComp);
+    }
+
+    private void ok() {
+        if (Utils.isEmpty(wTransformName.getText())) {
+            return;
+        }
+
+        input.setRuleFile(wRuleFilePath.getText());
+        input.setRuleDefinition(wRulesEditor.getText());
+
+        input.getRuleResultColumns().clear();
+
+        for (int i = 0; i < wResultColumnsFields.nrNonEmpty(); i++) {
+            TableItem item = wResultColumnsFields.getNonEmpty(i);
+
+            if (!Utils.isEmpty(item.getText(1))) {
+                input.getRuleResultColumns().add(new RuleResultItem(item.getText(1), item.getText(2)));
+            }
+        }
+
+        dispose();
+    }
+
+    private void cancel() {
+        transformName = null;
+        input.setChanged(false);
+        dispose();
+    }
+
+    public void getData() {
+
+        if (input.getRuleFile() != null) {
+            wlRuleFilePath.setText(input.getRuleFile());
+        }
+
+        if (input.getRuleDefinition() != null) {
+            wRulesEditor.setText(input.getRuleDefinition());
+        }
+
+        wbRulesInEditor.setSelection(input.getRuleDefinition() != null);
+
+        for (int i = 0; i<input.getRuleResultColumns().size(); i++) {
+            TableItem ti = wResultColumnsFields.table.getItem(i);
+            RuleResultItem ri = input.getRuleResultColumns().get(i);
+            ti.setText(1, ri.getName());
+            ti.setText(2, ri.getType());
+        }
+
+        wResultColumnsFields.optWidth(true);
+
+        wTransformName.selectAll();
+        wTransformName.setFocus();
+    }
+
+}
diff --git a/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesAccumulatorMeta.java b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesAccumulatorMeta.java
new file mode 100644
index 0000000000..62f3080135
--- /dev/null
+++ b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesAccumulatorMeta.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.drools;
+
+import org.apache.hop.core.annotations.Transform;
+import org.apache.hop.core.exception.HopPluginException;
+import org.apache.hop.core.exception.HopTransformException;
+import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.row.IValueMeta;
+import org.apache.hop.core.row.value.ValueMetaFactory;
+import org.apache.hop.core.variables.IVariables;
+import org.apache.hop.metadata.api.HopMetadataProperty;
+import org.apache.hop.metadata.api.IHopMetadataProvider;
+import org.apache.hop.pipeline.transform.BaseTransformMeta;
+import org.apache.hop.pipeline.transform.TransformMeta;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Transform(
+    id = "RuleAccumulator",
+    image = "rules_acc.svg",
+    name = "i18n::RulesAccumulator.Name",
+    description = "i18n::RulesAccumulator.Description",
+    categoryDescription = "i18n::Rules.Category",
+    keywords = "i18n::RulesAccumulator.keyword",
+    documentationUrl = "/pipeline/transforms/rulesaccumulator.html")
+public class RulesAccumulatorMeta
+    extends BaseTransformMeta<RulesAccumulator, RulesAccumulatorData> {
+  private static Class<?> PKG = Rules.class; // for i18n purposes
+
+  @HopMetadataProperty(groupKey = "fields", key = "field")
+  private List<RuleResultItem> ruleResultColumns = new ArrayList<>();
+
+  @HopMetadataProperty(key="rule-file")
+  private String ruleFile;
+
+  @HopMetadataProperty(key="rule-definition")
+  private String ruleDefinition;
+
+  private boolean keepInputFields = true;
+
+  public List<RuleResultItem> getRuleResultColumns() {
+    return ruleResultColumns;
+  }
+
+  public void setRuleResultColumns(List<RuleResultItem> ruleResultColumns) {
+    this.ruleResultColumns = ruleResultColumns;
+  }
+
+  public void setRuleFile(String ruleFile) {
+    this.ruleFile = ruleFile;
+  }
+
+  public String getRuleFile() {
+    return ruleFile;
+  }
+
+  public void setRuleDefinition(String ruleDefinition) {
+    this.ruleDefinition = ruleDefinition;
+  }
+
+  public String getRuleDefinition() {
+    return ruleDefinition;
+  }
+
+  public boolean isKeepInputFields() {
+    return keepInputFields;
+  }
+
+  public void setKeepInputFields(boolean keepInputFields) {
+    this.keepInputFields = keepInputFields;
+  }
+
+  @Override
+  public void setDefault() {}
+
+  @Override
+  public void getFields(
+          IRowMeta inputRowMeta,
+          String name,
+          IRowMeta[] info,
+          TransformMeta nextTransform,
+          IVariables variables,
+          IHopMetadataProvider metadataProvider)
+          throws HopTransformException {
+
+    if (!keepInputFields) {
+      inputRowMeta.clear();
+    }
+    try {
+      if (ruleResultColumns != null) {
+        for (int i = 0; i < ruleResultColumns.size(); i++) {
+          int type = ValueMetaFactory.getIdForValueMeta( ruleResultColumns.get(i).getType() ) ;
+          IValueMeta vm = ValueMetaFactory.createValueMeta( ruleResultColumns.get(i).getName(), type );
+
+          vm.setOrigin(name);
+          inputRowMeta.addValueMeta(vm);
+        }
+      }
+    } catch (HopPluginException e) {
+      throw new HopTransformException(
+              "Unable to get rule result columns");
+    }
+  }
+
+  public String[] getExpectedResultList() {
+    String[] result = new String[ruleResultColumns.size()];
+
+    for (int i = 0; i < ruleResultColumns.size(); i++) {
+      result[i] = ruleResultColumns.get(i).getName();
+    }
+
+    return result;
+  }
+}
diff --git a/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesExecutor.java b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesExecutor.java
new file mode 100644
index 0000000000..a50c38338b
--- /dev/null
+++ b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesExecutor.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.drools;
+
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.exception.HopTransformException;
+import org.apache.hop.pipeline.Pipeline;
+import org.apache.hop.pipeline.PipelineMeta;
+import org.apache.hop.pipeline.transform.BaseTransform;
+import org.apache.hop.pipeline.transform.TransformMeta;
+
+import java.util.Arrays;
+
+
+public class RulesExecutor extends BaseTransform<RulesExecutorMeta, RulesExecutorData> {
+  // private static Class<?> PKG = Rules.class; // for i18n purposes
+
+  public RulesExecutor(
+      TransformMeta transformMeta,
+      RulesExecutorMeta meta,
+      RulesExecutorData data,
+      int copyNr,
+      PipelineMeta pipelineMeta,
+      Pipeline pipeline) {
+    super(transformMeta, meta, data, copyNr, pipelineMeta, pipeline);
+  }
+
+  public boolean init() {
+
+    if (super.init()) {
+      return true;
+    }
+    return false;
+  }
+
+  public boolean runtimeInit() throws HopTransformException {
+    data.setOutputRowMeta(getInputRowMeta().clone());
+    meta.getFields(data.getOutputRowMeta(), getTransformName(), null, null, this, null);
+
+    data.setRuleFilePath(meta.getRuleFile());
+    data.setRuleString(meta.getRuleDefinition());
+
+    data.initializeRules();
+    data.initializeColumns(getInputRowMeta());
+
+    return true;
+  }
+
+  public void dispose() {
+    super.dispose();
+  }
+
+  public boolean processRow() throws HopException {
+
+    Object[] r = getRow(); // get row, set busy!
+    if (r == null) { // no more input to be expected...
+
+      data.shutdown();
+      setOutputDone();
+      return false;
+    }
+
+    if (first) {
+      if (!runtimeInit()) {
+        return false;
+      }
+
+      first = false;
+    }
+
+    // Load the column objects
+    data.loadRow(r);
+
+    data.execute();
+
+    Object[] outputRow;
+    int beginOutputRowFill = 0;
+
+    String[] expectedResults = meta.getExpectedResultList();
+
+    if (meta.isKeepInputFields()) {
+      int inputRowSize = getInputRowMeta().size();
+      outputRow = Arrays.copyOf(r, inputRowSize + expectedResults.length);
+      beginOutputRowFill = inputRowSize;
+    } else {
+      outputRow = new Object[expectedResults.length];
+    }
+
+    Rules.Column result = null;
+    for (int i = 0; i < expectedResults.length; i++) {
+      result = (Rules.Column) data.fetchResult(expectedResults[i]);
+      outputRow[i + beginOutputRowFill] = result == null ? null : result.getPayload();
+    }
+
+    putRow(data.getOutputRowMeta(), outputRow);
+
+    return true;
+  }
+}
diff --git a/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesExecutorData.java b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesExecutorData.java
new file mode 100644
index 0000000000..8600b473c8
--- /dev/null
+++ b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesExecutorData.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.drools;
+
+import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.row.IValueMeta;
+import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.pipeline.transform.BaseTransformData;
+import org.apache.hop.pipeline.transform.ITransformData;
+import org.kie.api.builder.Message;
+import org.kie.api.builder.Results;
+import org.kie.api.io.Resource;
+import org.kie.api.io.ResourceType;
+import org.kie.api.runtime.KieSession;
+import org.kie.api.runtime.ObjectFilter;
+import org.kie.internal.io.ResourceFactory;
+import org.kie.internal.utils.KieHelper;
+
+import java.io.StringReader;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class RulesExecutorData extends BaseTransformData implements ITransformData {
+  private static Class<?> PKG = RulesExecutor.class; // for i18n purposes
+
+  private IRowMeta outputRowMeta;
+
+  private KieHelper kieHelper;
+
+  private Rules.Column[] columnList;
+
+  private Map<String, Rules.Column> resultMap = new HashMap<>();
+
+  private String ruleString;
+
+  public String getRuleString() {
+    return ruleString;
+  }
+
+  public void setRuleString(String ruleString) {
+    this.ruleString = ruleString;
+  }
+
+  public String getRuleFilePath() {
+    return ruleFilePath;
+  }
+
+  public void setRuleFilePath(String ruleFilePath) {
+    this.ruleFilePath = ruleFilePath;
+  }
+
+  private String ruleFilePath;
+
+  public void setOutputRowMeta(IRowMeta outputRowMeta) {
+    this.outputRowMeta = outputRowMeta;
+  }
+
+  public IRowMeta getOutputRowMeta() {
+    return outputRowMeta;
+  }
+
+
+  public void initializeRules() {
+
+    // To ensure the plugin classloader use for dependency resolution
+    ClassLoader orig = Thread.currentThread().getContextClassLoader();
+    ClassLoader loader = getClass().getClassLoader();
+    Thread.currentThread().setContextClassLoader(loader);
+
+    Resource ruleSet = null;
+
+    if (ruleString != null) {
+      ruleSet = ResourceFactory.newReaderResource(new StringReader(ruleString));
+    } else {
+      ruleSet = ResourceFactory.newFileResource(ruleFilePath);
+    }
+
+    kieHelper = new KieHelper();
+    kieHelper.addResource(ruleSet, ResourceType.DRL);
+
+    Results results1 = kieHelper.verify();
+    if (results1.hasMessages(Message.Level.ERROR)) {
+      System.out.println(results1.getMessages());
+      throw new RuntimeException(BaseMessages.getString(PKG, "RulesData.Error.CompileDRL"));
+    }
+
+    // reset classloader back to original
+    Thread.currentThread().setContextClassLoader(orig);
+  }
+
+  public void initializeColumns(IRowMeta inputRowMeta) {
+    if (inputRowMeta == null) {
+      BaseMessages.getString(PKG, "RulesData.InitializeColumns.InputRowMetaIsNull");
+      return;
+    }
+
+    // Create objects for insertion into the rules engine
+    List<IValueMeta> columns = inputRowMeta.getValueMetaList();
+
+    // This array must 1-1 match the row[] fetched by getRow()
+    columnList = new Rules.Column[columns.size()];
+
+    for (int i = 0; i < columns.size(); i++) {
+      IValueMeta column = columns.get(i);
+
+      Rules.Column c = new Rules.Column(true);
+      c.setName(column.getName());
+      c.setType(column.getTypeDesc());
+      c.setPayload(null);
+
+      columnList[i] = c;
+    }
+  }
+
+  public void loadRow(Object[] r) {
+    for (int i = 0; i < columnList.length; i++) {
+      columnList[i].setPayload(r[i]);
+    }
+    resultMap.clear();
+  }
+
+  public void execute() {
+    // To ensure the plugin classloader use for dependency resolution
+    ClassLoader orig = Thread.currentThread().getContextClassLoader();
+    ClassLoader loader = getClass().getClassLoader();
+    Thread.currentThread().setContextClassLoader(loader);
+
+    if (kieHelper != null) {
+      KieSession session = kieHelper.getKieContainer().newKieSession();
+
+      for (int i = 0; i < columnList.length; i++) {
+        session.insert(columnList[i]);
+      }
+
+      session.fireAllRules();
+
+      Collection<Object> oList = fetchColumns(session);
+      for (Object o : oList) {
+        resultMap.put(((Rules.Column) o).getName(), (Rules.Column) o);
+      }
+
+      session.dispose();
+    }
+
+    Thread.currentThread().setContextClassLoader(orig);
+  }
+
+  protected Collection<Object> fetchColumns(KieSession session) {
+
+    Collection<Object> oList =
+        (Collection<Object>)
+            session.getObjects(
+                new ObjectFilter() {
+                  @Override
+                  public boolean accept(Object o) {
+                    if (o instanceof Rules.Column && !((Rules.Column) o).isExternalSource()) {
+                      return true;
+                    }
+                    return false;
+                  }
+                });
+
+    return oList;
+  }
+
+  /**
+   * @param columnName Column.payload associated with the result, or null if not found
+   * @return
+   */
+  public Object fetchResult(String columnName) {
+    return resultMap.get(columnName);
+  }
+
+  public void shutdown() {}
+}
diff --git a/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesExecutorDialog.java b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesExecutorDialog.java
new file mode 100644
index 0000000000..4d6b8daf51
--- /dev/null
+++ b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesExecutorDialog.java
@@ -0,0 +1,415 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.drools;
+
+import org.apache.hop.core.Const;
+import org.apache.hop.core.Props;
+import org.apache.hop.core.row.value.*;
+import org.apache.hop.core.util.Utils;
+import org.apache.hop.core.variables.IVariables;
+import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.pipeline.PipelineMeta;
+import org.apache.hop.pipeline.transform.BaseTransformMeta;
+import org.apache.hop.pipeline.transform.ITransformDialog;
+import org.apache.hop.ui.core.dialog.BaseDialog;
+import org.apache.hop.ui.core.widget.*;
+import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.layout.FormAttachment;
+import org.eclipse.swt.layout.FormData;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.widgets.*;
+
+public class RulesExecutorDialog extends BaseTransformDialog implements ITransformDialog {
+
+  private static final Class<?> PKG = Rules.class;
+
+  private RulesExecutorMeta input;
+
+  private Label wlRuleFilePath;
+  private Button wbBrowse;
+  private Button wbRulesInEditor;
+  private TextVar wRuleFilePath;
+  private StyledTextComp wRulesEditor;
+  private Label wlPosition;
+  private TableView wResultColumnsFields;
+
+  public RulesExecutorDialog(
+      Shell parent, IVariables variables, Object in, PipelineMeta pipelineMeta, String sname) {
+    super(parent, variables, (BaseTransformMeta) in, pipelineMeta, sname);
+    input = (RulesExecutorMeta) in;
+  }
+
+  @Override
+  public String open() {
+    Shell parent = getParent();
+
+    shell = new Shell(parent, SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MAX | SWT.MIN);
+    props.setLook(shell);
+    setShellImage(shell, input);
+
+    FormLayout formLayout = new FormLayout();
+    formLayout.marginWidth = Const.FORM_MARGIN;
+    formLayout.marginHeight = Const.FORM_MARGIN;
+
+    shell.setLayout(formLayout);
+    shell.setText(BaseMessages.getString(PKG, "RulesExecutor.Shell.Title"));
+
+    int middle = props.getMiddlePct();
+    int margin = props.getMargin();
+
+    // THE BUTTONS
+    wOk = new Button(shell, SWT.PUSH);
+    wOk.setText(BaseMessages.getString(PKG, "System.Button.OK"));
+    wCancel = new Button(shell, SWT.PUSH);
+    wCancel.setText(BaseMessages.getString(PKG, "System.Button.Cancel"));
+    setButtonPositions(new Button[] {wOk, wCancel}, margin, null);
+
+    // TransformName line
+    wlTransformName = new Label(shell, SWT.RIGHT);
+    wlTransformName.setText(BaseMessages.getString(PKG, "RulesDialog.TransformName.Label"));
+    props.setLook(wlTransformName);
+    fdlTransformName = new FormData();
+    fdlTransformName.left = new FormAttachment(0, 0);
+    fdlTransformName.right = new FormAttachment(middle, -margin);
+    fdlTransformName.top = new FormAttachment(0, margin);
+    wlTransformName.setLayoutData(fdlTransformName);
+    wTransformName = new Text(shell, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+    wTransformName.setText(transformName);
+    props.setLook(wTransformName);
+    fdTransformName = new FormData();
+    fdTransformName.left = new FormAttachment(middle, 0);
+    fdTransformName.top = new FormAttachment(0, margin);
+    fdTransformName.right = new FormAttachment(100, 0);
+    wTransformName.setLayoutData(fdTransformName);
+
+    CTabFolder wTabFolder = new CTabFolder(shell, SWT.BORDER);
+    props.setLook(wTabFolder, Props.WIDGET_STYLE_TAB);
+    wTabFolder.setUnselectedCloseVisible(true);
+
+    FormData fdTabFolder = new FormData();
+    fdTabFolder.left = new FormAttachment(0, 0);
+    fdTabFolder.top = new FormAttachment(wTransformName, 20);
+    fdTabFolder.right = new FormAttachment(100, 0);
+    fdTabFolder.bottom = new FormAttachment(100, 0);
+    wTabFolder.setLayoutData(fdTabFolder);
+
+    addRulesTab(wTabFolder, margin);
+    addRulesResultsTab(wTabFolder, margin);
+
+    FormData fdAgg = new FormData();
+    fdAgg.left = new FormAttachment(0, 0);
+    fdAgg.bottom = new FormAttachment(wOk, -margin);
+
+    // Add listeners
+    wOk.addListener(SWT.Selection, e -> ok());
+    wCancel.addListener(SWT.Selection, e -> cancel());
+
+    wTabFolder.setSelection(0);
+
+    getData();
+
+    activeRuleFilenameField();
+    input.setChanged(changed);
+
+    BaseDialog.defaultShellHandling(shell, c -> ok(), c -> cancel());
+
+    return transformName;
+  }
+
+  private void activeRuleFilenameField() {
+    wlRuleFilePath.setEnabled(!wbRulesInEditor.getSelection());
+    wRuleFilePath.setEnabled(!wbRulesInEditor.getSelection());
+
+    wRulesEditor.setEnabled(wbRulesInEditor.getSelection());
+  }
+
+  private void addRulesTab(CTabFolder wTabFolder, int margin) {
+
+    ModifyListener lsMod =
+        e -> {
+          // changedInDialog = true;
+          input.setChanged();
+        };
+
+    CTabItem wRulesTab = new CTabItem(wTabFolder, SWT.NONE);
+    wRulesTab.setText(BaseMessages.getString(PKG, "RulesDialog.Tabs.RuleDefinition"));
+
+    Composite wRulesComp = new Composite(wTabFolder, SWT.NONE);
+    props.setLook(wRulesComp);
+
+    FormLayout rulesLayout = new FormLayout();
+    rulesLayout.marginWidth = 3;
+    rulesLayout.marginHeight = 3;
+    wRulesComp.setLayout(rulesLayout);
+
+    wlRuleFilePath = new Label(wRulesComp, SWT.LEFT);
+    props.setLook(wlRuleFilePath);
+    wlRuleFilePath.setText(BaseMessages.getString(PKG, "RulesDialog.RulesFile.Label"));
+    FormData fdlTransformation = new FormData();
+    fdlTransformation.left = new FormAttachment(0, 0);
+    fdlTransformation.top = new FormAttachment(0, 20);
+    fdlTransformation.right = new FormAttachment(50, 0);
+    wlRuleFilePath.setLayoutData(fdlTransformation);
+
+    wbBrowse = new Button(wRulesComp, SWT.PUSH);
+    props.setLook(wbBrowse);
+    wbBrowse.setText(BaseMessages.getString(PKG, "RulesDialog.Browse.Label"));
+    FormData fdBrowse = new FormData();
+    fdBrowse.right = new FormAttachment(100, 0);
+    fdBrowse.top = new FormAttachment(wlRuleFilePath, Const.isOSX() ? 0 : 5);
+    wbBrowse.setLayoutData(fdBrowse);
+    wbBrowse.addListener(
+        SWT.Selection,
+        e ->
+            BaseDialog.presentFileDialog(
+                shell,
+                wRuleFilePath,
+                variables,
+                new String[] {"*"},
+                new String[] {BaseMessages.getString(PKG, "System.FileType.AllFiles")},
+                true));
+
+    wRuleFilePath = new TextVar(variables, wRulesComp, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
+    FormData fdRuleFilePath = new FormData();
+    fdRuleFilePath.left = new FormAttachment(0, 0);
+    fdRuleFilePath.top = new FormAttachment(wlRuleFilePath, 5);
+    fdRuleFilePath.right = new FormAttachment(wbBrowse, -props.getMargin());
+    wRuleFilePath.setLayoutData(fdRuleFilePath);
+
+    wbRulesInEditor = new Button(wRulesComp, SWT.CHECK);
+    props.setLook(wbRulesInEditor);
+    wbRulesInEditor.setText(
+        BaseMessages.getString(PKG, "RulesDialog.RuleDefinition.EnableScriptEditor.Label"));
+    FormData fdPipelineNameInField = new FormData();
+    fdPipelineNameInField.left = new FormAttachment(0, 0);
+    fdPipelineNameInField.top = new FormAttachment(wRuleFilePath, margin);
+    wbRulesInEditor.setLayoutData(fdPipelineNameInField);
+    wbRulesInEditor.addSelectionListener(
+            new SelectionAdapter() {
+              @Override
+              public void widgetSelected(SelectionEvent e) {
+                input.setChanged();
+                activeRuleFilenameField();
+              }
+            });
+
+    wRulesEditor =
+        new StyledTextComp(
+            variables, wRulesComp, SWT.MULTI | SWT.LEFT | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
+    props.setLook(wRulesEditor, Props.WIDGET_STYLE_FIXED);
+
+    FormData fdRulesEditor = new FormData();
+    fdRulesEditor.left = new FormAttachment(0, 0);
+    fdRulesEditor.top = new FormAttachment(wbRulesInEditor, 5);
+    fdRulesEditor.right = new FormAttachment(100, -2 * margin);
+    fdRulesEditor.bottom = new FormAttachment(100, -12 * margin);
+    wRulesEditor.setLayoutData(fdRulesEditor);
+
+    wRulesEditor.addModifyListener(lsMod);
+    wRulesEditor.addModifyListener(arg0 -> setPosition());
+
+    wRulesEditor.addKeyListener(
+        new KeyAdapter() {
+          @Override
+          public void keyPressed(KeyEvent e) {
+            setPosition();
+          }
+
+          @Override
+          public void keyReleased(KeyEvent e) {
+            setPosition();
+          }
+        });
+    wRulesEditor.addFocusListener(
+        new FocusAdapter() {
+          @Override
+          public void focusGained(FocusEvent e) {
+            setPosition();
+          }
+
+          @Override
+          public void focusLost(FocusEvent e) {
+            setPosition();
+          }
+        });
+    wRulesEditor.addMouseListener(
+        new MouseAdapter() {
+          @Override
+          public void mouseDoubleClick(MouseEvent e) {
+            setPosition();
+          }
+
+          @Override
+          public void mouseDown(MouseEvent e) {
+            setPosition();
+          }
+
+          @Override
+          public void mouseUp(MouseEvent e) {
+            setPosition();
+          }
+        });
+
+    // Position label under the SQL editor
+    //
+    wlPosition = new Label(wRulesComp, SWT.NONE);
+    props.setLook(wlPosition);
+    FormData fdlPosition = new FormData();
+    fdlPosition.left = new FormAttachment(0, 0);
+    fdlPosition.top =
+        new FormAttachment(wRulesEditor, margin); // 2 times since we deal with bottom instead of
+    fdlPosition.right = new FormAttachment(100, 0);
+    // top
+    wlPosition.setLayoutData(fdlPosition);
+
+    FormData fdRulesComp = new FormData();
+    fdRulesComp.left = new FormAttachment(0, 0);
+    fdRulesComp.top = new FormAttachment(0, 0);
+    fdRulesComp.right = new FormAttachment(100, 0);
+    fdRulesComp.bottom = new FormAttachment(100, 0);
+    wRulesComp.setLayoutData(fdRulesComp);
+
+    wRulesComp.layout();
+    wRulesTab.setControl(wRulesComp);
+  }
+
+  private void setPosition() {
+    int lineNumber = wRulesEditor.getLineNumber();
+    int columnNumber = wRulesEditor.getColumnNumber();
+    wlPosition.setText(
+        BaseMessages.getString(
+            PKG, "RulesDialog.Position.Label", "" + lineNumber, "" + columnNumber));
+  }
+
+  private void addRulesResultsTab(CTabFolder wTabFolder, int margin) {
+
+    CTabItem wRulesResultsTab = new CTabItem(wTabFolder, SWT.NONE);
+    wRulesResultsTab.setText(BaseMessages.getString(PKG, "RulesDialog.Tabs.ColumnSelection"));
+
+    Composite wRulesResultsComp = new Composite(wTabFolder, SWT.NONE);
+    props.setLook(wRulesResultsComp);
+
+    FormLayout rulesResultsLayout = new FormLayout();
+    rulesResultsLayout.marginWidth = 3;
+    rulesResultsLayout.marginHeight = 3;
+    wRulesResultsComp.setLayout(rulesResultsLayout);
+
+    int nrRows = (input.getRuleResultColumns() != null ? input.getRuleResultColumns().size() : 1);
+
+    ColumnInfo[] ciResultFields =
+        new ColumnInfo[] {
+          new ColumnInfo(
+              BaseMessages.getString(PKG, "RulesDialog.ColumnSelection.ColumnName"),
+              ColumnInfo.COLUMN_TYPE_TEXT,
+              false,
+              false),
+          new ColumnInfo(
+              BaseMessages.getString(PKG, "RulesDialog.ColumnSelection.ColumnType"),
+              ColumnInfo.COLUMN_TYPE_CCOMBO,
+              ValueMetaFactory.getValueMetaNames()),
+        };
+
+    wResultColumnsFields =
+        new TableView(
+            variables,
+            wRulesResultsComp,
+            SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL,
+            ciResultFields,
+            nrRows,
+            false,
+            null,
+            props,
+            false);
+
+    FormData fdResultFields = new FormData();
+    fdResultFields.left = new FormAttachment(0, 0);
+    fdResultFields.top = new FormAttachment(0, 5);
+    fdResultFields.right = new FormAttachment(100, 0);
+    fdResultFields.bottom = new FormAttachment(100, -margin * 8);
+    wResultColumnsFields.setLayoutData(fdResultFields);
+    wResultColumnsFields.getTable().addListener(SWT.Resize, new ColumnsResizer(0, 25, 25));
+
+    FormData fdRulesResultsComp = new FormData();
+    fdRulesResultsComp.left = new FormAttachment(0, 0);
+    fdRulesResultsComp.top = new FormAttachment(0, 0);
+    fdRulesResultsComp.right = new FormAttachment(100, 0);
+    fdRulesResultsComp.bottom = new FormAttachment(100, 0);
+    wRulesResultsComp.setLayoutData(fdRulesResultsComp);
+
+    wRulesResultsComp.layout();
+    wRulesResultsTab.setControl(wRulesResultsComp);
+
+    getData();
+  }
+
+  private void ok() {
+    if (Utils.isEmpty(wTransformName.getText())) {
+      return;
+    }
+
+    input.setRuleFile(wRuleFilePath.getText());
+    input.setRuleDefinition(wRulesEditor.getText());
+
+    input.getRuleResultColumns().clear();
+
+    for (int i = 0; i < wResultColumnsFields.nrNonEmpty(); i++) {
+      TableItem item = wResultColumnsFields.getNonEmpty(i);
+
+      if (!Utils.isEmpty(item.getText(1))) {
+        input.getRuleResultColumns().add(new RuleResultItem(item.getText(1), item.getText(2)));
+      }
+    }
+
+    dispose();
+  }
+
+  private void cancel() {
+    transformName = null;
+    input.setChanged(false);
+    dispose();
+  }
+
+  public void getData() {
+
+    if (input.getRuleFile() != null) {
+      wlRuleFilePath.setText(input.getRuleFile());
+    }
+
+    if (input.getRuleDefinition() != null) {
+      wRulesEditor.setText(input.getRuleDefinition());
+    }
+
+    wbRulesInEditor.setSelection(input.getRuleDefinition() != null);
+
+    for (int i = 0; i<input.getRuleResultColumns().size(); i++) {
+      TableItem ti = wResultColumnsFields.table.getItem(i);
+      RuleResultItem ri = input.getRuleResultColumns().get(i);
+      ti.setText(1, ri.getName());
+      ti.setText(2, ri.getType());
+    }
+
+    wResultColumnsFields.optWidth(true);
+
+    wTransformName.selectAll();
+    wTransformName.setFocus();
+  }
+}
diff --git a/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesExecutorMeta.java b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesExecutorMeta.java
new file mode 100644
index 0000000000..96a3b5b305
--- /dev/null
+++ b/plugins/transforms/drools/src/main/java/org/apache/hop/pipeline/transforms/drools/RulesExecutorMeta.java
@@ -0,0 +1,138 @@
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.pipeline.transforms.drools;
+
+import org.apache.hop.core.annotations.Transform;
+import org.apache.hop.core.exception.HopPluginException;
+import org.apache.hop.core.exception.HopTransformException;
+import org.apache.hop.core.row.IRowMeta;
+import org.apache.hop.core.row.IValueMeta;
+import org.apache.hop.core.row.value.ValueMetaFactory;
+import org.apache.hop.core.variables.IVariables;
+import org.apache.hop.metadata.api.HopMetadataProperty;
+import org.apache.hop.metadata.api.IHopMetadataProvider;
+import org.apache.hop.pipeline.transform.BaseTransformMeta;
+import org.apache.hop.pipeline.transform.TransformMeta;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+@Transform(
+        id = "RuleExecutor",
+        image = "rules_exec.svg",
+        name = "i18n::RulesExecutor.Name",
+        description = "i18n::RulesExecutor.Description",
+        categoryDescription = "i18n::Rules.Category",
+        keywords = "i18n::RulesExecutor.keyword",
+        documentationUrl = "/pipeline/transforms/rulesexecutor.html")
+public class RulesExecutorMeta
+        extends BaseTransformMeta<RulesExecutor, RulesExecutorData> {
+  private static Class<?> PKG = Rules.class; // for i18n purposes
+
+  // Contain storage keys in single location to cut down on save/load bugs
+
+  @HopMetadataProperty(groupKey = "fields", key = "field")
+  private List<RuleResultItem> ruleResultColumns = new ArrayList<>();
+
+  @HopMetadataProperty(key="rule-file")
+  private String ruleFile;
+
+  @HopMetadataProperty(key="rule-definition")
+  private String ruleDefinition;
+
+  private boolean keepInputFields = true;
+
+  public List<RuleResultItem> getRuleResultColumns() {
+    return ruleResultColumns;
+  }
+
+  public void setRuleResultColumns( List<RuleResultItem> ruleResultColumns ) {
+    this.ruleResultColumns = ruleResultColumns;
+  }
+
+  public void setRuleFile( String ruleFile ) {
+    this.ruleFile = ruleFile;
+  }
+
+  public String getRuleFile() {
+    return ruleFile;
+  }
+
+  public void setRuleDefinition( String ruleDefinition ) {
+    this.ruleDefinition = ruleDefinition;
+  }
+
+  public String getRuleDefinition() {
+    return ruleDefinition;
+  }
+
+  public boolean isKeepInputFields() {
+    return keepInputFields;
+  }
+
+  public void setKeepInputFields( boolean keepInputFields ) {
+    this.keepInputFields = keepInputFields;
+  }
+
+
+  @Override
+  public void setDefault() {
+  }
+
+  @Override
+  public void getFields(
+          IRowMeta inputRowMeta,
+          String name,
+          IRowMeta[] info,
+          TransformMeta nextTransform,
+          IVariables variables,
+          IHopMetadataProvider metadataProvider)
+          throws HopTransformException {
+
+    if (!keepInputFields) {
+      inputRowMeta.clear();
+    }
+    try {
+    if (ruleResultColumns != null) {
+      for (int i = 0; i < ruleResultColumns.size(); i++) {
+        int type = ValueMetaFactory.getIdForValueMeta( ruleResultColumns.get(i).getType() ) ;
+        IValueMeta vm = ValueMetaFactory.createValueMeta( ruleResultColumns.get(i).getName(), type );
+
+        vm.setOrigin(name);
+        inputRowMeta.addValueMeta(vm);
+      }
+    }
+    } catch (HopPluginException e) {
+      throw new HopTransformException(
+              "Unable to get rule result columns");
+    }
+  }
+
+  public String[] getExpectedResultList() {
+    String[] result = new String[ruleResultColumns.size()];
+
+    for ( int i = 0; i < ruleResultColumns.size(); i++ ) {
+      result[i] = ruleResultColumns.get( i ).getName();
+    }
+
+    return result;
+  }
+
+}
diff --git a/plugins/transforms/drools/src/main/resources/org/apache/hop/pipeline/transforms/drools/messages/messages_en_US.properties b/plugins/transforms/drools/src/main/resources/org/apache/hop/pipeline/transforms/drools/messages/messages_en_US.properties
new file mode 100644
index 0000000000..ceda33d74e
--- /dev/null
+++ b/plugins/transforms/drools/src/main/resources/org/apache/hop/pipeline/transforms/drools/messages/messages_en_US.properties
@@ -0,0 +1,45 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+RulesExecutor.Name=Rules executor
+RulesAccumulator.Name=Rules accumulator
+RulesExecutor.Shell.Title=Rules executor
+RulesAccumulator.Shell.Title=Rules accumulator
+RulesExecutor.Description=Rules executor transform
+RulesAccumulator.Description=Rules accumulator transform
+Rules.Category=Scripting
+RulesDialog.RuleDefinition.EnableScriptEditor.Label=Rules script to execute
+RulesDialog.TransformName.Label=Transform name
+RulesDialog.Browse.Label=Browse...
+RulesDialog.RulesFile.Label=Rules filename\:
+RulesDialog.Tabs.RuleDefinition=Rules
+RulesDialog.Tabs.ColumnSelection=Rules results
+RulesDialog.Position.Label=Line {0} Column {1}
+RulesDialog.ColumnSelection.ColumnName=Result column name
+RulesDialog.ColumnSelection.ColumnType=Result column type
+RulesAccumulator.keyword=Drools
+RulesExecutor.keyword=Drools
+
+
+
+
+
+RulesData.Error.CompileDRL=Failed to compile DRL
+RulesData.InitializeColumns.InputRowMetaIsNull=No input rows found
+RulesDialog.RuleDefinition.RuleDefinition=Rules definition
+
+
diff --git a/plugins/transforms/drools/src/main/resources/org/apache/hop/pipeline/transforms/drools/messages/messages_it_IT.properties b/plugins/transforms/drools/src/main/resources/org/apache/hop/pipeline/transforms/drools/messages/messages_it_IT.properties
new file mode 100644
index 0000000000..3b879c9b27
--- /dev/null
+++ b/plugins/transforms/drools/src/main/resources/org/apache/hop/pipeline/transforms/drools/messages/messages_it_IT.properties
@@ -0,0 +1,45 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+RulesExecutor.Name=Rules executor
+RulesAccumulator.Name=Rules accumulator
+RulesExecutor.Shell.Title=Rules executor
+RulesAccumulator.Shell.Title=Rules accumulator
+RulesExecutor.Description=Rules executor transform
+RulesAccumulator.Description=Rules accumulator transform
+Rules.Category=Scripting
+RulesDialog.RuleDefinition.EnableScriptEditor.Label=Script di regole da eseguire
+RulesDialog.TransformName.Label=Transform name
+RulesDialog.Browse.Label=Browse...
+RulesDialog.RulesFile.Label=Nome file di regole\:
+RulesDialog.Tabs.RuleDefinition=Regole
+RulesDialog.Tabs.ColumnSelection=Risultat delle regole
+RulesDialog.Position.Label=Linea {0} Colonna {1}
+RulesDialog.ColumnSelection.ColumnName=Nome colonna risultato
+RulesDialog.ColumnSelection.ColumnType=Tipo colonna risultato
+RulesAccumulator.keyword=Drools
+RulesExecutor.keyword=Drools
+
+
+
+
+
+RulesData.Error.CompileDRL=Failed to compile DRL
+RulesData.InitializeColumns.InputRowMetaIsNull=No input rows found
+RulesDialog.RuleDefinition.RuleDefinition=Rules definition
+
+
diff --git a/plugins/transforms/drools/src/main/resources/rules_acc.svg b/plugins/transforms/drools/src/main/resources/rules_acc.svg
new file mode 100644
index 0000000000..44cf95140f
--- /dev/null
+++ b/plugins/transforms/drools/src/main/resources/rules_acc.svg
@@ -0,0 +1,49 @@
+<!--
+  - Licensed to the Apache Software Foundation (ASF) under one or more
+  - contributor license agreements.  See the NOTICE file distributed with
+  - this work for additional information regarding copyright ownership.
+  - The ASF licenses this file to You under the Apache License, Version 2.0
+  - (the "License"); you may not use this file except in compliance with
+  - the License.  You may obtain a copy of the License at
+  -
+  -       http://www.apache.org/licenses/LICENSE-2.0
+  -
+  - Unless required by applicable law or agreed to in writing, software
+  - distributed under the License is distributed on an "AS IS" BASIS,
+  - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  - See the License for the specific language governing permissions and
+  - limitations under the License.
+  -->
+
+<svg
+        version="1.1"
+        xmlns="http://www.w3.org/2000/svg"
+        x="0px"
+        y="0px"
+        width="256px"
+        height="256px"
+        viewBox="0 0 256 256"
+        enable-background="new 0 0 256 256"
+        xml:space="preserve">
+    <g>
+        <path fill="#0095DD" d="M70.956,195.781c-0.403,0-0.811-0.052-1.219-0.16c-2.502-0.671-3.99-3.247-3.319-5.751
+            c7.757-28.959,6.274-106.436,6.257-107.217c-0.053-2.589,2.007-4.735,4.599-4.785c0.033-0.003,0.065-0.003,0.096-0.003
+            c2.549,0,4.64,2.038,4.692,4.6c0.066,3.235,1.523,79.604-6.574,109.838C74.926,194.398,73.028,195.781,70.956,195.781z"/>
+        <path fill="#7658B1" d="M138.613,137.416c-9.441,0-17.392-3.89-17.905-4.149c-2.318-1.158-3.258-3.978-2.099-6.295
+            c1.157-2.312,3.966-3.257,6.291-2.104c0.279,0.138,14.777,7.093,24.519-0.106c2.083-1.546,5.022-1.103,6.564,0.982
+            c1.542,2.084,1.101,5.022-0.983,6.564C149.78,136.168,143.958,137.416,138.613,137.416z"/>
+        <path fill="#FF8A12" d="M157.21,182.926c-3.751,0-7.997-0.229-12.773-0.52c-2.588-0.159-4.557-2.386-4.399-4.976
+            c0.158-2.587,2.364-4.541,4.972-4.397c9.839,0.604,20.985,1.291,23.67-1.979c1.954-2.384,1.542-8.912-1.228-19.4
+            c-6.796-25.732-0.888-34.402,5.185-40.505c0.425-0.423,0.82-0.817,1.159-1.207c4.105-4.685,17.407-11.937,20.03-13.342
+            c2.286-1.226,5.131-0.364,6.354,1.919c1.224,2.286,0.365,5.129-1.919,6.356c-5.905,3.163-15.207,8.747-17.399,11.248
+            c-0.459,0.527-0.993,1.069-1.567,1.647c-3.487,3.505-8.758,8.8-2.767,31.483c3.834,14.525,3.664,22.566-0.591,27.753
+            C172.118,181.664,166.024,182.926,157.21,182.926z"/>
+        <path fill="#DB0012" d="M147.883,250.904c-11.599,0-22.975-5.176-30.923-14.549c-18.702-22.059-32.376-21.854-32.535-21.873
+            c-2.591,0.174-4.751-1.9-4.878-4.456c-0.128-2.556,1.769-4.738,4.32-4.914c1.832-0.107,18.498-0.489,40.253,25.171
+            c8.251,9.731,20.661,13.542,32.386,9.94c22.843-7.014,39.016-39.527,44.365-89.201c4.729-43.903,7.528-88.793-14.181-112.962
+            c-12.908-14.376-33.947-21.362-64.317-21.362c-65.216,0-78.645,42.503-81.391,60.778c-4.135,27.518,6.459,56.024,22.668,61.009
+            c2.478,0.763,3.87,3.389,3.107,5.868c-0.763,2.476-3.388,3.861-5.867,3.104c-20.683-6.359-34.051-39.044-29.193-71.376
+            C38.17,33.018,72.067,7.309,122.374,7.309c33.188,0,56.511,8.008,71.303,24.479c24.289,27.045,21.484,74.229,16.529,120.24
+            c-7.904,73.36-35.312,92.368-50.944,97.17C155.52,250.346,151.69,250.904,147.883,250.904z"/>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/plugins/transforms/drools/src/main/resources/rules_exec.svg b/plugins/transforms/drools/src/main/resources/rules_exec.svg
new file mode 100644
index 0000000000..44cf95140f
--- /dev/null
+++ b/plugins/transforms/drools/src/main/resources/rules_exec.svg
@@ -0,0 +1,49 @@
+<!--
+  - Licensed to the Apache Software Foundation (ASF) under one or more
+  - contributor license agreements.  See the NOTICE file distributed with
+  - this work for additional information regarding copyright ownership.
+  - The ASF licenses this file to You under the Apache License, Version 2.0
+  - (the "License"); you may not use this file except in compliance with
+  - the License.  You may obtain a copy of the License at
+  -
+  -       http://www.apache.org/licenses/LICENSE-2.0
+  -
+  - Unless required by applicable law or agreed to in writing, software
+  - distributed under the License is distributed on an "AS IS" BASIS,
+  - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  - See the License for the specific language governing permissions and
+  - limitations under the License.
+  -->
+
+<svg
+        version="1.1"
+        xmlns="http://www.w3.org/2000/svg"
+        x="0px"
+        y="0px"
+        width="256px"
+        height="256px"
+        viewBox="0 0 256 256"
+        enable-background="new 0 0 256 256"
+        xml:space="preserve">
+    <g>
+        <path fill="#0095DD" d="M70.956,195.781c-0.403,0-0.811-0.052-1.219-0.16c-2.502-0.671-3.99-3.247-3.319-5.751
+            c7.757-28.959,6.274-106.436,6.257-107.217c-0.053-2.589,2.007-4.735,4.599-4.785c0.033-0.003,0.065-0.003,0.096-0.003
+            c2.549,0,4.64,2.038,4.692,4.6c0.066,3.235,1.523,79.604-6.574,109.838C74.926,194.398,73.028,195.781,70.956,195.781z"/>
+        <path fill="#7658B1" d="M138.613,137.416c-9.441,0-17.392-3.89-17.905-4.149c-2.318-1.158-3.258-3.978-2.099-6.295
+            c1.157-2.312,3.966-3.257,6.291-2.104c0.279,0.138,14.777,7.093,24.519-0.106c2.083-1.546,5.022-1.103,6.564,0.982
+            c1.542,2.084,1.101,5.022-0.983,6.564C149.78,136.168,143.958,137.416,138.613,137.416z"/>
+        <path fill="#FF8A12" d="M157.21,182.926c-3.751,0-7.997-0.229-12.773-0.52c-2.588-0.159-4.557-2.386-4.399-4.976
+            c0.158-2.587,2.364-4.541,4.972-4.397c9.839,0.604,20.985,1.291,23.67-1.979c1.954-2.384,1.542-8.912-1.228-19.4
+            c-6.796-25.732-0.888-34.402,5.185-40.505c0.425-0.423,0.82-0.817,1.159-1.207c4.105-4.685,17.407-11.937,20.03-13.342
+            c2.286-1.226,5.131-0.364,6.354,1.919c1.224,2.286,0.365,5.129-1.919,6.356c-5.905,3.163-15.207,8.747-17.399,11.248
+            c-0.459,0.527-0.993,1.069-1.567,1.647c-3.487,3.505-8.758,8.8-2.767,31.483c3.834,14.525,3.664,22.566-0.591,27.753
+            C172.118,181.664,166.024,182.926,157.21,182.926z"/>
+        <path fill="#DB0012" d="M147.883,250.904c-11.599,0-22.975-5.176-30.923-14.549c-18.702-22.059-32.376-21.854-32.535-21.873
+            c-2.591,0.174-4.751-1.9-4.878-4.456c-0.128-2.556,1.769-4.738,4.32-4.914c1.832-0.107,18.498-0.489,40.253,25.171
+            c8.251,9.731,20.661,13.542,32.386,9.94c22.843-7.014,39.016-39.527,44.365-89.201c4.729-43.903,7.528-88.793-14.181-112.962
+            c-12.908-14.376-33.947-21.362-64.317-21.362c-65.216,0-78.645,42.503-81.391,60.778c-4.135,27.518,6.459,56.024,22.668,61.009
+            c2.478,0.763,3.87,3.389,3.107,5.868c-0.763,2.476-3.388,3.861-5.867,3.104c-20.683-6.359-34.051-39.044-29.193-71.376
+            C38.17,33.018,72.067,7.309,122.374,7.309c33.188,0,56.511,8.008,71.303,24.479c24.289,27.045,21.484,74.229,16.529,120.24
+            c-7.904,73.36-35.312,92.368-50.944,97.17C155.52,250.346,151.69,250.904,147.883,250.904z"/>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/plugins/transforms/drools/src/main/samples/transforms/rules-accumulator-solve-golfer-rule.hpl b/plugins/transforms/drools/src/main/samples/transforms/rules-accumulator-solve-golfer-rule.hpl
new file mode 100644
index 0000000000..a8aad2b030
--- /dev/null
+++ b/plugins/transforms/drools/src/main/samples/transforms/rules-accumulator-solve-golfer-rule.hpl
@@ -0,0 +1,432 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-->
+<pipeline>
+  <info>
+    <name>rules-accumulator-solve-golfer-rule</name>
+    <name_sync_with_filename>Y</name_sync_with_filename>
+    <description/>
+    <extended_description/>
+    <pipeline_version/>
+    <pipeline_type>Normal</pipeline_type>
+    <pipeline_status>0</pipeline_status>
+    <parameters>
+    </parameters>
+    <capture_transform_performance>N</capture_transform_performance>
+    <transform_performance_capturing_delay>1000</transform_performance_capturing_delay>
+    <transform_performance_capturing_size_limit>100</transform_performance_capturing_size_limit>
+    <created_user>-</created_user>
+    <created_date>2011/08/29 13:57:42.720</created_date>
+    <modified_user>-</modified_user>
+    <modified_date>2011/08/29 13:57:42.720</modified_date>
+    <key_for_session_key>H4sIAAAAAAAAAAMAAAAAAAAAAAA=</key_for_session_key>
+    <is_key_private>N</is_key_private>
+  </info>
+  <notepads>
+    <notepad>
+      <note>Puzzle rules:
+
+- A foursome of golfers is standing at a tee, in a line from left to right.
+- Each golfer wears different colored pants; one is wearing red pants.
+- The golfer to Fred’s immediate right is wearing blue pants.
+- Joe is second in line.
+- Bob is wearing plaid pants.
+- Tom isn’t in position one or four, and he isn’t wearing the hideous orange pants.
+- In what order will the four golfers tee off, and what color are each golfer’s pants?”</note>
+      <xloc>64</xloc>
+      <yloc>240</yloc>
+      <width>630</width>
+      <heigth>186</heigth>
+      <fontname>Arial</fontname>
+      <fontsize>10</fontsize>
+      <fontbold>N</fontbold>
+      <fontitalic>N</fontitalic>
+      <fontcolorred>0</fontcolorred>
+      <fontcolorgreen>0</fontcolorgreen>
+      <fontcolorblue>0</fontcolorblue>
+      <backgroundcolorred>255</backgroundcolorred>
+      <backgroundcolorgreen>165</backgroundcolorgreen>
+      <backgroundcolorblue>0</backgroundcolorblue>
+      <bordercolorred>100</bordercolorred>
+      <bordercolorgreen>100</bordercolorgreen>
+      <bordercolorblue>100</bordercolorblue>
+    </notepad>
+  </notepads>
+  <order>
+    <hop>
+      <from>Generate Golfers</from>
+      <to>Insert Position</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Generate Position</from>
+      <to>Insert Position</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Insert Position</from>
+      <to>Insert Color</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Generate Color</from>
+      <to>Insert Color</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Insert Color</from>
+      <to>Rule Accumulator</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>Rule Accumulator</from>
+      <to>Sort rows</to>
+      <enabled>Y</enabled>
+    </hop>
+  </order>
+  <transform>
+    <name>Generate Color</name>
+    <type>DataGrid</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <fields>
+      <field>
+        <set_empty_string>N</set_empty_string>
+        <length>-1</length>
+        <name>color</name>
+        <precision>-1</precision>
+      </field>
+    </fields>
+    <data>
+      <line>
+        <item>red</item>
+      </line>
+      <line>
+        <item>blue</item>
+      </line>
+      <line>
+        <item>plaid</item>
+      </line>
+      <line>
+        <item>orange</item>
+      </line>
+    </data>
+    <attributes/>
+    <GUI>
+      <xloc>480</xloc>
+      <yloc>48</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Generate Golfers</name>
+    <type>DataGrid</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <fields>
+      <field>
+        <set_empty_string>N</set_empty_string>
+        <length>-1</length>
+        <name>name</name>
+        <precision>-1</precision>
+        <type>String</type>
+      </field>
+    </fields>
+    <data>
+      <line>
+        <item>Fred</item>
+      </line>
+      <line>
+        <item>Joe</item>
+      </line>
+      <line>
+        <item>Bob</item>
+      </line>
+      <line>
+        <item>Tom</item>
+      </line>
+    </data>
+    <attributes/>
+    <GUI>
+      <xloc>128</xloc>
+      <yloc>160</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Generate Position</name>
+    <type>DataGrid</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <fields>
+      <field>
+        <set_empty_string>N</set_empty_string>
+        <length>-1</length>
+        <name>position</name>
+        <precision>-1</precision>
+        <type>Integer</type>
+      </field>
+    </fields>
+    <data>
+      <line>
+        <item>1</item>
+      </line>
+      <line>
+        <item>2</item>
+      </line>
+      <line>
+        <item>3</item>
+      </line>
+      <line>
+        <item>4</item>
+      </line>
+    </data>
+    <attributes/>
+    <GUI>
+      <xloc>304</xloc>
+      <yloc>48</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Insert Color</name>
+    <type>JoinRows</type>
+    <description/>
+    <distribute>N</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <directory>%%java.io.tmpdir%%</directory>
+    <prefix>out</prefix>
+    <cache_size>500</cache_size>
+    <main/>
+    <compare>
+      <condition>
+        <negated>N</negated>
+        <leftvalue>name</leftvalue>
+        <function>IS NOT NULL</function>
+        <rightvalue/>
+      </condition>
+    </compare>
+    <attributes/>
+    <GUI>
+      <xloc>480</xloc>
+      <yloc>160</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Insert Position</name>
+    <type>JoinRows</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <directory>%%java.io.tmpdir%%</directory>
+    <prefix>out</prefix>
+    <cache_size>500</cache_size>
+    <main/>
+    <compare>
+      <condition>
+        <negated>N</negated>
+        <leftvalue>name</leftvalue>
+        <function>IS NOT NULL</function>
+        <rightvalue/>
+      </condition>
+    </compare>
+    <attributes/>
+    <GUI>
+      <xloc>304</xloc>
+      <yloc>160</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Rule Accumulator</name>
+    <type>RuleAccumulator</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <rule-definition>package org.apache.hop.pipeline.transforms.drools
+ 
+import org.apache.hop.pipeline.transforms.drools.Rules.Row;
+
+// Courtesy of http://docs.huihoo.com/drools/4.0.7/ch10.html
+
+rule "Golfers problem"
+    dialect "mvel"
+    when
+
+	// Define Fred
+	$fred : Row ( externalSource == true,
+				column["name"] == "Fred"
+	)
+
+	// Define Joe
+	$joe : Row ( 	externalSource == true, 
+			      	column["name"] == "Joe",
+			      	column["position"] == 2,
+				column["position"] != $fred.column["position"],
+				column["color"] != $fred.column["color"]
+	)
+       
+	// Define Bob
+	$bob : Row ( externalSource == true,
+				column["name"] == "Bob",
+				column["position"] != $fred.column["position"],
+				column["position"] != $joe.column["position"],
+				column["color"] == "plaid",
+				column["color"] != $fred.column["color"],
+				column["color"] != $joe.column["color"]
+	)
+
+	// Define Tom
+	$tom : Row ( externalSource == true,
+				column["name"] == "Tom",
+				column["position"] != 1,
+				column["position"] != 4,
+				column["position"] != $fred.column["position"],
+				column["position"] != $joe.column["position"],
+				column["position"] != $bob.column["position"],
+				column["color"] != "orange",
+				column["color"] != $fred.column["color"],
+				column["color"] != $joe.column["color"],
+				column["color"] != $bob.column["color"]
+	)
+
+	// Define Unknown
+	$unknown : Row ( externalSource == true,
+					column["position"] == ($fred.column["position"] + 1),
+					column["color"] == "blue",
+					this in ( $joe, $bob, $tom)
+	)
+
+    then
+
+      Row fredRow = new Row();
+      Row joeRow = new Row();
+      Row bobRow = new Row();
+      Row tomRow = new Row();
+
+	fredRow.addColumn("name", "Fred");
+	fredRow.addColumn("position", $fred.column["position"]);
+	fredRow.addColumn("color", $fred.column["color"]);
+
+	joeRow.addColumn("name", "Joe");
+	joeRow.addColumn("position", $joe.column["position"]);
+	joeRow.addColumn("color", $joe.column["color"]);
+
+	bobRow.addColumn("name", "Bob");
+	bobRow.addColumn("position", $bob.column["position"]);
+	bobRow.addColumn("color", $bob.column["color"]);
+
+	tomRow.addColumn("name", "Tom");
+	tomRow.addColumn("position", $tom.column["position"]);
+	tomRow.addColumn("color", $tom.column["color"]);
+
+      	insert(fredRow);
+      	insert(joeRow);
+      	insert(bobRow);
+      	insert(tomRow); 
+
+end</rule-definition>
+    <rule-file/>
+    <fields>
+      <field>
+        <column-name>position</column-name>
+        <column-type>Integer</column-type>
+      </field>
+      <field>
+        <column-name>name</column-name>
+        <column-type>String</column-type>
+      </field>
+      <field>
+        <column-name>color</column-name>
+        <column-type>String</column-type>
+      </field>
+    </fields>
+    <attributes/>
+    <GUI>
+      <xloc>688</xloc>
+      <yloc>160</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>Sort rows</name>
+    <type>SortRows</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <directory>%%java.io.tmpdir%%</directory>
+    <prefix>out</prefix>
+    <sort_size>1000000</sort_size>
+    <free_memory/>
+    <compress>N</compress>
+    <compress_variable/>
+    <unique_rows>N</unique_rows>
+    <fields>
+      <field>
+        <name>position</name>
+        <ascending>Y</ascending>
+        <case_sensitive>N</case_sensitive>
+        <collator_enabled>N</collator_enabled>
+        <collator_strength>0</collator_strength>
+        <presorted>N</presorted>
+      </field>
+    </fields>
+    <attributes/>
+    <GUI>
+      <xloc>864</xloc>
+      <yloc>160</yloc>
+    </GUI>
+  </transform>
+  <transform_error_handling>
+  </transform_error_handling>
+  <attributes/>
+</pipeline>
diff --git a/plugins/transforms/pom.xml b/plugins/transforms/pom.xml
index bf5c816303..95d775a7ba 100644
--- a/plugins/transforms/pom.xml
+++ b/plugins/transforms/pom.xml
@@ -94,6 +94,7 @@
                 <module>detectemptystream</module>
                 <module>detectlastrow</module>
                 <module>dimensionlookup</module>
+                <module>drools</module>
                 <module>dynamicsqlrow</module>
                 <module>edi2xml</module>
                 <module>excelinput</module>