cancel
Showing results for 
Search instead for 
Did you mean: 

JUEL Exception evaluating variables with punctuation

midiman
Champ in-the-making
Champ in-the-making
Hi,
I've encountered an issue when setting a juel conditionExpression that contains a period '.'
My sequence flow looks like this: [extract from the model]

<sequenceFlow id="sid-DF4F1F47-4C84-45BE-BF9F-C297DFDC88D3" name="approved" sourceRef="sid-6C005338-CA27-4D4E-B969-BA2410284FFE" targetRef="doapproved">
    <conditionExpression xsi:type="tFormalExpression"><![CDATA[${global.response == allow}]]></conditionExpression>
</sequenceFlow>


'global.response' is our own internal namespace name - it's just a variable key.

The salient extract of exception stackTrace I get when the exclusiveGateway that the above sequenceFlow is connected to 'leave's is:

.
.
..lots of trace..
.
.
Caused by: org.activiti.engine.ActivitiException: Unknown property used in expression: ${global.response == allow}
.
'
..lots more irrelevant trace..
.
.
Caused by: org.activiti.engine.impl.javax.el.PropertyNotFoundException: Cannot resolve identifier 'global'
   at org.activiti.engine.impl.juel.AstIdentifier.eval(AstIdentifier.java:83)
   at org.activiti.engine.impl.juel.AstBinary$SimpleOperator.eval(AstBinary.java:27)
   at org.activiti.engine.impl.juel.AstBinary.eval(AstBinary.java:106)
   at org.activiti.engine.impl.juel.AstBinary$SimpleOperator.eval(AstBinary.java:27)
   at org.activiti.engine.impl.juel.AstBinary.eval(AstBinary.java:106)
   at org.activiti.engine.impl.juel.AstEval.eval(AstEval.java:50)
   at org.activiti.engine.impl.juel.AstNode.getValue(AstNode.java:26)
   at org.activiti.engine.impl.juel.TreeValueExpression.getValue(TreeValueExpression.java:114)
   at org.activiti.engine.impl.delegate.ExpressionGetInvocation.invoke(ExpressionGetInvocation.java:33)
   at org.activiti.engine.impl.delegate.DelegateInvocation.proceed(DelegateInvocation.java:37)
   at org.activiti.engine.impl.delegate.DefaultDelegateInterceptor.handleInvocation(DefaultDelegateInterceptor.java:25)
   at org.activiti.engine.impl.el.JuelExpression.getValue(JuelExpression.java:50)


So I had a look at this, half-expecting it to point me to some BeanELContext or similar, but the exception is in the AstBinary evaluator, for which there is no matching AstBinary$SimpleOperator that I can see in the source for a period .

Some javadoc in the Activiti EL evaluators talk about coercing to an Integer or otherwise ignoring. Is there a way to evaluate a processVariable as a String value? - i.e. in sequence flows: 'if this variable is the String "approve" follow this sequence flow'..

Looking at the JUEL spec, it implies that . is used for beans, but then shows examples where regular names are used that contain '.', so the spec is inconclusive in this area.
Also, JUEL doesn't let you 'escape' anything except ${ and #{
(I did try this, but I got this exception:
Caused by: org.activiti.engine.impl.juel.TreeBuilderException: Error parsing '${global\.response == allow}': lexical error at position 8, encountered invalid character '\', expected expression token
)

So..the question is, how to put a period '.' in a sequenceFlow conditionExpression without the evaluator thinking it's something other than simply a period?
In fact, apart from the obvious operator types ( + - & | / etc.) is it possible to include punctuation (i.e. non-alpahnumeric and non-reserved characters)?

Many thanks for any insights,
+Peter



2 REPLIES 2

midiman
Champ in-the-making
Champ in-the-making
A SOLUTION!
So although the exception is showing AstBinary, the real culprit is the original suspect: the bean resolver (to be fair, this is well documented in the User Guide under Expressions)…so…for anyone encountering this problem, here's the way I solved it:

I added a bean id to my socflow.cfg.xml, that points to a simple bean resolver class, passing the DelegateExecution and the key.

So the sequenceFlow xml looks like this:
<code>
    <sequenceFlow id="sid-DF4F1F47-4C84-45BE-BF9F-C297DFDC88D3" name="approved" sourceRef="sid-6C005338-CA27-4D4E-B969-BA2410284FFE" targetRef="doapproved">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${global.resolve('global.response', execution) == 'approve'}]]></conditionExpression>
    </sequenceFlow>
</code>

My bean id looks like this:
<code>
  <bean id="global" class="package.where.your.resolover.lives.NamespaceResolver" />
</code>

And my bean looks like this:
<code>
package package.where.your.resolover.lives;

import java.io.*;
import org.activiti.engine.delegate.*;

public class NamespaceResolver implements Serializable
{
public CNamespaceResolver() {}

public Object resolve(String key, DelegateExecution execution)
{
  /*
  This will return an Object. When comparing in UEL, you'll need to know it's a String - see the single-quotes 'approve'
  */
  return execution.getVariable(key);
}
}
</code>

Doing it this way allows JUEL to stay happy and evaluate 'string' values.

Thanks,
Peter

warper
Star Contributor
Star Contributor
Hi Peter!
You can use execution.getVariable("global.response") in JUEL expression, there's no need to derive new bean in this case .