cancel
Showing results for 
Search instead for 
Did you mean: 

Cannot set value of a non-lvalue expression

martin_p
Champ in-the-making
Champ in-the-making
Hi,

My process has custom widgets associated with formProperties. One of these properties may or not be defined, according to an external condition. My main goal is to be able to pass an information to the FormRenderer so that it is able to decide by itself if it should display or not.

I implemented this as follow, and it worked at the beginning, but now I got this weird exception :

Caused by: org.activiti.engine.impl.javax.el.ELException: Cannot set value of a non-lvalue expression 'execution.getVariable('flightEventId')'
at org.activiti.engine.impl.juel.AstMethod.setValue(AstMethod.java:48)
at org.activiti.engine.impl.juel.AstEval.setValue(AstEval.java:86)
at org.activiti.engine.impl.juel.TreeValueExpression.setValue(TreeValueExpression.java:138)
at org.activiti.engine.impl.delegate.ExpressionSetInvocation.invoke(ExpressionSetInvocation.java:37)
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.setValue(JuelExpression.java:69)
… 64 more

Can someone help me understanding this error, or indicate a better way to implement conditional forms?
I am using Activiti 5.10

Process task:

      <userTask id="validate" name="Valider" activiti:candidateGroups="${execution.getVariable(&quot;guarantor&quot;)}">
        <extensionElements>
           …
          <activiti:formProperty id="is-responsible" name="responsability" type="is-responsible" expression="${execution.getVariable(&quot;flightEventId&quot;)}"></activiti:formProperty>
           …
      </extensionElements>
      </userTask>


The "is-responsible" form type let us have a IsResponsibleFormPropertyRenderer:


public class IsResponsibleFormPropertyRenderer extends AbstractFormPropertyRenderer {
    public IsResponsibleFormPropertyRenderer() {
        super(IsResponsibleFormType.class);
    }

    @Override
    public Field getPropertyField(FormProperty formProperty) {
        boolean visible = isCategoryAssignableForResponsability(formProperty);
        OptionGroup og = buildOptionGroup(visible);
        return og;
    }

    private boolean isCategoryAssignableForResponsability(FormProperty formProperty) {
        String eventFlightId = formProperty.getValue();
        if (eventFlightId != null) {
            FlightEventService flightEventService = ExplorerApp.get().getFlightEventService();
            return getFlightEventCategoryAssignability(eventFlightId, flightEventService);
        }
        else{
            return false;           
        }
    }

    private boolean getFlightEventCategoryAssignability(String eventFlightId, FlightEventService flightEventService) {
       … // doing complex things
    }
    …
}


To summarize, I need to provide a variable to the form that will be used to retrieve informations that let me decide how to render the widget.

Many thanks in advance for your help.
4 REPLIES 4

martin_p
Champ in-the-making
Champ in-the-making
I assume (but I'm not sure) that I am not using "expression" properly.
I use "expression" to provide input variable to a form that is expected to return a completely different output value.
It seems to me that :
<code>
<formProperty … expression=…>
</code>
Should be use to set the form with a default value of the same type than the output value of the form.

martin_p
Champ in-the-making
Champ in-the-making
I dived in activiti in debug mode.

The exception happens in FormPropertyHandler while trying to submit form values :
<code>
public void submitFormProperty(ExecutionEntity execution, Map<String, String> properties) {
    if (!isWritable && properties.containsKey(id)) {
      throw new ActivitiException("form property '"+id+"' is not writable");
    }
   
    if (isRequired && !properties.containsKey(id) && defaultExpression == null) {
      throw new ActivitiException("form property '"+id+"' is required");
    }
   
    Object modelValue = null;
    if (properties.containsKey(id)) {
      final String propertyValue = properties.remove(id);
      if (type != null) {
        modelValue = type.convertFormValueToModelValue(propertyValue);
      } else {
        modelValue = propertyValue;
      }
    } else if (defaultExpression != null) {
      final Object expressionValue = defaultExpression.getValue(execution);
      if (type != null && expressionValue != null) {
        modelValue = type.convertFormValueToModelValue(expressionValue.toString());
      } else if (expressionValue != null) {
        modelValue = expressionValue.toString();
      } else if (isRequired) {
        throw new ActivitiException("form property '"+id+"' is required");
      }
    }
   
    if (modelValue != null) {
      if (variableName != null) {
        execution.setVariable(variableName, modelValue);            <<<<<<<<<< where it should go
      } else if (variableExpression != null) {
        variableExpression.setValue(modelValue, execution);        <<<<<<<<<< where it fails
      } else {
        execution.setVariable(id, modelValue);
      }
    }
  }
</code>

Reading the code it seams logical : we can't expect to assign the result to an expression.

So I modified the workflow property to define a variable name :
<code>
<activiti:formProperty id="is-responsible" name="responsability" type="is-responsible" variable="is-responsible" expression="${execution.getVariable(&quot;flightEventId&quotSmiley Wink}"></activiti:formProperty>
</code>

Which at least avoid the crash. Still I got unexpected behaviour so I believe I am not using the workflow properly to do what I expect.

Cheers

trademak
Star Contributor
Star Contributor
Just using a value attribute with a value like "${flightEventId}" would be sufficient. That will retrieve the value from the execution.

Best regards,

martin_p
Champ in-the-making
Champ in-the-making
Thanks for your suggestion.
Unfortunately it does not work, even with a new process instance.

As you suggest I changed the workflow to :
<code>
<activiti:formProperty id="is-responsible" name="responsability" type="is-responsible" value="${flightEventId}">
</code>

Breaking into Activiti code, I noticed that the below modelValue variable remains null
<java>
  public FormProperty createFormProperty(ExecutionEntity execution) {
    FormPropertyImpl formProperty = new FormPropertyImpl(this);
    Object modelValue = null;
   
    if (execution!=null) {
      if (variableName != null || variableExpression == null) {
        final String varName = variableName != null ? variableName : id;
        if (execution.hasVariable(varName)) {
          modelValue = execution.getVariable(varName);
        } else if (defaultExpression != null) {
          modelValue = defaultExpression.getValue(execution);
        }
      } else {
        modelValue = variableExpression.getValue(execution);
      }
    } else {
      // Execution is null, the form-property is used in a start-form. Default value
      // should be available (ACT-1028) even though no execution is available.
      if (defaultExpression != null) {
        modelValue = defaultExpression.getValue(StartFormVariableScope.getSharedInstance());
      }
    }
                                      <<<<<<<<<<<<<<< AT THIS POINT MODELVALUE IS STILL NULL
    if (modelValue instanceof String) {
      formProperty.setValue((String) modelValue);
    } else if (type != null) {
      String formValue = type.convertModelValueToFormValue(modelValue);
      formProperty.setValue(formValue);
    } else if (modelValue != null) {
      formProperty.setValue(modelValue.toString());
    }
   
    return formProperty;
  }
</java>