Uploaded image for project: 'camunda BPM'
  1. camunda BPM
  2. CAM-14530

Improve Expression Language pluggability

    • Icon: Feature Request Feature Request
    • Resolution: Fixed
    • Icon: L3 - Default L3 - Default
    • 7.18.0, 7.18.0-alpha2
    • None
    • engine
    • None

      User Story (Required on creation):

      I want to use an alternative default Expression Language than JUEL, e.g. Spring Expression Language (SpEL). Since the ExpressionManager used in the engine configuration is not an interface, I have to subclass it and overwrite JUEL-specific code. This is an inconvenient API experience. Furthermore, the ExpressionManager is an internal API and might change at any point without further notice.

      Functional Requirements (Required before implementation):

      Have the process engine configuration rely on an interface, using a default implementation that uses JUEL.

      Technical Requirements (Required before implementation):

      • Turn the ExpressionManager into an interface
      • Turn the current implementation into a concrete implementation of that interface using JUEL
      • Allow setting a different expression manager in the engine configuration
      • Take care of providing an ElProvider in the DMN engine configuration from the process engine configuration. This can be based on the Expression Manager and derived from it by default, but could also be a new configuration option.

      Limitations of Scope (Optional):

      Hints (optional):

        This is the controller panel for Smart Panels app

            [CAM-14530] Improve Expression Language pluggability

            Hey jan,

            thanks for creating this proposal. We'll look into it as soon as possible and get back with proper feedback.

            Best,
            Tobias

            Tobias Metzke-Bernstein added a comment - Hey jan , thanks for creating this proposal. We'll look into it as soon as possible and get back with proper feedback. Best, Tobias

            Hi jan,

            thanks again for creating this.

            Let me know if the following is correct:
            You would like to replace the current ExpressionManager with a custom solution that uses a different EL than JUEL.
            However, we currently rely on a concrete class for the expression manager in the process engine configuration instead of relying on an interface.
            This makes it more inconvenient to replace the current expression manager with a custom implementation.

            If that is the case, I think your proposal makes sense in the scope of the product in general.
            It would make users more independent from the default choice of JUEL as the general EL.
            I will forward your proposal to product management to consider this in our roadmap planning.
            Be aware that this does not yet mean that we will work on this in the near future.

            If you would like to speed up the process and provide a code contribution, feel free to create an appropriate Pull Request in our GitHub repository. If you intend to go this way, please also make sure your changes are covered with tests.

            Best,
            Tobias

            Tobias Metzke-Bernstein added a comment - Hi jan , thanks again for creating this. Let me know if the following is correct: You would like to replace the current ExpressionManager with a custom solution that uses a different EL than JUEL. However, we currently rely on a concrete class for the expression manager in the process engine configuration instead of relying on an interface. This makes it more inconvenient to replace the current expression manager with a custom implementation. If that is the case, I think your proposal makes sense in the scope of the product in general. It would make users more independent from the default choice of JUEL as the general EL. I will forward your proposal to product management to consider this in our roadmap planning. Be aware that this does not yet mean that we will work on this in the near future. If you would like to speed up the process and provide a code contribution, feel free to create an appropriate Pull Request in our GitHub repository . If you intend to go this way, please also make sure your changes are covered with tests. Best, Tobias

            Jan Cheng added a comment - - edited

            Hi tobias.metzke,

            thanks for revising my proposal, you totally got my point.

            Jan Cheng added a comment - - edited Hi tobias.metzke , thanks for revising my proposal, you totally got my point.

            Hi jan,

            glad to hear that's also what you had in mind.
            Let us know if you intend to work on this yourself, happy to help you get started.

            Best,
            Tobias

            Tobias Metzke-Bernstein added a comment - Hi jan , glad to hear that's also what you had in mind. Let us know if you intend to work on this yourself, happy to help you get started. Best, Tobias

            Jan Cheng added a comment -

            Hi tobias.metzke,

            I'm glad to get involved. I think this proposal is a good start, I will try to work on this. Thanks for helping.

             

            Jan Cheng added a comment - Hi tobias.metzke , I'm glad to get involved. I think this proposal is a good start, I will try to work on this. Thanks for helping.  

            Hi jan,

            happy to hear that! Let us know if you need any input or pointers.
            Looking forward to your contribution.

            Best,
            Tobias

            Tobias Metzke-Bernstein added a comment - Hi jan , happy to hear that! Let us know if you need any input or pointers. Looking forward to your contribution. Best, Tobias

            Jan Cheng added a comment - - edited

            Hi tobias.metzke,

            There are 2 set of abstract about el: ExpressionManager/Expression/VariableScope used in BPM engine, and ElProvider/ElExpression/VariableContext used in DMN engine. When init engine, we try to use the same el implement both in bpm and dmn. Here is the problem, DmnEngineConfigurationBuilder depends on Juel-specific implement of ExpresionManager. DmnEngineConfigurationBuilder should only depend on ElProvider instead of ExpresionManager. 2 solution below:

            1. Make ExpressionManager/Expression/VariableScope the generalization of ElProvider/ElExpression/VariableContext,  ExpressionManager extends ElProvider, Expression extends ElExpression and VariableScope extends VariableContext.
            2. Keep the 2 set of abstract independent, but bpm and dmn engine only share the same expressionManager when expressionManager implements both ExpressionManager and ElProvider.
            // ProcessEngineConfigurationImpl.java
            
              protected void initDmnEngine() {
                if (dmnEngine == null) {
            
                  if (dmnEngineConfiguration == null) {
                    dmnEngineConfiguration = (DefaultDmnEngineConfiguration) DmnEngineConfiguration.createDefaultDmnEngineConfiguration();
                  }
            
                  dmnEngineConfiguration = new DmnEngineConfigurationBuilder(dmnEngineConfiguration)
                      .dmnHistoryEventProducer(dmnHistoryEventProducer)
                      .scriptEngineResolver(scriptingEngines)
                      //.expressionManager(expressionManager)
                      // should be 
                      // if (expressionManager instanceof ElProvider) then
                      //.elProvider((ElProvider) expressionManager) 
                      .feelCustomFunctionProviders(dmnFeelCustomFunctionProviders)
                      .enableFeelLegacyBehavior(dmnFeelEnableLegacyBehavior)
                      .build();
            
                  dmnEngine = dmnEngineConfiguration.buildEngine();
            
                } else if (dmnEngineConfiguration == null) {
                  dmnEngineConfiguration = (DefaultDmnEngineConfiguration) dmnEngine.getConfiguration();
                }
              }
            

            what's your suggestion?

            Jan Cheng added a comment - - edited Hi tobias.metzke , There are 2 set of abstract about el: ExpressionManager/Expression/VariableScope used in BPM engine, and ElProvider/ElExpression/VariableContext used in DMN engine. When init engine, we try to use the same el implement both in bpm and dmn. Here is the problem, DmnEngineConfigurationBuilder depends on Juel-specific implement of ExpresionManager. DmnEngineConfigurationBuilder should only depend on ElProvider instead of ExpresionManager. 2 solution below: Make ExpressionManager/Expression/VariableScope the generalization of ElProvider/ElExpression/VariableContext,  ExpressionManager extends ElProvider, Expression extends ElExpression and VariableScope extends VariableContext. Keep the 2 set of abstract independent, but bpm and dmn engine only share the same expressionManager when expressionManager implements both ExpressionManager and ElProvider. // ProcessEngineConfigurationImpl.java protected void initDmnEngine() { if (dmnEngine == null ) { if (dmnEngineConfiguration == null ) { dmnEngineConfiguration = (DefaultDmnEngineConfiguration) DmnEngineConfiguration.createDefaultDmnEngineConfiguration(); } dmnEngineConfiguration = new DmnEngineConfigurationBuilder(dmnEngineConfiguration) .dmnHistoryEventProducer(dmnHistoryEventProducer) .scriptEngineResolver(scriptingEngines) //.expressionManager(expressionManager) // should be // if (expressionManager instanceof ElProvider) then //.elProvider((ElProvider) expressionManager) .feelCustomFunctionProviders(dmnFeelCustomFunctionProviders) .enableFeelLegacyBehavior(dmnFeelEnableLegacyBehavior) .build(); dmnEngine = dmnEngineConfiguration.buildEngine(); } else if (dmnEngineConfiguration == null ) { dmnEngineConfiguration = (DefaultDmnEngineConfiguration) dmnEngine.getConfiguration(); } } what's your suggestion?

            Hey jan,

            thanks for looking into this.

            Looking at the code, I think we can still keep the two things separate. There are also roadmap plans on our end to adjust the DMN capabilities in the platform in the future. We might consolidate expression handling in that effort as well.
            That being said, do you think we could just keep the two things as separate as they are? The DMN engine configuration is taking care of wrapping the expression manager into a ProcessEngineElProvider in case there is no ElProvider in the DMN engine configuration yet. Do you see any issues with that mechanism, given the new abstraction idea?

            Thanks again and best,
            Tobias

            Tobias Metzke-Bernstein added a comment - Hey jan , thanks for looking into this. Looking at the code, I think we can still keep the two things separate. There are also roadmap plans on our end to adjust the DMN capabilities in the platform in the future. We might consolidate expression handling in that effort as well. That being said, do you think we could just keep the two things as separate as they are? The DMN engine configuration is taking care of wrapping the expression manager into a ProcessEngineElProvider in case there is no ElProvider in the DMN engine configuration yet. Do you see any issues with that mechanism, given the new abstraction idea? Thanks again and best, Tobias

            Jan Cheng added a comment - - edited

            Hi tobias.metzke,

            1. There is no need DmnEngineConfigurationBuilder depends on ExpresionManager.
            2. Implement ElProvider by wrapping ExpressionManager is not easy and not reasonable, because ExpressionManager/Expression/VariableScope is more general than ElProvider/ElExpression/VariableContext. The ProcessEngineElProvider is actually not a wrapper of ExpressionManager, instead, there are VariableContext&Juel-related code implemented in ExpressionManager - ExpressionManager.createElContext(VariableContext).

            I have created a pull request, check it out.

            Jan Cheng added a comment - - edited Hi tobias.metzke , There is no need DmnEngineConfigurationBuilder depends on ExpresionManager. Implement ElProvider by wrapping ExpressionManager is not easy and not reasonable, because ExpressionManager/Expression/VariableScope is more general than ElProvider/ElExpression/VariableContext. The ProcessEngineElProvider is actually not a wrapper of ExpressionManager, instead, there are VariableContext&Juel-related code implemented in ExpressionManager - ExpressionManager.createElContext(VariableContext). I have created a pull request , check it out.

            Hi jan,

            thanks for your additional input here and for creating the PR.
            I will have a look at this as soon as I can, hopefully by next week.
            Sorry for the delay, I'll keep you in the loop.

            Best,
            Tobias

            Tobias Metzke-Bernstein added a comment - Hi jan , thanks for your additional input here and for creating the PR. I will have a look at this as soon as I can, hopefully by next week. Sorry for the delay, I'll keep you in the loop. Best, Tobias

            Hi jan,

            I provided some feedback in the PR. Feel free to answer at your leisure in the PR.
            If you have any questions, don't hesitate to reach out.

            Best,
            Tobias

            Tobias Metzke-Bernstein added a comment - Hi jan , I provided some feedback in the PR. Feel free to answer at your leisure in the PR. If you have any questions, don't hesitate to reach out. Best, Tobias

            Hey jan,

            thanks again for this contribution, it was a pleasure reviewing this.
            We also added some documentation to the Update Guide. This will soon be available publicly.
            Plus, we will feature this in our upcoming 7.18.0-alpha2 release blog post.

            Best,
            Tobias

            Tobias Metzke-Bernstein added a comment - Hey jan , thanks again for this contribution, it was a pleasure reviewing this. We also added some documentation to the Update Guide . This will soon be available publicly. Plus, we will feature this in our upcoming 7.18.0-alpha2 release blog post. Best, Tobias

              Unassigned Unassigned
              jan Jan Cheng
              Tobias Metzke-Bernstein Tobias Metzke-Bernstein
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

                Created:
                Updated:
                Resolved: