cancel
Showing results for 
Search instead for 
Did you mean: 

Adding custom functions to ExpressionManager

zeroflag
Champ in-the-making
Champ in-the-making

I would like to create a list inside an activiti expression like in the following example:

<serviceTask activiti:class="com.example.workflow.servicetask.Foo" activiti:exclusive="true" id="foo" >
<extensionElements>
  <activiti:field expression="${listSmiley Surprisedf(host1, host2, host3)}" name="hosts"/>
</extensionElements>
</serviceTask>

This seems to be not supported by default, but JUEL (the unified expression language implementation used by Activiti) provides some methods for adding custom functions.

In the debugger I was able to come up with some code that could evaluate a list expression:

org.activiti.engine.impl.juel.SimpleContext c = new org.activiti.engine.impl.juel.SimpleContext();
c.setFunction("list", "of", Arrays.class.getMethod("asList", Object[].class));

Context.getProcessEngineConfiguration().getExpressionManager()
.expressionFactory
.createValueExpression(c, "${listSmiley Surprisedf(host1, host2)}", Object.class)
.getValue(((ExecutionEntity) activityExecution).cachedElContext)

How can I configure Activiti to use custom functions like this?

1 ACCEPTED ANSWER

zeroflag
Champ in-the-making
Champ in-the-making

Since then I managed to make this work by extending the ExpressionManager and overriding a few things. But this is a bit brittle, I afraid a API change will break this. Hopefully there is a better way to do this.

public class ExpressionManagerWithPredefinedFunctions extends ExpressionManager {
  private final SimpleContext context = new SimpleContext();

  public ExpressionManagerWithPredefinedFunctions() {
    try {
      context.setFunction("list", "of", Arrays.class.getMethod("asList",Object[].class));
    } catch (NoSuchMethodException e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  public Expression createExpression(String expression) {
    ValueExpression valueExpression = expressionFactory.createValueExpression(context, expression.trim(), Object.class);
    return new JuelExpression(valueExpression, expression);
  }
}
private ProcessEngine processEngine() {
  return new StandaloneProcessEngineConfiguration()
    .setExpressionManager(new ExpressionManagerWithPredefinedFunctions())
    .setAsyncExecutorEnabled(true)
    .setJdbcUrl("jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000")
    .setJdbcUsername("sa")
    .setJdbcPassword("")
    .setJdbcDriver("org.h2.Driver")
    .setDatabaseSchemaUpdate(DB_SCHEMA_UPDATE_TRUE)
    .buildProcessEngine();
}

View answer in original post

1 REPLY 1

zeroflag
Champ in-the-making
Champ in-the-making

Since then I managed to make this work by extending the ExpressionManager and overriding a few things. But this is a bit brittle, I afraid a API change will break this. Hopefully there is a better way to do this.

public class ExpressionManagerWithPredefinedFunctions extends ExpressionManager {
  private final SimpleContext context = new SimpleContext();

  public ExpressionManagerWithPredefinedFunctions() {
    try {
      context.setFunction("list", "of", Arrays.class.getMethod("asList",Object[].class));
    } catch (NoSuchMethodException e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  public Expression createExpression(String expression) {
    ValueExpression valueExpression = expressionFactory.createValueExpression(context, expression.trim(), Object.class);
    return new JuelExpression(valueExpression, expression);
  }
}
private ProcessEngine processEngine() {
  return new StandaloneProcessEngineConfiguration()
    .setExpressionManager(new ExpressionManagerWithPredefinedFunctions())
    .setAsyncExecutorEnabled(true)
    .setJdbcUrl("jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000")
    .setJdbcUsername("sa")
    .setJdbcPassword("")
    .setJdbcDriver("org.h2.Driver")
    .setDatabaseSchemaUpdate(DB_SCHEMA_UPDATE_TRUE)
    .buildProcessEngine();
}