package org.drools.integrationtests;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Reader;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.drools.Cheese;
import org.drools.FactA;
import org.drools.FactB;
import org.drools.Person;
import org.drools.PersonInterface;
import org.drools.Precondition;
import org.drools.RuleBase;
import org.drools.RuleBaseConfiguration;
import org.drools.RuleBaseFactory;
import org.drools.StatefulSession;
import org.drools.WorkingMemory;
import org.drools.compiler.PackageBuilder;
import org.drools.compiler.PackageBuilderConfiguration;
import org.drools.rule.Package;
public class DynamicRulesTest extends TestCase {
protected RuleBase getRuleBase() throws Exception {
return RuleBaseFactory.newRuleBase( RuleBase.RETEOO,
null );
}
protected RuleBase getRuleBase(final RuleBaseConfiguration config) throws Exception {
return RuleBaseFactory.newRuleBase( RuleBase.RETEOO,
config );
}
public void testDynamicRuleAdditions() throws Exception {
Reader reader = new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic1.drl" ) );
PackageBuilder builder = new PackageBuilder();
builder.addPackageFromDrl( reader );
final Package pkg1 = serialisePackage( builder.getPackage() );
final RuleBase ruleBase = getRuleBase();
ruleBase.addPackage( pkg1 );
final WorkingMemory workingMemory = ruleBase.newStatefulSession();
workingMemory.setGlobal( "total",
new Integer( 0 ) );
final List list = new ArrayList();
workingMemory.setGlobal( "list",
list );
// Adding person in advance. There is no Person() object
// type node in memory yet, but the rule engine is supposed
// to handle that correctly
final PersonInterface bob = new Person( "bob",
"stilton" );
bob.setStatus( "Not evaluated" );
workingMemory.insert( bob );
final Cheese stilton = new Cheese( "stilton",
5 );
workingMemory.insert( stilton );
final Cheese cheddar = new Cheese( "cheddar",
5 );
workingMemory.insert( cheddar );
workingMemory.fireAllRules();
assertEquals( 1,
list.size() );
assertEquals( "stilton",
list.get( 0 ) );
reader = new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic2.drl" ) );
builder = new PackageBuilder();
builder.addPackageFromDrl( reader );
final Package pkg2 = serialisePackage( builder.getPackage() );
ruleBase.addPackage( pkg2 );
assertEquals( 3,
list.size() );
assertEquals( "stilton",
list.get( 0 ) );
assertTrue( "cheddar".equals( list.get( 1 ) ) || "cheddar".equals( list.get( 2 ) ) );
assertTrue( "stilton".equals( list.get( 1 ) ) || "stilton".equals( list.get( 2 ) ) );
list.clear();
reader = new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic3.drl" ) );
builder = new PackageBuilder();
builder.addPackageFromDrl( reader );
final Package pkg3 = serialisePackage( builder.getPackage() );
ruleBase.addPackage( pkg3 );
// Package 3 has a rule working on Person instances.
// As we added person instance in advance, rule should fire now
Assert.assertEquals( "Rule from package 3 should have been fired",
"match Person ok",
bob.getStatus() );
assertEquals( 1,
list.size() );
assertEquals( bob,
list.get( 0 ) );
reader = new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic4.drl" ) );
builder = new PackageBuilder();
builder.addPackageFromDrl( reader );
final Package pkg4 = serialisePackage( builder.getPackage() );
ruleBase.addPackage( pkg4 );
Assert.assertEquals( "Rule from package 4 should have been fired",
"Who likes Stilton ok",
bob.getStatus() );
assertEquals( 2,
list.size() );
assertEquals( bob,
list.get( 1 ) );
}
public void testDynamicRuleRemovals() throws Exception {
final PackageBuilder builder = new PackageBuilder();
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic1.drl" ) ) );
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic3.drl" ) ) );
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic4.drl" ) ) );
final Package pkg = serialisePackage( builder.getPackage() );
org.drools.reteoo.ReteooRuleBase reteooRuleBase = null;
// org.drools.leaps.LeapsRuleBase leapsRuleBase = null;
final RuleBase ruleBase = getRuleBase();
reteooRuleBase = (org.drools.reteoo.ReteooRuleBase) ruleBase;
ruleBase.addPackage( pkg );
final PackageBuilder builder2 = new PackageBuilder();
builder2.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic2.drl" ) ) );
ruleBase.addPackage( serialisePackage( builder2.getPackage() ) );
final WorkingMemory workingMemory = ruleBase.newStatefulSession();
final List list = new ArrayList();
workingMemory.setGlobal( "list",
list );
final PersonInterface bob = new Person( "bob",
"stilton" );
bob.setStatus( "Not evaluated" );
workingMemory.insert( bob );
final Cheese stilton1 = new Cheese( "stilton",
5 );
workingMemory.insert( stilton1 );
final Cheese stilton2 = new Cheese( "stilton",
3 );
workingMemory.insert( stilton2 );
final Cheese stilton3 = new Cheese( "stilton",
1 );
workingMemory.insert( stilton3 );
final Cheese cheddar = new Cheese( "cheddar",
5 );
workingMemory.insert( cheddar );
assertEquals( 15,
workingMemory.getAgenda().getActivations().length );
reteooRuleBase.removeRule( "org.drools.test",
"Who likes Stilton" );
assertEquals( 12,
workingMemory.getAgenda().getActivations().length );
reteooRuleBase.removeRule( "org.drools.test",
"like cheese" );
// reteooRuleBase.removeRule( "org.drools.test",
// "like cheese2" );
final Cheese muzzarela = new Cheese( "muzzarela",
5 );
assertEquals( 8,
workingMemory.getAgenda().getActivations().length );
workingMemory.insert( muzzarela );
assertEquals( 9,
workingMemory.getAgenda().getActivations().length );
reteooRuleBase.removePackage( "org.drools.test" );
assertEquals( 0,
workingMemory.getAgenda().getActivations().length );
}
public void testDynamicRuleRemovalsUnusedWorkingMemory() throws Exception {
final PackageBuilder builder = new PackageBuilder();
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic1.drl" ) ) );
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic2.drl" ) ) );
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic3.drl" ) ) );
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic4.drl" ) ) );
final Package pkg = builder.getPackage();
org.drools.reteoo.ReteooRuleBase reteooRuleBase = null;
final RuleBase ruleBase = getRuleBase();
reteooRuleBase = (org.drools.reteoo.ReteooRuleBase) ruleBase;
ruleBase.addPackage( pkg );
final WorkingMemory workingMemory = ruleBase.newStatefulSession();
if ( reteooRuleBase != null ) {
assertEquals( 1,
reteooRuleBase.getPackages().length );
assertEquals( 5,
reteooRuleBase.getPackages()[0].getRules().length );
reteooRuleBase.removeRule( "org.drools.test",
"Who likes Stilton" );
assertEquals( 4,
reteooRuleBase.getPackages()[0].getRules().length );
reteooRuleBase.removeRule( "org.drools.test",
"like cheese" );
assertEquals( 3,
reteooRuleBase.getPackages()[0].getRules().length );
reteooRuleBase.removePackage( "org.drools.test" );
assertEquals( 0,
reteooRuleBase.getPackages().length );
}
}
public void testDynamicFunction() throws Exception {
PackageBuilder builder = new PackageBuilder();
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_DynamicFunction1.drl" ) ) );
//FIXME JBRULES-1258 serialising a package breaks function removal -- left the serialisation commented out for now
//final Package pkg = serialisePackage( builder.getPackage() );
final Package pkg = builder.getPackage();
final RuleBase ruleBase = getRuleBase();
ruleBase.addPackage( pkg );
final WorkingMemory workingMemory = ruleBase.newStatefulSession();
final List list = new ArrayList();
workingMemory.setGlobal( "list",
list );
final Cheese stilton = new Cheese( "stilton",
5 );
workingMemory.insert( stilton );
workingMemory.fireAllRules();
assertEquals( new Integer( 5 ),
list.get( 0 ) );
// Check a function can be removed from a package.
// Once removed any efforts to use it should throw an Exception
ruleBase.removeFunction( "org.drools.test",
"addFive" );
final Cheese cheddar = new Cheese( "cheddar",
5 );
workingMemory.insert( cheddar );
try {
workingMemory.fireAllRules();
fail( "Function should have been removed and NoClassDefFoundError thrown from the Consequence" );
} catch ( final NoClassDefFoundError e ) {
}
// Check a new function can be added to replace an old function
builder = new PackageBuilder();
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_DynamicFunction2.drl" ) ) );
ruleBase.addPackage( serialisePackage( builder.getPackage() ) );
final Cheese brie = new Cheese( "brie",
5 );
workingMemory.insert( brie );
workingMemory.fireAllRules();
assertEquals( new Integer( 6 ),
list.get( 1 ) );
builder = new PackageBuilder();
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_DynamicFunction3.drl" ) ) );
ruleBase.addPackage( serialisePackage( builder.getPackage() ) );
final Cheese feta = new Cheese( "feta",
5 );
workingMemory.insert( feta );
workingMemory.fireAllRules();
assertEquals( new Integer( 5 ),
list.get( 2 ) );
}
public void testRemovePackage() throws Exception {
final PackageBuilder builder = new PackageBuilder();
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_RemovePackage.drl" ) ) );
final RuleBase ruleBase = getRuleBase();
final String packageName = builder.getPackage().getName();
ruleBase.addPackage( serialisePackage( builder.getPackage() ) );
final WorkingMemory workingMemory = ruleBase.newStatefulSession();
workingMemory.insert( new Precondition( "genericcode",
"genericvalue" ) );
workingMemory.fireAllRules();
final RuleBase ruleBaseWM = workingMemory.getRuleBase();
ruleBaseWM.removePackage( packageName );
final PackageBuilder builder1 = new PackageBuilder();
builder1.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_RemovePackage.drl" ) ) );
ruleBaseWM.addPackage( serialisePackage( builder1.getPackage() ) );
workingMemory.fireAllRules();
ruleBaseWM.removePackage( packageName );
ruleBaseWM.addPackage( serialisePackage( builder1.getPackage() ) );
ruleBaseWM.removePackage( packageName );
ruleBaseWM.addPackage( serialisePackage( builder1.getPackage() ) );
}
public void testDynamicRules() throws Exception {
final RuleBase ruleBase = getRuleBase();
final WorkingMemory workingMemory = ruleBase.newStatefulSession();
final Cheese a = new Cheese( "stilton",
10 );
final Cheese b = new Cheese( "stilton",
15 );
final Cheese c = new Cheese( "stilton",
20 );
workingMemory.insert( a );
workingMemory.insert( b );
workingMemory.insert( c );
final PackageBuilder builder = new PackageBuilder();
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_DynamicRules.drl" ) ) );
final Package pkg = builder.getPackage();
ruleBase.addPackage( serialisePackage( pkg ) );
workingMemory.fireAllRules();
}
public void testDynamicRules2() throws Exception {
final RuleBase ruleBase = getRuleBase();
final WorkingMemory workingMemory = ruleBase.newStatefulSession();
// Assert some simple facts
final FactA a = new FactA( "hello",
new Integer( 1 ),
new Float( 3.14 ) );
final FactB b = new FactB( "hello",
new Integer( 2 ),
new Float( 6.28 ) );
workingMemory.insert( a );
workingMemory.insert( b );
final PackageBuilder builder = new PackageBuilder();
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_DynamicRules2.drl" ) ) );
final Package pkg = serialisePackage( builder.getPackage() );
ruleBase.addPackage( pkg );
workingMemory.fireAllRules();
}
public void testRuleBaseAddRemove() throws Exception {
RuleBase ruleBase = RuleBaseFactory.newRuleBase();
//add and remove
PackageBuilder builder = new PackageBuilder();
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic1.drl" ) ) );
Package pkg = serialisePackage( builder.getPackage() );
ruleBase.addPackage( pkg );
ruleBase.removePackage( pkg.getName() );
//add and remove again
builder = new PackageBuilder();
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic1.drl" ) ) );
pkg = serialisePackage( builder.getPackage() );
ruleBase.addPackage( pkg );
ruleBase.removePackage( pkg.getName() );
}
public void testClassLoaderSwitchsUsingConf() throws Exception {
try {
// Creates first class loader and use it to load fact classes
ClassLoader loader1 = new SubvertedClassLoader( new URL[]{getClass().getResource( "/" )},
this.getClass().getClassLoader() );
Class cheeseClass = loader1.loadClass( "org.drools.Cheese" );
PackageBuilderConfiguration conf = new PackageBuilderConfiguration( loader1 );
PackageBuilder builder = new PackageBuilder( conf );
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic1.drl" ) ) );
// must set the classloader for rulebase conf too
RuleBaseConfiguration rbconf = new RuleBaseConfiguration( loader1 );
RuleBase ruleBase = RuleBaseFactory.newRuleBase( rbconf );
Package pkg = serialisePackage( builder.getPackage() );
ruleBase.addPackage( pkg );
StatefulSession wm = ruleBase.newStatefulSession();
wm.insert( cheeseClass.newInstance() );
wm.fireAllRules();
// Creates second class loader and use it to load fact classes
ClassLoader loader2 = new SubvertedClassLoader( new URL[]{getClass().getResource( "/" )},
this.getClass().getClassLoader() );
cheeseClass = loader2.loadClass( "org.drools.Cheese" );
conf = new PackageBuilderConfiguration( loader2 );
builder = new PackageBuilder( conf );
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic1.drl" ) ) );
rbconf = new RuleBaseConfiguration( loader2 );
ruleBase = RuleBaseFactory.newRuleBase( rbconf );
pkg = serialisePackage( builder.getPackage() );
ruleBase.addPackage( pkg );
wm = ruleBase.newStatefulSession();
wm.insert( cheeseClass.newInstance() );
wm.fireAllRules();
} catch ( ClassCastException cce ) {
cce.printStackTrace();
fail( "No ClassCastException should be raised." );
}
}
public void testClassLoaderSwitchsUsingContext() throws Exception {
try {
// Creates first class loader and use it to load fact classes
ClassLoader original = Thread.currentThread().getContextClassLoader();
ClassLoader loader1 = new SubvertedClassLoader( new URL[]{getClass().getResource( "/" )},
this.getClass().getClassLoader() );
Thread.currentThread().setContextClassLoader( loader1 );
Class cheeseClass = loader1.loadClass( "org.drools.Cheese" );
PackageBuilder builder = new PackageBuilder();
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic1.drl" ) ) );
RuleBase ruleBase = RuleBaseFactory.newRuleBase();
Package pkg = serialisePackage( builder.getPackage() );
ruleBase.addPackage( pkg );
StatefulSession wm = ruleBase.newStatefulSession();
wm.insert( cheeseClass.newInstance() );
wm.fireAllRules();
// Creates second class loader and use it to load fact classes
ClassLoader loader2 = new SubvertedClassLoader( new URL[]{getClass().getResource( "/" )},
this.getClass().getClassLoader() );
Thread.currentThread().setContextClassLoader( loader2 );
cheeseClass = loader2.loadClass( "org.drools.Cheese" );
builder = new PackageBuilder();
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_Dynamic1.drl" ) ) );
ruleBase = RuleBaseFactory.newRuleBase();
pkg = serialisePackage( builder.getPackage() );
ruleBase.addPackage( pkg );
wm = ruleBase.newStatefulSession();
wm.insert( cheeseClass.newInstance() );
wm.fireAllRules();
Thread.currentThread().setContextClassLoader( original );
} catch ( ClassCastException cce ) {
cce.printStackTrace();
fail( "No ClassCastException should be raised." );
}
}
public class SubvertedClassLoader extends URLClassLoader {
private static final long serialVersionUID = 400L;
public SubvertedClassLoader(final URL[] urls,
final ClassLoader parentClassLoader) {
super( urls,
parentClassLoader );
}
protected synchronized Class loadClass(String name,
boolean resolve) throws ClassNotFoundException {
// First, check if the class has already been loaded
Class c = findLoadedClass( name );
if ( c == null ) {
try {
c = findClass( name );
} catch ( ClassNotFoundException e ) {
c = super.loadClass( name,
resolve );
}
}
return c;
}
}
protected Package serialisePackage(Package pkg) {
try {
byte[] bytes = serializeOut( pkg );
return (Package) serializeIn( bytes );
} catch ( Exception e ) {
throw new RuntimeException( "trouble serialising package.", e);
}
}
protected Object serializeIn(final byte[] bytes) throws IOException,
ClassNotFoundException {
final ObjectInput in = new ObjectInputStream( new ByteArrayInputStream( bytes ) );
final Object obj = in.readObject();
in.close();
return obj;
}
protected byte[] serializeOut(final Object obj) throws IOException {
// Serialize to a byte array
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
final ObjectOutput out = new ObjectOutputStream( bos );
out.writeObject( obj );
out.close();
// Get the bytes of the serialized object
final byte[] bytes = bos.toByteArray();
return bytes;
}
}