[jboss-svn-commits] JBL Code SVN: r30648 - in labs/jbossrules/trunk: drools-api/src/main/java/org/drools/agent and 3 other directories.
jboss-svn-commits at lists.jboss.org
jboss-svn-commits at lists.jboss.org
Mon Dec 14 15:13:29 EST 2009
Author: eaa
Date: 2009-12-14 15:13:28 -0500 (Mon, 14 Dec 2009)
New Revision: 30648
Added:
labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/agent/KnowledgeAgentRemoveRuleChangeSetTest.java
Modified:
labs/jbossrules/trunk/drools-api/src/main/java/org/drools/ChangeSet.java
labs/jbossrules/trunk/drools-api/src/main/java/org/drools/agent/KnowledgeAgent.java
labs/jbossrules/trunk/drools-api/src/main/java/org/drools/agent/KnowledgeAgentConfiguration.java
labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/agent/KnowledgeAgentIncrementalChangeSetTest.java
labs/jbossrules/trunk/drools-core/src/main/java/org/drools/agent/impl/KnowledgeAgentImpl.java
labs/jbossrules/trunk/drools-core/src/main/java/org/drools/io/impl/ChangeSetImpl.java
Log:
JBRULES-2082: support for delete/modify in changesets
- ChangeSet: added new property "knowledgeDefinitionsRemoved" to store the rules to be removed from kbase.
- KnowledgeAgent: Added support for individual rules deletion using ChangeSet's property.
- Created new unit test for individual rules deletion using Change Sets: KnowledgeAgentRemoveRuleChangeSetTest
Modified: labs/jbossrules/trunk/drools-api/src/main/java/org/drools/ChangeSet.java
===================================================================
--- labs/jbossrules/trunk/drools-api/src/main/java/org/drools/ChangeSet.java 2009-12-14 19:17:11 UTC (rev 30647)
+++ labs/jbossrules/trunk/drools-api/src/main/java/org/drools/ChangeSet.java 2009-12-14 20:13:28 UTC (rev 30648)
@@ -1,6 +1,7 @@
package org.drools;
import java.util.Collection;
+import java.util.Map;
import org.drools.io.Resource;
@@ -81,4 +82,8 @@
*/
public Collection<Resource> getResourcesModified();
+ Map<Resource, String> getKnowledgeDefinitionsRemoved();
+
+ void setKnowledgeDefinitionsRemoved(Map<Resource, String> knowledgeDefinitionsRemoved);
+
}
Modified: labs/jbossrules/trunk/drools-api/src/main/java/org/drools/agent/KnowledgeAgent.java
===================================================================
--- labs/jbossrules/trunk/drools-api/src/main/java/org/drools/agent/KnowledgeAgent.java 2009-12-14 19:17:11 UTC (rev 30647)
+++ labs/jbossrules/trunk/drools-api/src/main/java/org/drools/agent/KnowledgeAgent.java 2009-12-14 20:13:28 UTC (rev 30648)
@@ -1,5 +1,6 @@
package org.drools.agent;
+import org.drools.ChangeSet;
import org.drools.KnowledgeBase;
import org.drools.SystemEventListener;
import org.drools.io.Resource;
@@ -45,5 +46,7 @@
void applyChangeSet(Resource resource);
+ void applyChangeSet(ChangeSet changeSet);
+
void setSystemEventListener(SystemEventListener listener);
}
Modified: labs/jbossrules/trunk/drools-api/src/main/java/org/drools/agent/KnowledgeAgentConfiguration.java
===================================================================
--- labs/jbossrules/trunk/drools-api/src/main/java/org/drools/agent/KnowledgeAgentConfiguration.java 2009-12-14 19:17:11 UTC (rev 30647)
+++ labs/jbossrules/trunk/drools-api/src/main/java/org/drools/agent/KnowledgeAgentConfiguration.java 2009-12-14 20:13:28 UTC (rev 30648)
@@ -16,4 +16,13 @@
extends
PropertiesConfiguration {
+ public boolean isScanResources();
+
+ public boolean isScanDirectories();
+
+ public boolean isMonitorChangeSetEvents();
+
+ public boolean isNewInstance();
+
+
}
Modified: labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/agent/KnowledgeAgentIncrementalChangeSetTest.java
===================================================================
--- labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/agent/KnowledgeAgentIncrementalChangeSetTest.java 2009-12-14 19:17:11 UTC (rev 30647)
+++ labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/agent/KnowledgeAgentIncrementalChangeSetTest.java 2009-12-14 20:13:28 UTC (rev 30648)
@@ -265,15 +265,15 @@
kagent.monitorResourceChangeEvents(false);
}
-
+
/**
* Tests that if we have two DRL files, where one file overwrites a rule in
* a prior file, that if we modify the first file that was overwritten, that
* it will gain precedence and overwrite the other.
*
* @throws Exception
- */
+ */
public void testModifyFileUrlOverwriteIncremental() throws Exception {
String rule1 = "";
rule1 += "package org.drools.test\n";
@@ -283,22 +283,31 @@
rule1 += "then\n";
rule1 += "list.add( drools.getRule().getName() );\n";
rule1 += "end\n";
+
+ String rule2 = "";
+ rule2 += "rule rule2\n";
+ rule2 += "when\n";
+ rule2 += "then\n";
+ rule2 += "list.add( drools.getRule().getName() );\n";
+ rule2 += "end\n";
+
File f1 = fileManager.newFile("rule1.drl");
Writer output = new BufferedWriter(new FileWriter(f1));
output.write(rule1);
+ output.write(rule2);
output.close();
- String rule2 = "";
- rule2 += "package org.drools.test\n";
- rule2 += "global java.util.List list\n";
- rule2 += "rule rule1\n";
- rule2 += "when\n";
- rule2 += "then\n";
- rule2 += "list.add( drools.getRule().getName() + \"_v2\");\n";
- rule2 += "end\n";
+ String rule1v2 = "";
+ rule1v2 += "package org.drools.test\n";
+ rule1v2 += "global java.util.List list\n";
+ rule1v2 += "rule rule1\n";
+ rule1v2 += "when\n";
+ rule1v2 += "then\n";
+ rule1v2 += "list.add( drools.getRule().getName() + \"_v2\");\n";
+ rule1v2 += "end\n";
File f2 = fileManager.newFile("rule2.drl");
output = new BufferedWriter(new FileWriter(f2));
- output.write(rule2);
+ output.write(rule1v2);
output.close();
String xml = "";
@@ -339,8 +348,9 @@
ksession.fireAllRules();
ksession.dispose();
- assertEquals(1, list.size());
+ assertEquals(2, list.size());
assertTrue(list.contains("rule1_v2"));
+ assertTrue(list.contains("rule2"));
list.clear();
@@ -348,16 +358,16 @@
// http://saloon.javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb=get_topic&f=1&t=019789
Thread.sleep(2000);
- rule1 = "";
- rule1 += "package org.drools.test\n";
- rule1 += "global java.util.List list\n";
- rule1 += "rule rule1\n";
- rule1 += "when\n";
- rule1 += "then\n";
- rule1 += "list.add( drools.getRule().getName() + \"_v3\" );\n";
- rule1 += "end\n";
- output = new BufferedWriter(new FileWriter(f1));
- output.write(rule1);
+ String rule1v3 = "";
+ rule1v3 += "package org.drools.test\n";
+ rule1v3 += "global java.util.List list\n";
+ rule1v3 += "rule rule1\n";
+ rule1v3 += "when\n";
+ rule1v3 += "then\n";
+ rule1v3 += "list.add( drools.getRule().getName() + \"_v3\" );\n";
+ rule1v3 += "end\n";
+ output = new BufferedWriter(new FileWriter(f2));
+ output.write(rule1v3);
output.close();
Thread.sleep(3000);
@@ -367,8 +377,9 @@
ksession.fireAllRules();
ksession.dispose();
- assertEquals(1, list.size());
+ assertEquals(2, list.size());
assertTrue(list.contains("rule1_v3"));
+ assertTrue(list.contains("rule2"));
//Delete f2 now, rule1 should still fire if the indexing worked properly
list.clear();
@@ -381,18 +392,18 @@
ksession.dispose();
assertEquals(1, list.size());
- assertTrue(list.contains("rule1_v3"));
+ assertTrue(list.contains("rule2"));
kagent.monitorResourceChangeEvents(false);
}
-
+
/**
* Creates two rules (rule1 and rule2) in a drl file. Then it modifies the
* drl file to change rule2 with rule3.
* @throws Exception
- */
+ */
public void testMultipleRulesOnFileUrlIncremental() throws Exception {
String rule1 = "";
rule1 += "package org.drools.test\n";
@@ -402,8 +413,8 @@
rule1 += "then\n";
rule1 += "list.add( drools.getRule().getName() );\n";
rule1 += "end\n\n";
-
+
String rule2 = "";
rule2 += "rule rule2\n";
rule2 += "when\n";
@@ -488,8 +499,8 @@
kagent.monitorResourceChangeEvents(false);
}
-
-
+
+
public void testMultipleRulesOnFilesUrlIncremental() throws Exception {
String header = "";
header += "package org.drools.test\n";
@@ -501,8 +512,8 @@
rule1 += "then\n";
rule1 += "list.add( drools.getRule().getName() );\n";
rule1 += "end\n\n";
-
+
String rule2 = "";
rule2 += "rule rule2\n";
rule2 += "when\n";
@@ -593,7 +604,7 @@
// http://saloon.javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb=get_topic&f=1&t=019789
Thread.sleep(2000);
-
+
output = new BufferedWriter(new FileWriter(f2));
output.write(header);
output.write(rule4);
@@ -642,7 +653,7 @@
kagent.monitorResourceChangeEvents(false);
}
-
+
public void testModifyPackageUrlIncremental() throws Exception {
String rule1 = "";
rule1 += "package org.drools.test\n";
@@ -862,6 +873,138 @@
kagent.monitorResourceChangeEvents(false);
}
+
+ public void testUpdatePackageUrlOverwriteIncremental() throws Exception {
+ String rule1 = "";
+ rule1 += "package org.drools.test\n";
+ rule1 += "global java.util.List list\n";
+ rule1 += "rule rule1\n";
+ rule1 += "when\n";
+ rule1 += "then\n";
+ rule1 += "list.add( drools.getRule().getName() );\n";
+ rule1 += "end\n";
+
+ String rule1v2 = "";
+ rule1v2 += "package org.drools.test\n";
+ rule1v2 += "global java.util.List list\n";
+ rule1v2 += "rule rule1\n";
+ rule1v2 += "when\n";
+ rule1v2 += "then\n";
+ rule1v2 += "list.add( drools.getRule().getName()+\"_V2\");\n";
+ rule1v2 += "end\n";
+
+ String rule2 = "";
+ rule2 += "package org.drools.test\n";
+ rule2 += "global java.util.List list\n";
+ rule2 += "rule rule2\n";
+ rule2 += "when\n";
+ rule2 += "then\n";
+ rule2 += "list.add( drools.getRule().getName() );\n";
+ rule2 += "end\n";
+
+ String rule3 = "";
+ rule3 += "package org.drools.test\n";
+ rule3 += "global java.util.List list\n";
+ rule3 += "rule rule3\n";
+ rule3 += "when\n";
+ rule3 += "then\n";
+ rule3 += "list.add( drools.getRule().getName() );\n";
+ rule3 += "end\n";
+
+ // Add Rule1 and Rule2 in the first package
+ File pkgF1 = fileManager.newFile("pkg1.pkg");
+ KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
+ kbuilder.add(ResourceFactory.newByteArrayResource(rule1.getBytes()),
+ ResourceType.DRL);
+ kbuilder.add(ResourceFactory.newByteArrayResource(rule2.getBytes()),
+ ResourceType.DRL);
+ if (kbuilder.hasErrors()) {
+ fail(kbuilder.getErrors().toString());
+ }
+ KnowledgePackage pkg1 = (KnowledgePackage) kbuilder.getKnowledgePackages().iterator().next();
+ writePackage(pkg1, pkgF1);
+
+ // Add Rule3 in the second package
+ File pkgF2 = fileManager.newFile("pkg2.pkg");
+ kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
+ kbuilder.add(ResourceFactory.newByteArrayResource(rule3.getBytes()),
+ ResourceType.DRL);
+ if (kbuilder.hasErrors()) {
+ fail(kbuilder.getErrors().toString());
+ }
+ KnowledgePackage pkg2 = (KnowledgePackage) kbuilder.getKnowledgePackages().iterator().next();
+ writePackage(pkg2, pkgF2);
+
+ String xml = "";
+ xml += "<change-set xmlns='http://drools.org/drools-5.0/change-set'";
+ xml += " xmlns:xs='http://www.w3.org/2001/XMLSchema-instance'";
+ xml += " xs:schemaLocation='http://drools.org/drools-5.0/change-set drools-change-set-5.0.xsd' >";
+ xml += " <add> ";
+ xml += " <resource source='http://localhost:9000/pkg1.pkg' type='PKG' />";
+ xml += " <resource source='http://localhost:9000/pkg2.pkg' type='PKG' />";
+ xml += " </add> ";
+ xml += "</change-set>";
+ File fxml = fileManager.newFile("changeset.xml");
+ Writer output = new BufferedWriter(new FileWriter(fxml));
+ output.write(xml);
+ output.close();
+
+ KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
+
+ ResourceChangeScannerConfiguration sconf = ResourceFactory.getResourceChangeScannerService().newResourceChangeScannerConfiguration();
+ sconf.setProperty("drools.resource.scanner.interval", "2");
+ ResourceFactory.getResourceChangeScannerService().configure(sconf);
+
+ KnowledgeAgentConfiguration aconf = KnowledgeAgentFactory.newKnowledgeAgentConfiguration();
+ aconf.setProperty("drools.agent.scanDirectories", "true");
+ aconf.setProperty("drools.agent.scanResources", "true");
+ aconf.setProperty("drools.agent.newInstance", "false");
+ KnowledgeAgent kagent = KnowledgeAgentFactory.newKnowledgeAgent(
+ "test agent", kbase, aconf);
+
+ kagent.applyChangeSet(ResourceFactory.newUrlResource(fxml.toURI().toURL()));
+
+ StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
+ List<String> list = new ArrayList<String>();
+ ksession.setGlobal("list", list);
+ ksession.fireAllRules();
+ ksession.dispose();
+
+ assertEquals(3, list.size());
+ assertTrue(list.contains("rule1"));
+ assertTrue(list.contains("rule2"));
+ assertTrue(list.contains("rule3"));
+
+ list.clear();
+
+ // have to sleep here as linux lastModified does not do milliseconds
+ // http://saloon.javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb=get_topic&f=1&t=019789
+ Thread.sleep(2000);
+
+ kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
+ kbuilder.add(ResourceFactory.newByteArrayResource(rule1v2.getBytes()),
+ ResourceType.DRL);
+ if (kbuilder.hasErrors()) {
+ fail(kbuilder.getErrors().toString());
+ }
+ pkg2 = (KnowledgePackage) kbuilder.getKnowledgePackages().iterator().next();
+ writePackage(pkg2, pkgF2);
+
+ Thread.sleep(3000);
+
+ ksession = kbase.newStatefulKnowledgeSession();
+ list = new ArrayList<String>();
+ ksession.setGlobal("list", list);
+ ksession.fireAllRules();
+ ksession.dispose();
+
+ assertEquals(2, list.size());
+
+ assertTrue(list.contains("rule1_V2"));
+ assertTrue(list.contains("rule2"));
+ kagent.monitorResourceChangeEvents(false);
+ }
+
private static void writePackage(Object pkg, File p1file)
Added: labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/agent/KnowledgeAgentRemoveRuleChangeSetTest.java
===================================================================
--- labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/agent/KnowledgeAgentRemoveRuleChangeSetTest.java (rev 0)
+++ labs/jbossrules/trunk/drools-compiler/src/test/java/org/drools/agent/KnowledgeAgentRemoveRuleChangeSetTest.java 2009-12-14 20:13:28 UTC (rev 30648)
@@ -0,0 +1,365 @@
+package org.drools.agent;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.drools.KnowledgeBase;
+import org.drools.KnowledgeBaseFactory;
+import org.drools.io.Resource;
+import org.drools.io.ResourceChangeScannerConfiguration;
+import org.drools.io.ResourceFactory;
+import org.drools.io.impl.ChangeSetImpl;
+import org.drools.io.impl.ResourceChangeNotifierImpl;
+import org.drools.io.impl.ResourceChangeScannerImpl;
+import org.drools.runtime.StatefulKnowledgeSession;
+import org.drools.runtime.pipeline.ResultHandler;
+import org.drools.util.DroolsStreamUtils;
+import org.drools.util.FileManager;
+import org.mortbay.jetty.Server;
+import org.mortbay.jetty.handler.ResourceHandler;
+
+public class KnowledgeAgentRemoveRuleChangeSetTest extends TestCase {
+
+ FileManager fileManager;
+ private Server server;
+
+ @Override
+ protected void setUp() throws Exception {
+ fileManager = new FileManager();
+ fileManager.setUp();
+ ((ResourceChangeScannerImpl) ResourceFactory.getResourceChangeScannerService()).reset();
+ ResourceFactory.getResourceChangeNotifierService().start();
+ ResourceFactory.getResourceChangeScannerService().start();
+
+ this.server = new Server(9000);
+ ResourceHandler resourceHandler = new ResourceHandler();
+ resourceHandler.setResourceBase(fileManager.getRootDirectory().getPath());
+ System.out.println("root : " + fileManager.getRootDirectory().getPath());
+
+ server.setHandler(resourceHandler);
+
+ server.start();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ fileManager.tearDown();
+ ResourceFactory.getResourceChangeNotifierService().stop();
+ ResourceFactory.getResourceChangeScannerService().stop();
+ ((ResourceChangeNotifierImpl) ResourceFactory.getResourceChangeNotifierService()).reset();
+ ((ResourceChangeScannerImpl) ResourceFactory.getResourceChangeScannerService()).reset();
+
+ server.stop();
+ }
+
+ public void testModifyFileUrlIncremental() throws Exception {
+
+ String header = "";
+ header += "package org.drools.test\n";
+ header += "global java.util.List list\n\n";
+
+ String rule1 = this.createCommonRule("rule1");
+ String rule2 = this.createCommonRule("rule2");
+ String rule3 = this.createCommonRule("rule3");
+ String rule4 = this.createCommonRule("rule4");
+
+ String function1 = this.createDummyFunction("function1");
+
+ String query1 = "";
+ query1 += "query \"all the Strings\"\n";
+ //query1 += "query \"rule1\"\n";
+ query1 += " str : String()\n";
+ query1 += "end\n";
+
+ String type1 = "";
+ type1 += "declare Address\n";
+ type1 += " number : int\n";
+ type1 += " streetName : String\n";
+ type1 += " city : String\n";
+ type1 += "end\n";
+
+
+ File f1 = fileManager.newFile("rule1.drl");
+ Writer output = new BufferedWriter(new FileWriter(f1));
+ output.write(header);
+ output.write(type1);
+ output.write(query1);
+ output.write(function1);
+ output.write(rule1);
+ output.write(rule2);
+ output.close();
+
+ File f2 = fileManager.newFile("rule2.drl");
+ output = new BufferedWriter(new FileWriter(f2));
+ output.write(header);
+ output.write(rule3);
+ output.close();
+
+ String xml = "";
+ xml += "<change-set xmlns='http://drools.org/drools-5.0/change-set'";
+ xml += " xmlns:xs='http://www.w3.org/2001/XMLSchema-instance'";
+ xml += " xs:schemaLocation='http://drools.org/drools-5.0/change-set drools-change-set-5.0.xsd' >";
+ xml += " <add> ";
+ xml += " <resource source='http://localhost:9000/rule1.drl' type='DRL' />";
+ xml += " <resource source='http://localhost:9000/rule2.drl' type='DRL' />";
+ xml += " </add> ";
+ xml += "</change-set>";
+ File fxml = fileManager.newFile("changeset.xml");
+ output = new BufferedWriter(new FileWriter(fxml));
+ output.write(xml);
+ output.close();
+
+ KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
+ KnowledgeAgent kagent = this.createKAgent(kbase);
+ kagent.applyChangeSet(ResourceFactory.newUrlResource(fxml.toURI().toURL()));
+
+ StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
+ List<String> list = new ArrayList<String>();
+ ksession.setGlobal("list", list);
+ ksession.fireAllRules();
+ ksession.dispose();
+
+ assertEquals(3, list.size());
+ assertTrue(list.contains("rule1"));
+ assertTrue(list.contains("rule2"));
+ assertTrue(list.contains("rule3"));
+
+ list.clear();
+
+ // have to sleep here as linux lastModified does not do milliseconds
+ // http://saloon.javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb=get_topic&f=1&t=019789
+ Thread.sleep(2000);
+
+
+ output = new BufferedWriter(new FileWriter(f1));
+ output.write(header);
+ output.write(rule2);
+ output.write(rule4);
+ output.close();
+ Thread.sleep(3000);
+
+ // Use the same session for incremental build test
+ ksession = kbase.newStatefulKnowledgeSession();
+ list = new ArrayList<String>();
+ ksession.setGlobal("list", list);
+ ksession.fireAllRules();
+ ksession.dispose();
+
+ assertEquals(3, list.size());
+
+ assertTrue(list.contains("rule3"));
+ assertTrue(list.contains("rule2"));
+ assertTrue(list.contains("rule4"));
+ kagent.monitorResourceChangeEvents(false);
+ }
+
+ public void testRemoveRuleChangeSet() throws Exception {
+
+ String header = "";
+ header += "package org.drools.test\n";
+ header += "global java.util.List list\n\n";
+
+ String rule1 = this.createCommonRule("rule1");
+ String rule2 = this.createCommonRule("rule2");
+ String rule3 = this.createCommonRule("rule3");
+
+ String function1 = this.createDummyFunction("function1");
+
+ File f1 = fileManager.newFile("rule1.drl");
+ Writer output = new BufferedWriter(new FileWriter(f1));
+ output.write(header);
+ output.write(function1);
+ output.write(rule1);
+ output.write(rule2);
+ output.close();
+
+ File f2 = fileManager.newFile("rule2.drl");
+ output = new BufferedWriter(new FileWriter(f2));
+ output.write(header);
+ output.write(rule3);
+ output.close();
+
+ String xml = "";
+ xml += "<change-set xmlns='http://drools.org/drools-5.0/change-set'";
+ xml += " xmlns:xs='http://www.w3.org/2001/XMLSchema-instance'";
+ xml += " xs:schemaLocation='http://drools.org/drools-5.0/change-set drools-change-set-5.0.xsd' >";
+ xml += " <add> ";
+ xml += " <resource source='http://localhost:9000/rule1.drl' type='DRL' />";
+ xml += " <resource source='http://localhost:9000/rule2.drl' type='DRL' />";
+ xml += " </add> ";
+ xml += "</change-set>";
+ final File fxml = fileManager.newFile("changeset.xml");
+ output = new BufferedWriter(new FileWriter(fxml));
+ output.write(xml);
+ output.close();
+
+ KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
+ KnowledgeAgent kagent = this.createKAgent(kbase);
+ kagent.applyChangeSet(ResourceFactory.newUrlResource(fxml.toURI().toURL()));
+
+ StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
+ List<String> list = new ArrayList<String>();
+ ksession.setGlobal("list", list);
+ ksession.fireAllRules();
+ ksession.dispose();
+
+ assertEquals(3, list.size());
+ assertTrue(list.contains("rule1"));
+ assertTrue(list.contains("rule2"));
+ assertTrue(list.contains("rule3"));
+
+ list.clear();
+
+ //Creates a new changeSet that will delete Rule1 from kbase
+ ChangeSetImpl cs = new ChangeSetImpl();
+ cs.setKnowledgeDefinitionsRemoved(new HashMap<Resource, String>() {
+
+ {
+ Resource r = ResourceFactory.newUrlResource("http://localhost:9000/rule1.drl");
+ this.put(r, "rule1");
+ }
+ });
+
+ kagent.applyChangeSet(cs);
+
+
+ ksession = kbase.newStatefulKnowledgeSession();
+ list = new ArrayList<String>();
+ ksession.setGlobal("list", list);
+ ksession.fireAllRules();
+ ksession.dispose();
+
+ assertEquals(2, list.size());
+
+ assertTrue(list.contains("rule2"));
+ assertTrue(list.contains("rule3"));
+
+
+
+ //Creates a new changeSet that will try to delete Rule3 from rule1.drl.
+ //Because Rule3 is defined in rule2.drl, this shouldn't remove any rule.
+ cs = new ChangeSetImpl();
+ cs.setKnowledgeDefinitionsRemoved(new HashMap<Resource, String>() {
+
+ {
+ Resource r = ResourceFactory.newUrlResource("http://localhost:9000/rule1.drl");
+ this.put(r, "rule3");
+ }
+ });
+
+ kagent.applyChangeSet(cs);
+
+
+ ksession = kbase.newStatefulKnowledgeSession();
+ list = new ArrayList<String>();
+ ksession.setGlobal("list", list);
+ ksession.fireAllRules();
+ ksession.dispose();
+
+ assertEquals(2, list.size());
+
+ assertTrue(list.contains("rule2"));
+ assertTrue(list.contains("rule3"));
+
+
+ //Finally, remove Rule3
+ cs = new ChangeSetImpl();
+ cs.setKnowledgeDefinitionsRemoved(new HashMap<Resource, String>() {
+
+ {
+ Resource r = ResourceFactory.newUrlResource("http://localhost:9000/rule2.drl");
+ this.put(r, "rule3");
+ }
+ });
+
+ kagent.applyChangeSet(cs);
+
+
+ ksession = kbase.newStatefulKnowledgeSession();
+ list = new ArrayList<String>();
+ ksession.setGlobal("list", list);
+ ksession.fireAllRules();
+ ksession.dispose();
+
+ assertEquals(1, list.size());
+
+ assertTrue(list.contains("rule2"));
+
+ }
+
+
+ private KnowledgeAgent createKAgent(KnowledgeBase kbase){
+ ResourceChangeScannerConfiguration sconf = ResourceFactory.getResourceChangeScannerService().newResourceChangeScannerConfiguration();
+ sconf.setProperty("drools.resource.scanner.interval", "2");
+ ResourceFactory.getResourceChangeScannerService().configure(sconf);
+
+ KnowledgeAgentConfiguration aconf = KnowledgeAgentFactory.newKnowledgeAgentConfiguration();
+ aconf.setProperty("drools.agent.scanDirectories", "true");
+ aconf.setProperty("drools.agent.scanResources", "true");
+ // Testing incremental build here
+ aconf.setProperty("drools.agent.newInstance", "false");
+ KnowledgeAgent kagent = KnowledgeAgentFactory.newKnowledgeAgent(
+ "test agent", kbase, aconf);
+
+ assertEquals("test agent", kagent.getName());
+
+ return kagent;
+ }
+
+ private String createCommonRule(String ruleName) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("rule ");
+ sb.append(ruleName);
+ sb.append("\n");
+ sb.append("when\n");
+ sb.append("then\n");
+ sb.append("list.add( drools.getRule().getName() );\n");
+ sb.append("end\n");
+
+ return sb.toString();
+ }
+
+ private String createDummyFunction(String functionName) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("function void ");
+ sb.append(functionName);
+ sb.append("(){\n");
+ sb.append(" System.out.println(\"Function executed\");\n");
+ sb.append("}\n");
+
+ return sb.toString();
+ }
+
+ private static void writePackage(Object pkg, File p1file)
+ throws IOException, FileNotFoundException {
+ FileOutputStream out = new FileOutputStream(p1file);
+ try {
+ DroolsStreamUtils.streamOut(out, pkg);
+ } finally {
+ out.close();
+ }
+ }
+
+ public static class ResultHandlerImpl implements ResultHandler {
+
+ Object object;
+
+ public void handleResult(Object object) {
+ this.object = object;
+ }
+
+ public Object getObject() {
+ return this.object;
+ }
+ }
+}
Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/agent/impl/KnowledgeAgentImpl.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/agent/impl/KnowledgeAgentImpl.java 2009-12-14 19:17:11 UTC (rev 30647)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/agent/impl/KnowledgeAgentImpl.java 2009-12-14 20:13:28 UTC (rev 30648)
@@ -38,6 +38,7 @@
import org.drools.io.impl.ResourceChangeNotifierImpl;
import org.drools.rule.Function;
import org.drools.rule.Package;
+import org.drools.rule.Query;
import org.drools.rule.Rule;
import org.drools.rule.TypeDeclaration;
import org.drools.runtime.KnowledgeSessionConfiguration;
@@ -56,898 +57,953 @@
* @author Mark Proctor, Sam Romano
*/
public class KnowledgeAgentImpl implements KnowledgeAgent,
- ResourceChangeListener {
+ ResourceChangeListener {
- private String name;
- private ResourceMap resourcesMap;
- private Set<Resource> resourceDirectories;
- private KnowledgeBase kbase;
- private ResourceChangeNotifierImpl notifier;
- private boolean newInstance;
- private SystemEventListener listener;
- private boolean scanDirectories;
- private LinkedBlockingQueue<ChangeSet> queue;
- private Thread thread;
- private ChangeSetNotificationDetector changeSetNotificationDetector;
- private SemanticModules semanticModules;
+ private String name;
+ private ResourceMap resourcesMap;
+ private Set<Resource> resourceDirectories;
+ private KnowledgeBase kbase;
+ private ResourceChangeNotifierImpl notifier;
+ private boolean newInstance;
+ private SystemEventListener listener;
+ private boolean scanDirectories;
+ private LinkedBlockingQueue<ChangeSet> queue;
+ private Thread thread;
+ private ChangeSetNotificationDetector changeSetNotificationDetector;
+ private SemanticModules semanticModules;
- /**
- * Default constructor for KnowledgeAgentImpl
- *
- * @param name
- * @param kbase
- * @param configuration
- */
- public KnowledgeAgentImpl(String name, KnowledgeBase kbase,
- KnowledgeAgentConfiguration configuration) {
- this.name = name;
- this.kbase = kbase;
- this.resourcesMap = new ResourceMap(this);
- this.resourceDirectories = new HashSet<Resource>();
- // this.listener = listener;
- this.listener = SystemEventListenerFactory.getSystemEventListener();
- this.queue = new LinkedBlockingQueue<ChangeSet>();
- boolean scanResources = false;
- boolean monitor = false;
- if (configuration != null) {
- // New Instance describes if we do incremental builds or not
- this.newInstance = ((KnowledgeAgentConfigurationImpl) configuration)
- .isNewInstance();
- this.notifier = (ResourceChangeNotifierImpl) ResourceFactory
- .getResourceChangeNotifierService();
- if (((KnowledgeAgentConfigurationImpl) configuration)
- .isMonitorChangeSetEvents()) {
- monitor = true;
- }
+ /**
+ * Default constructor for KnowledgeAgentImpl
+ *
+ * @param name
+ * @param kbase
+ * @param configuration
+ */
+ public KnowledgeAgentImpl(String name, KnowledgeBase kbase,
+ KnowledgeAgentConfiguration configuration) {
+ this.name = name;
+ this.kbase = kbase;
+ this.resourcesMap = new ResourceMap(this);
+ this.resourceDirectories = new HashSet<Resource>();
+ // this.listener = listener;
+ this.listener = SystemEventListenerFactory.getSystemEventListener();
+ this.queue = new LinkedBlockingQueue<ChangeSet>();
+ boolean scanResources = false;
+ boolean monitor = false;
+ if (configuration != null) {
+ // New Instance describes if we do incremental builds or not
+ this.newInstance = ((KnowledgeAgentConfigurationImpl) configuration).isNewInstance();
+ this.notifier = (ResourceChangeNotifierImpl) ResourceFactory.getResourceChangeNotifierService();
+ if (((KnowledgeAgentConfigurationImpl) configuration).isMonitorChangeSetEvents()) {
+ monitor = true;
+ }
- if (((KnowledgeAgentConfigurationImpl) configuration)
- .isScanDirectories()) {
- this.scanDirectories = true;
- }
+ if (((KnowledgeAgentConfigurationImpl) configuration).isScanDirectories()) {
+ this.scanDirectories = true;
+ }
- scanResources = ((KnowledgeAgentConfigurationImpl) configuration)
- .isScanResources();
- if (scanResources) {
- this.notifier.addResourceChangeMonitor(ResourceFactory
- .getResourceChangeScannerService());
- monitor = true; // if scanning, monitor must be true;
- }
- }
+ scanResources = ((KnowledgeAgentConfigurationImpl) configuration).isScanResources();
+ if (scanResources) {
+ this.notifier.addResourceChangeMonitor(ResourceFactory.getResourceChangeScannerService());
+ monitor = true; // if scanning, monitor must be true;
+ }
+ }
- monitorResourceChangeEvents(monitor);
+ monitorResourceChangeEvents(monitor);
- buildResourceMapping();
+ buildResourceMapping();
- this.listener
- .info("KnowledgeAgent created, with configuration:\nmonitorChangeSetEvents="
- + monitor
- + " scanResources="
- + scanResources
- + " scanDirectories="
- + this.scanDirectories
- + " newInstance=" + this.newInstance);
- }
+ this.listener.info("KnowledgeAgent created, with configuration:\nmonitorChangeSetEvents="
+ + monitor
+ + " scanResources="
+ + scanResources
+ + " scanDirectories="
+ + this.scanDirectories
+ + " newInstance=" + this.newInstance);
+ }
- public void setSystemEventListener(SystemEventListener listener) {
- this.listener = listener;
- }
+ public void setSystemEventListener(SystemEventListener listener) {
+ this.listener = listener;
+ }
- public void applyChangeSet(Resource resource) {
- applyChangeSet(getChangeSet(resource));
- }
+ public void applyChangeSet(Resource resource) {
+ applyChangeSet(getChangeSet(resource));
+ }
- public void applyChangeSet(ChangeSet changeSet) {
- synchronized (this.resourcesMap) {
- this.listener.info("KnowledgeAgent applying ChangeSet");
+ public void applyChangeSet(ChangeSet changeSet) {
+ synchronized (this.resourcesMap) {
+ this.listener.info("KnowledgeAgent applying ChangeSet");
- ChangeSetState changeSetState = new ChangeSetState();
- changeSetState.scanDirectories = this.scanDirectories;
- // incremental build is inverse of newInstance
- changeSetState.incrementalBuild = !(this.newInstance);
+ ChangeSetState changeSetState = new ChangeSetState();
+ changeSetState.scanDirectories = this.scanDirectories;
+ // incremental build is inverse of newInstance
+ changeSetState.incrementalBuild = !(this.newInstance);
- // Process the new ChangeSet
- processChangeSet(changeSet, changeSetState);
- // Rebuild or do an update to the KnowledgeBase
- buildKnowledgeBase(changeSetState);
- // Rebuild the resource mapping
- buildResourceMapping();
- }
- }
+ // Process the new ChangeSet
+ processChangeSet(changeSet, changeSetState);
+ // Rebuild or do an update to the KnowledgeBase
+ buildKnowledgeBase(changeSetState);
+ // Rebuild the resource mapping
+ buildResourceMapping();
+ }
+ }
- public void processChangeSet(Resource resource,
- ChangeSetState changeSetState) {
- processChangeSet(getChangeSet(resource), changeSetState);
- }
+ public void processChangeSet(Resource resource,
+ ChangeSetState changeSetState) {
+ processChangeSet(getChangeSet(resource), changeSetState);
+ }
- public void processChangeSet(ChangeSet changeSet,
- ChangeSetState changeSetState) {
- synchronized (this.resourcesMap) {
- /*
- * Process the added resources from a ChangeSet by subscribing to
- * the notifier and inserting a new ResourceMapping.
- */
- for (Resource resource : changeSet.getResourcesAdded()) {
- if (((InternalResource) resource).getResourceType() == ResourceType.CHANGE_SET) {
- // @TODO We should not ignore an added change set
- this.listener
- .debug("KnowledgeAgent processing sub ChangeSet="
- + resource);
- processChangeSet(resource, changeSetState);
- } else if (((InternalResource) resource).isDirectory()) {
- this.resourceDirectories.add(resource);
- this.listener
- .debug("KnowledgeAgent subscribing to directory="
- + resource);
- this.notifier.subscribeResourceChangeListener(this,
- resource);
- // if it's a dir, subscribe it's children first
- for (Resource child : ((InternalResource) resource)
- .listResources()) {
+ public void processChangeSet(ChangeSet changeSet,
+ ChangeSetState changeSetState) {
+ synchronized (this.resourcesMap) {
+ /*
+ * Process the added resources from a ChangeSet by subscribing to
+ * the notifier and inserting a new ResourceMapping.
+ */
+ for (Resource resource : changeSet.getResourcesAdded()) {
+ if (((InternalResource) resource).getResourceType() == ResourceType.CHANGE_SET) {
+ // @TODO We should not ignore an added change set
+ this.listener.debug("KnowledgeAgent processing sub ChangeSet="
+ + resource);
+ processChangeSet(resource, changeSetState);
+ } else if (((InternalResource) resource).isDirectory()) {
+ this.resourceDirectories.add(resource);
+ this.listener.debug("KnowledgeAgent subscribing to directory="
+ + resource);
+ this.notifier.subscribeResourceChangeListener(this,
+ resource);
+ // if it's a dir, subscribe it's children first
+ for (Resource child : ((InternalResource) resource).listResources()) {
- // ignore sub directories
- if (((InternalResource) child).isDirectory()) {
- continue;
- }
+ // ignore sub directories
+ if (((InternalResource) child).isDirectory()) {
+ continue;
+ }
- ((InternalResource) child)
- .setResourceType(((InternalResource) resource)
- .getResourceType());
- if (this.resourcesMap.addResourceMapping(child, true)
- && changeSetState.incrementalBuild) {
- changeSetState.addedResources.add(child);
- }
- }
- } else {
- if (this.resourcesMap.addResourceMapping(resource, true)
- && changeSetState.incrementalBuild) {
- changeSetState.addedResources.add(resource);
- }
- }
- }
+ ((InternalResource) child).setResourceType(((InternalResource) resource).getResourceType());
+ if (this.resourcesMap.addResourceMapping(child, true)
+ && changeSetState.incrementalBuild) {
+ changeSetState.addedResources.add(child);
+ }
+ }
+ } else {
+ if (this.resourcesMap.addResourceMapping(resource, true)
+ && changeSetState.incrementalBuild) {
+ // parse the reource using DlrParser. save the packageDescr into a Map<Resource,PackageDescr
+ changeSetState.addedResources.add(resource);
- /*
- * For those marked as removed by the ChangeSet, remove their
- * mappings, index them if we are doing incremental builds so the
- * incremental building process knows what to remove.
- */
- for (Resource resource : changeSet.getResourcesRemoved()) {
- if (((InternalResource) resource).getResourceType() == ResourceType.CHANGE_SET) {
- // @TODO Is this true? Shouldn't we just ignore it in
- // removed?
- processChangeSet(resource, changeSetState);
- } else if (changeSetState.scanDirectories
- && ((InternalResource) resource).isDirectory()) {
- this.listener
- .debug("KnowledgeAgent unsubscribing from directory resource="
- + resource);
- this.resourceDirectories.remove(resource);
- this.notifier.unsubscribeResourceChangeListener(this,
- resource);
- } else {
- ResourceMapEntry removedEntry = this.resourcesMap
- .removeResourceMapping(resource, true);
- if (removedEntry != null && changeSetState.incrementalBuild) {
- changeSetState.removedResourceMappings
- .add(removedEntry);
- }
- }
- }
+ }
+ }
+ }
- /*
- * For those marked as modified, remove their ResourceMapping,
- * attach it to the ChangeSetState, and add a new one - it will be
- * repopulated with the KnowledgeDefinitions later after rebuilding.
- * Process any modified ChangeSets - treat them as if they were new.
- */
- for (Resource resource : changeSet.getResourcesModified()) {
- if (((InternalResource) resource).getResourceType() == ResourceType.CHANGE_SET) {
- // processChangeSet(resource, changeSetState);
- continue;
- } else if (((InternalResource) resource).isDirectory()) {
- if (this.resourceDirectories.add(resource)) {
- this.listener
- .warning("KnowledgeAgent is subscribing to a modified directory="
- + resource
- + " when it should have already been subscribed");
- this.notifier.subscribeResourceChangeListener(this,
- resource);
- }
- // if it's a dir, subscribe it's children first
- for (Resource child : ((InternalResource) resource)
- .listResources()) {
+ /*
+ * For those marked as removed by the ChangeSet, remove their
+ * mappings, index them if we are doing incremental builds so the
+ * incremental building process knows what to remove.
+ */
+ for (Resource resource : changeSet.getResourcesRemoved()) {
+ if (((InternalResource) resource).getResourceType() == ResourceType.CHANGE_SET) {
+ // @TODO Is this true? Shouldn't we just ignore it in
+ // removed?
+ processChangeSet(resource, changeSetState);
+ } else if (changeSetState.scanDirectories
+ && ((InternalResource) resource).isDirectory()) {
+ this.listener.debug("KnowledgeAgent unsubscribing from directory resource="
+ + resource);
+ this.resourceDirectories.remove(resource);
+ this.notifier.unsubscribeResourceChangeListener(this,
+ resource);
+ } else {
+ ResourceMapEntry removedEntry = this.resourcesMap.removeResourceMapping(resource, true);
- // ignore sub directories
- if (((InternalResource) child).isDirectory()) {
- continue;
- }
+ if (removedEntry != null && changeSetState.incrementalBuild) {
+ changeSetState.removedResourceMappings.add(removedEntry);
+ }
+ }
+ }
- if (this.resourcesMap.addResourceMapping(child, true)) {
- ((InternalResource) child)
- .setResourceType(((InternalResource) resource)
- .getResourceType());
- if (changeSetState.incrementalBuild) {
- changeSetState.addedResources.add(child);
- }
- }
- }
- } else {
- ResourceMapEntry modifiedMapping = this.resourcesMap
- .removeResourceMapping(resource, false);
- if (modifiedMapping == null) {
- this.listener
- .warning("KnowledgeAgent subscribing to new resource="
- + resource
- + ", though it was marked as modified.");
- this.resourcesMap.addResourceMapping(resource, true);
- if (changeSetState.incrementalBuild) {
- changeSetState.addedResources.add(resource);
- }
- } else {
- /*
- * Put a new one, but no need to subscribe or update
- * since this will be done in the buildResourceMapping
- * later
- */
- this.resourcesMap.addResourceMapping(resource, false);
- if (changeSetState.incrementalBuild) {
- changeSetState.modifiedResourceMappings
- .add(modifiedMapping);
- }
- }
- }
- }
- }
- }
+ /*
+ * For those marked as modified, remove their ResourceMapping,
+ * attach it to the ChangeSetState, and add a new one - it will be
+ * repopulated with the KnowledgeDefinitions later after rebuilding.
+ * Process any modified ChangeSets - treat them as if they were new.
+ */
+ for (Resource resource : changeSet.getResourcesModified()) {
+ if (((InternalResource) resource).getResourceType() == ResourceType.CHANGE_SET) {
+ // processChangeSet(resource, changeSetState);
+ continue;
+ } else if (((InternalResource) resource).isDirectory()) {
+ if (this.resourceDirectories.add(resource)) {
+ this.listener.warning("KnowledgeAgent is subscribing to a modified directory="
+ + resource
+ + " when it should have already been subscribed");
+ this.notifier.subscribeResourceChangeListener(this,
+ resource);
+ }
+ // if it's a dir, subscribe it's children first
+ for (Resource child : ((InternalResource) resource).listResources()) {
- /**
- * Returns a ChangeSet based on a resource with a resource type of
- * ChangeSet.
- *
- * @param resource
- * A resource with the type set to ChangeSet
- * @return A ChangeSet that can be processed by this Agent.
- */
- public ChangeSet getChangeSet(Resource resource) {
- if (this.semanticModules == null) {
- this.semanticModules = new SemanticModules();
- this.semanticModules
- .addSemanticModule(new ChangeSetSemanticModule());
- }
+ // ignore sub directories
+ if (((InternalResource) child).isDirectory()) {
+ continue;
+ }
- XmlChangeSetReader reader = new XmlChangeSetReader(this.semanticModules);
- if (resource instanceof ClassPathResource) {
- reader.setClassLoader(((ClassPathResource) resource)
- .getClassLoader());
- } else {
- reader
- .setClassLoader(((AbstractRuleBase) (((KnowledgeBaseImpl) this.kbase).ruleBase))
- .getConfiguration().getClassLoader());
- }
+ if (this.resourcesMap.addResourceMapping(child, true)) {
+ ((InternalResource) child).setResourceType(((InternalResource) resource).getResourceType());
+ if (changeSetState.incrementalBuild) {
+ changeSetState.addedResources.add(child);
+ }
+ }
+ }
+ } else {
+ ResourceMapEntry modifiedMapping = this.resourcesMap.removeResourceMapping(resource, false);
+ if (modifiedMapping == null) {
+ this.listener.warning("KnowledgeAgent subscribing to new resource="
+ + resource
+ + ", though it was marked as modified.");
+ this.resourcesMap.addResourceMapping(resource, true);
+ if (changeSetState.incrementalBuild) {
+ changeSetState.addedResources.add(resource);
+ }
+ } else {
+ /*
+ * Put a new one, but no need to subscribe or update
+ * since this will be done in the buildResourceMapping
+ * later
+ */
+ this.resourcesMap.addResourceMapping(resource, false);
+ if (changeSetState.incrementalBuild) {
+ changeSetState.modifiedResourceMappings.add(modifiedMapping);
+ }
+ }
+ }
+ }
- ChangeSet changeSet = null;
- try {
- changeSet = reader.read(resource.getReader());
- } catch (Exception e) {
- this.listener.exception(new RuntimeException(
- "Unable to parse ChangeSet", e));
- }
- if (changeSet == null) {
- this.listener.exception(new RuntimeException(
- "Unable to parse ChangeSet"));
- }
- return changeSet;
- }
- /**
- * Keeps state information during the 'state' of a ChangeSet alteration so
- * past information can be kept along the way.
- *
- * @author Mark Proctor
- */
- public static class ChangeSetState {
- List<Resource> addedResources = new ArrayList<Resource>();
- List<ResourceMapEntry> removedResourceMappings = new ArrayList<ResourceMapEntry>();
- List<ResourceMapEntry> modifiedResourceMappings = new ArrayList<ResourceMapEntry>();
- boolean scanDirectories;
- boolean incrementalBuild;
- }
+ //Removes each knowledgeDefinition marked as deleted from resourceMap.
+ //The deletion from kbase will be performed after by buildKnowledgeBase()
+ for (Resource resource : changeSet.getKnowledgeDefinitionsRemoved().keySet()) {
- /**
- * This indexes the rules, flows, type declarations, etc against their
- * respective URLs if they have any, to allow more fine grained removal and
- * not just removing of an entire package
- */
- public void buildResourceMapping() {
- this.listener.debug("KnowledgeAgent building resource map");
- synchronized (this.resourcesMap) {
- RuleBase rbase = ((KnowledgeBaseImpl) this.kbase).ruleBase;
- /*
- * Iterate each package for the different types of
- * KnowledgeDefinitions we want to track
- */
- for (Package pkg : rbase.getPackages()) {
- for (Rule rule : pkg.getRules()) {
- Resource resource = rule.getResource();
- if (resource == null
- || !((InternalResource) resource).hasURL()) {
- this.listener
- .debug("KnowledgeAgent no resource mapped for rule="
- + rule);
- continue;
- }
- this.resourcesMap.putResourceMappingEntry(resource, rule);
- }
+ KnowledgeDefinition removedDefinition = this.resourcesMap.removeResourceMappingEntry(resource, changeSet.getKnowledgeDefinitionsRemoved().get(resource));
- for (Process process : pkg.getRuleFlows().values()) {
- Resource resource = ((org.drools.process.core.Process) process)
- .getResource();
- if (resource == null
- || !((InternalResource) resource).hasURL()) {
- this.listener
- .debug("KnowledgeAgent no resource mapped for process="
- + process);
- continue;
- }
- this.resourcesMap
- .putResourceMappingEntry(resource, process);
- }
+ if (removedDefinition == null) {
+ this.listener.warning("Knowledge definition "
+ + changeSet.getKnowledgeDefinitionsRemoved().get(resource)
+ + " couldn't be removed from "
+ + resource);
+ } else{
+ //set the removedDefinition into the changeSetState so it
+ //can be removed from kbase by buildKnowledgeBase().
+ changeSetState.removedKnowledgeDefinitions.put(resource, removedDefinition);
+ }
+ }
- for (TypeDeclaration typeDeclaration : pkg
- .getTypeDeclarations().values()) {
- Resource resource = typeDeclaration.getResource();
- if (resource == null
- || !((InternalResource) resource).hasURL()) {
- this.listener
- .debug("KnowledgeAgent no resource mapped for typeDeclaration="
- + typeDeclaration);
- continue;
- }
- this.resourcesMap.putResourceMappingEntry(resource,
- typeDeclaration);
- }
+ }
+ }
- for (Function function : pkg.getFunctions().values()) {
- Resource resource = function.getResource();
- if (resource == null
- || !((InternalResource) resource).hasURL()) {
- this.listener
- .debug("KnowledgeAgent no resource mapped for function="
- + function);
- continue;
- }
- this.resourcesMap.putResourceMappingEntry(resource,
- function);
- }
- }
- }
- }
+ /**
+ * Returns a ChangeSet based on a resource with a resource type of
+ * ChangeSet.
+ *
+ * @param resource
+ * A resource with the type set to ChangeSet
+ * @return A ChangeSet that can be processed by this Agent.
+ */
+ public ChangeSet getChangeSet(Resource resource) {
+ if (this.semanticModules == null) {
+ this.semanticModules = new SemanticModules();
+ this.semanticModules.addSemanticModule(new ChangeSetSemanticModule());
+ }
- public KnowledgeBase getKnowledgeBase() {
- synchronized (this.resourcesMap) {
- return this.kbase;
- }
- }
+ XmlChangeSetReader reader = new XmlChangeSetReader(this.semanticModules);
+ if (resource instanceof ClassPathResource) {
+ reader.setClassLoader(((ClassPathResource) resource).getClassLoader());
+ } else {
+ reader.setClassLoader(((AbstractRuleBase) (((KnowledgeBaseImpl) this.kbase).ruleBase)).getConfiguration().getClassLoader());
+ }
- public StatelessKnowledgeSession newStatelessKnowledgeSession() {
- return new StatelessKnowledgeSessionImpl(null, this, null);
- }
+ ChangeSet changeSet = null;
+ try {
+ changeSet = reader.read(resource.getReader());
+ } catch (Exception e) {
+ this.listener.exception(new RuntimeException(
+ "Unable to parse ChangeSet", e));
+ }
+ if (changeSet == null) {
+ this.listener.exception(new RuntimeException(
+ "Unable to parse ChangeSet"));
+ }
+ return changeSet;
+ }
- public StatelessKnowledgeSession newStatelessKnowledgeSession(
- KnowledgeSessionConfiguration conf) {
- return new StatelessKnowledgeSessionImpl(null, this, conf);
- }
+ /**
+ * Keeps state information during the 'state' of a ChangeSet alteration so
+ * past information can be kept along the way.
+ *
+ * @author Mark Proctor
+ */
+ public static class ChangeSetState {
- public void resourcesChanged(ChangeSet changeSet) {
- try {
- this.listener
- .debug("KnowledgeAgent received ChangeSet changed notification");
- this.queue.put(changeSet);
- } catch (InterruptedException e) {
- this.listener
- .exception(new RuntimeException(
- "KnowledgeAgent error while adding ChangeSet notification to queue",
- e));
- }
- }
+ List<Resource> addedResources = new ArrayList<Resource>();
+ List<ResourceMapEntry> removedResourceMappings = new ArrayList<ResourceMapEntry>();
+ List<ResourceMapEntry> modifiedResourceMappings = new ArrayList<ResourceMapEntry>();
+ Map<Resource, KnowledgeDefinition> removedKnowledgeDefinitions = new HashMap<Resource, KnowledgeDefinition>();
+ boolean scanDirectories;
+ boolean incrementalBuild;
+ }
- /**
- * Rebuilds and creates a new KnowledgeBase for this KnowledgeAgent when
- * called based on the ChangeSet that comes in and if newInstance is set to
- * true. If incremental building or KnowledgeBase updates is on, then this
- * will attempt to update the KnowledgeBase instead.
- *
- * @param changeSetState
- * The state that the ChangeSet performed
- */
- public void buildKnowledgeBase(ChangeSetState changeSetState) {
- this.listener
- .debug("KnowledgeAgent rebuilding KnowledgeBase using ChangeSet");
- synchronized (this.resourcesMap) {
+ /**
+ * This indexes the rules, flows, type declarations, etc against their
+ * respective URLs if they have any, to allow more fine grained removal and
+ * not just removing of an entire package
+ */
+ public void buildResourceMapping() {
+ this.listener.debug("KnowledgeAgent building resource map");
+ synchronized (this.resourcesMap) {
+ RuleBase rbase = ((KnowledgeBaseImpl) this.kbase).ruleBase;
+ /*
+ * Iterate each package for the different types of
+ * KnowledgeDefinitions we want to track
+ */
+ for (Package pkg : rbase.getPackages()) {
+ for (Rule rule : pkg.getRules()) {
+ Resource resource = rule.getResource();
+ if (resource == null
+ || !((InternalResource) resource).hasURL()) {
+ this.listener.debug("KnowledgeAgent no resource mapped for rule="
+ + rule);
+ continue;
+ }
+ this.resourcesMap.putResourceMappingEntry(resource, rule);
+ }
- /*
- * Do the following only if we are building a new instance,
- * otherwise, do an incremental build/update
- */
- if (this.newInstance) {
- rebuildResources(changeSetState);
- } else {
- incrementalBuildResources(changeSetState);
- }
+ for (Process process : pkg.getRuleFlows().values()) {
+ Resource resource = ((org.drools.process.core.Process) process).getResource();
+ if (resource == null
+ || !((InternalResource) resource).hasURL()) {
+ this.listener.debug("KnowledgeAgent no resource mapped for process="
+ + process);
+ continue;
+ }
+ this.resourcesMap.putResourceMappingEntry(resource, process);
+ }
- /*
- * If the ruleBase is sequential, after rebuilding or incremental
- * update, do an ordering of the ReteooBuilder
- */
- InternalRuleBase ruleBase = (InternalRuleBase) ((KnowledgeBaseImpl) this.kbase).ruleBase;
- synchronized (ruleBase.getPackagesMap()) {
- if (ruleBase.getConfiguration().isSequential()) {
- ruleBase.getReteooBuilder().order();
- }
- }
- }
- this.listener
- .debug("KnowledgeAgent finished rebuilding KnowledgeBase using ChangeSet");
- }
+ for (TypeDeclaration typeDeclaration : pkg.getTypeDeclarations().values()) {
+ Resource resource = typeDeclaration.getResource();
+ if (resource == null
+ || !((InternalResource) resource).hasURL()) {
+ this.listener.debug("KnowledgeAgent no resource mapped for typeDeclaration="
+ + typeDeclaration);
+ continue;
+ }
+ this.resourcesMap.putResourceMappingEntry(resource,
+ typeDeclaration);
+ }
- /**
- * This method is meant to rebuild the entire KnowledgeBase. Cached
- * references outside of this Agent will no longer be valid to the current
- * KnowledgeBase
- *
- * @param changeSetState
- * The ChangeSetState
- */
- private void rebuildResources(ChangeSetState changeSetState) {
+ for (Function function : pkg.getFunctions().values()) {
+ Resource resource = function.getResource();
+ if (resource == null
+ || !((InternalResource) resource).hasURL()) {
+ this.listener.debug("KnowledgeAgent no resource mapped for function="
+ + function);
+ continue;
+ }
+ this.resourcesMap.putResourceMappingEntry(resource,
+ function);
+ }
+ }
+ }
+ }
- if (!this.newInstance) {
- listener
- .warning("KnowledgeAgent rebuilding KnowledgeBase when newInstance is false");
- }
+ public KnowledgeBase getKnowledgeBase() {
+ synchronized (this.resourcesMap) {
+ return this.kbase;
+ }
+ }
- /*
- * Rebuild a new knowledge base. Try to use the old configuration if
- * possible
- */
- if (this.kbase != null) {
- this.kbase = KnowledgeBaseFactory
- .newKnowledgeBase(((InternalRuleBase) ((KnowledgeBaseImpl) this.kbase).ruleBase)
- .getConfiguration());
- } else {
- this.kbase = KnowledgeBaseFactory.newKnowledgeBase();
- }
+ public StatelessKnowledgeSession newStatelessKnowledgeSession() {
+ return new StatelessKnowledgeSessionImpl(null, this, null);
+ }
- addResourcesToKnowledgeBase(resourcesMap.getAllResources());
+ public StatelessKnowledgeSession newStatelessKnowledgeSession(
+ KnowledgeSessionConfiguration conf) {
+ return new StatelessKnowledgeSessionImpl(null, this, conf);
+ }
- this.listener
- .info("KnowledgeAgent new KnowledgeBase now built and in use");
- }
+ public void resourcesChanged(ChangeSet changeSet) {
+ try {
+ this.listener.debug("KnowledgeAgent received ChangeSet changed notification");
+ this.queue.put(changeSet);
+ } catch (InterruptedException e) {
+ this.listener.exception(new RuntimeException(
+ "KnowledgeAgent error while adding ChangeSet notification to queue",
+ e));
+ }
+ }
- /**
- * This method is meant to incrementally build or update the current
- * KnowledgeBase.
- *
- * @param changeSet
- * @param changeSetState
- */
- private void incrementalBuildResources(ChangeSetState changeSetState) {
- if (this.newInstance) {
- this.listener
- .warning("KnowledgeAgent incremental build of KnowledgeBase when newInstance is true");
- }
- // Incrementally rebuild the resources
- synchronized (this.resourcesMap) {
- this.listener
- .info("KnowledgeAgent performing an incremental build of the ChangeSet");
+ /**
+ * Rebuilds and creates a new KnowledgeBase for this KnowledgeAgent when
+ * called based on the ChangeSet that comes in and if newInstance is set to
+ * true. If incremental building or KnowledgeBase updates is on, then this
+ * will attempt to update the KnowledgeBase instead.
+ *
+ * @param changeSetState
+ * The state that the ChangeSet performed
+ */
+ public void buildKnowledgeBase(ChangeSetState changeSetState) {
+ this.listener.debug("KnowledgeAgent rebuilding KnowledgeBase using ChangeSet");
+ synchronized (this.resourcesMap) {
- // Create the knowledge base if one does not exist
- if (this.kbase == null) {
- this.kbase = KnowledgeBaseFactory.newKnowledgeBase();
- }
+ /*
+ * Do the following only if we are building a new instance,
+ * otherwise, do an incremental build/update
+ */
+ if (this.newInstance) {
+ rebuildResources(changeSetState);
+ } else {
+ incrementalBuildResources(changeSetState);
+ }
- // Remove all rules from the resources removed and also those
- // modified
- for (ResourceMapEntry resourceMapEntry : changeSetState.removedResourceMappings) {
- for (KnowledgeDefinition kd : resourceMapEntry
- .getKnowledgeDefinitions()) {
- removeKnowledgeDefinitionFromBase(kd);
- }
- }
+ /*
+ * If the ruleBase is sequential, after rebuilding or incremental
+ * update, do an ordering of the ReteooBuilder
+ */
+ InternalRuleBase ruleBase = (InternalRuleBase) ((KnowledgeBaseImpl) this.kbase).ruleBase;
+ synchronized (ruleBase.getPackagesMap()) {
+ if (ruleBase.getConfiguration().isSequential()) {
+ ruleBase.getReteooBuilder().order();
+ }
+ }
+ }
+ this.listener.debug("KnowledgeAgent finished rebuilding KnowledgeBase using ChangeSet");
+ }
- for (ResourceMapEntry resourceMapEntry : changeSetState.modifiedResourceMappings) {
- for (KnowledgeDefinition kd : resourceMapEntry
- .getKnowledgeDefinitions()) {
- removeKnowledgeDefinitionFromBase(kd);
- }
- changeSetState.addedResources.add(resourceMapEntry
- .getResource());
- }
+ /**
+ * This method is meant to rebuild the entire KnowledgeBase. Cached
+ * references outside of this Agent will no longer be valid to the current
+ * KnowledgeBase
+ *
+ * @param changeSetState
+ * The ChangeSetState
+ */
+ private void rebuildResources(ChangeSetState changeSetState) {
- /*
- * Adds both the newly added resources and the modified resources
- */
- addResourcesToKnowledgeBase(changeSetState.addedResources);
- }
- this.listener
- .info("KnowledgeAgent incremental build of KnowledgeBase finished and in use");
- }
+ if (!this.newInstance) {
+ listener.warning("KnowledgeAgent rebuilding KnowledgeBase when newInstance is false");
+ }
- /**
- *
- * @param kd
- */
- private void removeKnowledgeDefinitionFromBase(KnowledgeDefinition kd) {
- if (kd instanceof Rule) {
- Rule rule = (Rule) kd;
- this.listener.debug("KnowledgeAgent removing Rule=" + rule
- + " from package=" + rule.getPackageName());
- this.kbase.removeRule(rule.getPackageName(), rule.getName());
- } else if (kd instanceof Process) {
- Process process = (Process) kd;
- this.listener.debug("KnowledgeAgent removing Process=" + process);
- this.kbase.removeProcess(process.getId());
- } else if (kd instanceof TypeDeclaration) {
- // @TODO Handle Type Declarations... is there a way to remove this?
- } else if (kd instanceof Function) {
- // @TODO functions and type declarations
- }
- }
+ /*
+ * Rebuild a new knowledge base. Try to use the old configuration if
+ * possible
+ */
+ if (this.kbase != null) {
+ this.kbase = KnowledgeBaseFactory.newKnowledgeBase(((InternalRuleBase) ((KnowledgeBaseImpl) this.kbase).ruleBase).getConfiguration());
+ } else {
+ this.kbase = KnowledgeBaseFactory.newKnowledgeBase();
+ }
- /**
- * Adds the resources to the current KnowledgeBase on this KnowledgeAgent.
- * Only uses the KnowledgeBuilder when necessary.
- *
- * @param resources
- */
- private void addResourcesToKnowledgeBase(Collection<Resource> resources) {
+ addResourcesToKnowledgeBase(resourcesMap.getAllResources());
- KnowledgeBuilder kbuilder = null;
- List<Package> packages = new ArrayList<Package>();
- for (Resource resource : resources) {
- /*
- * If it's not a PKG, clearly we need the knowledge builder, so
- * build it
- */
- if (((InternalResource) resource).getResourceType() != ResourceType.PKG) {
- this.listener.debug("KnowledgeAgent building resource="
- + resource);
- if (kbuilder == null) {
- kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
- }
- kbuilder.add(resource, ((InternalResource) resource)
- .getResourceType());
- } else {
- // For PKG (.pks) just add them
- this.listener.debug("KnowledgeAgent obtaining pkg resource="
- + resource);
+ this.listener.info("KnowledgeAgent new KnowledgeBase now built and in use");
+ }
- InputStream is = null;
- try {
- // .pks are handled as a special case.
- is = resource.getInputStream();
- Object object = DroolsStreamUtils.streamIn(is);
- Package pkg = null;
- if (object instanceof KnowledgePackage) {
- pkg = ((KnowledgePackageImp) object).pkg;
+ /**
+ * This method is meant to incrementally build or update the current
+ * KnowledgeBase.
+ *
+ * @param changeSet
+ * @param changeSetState
+ */
+ private void incrementalBuildResources(ChangeSetState changeSetState) {
+ if (this.newInstance) {
+ this.listener.warning("KnowledgeAgent incremental build of KnowledgeBase when newInstance is true");
+ }
+ // Incrementally rebuild the resources
+ synchronized (this.resourcesMap) {
+ this.listener.info("KnowledgeAgent performing an incremental build of the ChangeSet");
- } else {
- pkg = (Package) object;
- }
- for (Rule rule : pkg.getRules()) {
- rule.setResource(resource);
- }
+ // Create the knowledge base if one does not exist
+ if (this.kbase == null) {
+ this.kbase = KnowledgeBaseFactory.newKnowledgeBase();
+ }
- packages.add(pkg);
- } catch (Exception e) {
- this.listener
- .exception(new RuntimeException(
- "KnowledgeAgent exception while trying to deserialize KnowledgeDefinitionsPackage ",
- e));
- } finally {
- try {
- if (is != null) {
- is.close();
- }
- } catch (IOException e) {
- this.listener
- .exception(new RuntimeException(
- "KnowledgeAgent exception while trying to close KnowledgeDefinitionsPackage ",
- e));
- }
- }
- }
- }
+ // Remove all rules from the resources removed and also those
+ // modified
+ for (ResourceMapEntry resourceMapEntry : changeSetState.removedResourceMappings) {
+ for (KnowledgeDefinition kd : resourceMapEntry.getKnowledgeDefinitions()) {
+ removeKnowledgeDefinitionFromBase(kd);
+ }
+ }
- if (kbuilder != null) {
- // Log any errors we come across
- if (kbuilder.hasErrors()) {
- this.listener.warning(
- "KnowledgeAgent has KnowledgeBuilder errors ", kbuilder
- .getErrors());
- }
- this.listener
- .debug("KnowledgeAgent adding KnowledgePackages from KnowledgeBuilder");
- this.kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
- }
- /*
- * Add all the packages we found, but did not build, from the resources
- * now
- */
- for (Package pkg : packages) {
- this.listener
- .debug("KnowledgeAgent adding KnowledgeDefinitionsPackage "
- + pkg.getName());
- ((KnowledgeBaseImpl) this.kbase).ruleBase.addPackage(pkg);
- }
- }
+ for (ResourceMapEntry resourceMapEntry : changeSetState.modifiedResourceMappings) {
+ for (KnowledgeDefinition kd : resourceMapEntry.getKnowledgeDefinitions()) {
+ removeKnowledgeDefinitionFromBase(kd);
+ }
+ changeSetState.addedResources.add(resourceMapEntry.getResource());
+ }
- /*
- * (non-Javadoc)
- *
- * @see org.drools.agent.KnowledgeAgent#getName()
- */
- public String getName() {
- return this.name;
- }
+ /*
+ * Adds both the newly added resources and the modified resources
+ */
+ addResourcesToKnowledgeBase(changeSetState.addedResources);
- /**
- * Kicks off the monitoring service for handling ResourceChangeEvents on a
- * separate process.
- *
- * @boolean monitor True if monitoring should take place, false otherwise
- */
- public void monitorResourceChangeEvents(boolean monitor) {
- if (!monitor && this.changeSetNotificationDetector != null) {
- // we are running, but it wants to stop
- // this will stop the thread
- this.changeSetNotificationDetector.stop();
- this.thread.interrupt();
- this.changeSetNotificationDetector = null;
- } else if (monitor && this.changeSetNotificationDetector == null) {
- this.changeSetNotificationDetector = new ChangeSetNotificationDetector(
- this, this.queue, this.listener);
- this.thread = new Thread(this.changeSetNotificationDetector);
- this.thread.start();
- }
- }
- /**
- * A class to monitor and handle ChangeSets fired by the
- * ResourceChangeNotifier on a separate service (or process).
- *
- * @author Mark Proctor
- */
- public static class ChangeSetNotificationDetector implements Runnable {
- private LinkedBlockingQueue<ChangeSet> queue;
- private volatile boolean monitor;
- private KnowledgeAgentImpl kagent;
- private SystemEventListener listener;
+ //Remove the individual knowledgeDefinitions marked as deleted by the
+ //changeset
+ for (Resource resource : changeSetState.removedKnowledgeDefinitions.keySet()) {
+ removeKnowledgeDefinitionFromBase(changeSetState.removedKnowledgeDefinitions.get(resource));
+ }
+ }
+ this.listener.info("KnowledgeAgent incremental build of KnowledgeBase finished and in use");
+ }
- public ChangeSetNotificationDetector(KnowledgeAgentImpl kagent,
- LinkedBlockingQueue<ChangeSet> queue,
- SystemEventListener listener) {
- this.queue = queue;
- this.kagent = kagent;
- this.listener = listener;
- this.monitor = true;
- }
+ /**
+ *
+ * @param kd
+ */
+ private void removeKnowledgeDefinitionFromBase(KnowledgeDefinition kd) {
+ if (kd instanceof Query) {
+ // @TODO Queries. The way JavaDialectRuntimeData removes rules
+ // doesn't apply to Queries (Even when Query is a subclass of Rule).
+ } else if (kd instanceof Rule) {
+ Rule rule = (Rule) kd;
+ this.listener.debug("KnowledgeAgent removing Rule=" + rule
+ + " from package=" + rule.getPackageName());
+ this.kbase.removeRule(rule.getPackageName(), rule.getName());
+ } else if (kd instanceof Process) {
+ Process process = (Process) kd;
+ this.listener.debug("KnowledgeAgent removing Process=" + process);
+ this.kbase.removeProcess(process.getId());
+ } else if (kd instanceof TypeDeclaration) {
+ // @TODO Handle Type Declarations... is there a way to remove this?
+ } else if (kd instanceof Function) {
+ // @TODO functions and type declarations
+ }
+ }
- public void stop() {
- this.monitor = false;
- }
+ /**
+ * Adds the resources to the current KnowledgeBase on this KnowledgeAgent.
+ * Only uses the KnowledgeBuilder when necessary.
+ *
+ * @param resources
+ */
+ private void addResourcesToKnowledgeBase(Collection<Resource> resources) {
- public void run() {
- if (this.monitor) {
- this.listener
- .info("KnowledegAgent has started listening for ChangeSet notifications");
- }
- while (this.monitor) {
- Exception exception = null;
- try {
- kagent.applyChangeSet(this.queue.take());
- } catch (InterruptedException e) {
- exception = e;
- }
- Thread.yield();
- if (this.monitor && exception != null) {
- this.listener
- .exception(new RuntimeException(
- "KnowledgeAgent ChangeSet notification thread has been interrupted, but shutdown was not scheduled",
- exception));
- }
- }
+ KnowledgeBuilder kbuilder = null;
+ List<Package> packages = new ArrayList<Package>();
+ for (Resource resource : resources) {
+ /*
+ * If it's not a PKG, clearly we need the knowledge builder, so
+ * build it
+ */
+ if (((InternalResource) resource).getResourceType() != ResourceType.PKG) {
+ this.listener.debug("KnowledgeAgent building resource="
+ + resource);
+ if (kbuilder == null) {
+ kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
+ }
+ kbuilder.add(resource, ((InternalResource) resource).getResourceType());
+ } else {
+ // For PKG (.pks) just add them
+ this.listener.debug("KnowledgeAgent obtaining pkg resource="
+ + resource);
- this.listener
- .info("KnowledegAgent has stopped listening for ChangeSet notifications");
- }
- }
+ InputStream is = null;
+ try {
+ // .pks are handled as a special case.
+ is = resource.getInputStream();
+ Object object = DroolsStreamUtils.streamIn(is);
+ Package pkg = null;
+ if (object instanceof KnowledgePackage) {
+ pkg = ((KnowledgePackageImp) object).pkg;
- /**
- * Maps a set of KnowledgeDefinitions to the resource that created them so
- * we can perform incremental building of a KnowledgeBase.
- *
- * @author Mark Proctor
- */
- public static class ResourceMapEntry {
- private final Resource resource;
- private Set<KnowledgeDefinition> knowledgeDefinitions;
+ } else {
+ pkg = (Package) object;
+ }
+ for (Rule rule : pkg.getRules()) {
+ rule.setResource(resource);
+ }
- public ResourceMapEntry(Resource resource) {
- this.resource = resource;
- this.knowledgeDefinitions = new HashSet<KnowledgeDefinition>();
- }
+ packages.add(pkg);
+ } catch (Exception e) {
+ this.listener.exception(new RuntimeException(
+ "KnowledgeAgent exception while trying to deserialize KnowledgeDefinitionsPackage ",
+ e));
+ } finally {
+ try {
+ if (is != null) {
+ is.close();
+ }
+ } catch (IOException e) {
+ this.listener.exception(new RuntimeException(
+ "KnowledgeAgent exception while trying to close KnowledgeDefinitionsPackage ",
+ e));
+ }
+ }
+ }
+ }
- public Resource getResource() {
- return resource;
- }
+ if (kbuilder != null) {
+ // Log any errors we come across
+ if (kbuilder.hasErrors()) {
+ this.listener.warning(
+ "KnowledgeAgent has KnowledgeBuilder errors ", kbuilder.getErrors());
+ }
+ this.listener.debug("KnowledgeAgent adding KnowledgePackages from KnowledgeBuilder");
+ this.kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
+ }
+ /*
+ * Add all the packages we found, but did not build, from the resources
+ * now
+ */
+ for (Package pkg : packages) {
+ this.listener.debug("KnowledgeAgent adding KnowledgeDefinitionsPackage "
+ + pkg.getName());
+ ((KnowledgeBaseImpl) this.kbase).ruleBase.addPackage(pkg);
+ }
+ }
- public Set<KnowledgeDefinition> getKnowledgeDefinitions() {
- return knowledgeDefinitions;
- }
- }
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.drools.agent.KnowledgeAgent#getName()
+ */
+ public String getName() {
+ return this.name;
+ }
- /**
- * A Bidirectional map of Resources to KnowledgeDefinitions.This allows you
- * to go both ways due to a KnowledgeDefinition being able to be overwritten
- * by multiple resources, and we only want to track the Resource responsible
- * as tagged by the KnowledgeBase for creating that resource.
- *
- * @author Sam Romano
- */
- public static class ResourceMap {
- private final KnowledgeAgentImpl agent;
- private Map<Resource, ResourceMapEntry> resourceMappings;
- private Map<KnowledgeDefinition, Resource> knowledgeDefinitionMappings;
+ /**
+ * Kicks off the monitoring service for handling ResourceChangeEvents on a
+ * separate process.
+ *
+ * @boolean monitor True if monitoring should take place, false otherwise
+ */
+ public void monitorResourceChangeEvents(boolean monitor) {
+ if (!monitor && this.changeSetNotificationDetector != null) {
+ // we are running, but it wants to stop
+ // this will stop the thread
+ this.changeSetNotificationDetector.stop();
+ this.thread.interrupt();
+ this.changeSetNotificationDetector = null;
+ } else if (monitor && this.changeSetNotificationDetector == null) {
+ this.changeSetNotificationDetector = new ChangeSetNotificationDetector(
+ this, this.queue, this.listener);
+ this.thread = new Thread(this.changeSetNotificationDetector);
+ this.thread.start();
+ }
+ }
- public ResourceMap(KnowledgeAgentImpl agent) {
- this.agent = agent;
- this.resourceMappings = new HashMap<Resource, ResourceMapEntry>();
- this.knowledgeDefinitionMappings = new HashMap<KnowledgeDefinition, Resource>();
- }
+ /**
+ * A class to monitor and handle ChangeSets fired by the
+ * ResourceChangeNotifier on a separate service (or process).
+ *
+ * @author Mark Proctor
+ */
+ public static class ChangeSetNotificationDetector implements Runnable {
- /**
- * @param resource
- * The resource to add to the Map
- * @param notify
- * True if you want to notify the listener, false otherwise.
- * @return True if it was added, false otherwise.
- */
- public boolean addResourceMapping(Resource resource, boolean notify) {
- ResourceMapEntry rsrcMapping = this.resourceMappings.get(resource);
- if (rsrcMapping == null) {
- rsrcMapping = new ResourceMapEntry(resource);
- this.resourceMappings.put(resource, rsrcMapping);
- if (notify) {
- this.agent.listener
- .debug("KnowledgeAgent notifier subscribing to resource="
- + resource);
+ private LinkedBlockingQueue<ChangeSet> queue;
+ private volatile boolean monitor;
+ private KnowledgeAgentImpl kagent;
+ private SystemEventListener listener;
- this.agent.notifier.subscribeResourceChangeListener(agent,
- resource);
- }
- return true;
- }
- return false;
- }
+ public ChangeSetNotificationDetector(KnowledgeAgentImpl kagent,
+ LinkedBlockingQueue<ChangeSet> queue,
+ SystemEventListener listener) {
+ this.queue = queue;
+ this.kagent = kagent;
+ this.listener = listener;
+ this.monitor = true;
+ }
- /**
- * Returns the old ResourceMapping mapped to the Resource. If it finds a
- * resource mapping, it will unsubscribe from the
- * ResourceChangeListener.
- *
- * @param resource
- * @param unsubscribe
- * True if you want to unsubscribe on a successful removal,
- * false otherwise... normally false on attempting to remove
- * entries for Modifications
- * @return The old resourceMapping with the KnowledgeDefinitions that it
- * use to have
- */
- public ResourceMapEntry removeResourceMapping(Resource resource,
- boolean unsubscribe) {
- this.agent.listener
- .debug("KnowledgeAgent removing mappings for resource="
- + resource + " with unsubscribe=" + unsubscribe);
- ResourceMapEntry rsrcMapping = this.resourceMappings
- .remove(resource);
- if (rsrcMapping != null) {
- if (unsubscribe) {
- this.agent.listener
- .debug("KnowledgeAgent notifier unsubscribing to resource="
- + resource);
- this.agent.notifier.unsubscribeResourceChangeListener(
- agent, resource);
- }
+ public void stop() {
+ this.monitor = false;
+ }
- for (KnowledgeDefinition kd : rsrcMapping.knowledgeDefinitions) {
- this.knowledgeDefinitionMappings.remove(kd);
- }
- }
- return rsrcMapping;
- }
+ public void run() {
+ if (this.monitor) {
+ this.listener.info("KnowledegAgent has started listening for ChangeSet notifications");
+ }
+ while (this.monitor) {
+ Exception exception = null;
+ try {
+ kagent.applyChangeSet(this.queue.take());
+ } catch (InterruptedException e) {
+ exception = e;
+ }
+ Thread.yield();
+ if (this.monitor && exception != null) {
+ this.listener.exception(new RuntimeException(
+ "KnowledgeAgent ChangeSet notification thread has been interrupted, but shutdown was not scheduled",
+ exception));
+ }
+ }
- public Set<Resource> getAllResources() {
- return this.resourceMappings.keySet();
- }
+ this.listener.info("KnowledegAgent has stopped listening for ChangeSet notifications");
+ }
+ }
- /**
- * Maps a resource to the knowledge Definition, and vice versa for
- * bidirectional mapping and integrity. If the resource is not mapped at
- * all, this will subscribe the agent specified to this ResourceMap to
- * listen for those changes.
- *
- * @param resource
- * @param kd
- * @return The old resource the KnowledgeDefinition use to be mapped to
- * if it was
- */
- public Resource putResourceMappingEntry(Resource resource,
- KnowledgeDefinition kd) {
- ResourceMapEntry rsrcMapping = this.resourceMappings.get(resource);
- if (rsrcMapping == null) {
- addResourceMapping(resource, true);
- rsrcMapping = this.resourceMappings.get(resource);
- }
- /*
- * If adding this returns true, then we need to map it the other
- * way, otherwise don't bother with the bidirectional logic and
- * waste time - essentially we know we mapped it before.
- */
- if (rsrcMapping.knowledgeDefinitions.add(kd)) {
- this.agent.listener.debug("KnowledgeAgent mapping resource="
- + resource + " to KnowledgeDefinition=" + kd);
+ /**
+ * Maps a set of KnowledgeDefinitions to the resource that created them so
+ * we can perform incremental building of a KnowledgeBase.
+ *
+ * @author Mark Proctor
+ */
+ public static class ResourceMapEntry {
- Resource oldRsrc = this.knowledgeDefinitionMappings.put(kd,
- resource);
- /*
- * If an oldRsrc exists, make sure we remove the kd from that
- * mapping - but dont unsubscribe from it as the resource is
- * still being compiled in the KnowledgeBase, we need to know of
- * its updates
- */
- ResourceMapEntry oldRsrcMapping = this.resourceMappings
- .get(oldRsrc);
- if (oldRsrcMapping != null) {
- this.agent.listener
- .debug("KnowledgeAgent removing reference from resource="
- + oldRsrc + " to KnowledgeDefinition=" + kd);
- oldRsrcMapping.getKnowledgeDefinitions().remove(kd);
- }
+ private final Resource resource;
+ private Set<KnowledgeDefinition> knowledgeDefinitions;
- return oldRsrc;
- }
- return null;
- }
+ public ResourceMapEntry(Resource resource) {
+ this.resource = resource;
+ this.knowledgeDefinitions = new HashSet<KnowledgeDefinition>();
+ }
- public boolean removeResourceMappingEntry(Resource resource,
- KnowledgeDefinition kd) {
- ResourceMapEntry rsrcMapping = this.resourceMappings.get(resource);
- if (rsrcMapping != null) {
- /*
- * If the above didn't remove the kd, then we don't do the
- * bidirectional removal
- */
- if (rsrcMapping.getKnowledgeDefinitions().remove(kd)) {
- this.knowledgeDefinitionMappings.remove(kd);
- return true;
- }
- }
- return false;
- }
- }
+ public Resource getResource() {
+ return resource;
+ }
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Object#finalize()
- */
- @Override
- protected void finalize() throws Throwable {
- // users should turn off monitoring, but just in case when this class is
- // GC'd we turn off the thread
- if (this.changeSetNotificationDetector != null) {
- this.changeSetNotificationDetector.monitor = false;
- }
- }
+ public Set<KnowledgeDefinition> getKnowledgeDefinitions() {
+ return knowledgeDefinitions;
+ }
+ }
-}
\ No newline at end of file
+ /**
+ * A Bidirectional map of Resources to KnowledgeDefinitions.This allows you
+ * to go both ways due to a KnowledgeDefinition being able to be overwritten
+ * by multiple resources, and we only want to track the Resource responsible
+ * as tagged by the KnowledgeBase for creating that resource.
+ *
+ * @author Sam Romano
+ */
+ public static class ResourceMap {
+
+ private final KnowledgeAgentImpl agent;
+ private Map<Resource, ResourceMapEntry> resourceMappings;
+ private Map<KnowledgeDefinition, Resource> knowledgeDefinitionMappings;
+
+ public ResourceMap(KnowledgeAgentImpl agent) {
+ this.agent = agent;
+ this.resourceMappings = new HashMap<Resource, ResourceMapEntry>();
+ this.knowledgeDefinitionMappings = new HashMap<KnowledgeDefinition, Resource>();
+ }
+
+ /**
+ * @param resource
+ * The resource to add to the Map
+ * @param notify
+ * True if you want to notify the listener, false otherwise.
+ * @return True if it was added, false otherwise.
+ */
+ public boolean addResourceMapping(Resource resource, boolean notify) {
+ ResourceMapEntry rsrcMapping = this.resourceMappings.get(resource);
+ if (rsrcMapping == null) {
+ rsrcMapping = new ResourceMapEntry(resource);
+ this.resourceMappings.put(resource, rsrcMapping);
+ if (notify) {
+ this.agent.listener.debug("KnowledgeAgent notifier subscribing to resource="
+ + resource);
+
+ this.agent.notifier.subscribeResourceChangeListener(agent,
+ resource);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the old ResourceMapping mapped to the Resource. If it finds a
+ * resource mapping, it will unsubscribe from the
+ * ResourceChangeListener.
+ *
+ * @param resource
+ * @param unsubscribe
+ * True if you want to unsubscribe on a successful removal,
+ * false otherwise... normally false on attempting to remove
+ * entries for Modifications
+ * @return The old resourceMapping with the KnowledgeDefinitions that it
+ * use to have
+ */
+ public ResourceMapEntry removeResourceMapping(Resource resource,
+ boolean unsubscribe) {
+ this.agent.listener.debug("KnowledgeAgent removing mappings for resource="
+ + resource + " with unsubscribe=" + unsubscribe);
+ ResourceMapEntry rsrcMapping = this.resourceMappings.remove(resource);
+ if (rsrcMapping != null) {
+ if (unsubscribe) {
+ this.agent.listener.debug("KnowledgeAgent notifier unsubscribing to resource="
+ + resource);
+ this.agent.notifier.unsubscribeResourceChangeListener(
+ agent, resource);
+ }
+
+ for (KnowledgeDefinition kd : rsrcMapping.knowledgeDefinitions) {
+ this.knowledgeDefinitionMappings.remove(kd);
+ }
+ }
+ return rsrcMapping;
+ }
+
+ public Set<Resource> getAllResources() {
+ return this.resourceMappings.keySet();
+ }
+
+ /**
+ * Maps a resource to the knowledge Definition, and vice versa for
+ * bidirectional mapping and integrity. If the resource is not mapped at
+ * all, this will subscribe the agent specified to this ResourceMap to
+ * listen for those changes.
+ *
+ * @param resource
+ * @param kd
+ * @return The old resource the KnowledgeDefinition use to be mapped to
+ * if it was
+ */
+ public Resource putResourceMappingEntry(Resource resource,
+ KnowledgeDefinition kd) {
+ ResourceMapEntry rsrcMapping = this.resourceMappings.get(resource);
+ if (rsrcMapping == null) {
+ addResourceMapping(resource, true);
+ rsrcMapping = this.resourceMappings.get(resource);
+ }
+ /*
+ * If adding this returns true, then we need to map it the other
+ * way, otherwise don't bother with the bidirectional logic and
+ * waste time - essentially we know we mapped it before.
+ */
+ if (rsrcMapping.knowledgeDefinitions.add(kd)) {
+ this.agent.listener.debug("KnowledgeAgent mapping resource="
+ + resource + " to KnowledgeDefinition=" + kd);
+
+ Resource oldRsrc = this.knowledgeDefinitionMappings.put(kd,
+ resource);
+ /*
+ * If an oldRsrc exists, make sure we remove the kd from that
+ * mapping - but dont unsubscribe from it as the resource is
+ * still being compiled in the KnowledgeBase, we need to know of
+ * its updates
+ */
+ ResourceMapEntry oldRsrcMapping = this.resourceMappings.get(oldRsrc);
+ if (oldRsrcMapping != null) {
+ this.agent.listener.debug("KnowledgeAgent removing reference from resource="
+ + oldRsrc + " to KnowledgeDefinition=" + kd);
+ oldRsrcMapping.getKnowledgeDefinitions().remove(kd);
+ }
+
+ return oldRsrc;
+ }
+ return null;
+ }
+
+ public boolean removeResourceMappingEntry(Resource resource,
+ KnowledgeDefinition kd) {
+ ResourceMapEntry rsrcMapping = this.resourceMappings.get(resource);
+ if (rsrcMapping != null) {
+ /*
+ * If the above didn't remove the kd, then we don't do the
+ * bidirectional removal
+ */
+ if (rsrcMapping.getKnowledgeDefinitions().remove(kd)) {
+ this.knowledgeDefinitionMappings.remove(kd);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Removes a resource's knowledge definition from the maps given its name.
+ * To find the right knowledge definition, this method uses
+ * {@link #getKnowledgeDefinitionByName(org.drools.io.Resource, java.lang.String).
+ *
+ * <b>Author:<b> Esteban Aliverti.
+ *
+ * @param resource the resource where the kdefinition will be seached.
+ * @param knowledgeDefinitionName the name of the kdefinition.
+ * @return The removed kdefinition if it was found and succesfully
+ * removed. Oterwise, null.
+ */
+ public KnowledgeDefinition removeResourceMappingEntry(Resource resource,
+ String knowledgeDefinitionName) {
+
+ ResourceMapEntry rsrcMapping = this.resourceMappings.get(resource);
+
+
+ if (rsrcMapping != null) {
+ KnowledgeDefinition foundDefinition = this.getKnowledgeDefinitionByName(resource, knowledgeDefinitionName);
+ if (rsrcMapping.getKnowledgeDefinitions().remove(foundDefinition)) {
+ this.knowledgeDefinitionMappings.remove(foundDefinition);
+ return foundDefinition;
+ }
+ }
+
+
+ return null;
+ }
+
+ /**
+ * Return a knowledgeDeinition of a resource fiven its name. If
+ * the resource doesn't have a kdefinition with he specified name, null
+ * is returned.
+ * If the resouce has more than one kdefinition with the given name
+ * (according to some tests I made, functions and queries could have
+ * the same name), the first kdefinition found is returned. The search
+ * order is Rule/Query, Function, TypeDeclaration.
+ *
+ * <b>Author:<b> Esteban Aliverti.
+ *
+ * @param resource the resource where the kdefinition will be seached.
+ * @param knowledgeDefinitionName the name of the kdefinition.
+ * @return the kdefinition with the given name, or null if it doesn't exist.
+ *
+ */
+ private KnowledgeDefinition getKnowledgeDefinitionByName(Resource resource, String knowledgeDefinitionName) {
+
+ KnowledgeDefinition foundDefinition = null;
+
+ ResourceMapEntry rsrcMapping = this.resourceMappings.get(resource);
+ if (rsrcMapping != null) {
+
+
+ for (KnowledgeDefinition kd : rsrcMapping.knowledgeDefinitions) {
+
+ String currentKDefinitionName = "";
+
+ if (kd instanceof Rule) {
+ //rules and queries
+ currentKDefinitionName = ((Rule) kd).getName();
+ } else if (kd instanceof Function) {
+ currentKDefinitionName = ((Function) kd).getName();
+ } else if (kd instanceof TypeDeclaration) {
+ currentKDefinitionName = ((TypeDeclaration) kd).getTypeName();
+ }
+
+ if (currentKDefinitionName.equals(knowledgeDefinitionName)) {
+ foundDefinition = kd;
+ break;
+ }
+
+ }
+
+ }
+
+ return foundDefinition;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#finalize()
+ */
+ @Override
+ protected void finalize() throws Throwable {
+ // users should turn off monitoring, but just in case when this class is
+ // GC'd we turn off the thread
+ if (this.changeSetNotificationDetector != null) {
+ this.changeSetNotificationDetector.monitor = false;
+
+ }
+ }
+}
Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/io/impl/ChangeSetImpl.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/io/impl/ChangeSetImpl.java 2009-12-14 19:17:11 UTC (rev 30647)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/io/impl/ChangeSetImpl.java 2009-12-14 20:13:28 UTC (rev 30648)
@@ -2,6 +2,8 @@
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
import org.drools.ChangeSet;
import org.drools.definition.KnowledgeDefinition;
@@ -11,7 +13,10 @@
private Collection<Resource> resourcesRemoved = Collections.<Resource>emptyList();
private Collection<Resource> resourcesAdded = Collections.<Resource>emptyList();
private Collection<Resource> resourcesModified = Collections.<Resource>emptyList();
- private Collection<KnowledgeDefinition> knowledgeDefinitionsRemoved = Collections.<KnowledgeDefinition>emptyList();
+
+ //Map of removed kdefinitions. The key is the resource and the string is
+ //the knowledgeDefinition's name.
+ private Map<Resource,String> knowledgeDefinitionsRemoved = new HashMap<Resource, String>();
public ChangeSetImpl() {
@@ -41,12 +46,13 @@
this.resourcesModified = resourcesModified;
}
- public void setKnowledgeDefinitionsRemoved(Collection<KnowledgeDefinition> knowledgeDefinitionsRemoved) {
+ public Map<Resource, String> getKnowledgeDefinitionsRemoved() {
+ return knowledgeDefinitionsRemoved;
+ }
+
+ public void setKnowledgeDefinitionsRemoved(Map<Resource, String> knowledgeDefinitionsRemoved) {
this.knowledgeDefinitionsRemoved = knowledgeDefinitionsRemoved;
}
+
- public Collection<KnowledgeDefinition> getKnowledgeDefinitionsRemoved() {
- return knowledgeDefinitionsRemoved;
- }
-
}
More information about the jboss-svn-commits
mailing list