Problem with custom operator where standard operator works

100 views
Skip to first unread message

Florian Braun

unread,
Aug 2, 2016, 5:08:44 PM8/2/16
to Drools Usage
Hello Drools community,

I am running into a bit of a problem with a custom operator I have created (it works exactly like matches does for strings but caches the regex). For some very specific reasons I am working with 5.4.0. When I tested the new operator on small scale I got no errors and it all seemed to work fine. But when implementing it in our entire system and running tests on it we received a large number of errors of this type:

java.lang.ArrayIndexOutOfBoundsException: 0  at org.drools.base.mvel.MVELCompilationUnit.getFactHandle(MVELCompilationUnit.java:416)    
at org.drools.base.mvel.MVELCompilationUnit.updateFactory(MVELCompilationUnit.java:332)     at
org.drools.base.mvel.MVELCompilationUnit.updateFactory(MVELCompilationUnit.java:289)    
at org.drools.rule.constraint.MvelConditionEvaluator.evaluate(MvelConditionEvaluator.java:59)      at org.drools.rule.constraint.MvelConditionEvaluator.evaluate(MvelConditionEvaluator.java:49)      at org.drools.rule.constraint.MvelConstraint.evaluate(MvelConstraint.java:200) at org.drools.rule.constraint.MvelConstraint.isAllowed(MvelConstraint.java:157)    
at org.drools.reteoo.AlphaNode.assertObject(AlphaNode.java:137)  at org.drools.reteoo.SingleObjectSinkAdapter.propagateAssertObject(SingleObjectSinkAdapter.java:59)      
at org.drools.reteoo.AlphaNode.assertObject(AlphaNode.java:141)  at org.drools.reteoo.CompositeObjectSinkAdapter.doPropagateAssertObject(CompositeObjectSinkAdapter.java:497)      
at org.drools.reteoo.CompositeObjectSinkAdapter.propagateAssertObject(CompositeObjectSinkAdapter.java:372)       at org.drools.reteoo.AlphaNode.assertObject(AlphaNode.java:141)      at org.drools.reteoo.CompositeObjectSinkAdapter.doPropagateAssertObject(CompositeObjectSinkAdapter.java:497)       at org.drools.reteoo.CompositeObjectSinkAdapter.propagateAssertObject(CompositeObjectSinkAdapter.java:382)       at org.drools.reteoo.AlphaNode.assertObject(AlphaNode.java:141)      at org.drools.reteoo.CompositeObjectSinkAdapter.doPropagateAssertObject(CompositeObjectSinkAdapter.java:497)       at org.drools.reteoo.CompositeObjectSinkAdapter.propagateAssertObject(CompositeObjectSinkAdapter.java:382)       at org.drools.reteoo.ObjectTypeNode.assertObject(ObjectTypeNode.java:235)  at org.drools.reteoo.EntryPointNode.assertObject(EntryPointNode.java:240)    
at org.drools.common.NamedEntryPoint.insert(NamedEntryPoint.java:350)   at org.drools.common.NamedEntryPoint.insert(NamedEntryPoint.java:311)  
at org.drools.common.AbstractWorkingMemory.insert(AbstractWorkingMemory.java:903)    at
org.drools.common.AbstractWorkingMemory.insert(AbstractWorkingMemory.java:847)

I was able to trace the error to a single rule with this format before changing operators (I know it is a horribly written rule, I still need to find whoever came up with this logic and hit them over the head with it, but it works):

when

        code:   Fact( type not in ( <<types>> ),

                      documentType == <<doc_type>>,

                      (code not matches <<regex>> || code matches <<regex>> || code matches <<regex>>))

then

        retract( code );

end


When I went through I just replaced matches with the new operator like this:

when

        code:   Fact( type not in ( <<types>> ),

                      documentType == <<doc_type>>,

                      (code not cmatches <<regex>> || code cmatches <<regex>> || code cmatches <<regex>>))

then

        retract( code );

end


And all of a sudden I get the above error.

I was hoping someone would be able to help me figure out why this would be happening. I can easily fix the problem and have the same results by rewriting the rule but I would expect it to work the same.

-Florian


Mario Fusco

unread,
Aug 3, 2016, 5:33:04 AM8/3/16
to Drools Usage
Hi Florian,

at a first sight what you did looks correct, but it's very hard if not impossible for us to provide support on a so old version of Drools.
Please consider trying 6.x and if the error persist feel free to send a reproducer so we could investigate it.

Regards,
Mario

--
You received this message because you are subscribed to the Google Groups "Drools Usage" group.
To unsubscribe from this group and stop receiving emails from it, send an email to drools-usage...@googlegroups.com.
To post to this group, send email to drools...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/drools-usage/8bc10108-e524-4712-8870-12500bcc8bfd%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Florian Braun

unread,
Aug 4, 2016, 7:55:10 PM8/4/16
to Drools Usage
Hi Mario,

Thanks for getting back to me on this. I wish I could upgrade, sadly I am stuck right now. I did however do two more things to figure out where the error lives.

I went back and looked at the way we implemented our version of the operator and I realized some things were missing when looking at the drools implementation of 'matches' so I literally took the drools implementation and just added caching to it.

I have also figured out it is any use of the operator and a disjunction ( || ). The only thing I could think of is some issue with the fact that all evaluators use the same serializationID so I messed with those and it still did not work.


import java.io.IOException;

import java.io.ObjectInput;

import java.io.ObjectOutput;

import java.util.HashMap;

import java.util.Map;

import java.util.regex.Pattern;

import org.drools.base.BaseEvaluator;

import org.drools.base.ValueType;

import org.drools.common.InternalWorkingMemory;

import org.drools.rule.VariableRestriction.ObjectVariableContextEntry;

import org.drools.rule.VariableRestriction.VariableContextEntry;

import org.drools.spi.Evaluator;

import org.drools.spi.FieldValue;

import org.drools.spi.InternalReadAccessor;

import org.drools.base.evaluators.EvaluatorCache;

import org.drools.base.evaluators.EvaluatorDefinition;

import org.drools.base.evaluators.Operator;

/**

* This class defines the matches evaluator

*/

public class CacheMatchDefinition implements EvaluatorDefinition {

public static final Operator CMATCHES = Operator.addOperatorToRegistry( "cmatches", false );

public static final Operator NOT_CMATCHES = Operator.addOperatorToRegistry( "cmatches", true );

protected static final Map<String, Pattern> CACHE = new HashMap<String, Pattern>();

protected static final Map<String, Integer> COUNTS = new HashMap<String,Integer>();

private static final String[] SUPPORTED_IDS = { CMATCHES.getOperatorString() };

private EvaluatorCache evaluators = new EvaluatorCache() {

private static final long serialVersionUID = 510;

{

addEvaluator( ValueType.STRING_TYPE, CMATCHES, StringCMatchesEvaluator.INSTANCE );

addEvaluator( ValueType.OBJECT_TYPE, CMATCHES, StringCMatchesEvaluator.INSTANCE );

addEvaluator( ValueType.STRING_TYPE, NOT_CMATCHES, StringNotCMatchesEvaluator.INSTANCE );

addEvaluator( ValueType.OBJECT_TYPE, NOT_CMATCHES, StringNotCMatchesEvaluator.INSTANCE );

}

};

protected static boolean isMatch(String s, String regex){

if(s == null){

return false;

} else {

Pattern p = CACHE.get(regex);

if(p == null){

p = Pattern.compile(regex);

CACHE.put(regex, p);

COUNTS.put(regex, 1);

} else {

COUNTS.put(regex, COUNTS.get(regex) + 1);

}

return p.matcher(s).matches();

}

}

static public int getCacheSize() { return CACHE.size();}

static public Map<String, Integer> getCacheCounter() { return COUNTS;}

public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

evaluators = (EvaluatorCache)in.readObject();

}

public void writeExternal(ObjectOutput out) throws IOException {

out.writeObject(evaluators);

}

/**

* @inheridDoc

*/

public Evaluator getEvaluator(ValueType type, Operator operator) {

return this.evaluators.getEvaluator( type, operator );

}

/**

* @inheridDoc

*/

public Evaluator getEvaluator(ValueType type, Operator operator, String parameterText) {

return this.evaluators.getEvaluator( type, operator );

}

 

public Evaluator getEvaluator(final ValueType type, final String operatorId, final boolean isNegated, final String parameterText) {

return this.getEvaluator( type, operatorId, isNegated, parameterText, Target.FACT, Target.FACT );

}

/**

* @inheritDoc

*/

public Evaluator getEvaluator(final ValueType type, final String operatorId, final boolean isNegated, final String parameterText, final Target left, final Target right ) {

return this.evaluators.getEvaluator( type, Operator.determineOperator( operatorId, isNegated ) );

}

public String[] getEvaluatorIds() {

return SUPPORTED_IDS;

}

public boolean isNegatable() {

return true;

}

public Target getTarget() {

return Target.FACT;

}

public boolean supportsType(ValueType type) {

return this.evaluators.supportsType( type );

}

/* *********************************************************

* Evaluator Implementations

* *********************************************************

*/

public static class StringCMatchesEvaluator extends BaseEvaluator {

private static final long serialVersionUID = 400l;

public final static Evaluator INSTANCE = new StringCMatchesEvaluator();

 

public StringCMatchesEvaluator() {

super( ValueType.STRING_TYPE, CMATCHES );

}

public boolean evaluate(InternalWorkingMemory workingMemory, final InternalReadAccessor extractor, final Object object1, final FieldValue object2) {

final String value1 = (String) extractor.getValue( workingMemory, object1 );

final String value2 = (String) object2.getValue();

return isMatch(value1, value2);

}

public boolean evaluateCachedRight(InternalWorkingMemory workingMemory, final VariableContextEntry context, final Object left) {

final String value = (String) ((ObjectVariableContextEntry) context).right;

return isMatch(value, (String) context.declaration.getExtractor().getValue( workingMemory, left ) );

}

public boolean evaluateCachedLeft(InternalWorkingMemory workingMemory, final VariableContextEntry context, final Object right) {

final String value = (String) context.extractor.getValue( workingMemory, right );

return isMatch(value, (String) ((ObjectVariableContextEntry) context).left );

}

public boolean evaluate(InternalWorkingMemory workingMemory, final InternalReadAccessor extractor1, final Object object1, final InternalReadAccessor extractor2, final Object object2) {

final Object value1 = extractor1.getValue( workingMemory, object1 );

final Object value2 = extractor2.getValue( workingMemory, object2 );

return isMatch( (String) value1, (String) value2 );

}

public String toString() {

return "String cmatches";

}

}

public static class StringNotCMatchesEvaluator extends BaseEvaluator {

private static final long serialVersionUID = 400L;

public final static Evaluator INSTANCE = new StringNotCMatchesEvaluator();

public StringNotCMatchesEvaluator() {

super( ValueType.STRING_TYPE, NOT_CMATCHES );

}

public boolean evaluate(InternalWorkingMemory workingMemory, final InternalReadAccessor extractor, final Object object1, final FieldValue object2) {

final String value1 = (String) extractor.getValue( workingMemory, object1 );

final String value2 = (String) object2.getValue();

return !isMatch(value1, value2);

}

public boolean evaluateCachedRight(InternalWorkingMemory workingMemory, final VariableContextEntry context, final Object left) {

final String value = (String) ((ObjectVariableContextEntry) context).right;

return !isMatch(value, (String) context.declaration.getExtractor().getValue( workingMemory, left ) );

}

public boolean evaluateCachedLeft(InternalWorkingMemory workingMemory, final VariableContextEntry context, final Object right) {

final String value = (String) context.extractor.getValue( workingMemory, right );

return !isMatch(value, (String) ((ObjectVariableContextEntry) context).left );

}

public boolean evaluate(InternalWorkingMemory workingMemory, final InternalReadAccessor extractor1, final Object object1, final InternalReadAccessor extractor2, final Object object2) {

final Object value1 = extractor1.getValue( workingMemory, object1 );

final Object value2 = extractor2.getValue( workingMemory, object2 );

return !isMatch( (String) value1, (String) value2 );

}

public String toString() {

return "String not cmatches";

}

}

}




I don't really expect any help, I know I am stuck at an older drools version, but maybe someone has run into this before.

-Florian
Reply all
Reply to author
Forward
0 new messages