DMN: Create a custom function

Hi everyone,

I’m trying to find some help with custom functions for DMN engine. So, I’m using the flowable-spring-boot-starter-rest and I’m trying to execute a DMN rule with a custom function.

I created my custom function NextBusinessDay:

public class NextBusinessDay extends AbstractFlowableFunctionDelegate {

    public NextBusinessDay() {
    }

    @Override
    public String prefix() {
        return "dates";
    }

    @Override
    public String localName() {
        return "nextBusinessDay";
    }

    @Override
    public Class<?> functionClass() {
        return DateUtils.class;
    }

    @Override
    public Method functionMethod() {
        return getSingleObjectParameterMethod("nextBusinessDay");
    }
}

This is the static method that I aims to be called:

public class DateUtils {

    public static Date nextBusinessDay(Date d) {
        return org.apache.commons.lang3.time.DateUtils.addDays(d, 5);
    }

}

Here is my DMN engine setup where I have already deployed a DMN table and added my custom function NextBusinessDay.

@Configuration
public class DmnEngineSetup {
    @Autowired
    private DmnEngine engine;
    private URL url = getClass().getResource("/dmn");
    private List<FlowableFunctionDelegate> flowableFunctionDelegateList =
            Arrays.asList(new NextBusinessDay());

    @PostConstruct
    public void deploy() throws URISyntaxException, IOException {
        engine.getDmnEngineConfiguration()
                .setCustomFlowableFunctionDelegates(flowableFunctionDelegateList);

        Path path = Paths.get(url.toURI());
        Files.list(path).forEach(p -> {
            engine.getDmnRepositoryService()
                    .createDeployment()
                    .name("test")
                    .addClasspathResource("dmn/" + p.getFileName().toString())
                    .enableDuplicateFiltering()
                    .deploy();
        });
    }

}

Finally, I have my DMN definition using my custom function:

<definitions xmlns="http://www.omg.org/spec/DMN/20180521/MODEL/" id="definition_afbe03b6-6604-11eb-bbcf-0242ac110002" name="Next Business Day" namespace="http://www.flowable.org/dmn">
  <decision id="nextBusinessDay" name="Next Business Day">
    <decisionTable id="decisionTable_afbe03b6-6604-11eb-bbcf-0242ac110002" hitPolicy="FIRST">
      <input label="Date">
        <inputExpression id="inputExpression_1" typeRef="date">
          <text>inputVar</text>
        </inputExpression>
      </input>
      <output id="outputExpression_2" label="Next Business Day" name="nextBusinessDay" typeRef="date"></output>
      <rule>
        <inputEntry id="inputEntry_1_1">
          <text><![CDATA[-]]></text>
        </inputEntry>
        <outputEntry id="outputEntry_2_1">
          <text><![CDATA[dates:nextBusinessDay(inputVar)]]></text>
        </outputEntry>
      </rule>
    </decisionTable>
  </decision>
</definitions>

Thus when I try to execute the rule I get an error that says: “Could not resolve function ‘dates:nextBusinessDay’”

Thanks,
Bruno Alves

Hi,

problem seems that you’re setting the customFlowableFunctionDelegates after the DMN engine is initialised.
If you want to use the customFlowableFunctionDelegates then you need to do it before initialisation because this is used to build up a collection FlowableFunctionDelegates. (The build in ones plus the custom ones).

Please try a @configuration class like this one;

@Configuration
public class DmnEngineSetup {

    @Bean
    public EngineConfigurationConfigurer<SpringDmnEngineConfiguration> customDmnConfigurer() {

        return configuration -> {
            if (configuration.getCustomFlowableFunctionDelegates() == null) {
                configuration.setCustomFlowableFunctionDelegates(Collections.singletonList(new NextBusinessDay()));
            } else {
                configuration.getCustomFlowableFunctionDelegates().addAll(Collections.singletonList(new NextBusinessDay()));
            }
        };
    }
}

This adds the NextbusinessDay custom function delegate to the DmnEngineConfiguration before engine initialisation.

Regards,

Yvo

3 Likes

Thank you for the response!
This solves the problem :grinning: