Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package org.drools.integrationtests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import org.drools.Person;
import org.drools.RuleBase;
import org.drools.RuleBaseConfiguration;
import org.drools.RuleBaseFactory;
import org.drools.WorkingMemory;
import org.drools.common.InternalFactHandle;
import org.drools.compiler.PackageBuilder;
import org.drools.rule.Package;
import org.junit.Test;

public class LRUnlinkingTest {

@Test
public void multipleJoinsUsingSameOTN() throws Exception {

final PackageBuilder builder = new PackageBuilder();
builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "test_LRUnlinking.drl" ) ) );
final Package pkg = builder.getPackage();

final RuleBaseConfiguration conf = new RuleBaseConfiguration();
conf.setLRUnlinkingEnabled( true );
RuleBase ruleBase = RuleBaseFactory.newRuleBase( conf );

ruleBase.addPackage( pkg );
ruleBase = SerializationHelper.serializeObject( ruleBase );

final WorkingMemory wmOne = ruleBase.newStatefulSession();
final WorkingMemory wmTwo = ruleBase.newStatefulSession();

final List<Person> listOne = new ArrayList<Person>();
final List<Person> listTwo = new ArrayList<Person>();

wmOne.setGlobal( "results",
listOne );
wmTwo.setGlobal( "results",
listTwo );

Person name = new Person();
Person likes = new Person();
Person age = new Person();
Person hair = new Person();
Person happy = new Person();
Person match = new Person();

name.setName( "Ana" );
likes.setLikes( "Chocolate" );
age.setAge( 30 );
hair.setHair( "brown" );
happy.setHappy( true );

match.setName( "Leo" );
match.setLikes( "Chocolate" );
match.setAge( 30 );
match.setHair( "brown" );
match.setHappy( true );

// WM One - first round of inserts
wmOne.insert( name );
wmOne.insert( likes );
wmOne.insert( age );

wmOne.fireAllRules();

assertEquals( "Should not have fired",
0,
listOne.size() );

// WM Two - first round o inserts
wmTwo.insert( name );
wmTwo.insert( likes );
wmTwo.insert( age );

wmTwo.fireAllRules();

assertEquals( "Should not have fired",
0,
listTwo.size() );

wmOne.insert( hair );
wmOne.insert( happy );
InternalFactHandle matchHandle = (InternalFactHandle) wmOne.insert( match );

wmOne.fireAllRules();

assertTrue( "Should have fired",
listOne.size() > 0);

assertEquals("Should have inserted the match Person",
matchHandle.getObject(),
listOne.get( 0 ));

wmTwo.fireAllRules();

assertEquals( "Should not have fired",
0,
listTwo.size() );

wmTwo.insert( hair );
wmTwo.insert( happy );
wmTwo.insert( match );

wmTwo.fireAllRules();

assertTrue( "Should have fired",
listTwo.size() > 0);

}

}
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package org.drools.integrationtests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;

import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;

import org.drools.Cheese;
import org.drools.Cheesery;
import org.drools.KnowledgeBase;
Expand All @@ -36,16 +36,13 @@
import org.drools.command.runtime.BatchExecutionCommandImpl;
import org.drools.compiler.PackageBuilder;
import org.drools.definition.KnowledgePackage;
import org.drools.impl.StatefulKnowledgeSessionImpl;
import org.drools.io.Resource;
import org.drools.io.ResourceFactory;
import org.drools.rule.Package;
import org.drools.runtime.ExecutionResults;
import org.drools.runtime.StatefulKnowledgeSession;
import org.drools.runtime.StatelessKnowledgeSession;
import org.drools.runtime.StatelessKnowledgeSessionResults;
import org.drools.runtime.rule.impl.FlatQueryResults;
import org.drools.spi.GlobalResolver;
import org.junit.Test;

public class StatelessSessionTest {
final List list = new ArrayList();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.drools;

global java.util.List results;

rule "Test LR unlinking with multiple joins on the same object type"
when
$p1 : Person ($name : name != null)
$p2 : Person ($likes : likes != null)
$p3 : Person ($age : age != null)
$p4 : Person ($hair : hair != null)
$p5 : Person ($happy : happy != null)
$p6 : Person (name != $name,
likes == $likes,
age == $age,
hair == $hair,
happy == $happy)
then
results.add( $p6 );
end
49 changes: 49 additions & 0 deletions drools-core/src/main/java/org/drools/RuleBaseConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.Properties;

import org.drools.builder.conf.ClassLoaderCacheOption;
import org.drools.builder.conf.LRUnlinkingOption;
import org.drools.common.AgendaGroupFactory;
import org.drools.common.ArrayAgendaGroupFactory;
import org.drools.common.PriorityQueueAgendaGroupFactory;
Expand Down Expand Up @@ -101,6 +102,7 @@
* drools.multithreadEvaluation = &lt;true|false&gt;
* drools.mbeans = &lt;enabled|disabled&gt;
* drools.classLoaderCacheEnabled = &lt;true|false&gt;
* drools.lrUnlinkingEnabled = &lt;true|false&gt;
* </pre>
*/
public class RuleBaseConfiguration
Expand Down Expand Up @@ -132,6 +134,7 @@ public class RuleBaseConfiguration
private String consequenceExceptionHandler;
private String ruleBaseUpdateHandler;
private boolean classLoaderCacheEnabled;
private boolean lrUnlinkingEnabled;

private EventProcessingOption eventProcessingMode;

Expand Down Expand Up @@ -181,6 +184,7 @@ public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt( maxThreads );
out.writeObject( eventProcessingMode );
out.writeBoolean( classLoaderCacheEnabled );
out.writeBoolean( lrUnlinkingEnabled );
}

public void readExternal(ObjectInput in) throws IOException,
Expand Down Expand Up @@ -208,6 +212,7 @@ public void readExternal(ObjectInput in) throws IOException,
maxThreads = in.readInt();
eventProcessingMode = (EventProcessingOption) in.readObject();
classLoaderCacheEnabled = in.readBoolean();
lrUnlinkingEnabled = in.readBoolean();
}

/**
Expand Down Expand Up @@ -299,6 +304,8 @@ public void setProperty(String name,
setMBeansEnabled( MBeansOption.isEnabled( value ) );
} else if ( name.equals( ClassLoaderCacheOption.PROPERTY_NAME ) ) {
setClassLoaderCacheEnabled( StringUtils.isEmpty( value ) ? true : Boolean.valueOf( value ) );
} else if ( name.equals( LRUnlinkingOption.PROPERTY_NAME ) ) {
setLRUnlinkingEnabled( StringUtils.isEmpty( value ) ? false : Boolean.valueOf( value ) );
}
}

Expand Down Expand Up @@ -352,6 +359,8 @@ public String getProperty(String name) {
return isMBeansEnabled() ? "enabled" : "disabled";
} else if ( name.equals( ClassLoaderCacheOption.PROPERTY_NAME ) ) {
return Boolean.toString( isClassLoaderCacheEnabled() );
} else if ( name.equals( LRUnlinkingOption.PROPERTY_NAME ) ) {
return Boolean.toString( isLRUnlinkingEnabled() );
}

return null;
Expand Down Expand Up @@ -448,6 +457,10 @@ private void init(Properties properties,

setClassLoaderCacheEnabled( Boolean.valueOf( this.chainedProperties.getProperty( ClassLoaderCacheOption.PROPERTY_NAME,
"true" ) ) );

setLRUnlinkingEnabled( Boolean.valueOf( this.chainedProperties.getProperty( LRUnlinkingOption.PROPERTY_NAME,
"false" ) ) );

}

/**
Expand Down Expand Up @@ -475,6 +488,9 @@ private void checkCanChange() {

public void setSequential(boolean sequential) {
this.sequential = sequential;
if (sequential && isLRUnlinkingEnabled()) {
throw new IllegalArgumentException( "Sequential mode cannot be used when Left & Right unlinking is enabled." );
}
}

public boolean isSequential() {
Expand Down Expand Up @@ -645,6 +661,9 @@ public void setSequentialAgenda(final SequentialAgenda sequentialAgenda) {
public void setMultithreadEvaluation(boolean enableMultithread) {
checkCanChange();
this.multithread = enableMultithread;
if (multithread && isLRUnlinkingEnabled()) {
throw new IllegalArgumentException( "Multithread evaluation cannot be used when Left & Right Unlinking is enabled." );
}
}

/**
Expand Down Expand Up @@ -690,6 +709,32 @@ public void setClassLoaderCacheEnabled(final boolean classLoaderCacheEnabled) {
this.classLoaderCacheEnabled = classLoaderCacheEnabled;
this.classLoader.setCachingEnabled( this.classLoaderCacheEnabled );
}

/**
* @return whether or not Left & Right Unlinking is enabled.
*/
public boolean isLRUnlinkingEnabled() {
return this.lrUnlinkingEnabled;
}

/**
* Enable Left & Right Unlinking. It will also disable sequential mode
* and multithread evaluation as these are incompatible with L&R unlinking.
* @param enabled
*/
public void setLRUnlinkingEnabled(boolean enabled) {
checkCanChange(); // throws an exception if a change isn't possible;
this.lrUnlinkingEnabled = enabled;

if ( enabled && isSequential() ) {
throw new IllegalArgumentException( "Sequential mode cannot be used when Left & Right Unlinking is enabled." );
}

if ( enabled && isMultithreadEvaluation() ) {
throw new IllegalArgumentException( "Multithread evaluation cannot be used when Left & Right Unlinking is enabled." );
}
}


public List<Map<String, Object>> getWorkDefinitions() {
if ( this.workDefinitions == null ) {
Expand Down Expand Up @@ -1051,6 +1096,8 @@ public <T extends SingleValueKnowledgeBaseOption> T getOption(Class<T> option) {
return (T) (this.isMBeansEnabled() ? MBeansOption.ENABLED : MBeansOption.DISABLED);
} else if ( ClassLoaderCacheOption.class.equals( option ) ) {
return (T) (this.isClassLoaderCacheEnabled() ? ClassLoaderCacheOption.ENABLED : ClassLoaderCacheOption.DISABLED);
} else if ( LRUnlinkingOption.class.equals( option ) ) {
return (T) (this.isLRUnlinkingEnabled() ? LRUnlinkingOption.ENABLED : LRUnlinkingOption.DISABLED);
}
return null;

Expand Down Expand Up @@ -1093,6 +1140,8 @@ public <T extends KnowledgeBaseOption> void setOption(T option) {
setMBeansEnabled( ((MBeansOption) option).isEnabled() );
} else if ( option instanceof ClassLoaderCacheOption ) {
setClassLoaderCacheEnabled( ((ClassLoaderCacheOption) option).isClassLoaderCacheEnabled() );
} else if ( option instanceof LRUnlinkingOption ) {
setLRUnlinkingEnabled( ((LRUnlinkingOption) option).isLRUnlinkingEnabled() );
}

}
Expand Down
Loading