1 2 Previous Next 27 Replies Latest reply on Sep 14, 2007 10:04 AM by David Roberts

    required variables

    Laurent Michenaud Newbie

      Hi,

      I'm using required variables but i don't understand how
      it works because i can follow the process without affecting
      a value to the variable.

      <start-state name="Saisie de la demande">
       <task blocking="true" name="Saisie de la demande">
       <controller>
       <variable access="read,write,required"
       name="Description"></variable>
       </controller>
       </task>
       <transition to="Validation Chef de projet"
       name="Soumettre"></transition>
      </start-state>


      Another thing, when i generate the forms, the required="true"
      is not generated on the input field.

      Can you help ?

      thanks

        • 1. Re: required variables
          Ronald van Kuijk Master

          search the forum please... you are number 1001 in the last month...

          • 2. Re: required variables
            Laurent Michenaud Newbie

            I've searched in Jira and in the forum
            but i have seen no fix.

            • 3. Re: required variables
              Ronald van Kuijk Master

              It has been discussed and stated that indeed the required=true is not put on the form. This is not a bug since jbpm DOES validate if the required variable is not null but since JSF puts an empty string in there, jBPM thinks it is ok. The 'fix' therefor is to put the required=true on the form yourself at the moment. If you want you van extend the form generator to put it in there automatically.

              • 5. Re: required variables
                David Roberts Apprentice

                 

                This is not a bug since jbpm DOES validate if the required variable is not null

                Where/how does Jbpm do this validation?

                I have a variable which I have set to required:
                <variable name="IsReplacement" access="read,write,required"></variable>


                If I make sure the variable "IsReplacement" is null when ending the task instance with taskInstance.end(), it still goes through. Is there supposed to be an exeption thrown? How does the validation work?

                • 6. Re: required variables
                  Ronald van Kuijk Master

                  hmmmm.... yes, afaik, an exception should be thrown. I'll check the code cause now I'm getting worried. I hop it is not some kind of regression.

                  In org.jbpm.taskmgmt.exe..Taskinstance.submitVariables(), org.jbpm.taskmgmt.def.TaskControler.submitParameters() is called which has a nice loop:

                  if (variableAccesses!=null) {
                   String missingTaskVariables = null;
                   Iterator iter = variableAccesses.iterator();
                   while (iter.hasNext()) {
                   VariableAccess variableAccess = (VariableAccess) iter.next();
                   String mappedName = variableAccess.getMappedName();
                   // first check if the required variableInstances are present
                   if ( (variableAccess.isRequired())
                   && (! taskInstance.hasVariableLocally(mappedName))
                   ) {
                   if (missingTaskVariables==null) {
                   missingTaskVariables = mappedName;
                   } else {
                   missingTaskVariables += ", "+mappedName;
                   }
                   }
                   }
                  
                   // if there are missing, required parameters, throw an IllegalArgumentException
                   if (missingTaskVariables!=null) {
                   throw new IllegalArgumentException("missing task variables: "+missingTaskVariables);
                   }
                  


                  Strange thing is that I cannot find a test for this. I'm almost sure I've seen them in the past

                  • 7. Re: required variables
                    David Roberts Apprentice

                    I found that code in the submitParameters() method earlier, which is why I thought an exception should be thrown. However, I've just tried again, and still no exeption.

                    My task definition:

                    <start-state name="Request For New Employee">
                     <task name="Request For New Employee" swimlane="Initiator">
                     <controller>
                     <variable name="IsReplacement" access="read,write,required"></variable>
                     <variable name="EmployeeReplaced"></variable>
                     <variable name="TestVariable" access="read,write,required"></variable>
                     </controller>
                     </task>
                     <transition name="" to="Financial Director Approval 1"></transition>
                     </start-state>

                    My action class then completes the task in the following way, but I have added a few printout lines to make sure that my IsReplacement variable is null, and they confirm that the IsReplacement variable is still null, even after calling the end method.

                    if (completeTask != null && completeTask.equalsIgnoreCase("Yes")) {
                     try {
                     log.debug("Completing Task Instance: "+taskInstance.getId());
                     if (sys_transition != null && !sys_transition.equals("")) {
                     taskInstance.end(sys_transition);
                     }
                     else {
                     taskInstance.end();
                     }
                     messages.add(ActionMessages.GLOBAL_MESSAGE,
                     new ActionMessage("task.completed"));
                     }
                     catch (Exception e) {
                     System.out.println("####Exception when ending task!!");
                     e.printStackTrace();
                     }
                     }
                     else {
                     log.debug("Saving Task Instance: "+taskInstance.getId());
                     messages.add(ActionMessages.GLOBAL_MESSAGE,
                     new ActionMessage("task.saved"));
                     }
                    
                     String isReplacementVar = (String)taskInstance.getVariable("IsReplacement");
                     if (isReplacementVar == null) {
                     System.out.println("###isReplacementVar is NULL");
                     }
                     else {
                     System.out.println("###isReplacementVar is NOT NULL");
                     }

                    Please let me know if you figure out what's wrong. I will try a few more things and update this post if I find the answer.

                    • 8. Re: required variables
                      Ronald van Kuijk Master

                      can you try making a unittest of this? So it can run without struts etc... and an embedded processdefinition (look at othe jbpm testcases). Also trie what happens if you try to put a variable which is readonly.

                      • 9. Re: required variables
                        David Roberts Apprentice

                        I have created a TestCase to show the problem. It completes through to the end. Please let me know if you manage to find a solution/fix.

                        /**
                         * [JBPM 3.2.1]
                         * This TestCase should show that the when a variable it set to required,
                         * the taskInstance is still able to complete, even when said variable is null.
                         * Calling taskInstance.end() does not throw an exception.
                         *
                         * @author dleerob
                         *
                         */
                        public class ProcessTest extends TestCase {
                        
                         public void testProcess() throws Exception {
                        
                         ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
                         "<process-definition name='simple'>" +
                         " <start-state name='start'>" +
                         " <task name='Request For New Employee'>"+
                         " <controller>"+
                         " <variable name='IsReplacement' access='read,write,required'></variable>"+
                         " </controller>"+
                         " </task>"+
                         " <transition name='' to='Financial Director Approval 1' />" +
                         " </start-state>" +
                         " <task-node name='Financial Director Approval 1'>" +
                         " <task name='Financial Director Approval 1'>"+
                         " <controller>"+
                         " <variable name='Approved' access='read,write,required'></variable>"+
                         " </controller>"+
                         " </task>"+
                         " <transition name='' to='end' />" +
                         " </task-node>" +
                         " <end-state name='end' />" +
                         "</process-definition>"
                         );
                        
                         assertNotNull("Definition should not be null", processDefinition);
                        
                         //create an instance of the process definition.
                         ProcessInstance instance = new ProcessInstance(processDefinition);
                        
                         //create start task instance.
                         TaskMgmtInstance taskMgmtInstance = instance.getTaskMgmtInstance();
                         TaskInstance startTaskInstance = taskMgmtInstance.createStartTaskInstance();
                        
                         assertNotNull("Start Task Instance should not be null", startTaskInstance);
                         assertEquals("Request For New Employee", startTaskInstance.getName());
                        
                         Token token = instance.getRootToken();
                         assertSame(processDefinition.getNode("start"), token.getNode());
                        
                         //make sure our IsReplacement variable is null.
                         assertNull("IsReplacement variable is null",startTaskInstance.getVariable("IsReplacement"));
                        
                         //ending the startTaskInstance should throw an exception if IsReplacement variable
                         //is null, as the variable is set to "required" in the process definition above,
                         //but it doesn't seem to throw the exception.
                         startTaskInstance.end();
                        
                         //this should never of happened.
                         assertNull("IsReplacement variable is still null",startTaskInstance.getVariable("IsReplacement"));
                        
                        
                         //I also want to test if the next task node isn't working properly
                         //when saving required variables with a null value.
                         //This way we rule out the possiblity that there is only
                         //an error with the Start Task Instance.
                         assertSame(processDefinition.getNode("Financial Director Approval 1"), token.getNode());
                        
                         TaskInstance financeDirectorApproval1TaskInstance = null;
                         for(Iterator it = taskMgmtInstance.getTaskInstances().iterator();it.hasNext();) {
                         TaskInstance taskInstance = (TaskInstance)it.next();
                         if (taskInstance.getName().equals("Financial Director Approval 1")) {
                         financeDirectorApproval1TaskInstance = taskInstance;
                         break;
                         }
                         }
                        
                         assertNotNull("Finance Director Approval Task Instance is not null", financeDirectorApproval1TaskInstance);
                         assertEquals(financeDirectorApproval1TaskInstance.getName(),"Financial Director Approval 1");
                        
                         //make sure our Approved variable is null.
                         assertNull("Approved variable is null",financeDirectorApproval1TaskInstance.getVariable("Approved"));
                        
                         //ending the financeDirectorApproval1TaskInstance should throw an exception if
                         //Approved variable is null, as the variable is set to "required" in the process definition above,
                         //but it doesn't seem to throw the exception.
                         financeDirectorApproval1TaskInstance.end();
                        
                         //this should never of happened.
                         assertNull("Approved variable is still null",startTaskInstance.getVariable("Approved"));
                        
                         assertSame(processDefinition.getNode("end"), token.getNode());
                        
                         }


                        • 10. Re: required variables
                          Olivier Debels Newbie

                          The reason why this fails is because you actually have an IsAssignment variable in your task instance (which is null).

                          This because at creation time of the task instance all variables which have read access will be copied from the process instance to the task instance.

                          Since the process instance doesn't have an IsAssignment defined, null will be filled in on task instance level.

                          You can remove the 'read' access or add IsAssignment variable in the process instance.

                          Hope this helps,

                          Olivier.

                          • 11. Re: required variables
                            David Roberts Apprentice

                            Thanks for the help. I think you were referring to "IsReplacement" when you mentioned "IsAssignment".

                            I have changed the variable delaration in the process definition to:

                            <variable name='IsReplacement' access='write,required'></variable>


                            However, the task instance still has an "IsReplacement" variable with a null value. So it seems the all variables are copied across no matter what? Any clarification on this?

                            I did try deleting the "IsReplacement" variable, just before ending the task instance, as shown below:
                            startTaskInstance.deleteVariable("IsReplacement");
                            startTaskInstance.end();
                            

                            Now that the variable does not exist at all, the exception is thrown. This indicates that the jbpm validation only checks if the variable exists, but not if it is null. So now the problem is that variables are copied across with null values, and therefore the validation allows them through. I would think that the validation should check if a variable is null, and not allow it through if it is.

                            • 12. Re: required variables
                              Ronald van Kuijk Master

                              Olivier,

                              You are right that variables are copied from the processinstance. I would have sworn that was only the case if it existed and if not, none was created. I'm almost positive of this.

                              I'll look in the code but if the current behaviour is as you describe, boolean-expression: ((the docs have to be updated OR at least have to be more specific OR I have to read them again) AND there should be testcases for this) ....
                              (I'm sure there were, but that could have been for 3.1.x, I'll check)

                              but what about this scenario:

                              I want to have the user enter a value for a variable, but if through any other means this variable was already set (e.g. the esb) I want to display that and the user should be able to change it. With the current behaviour and the way you have to configure that, there always is a variable either with the value null or the value that is already there.

                              Then only putting a required=true on the input element in the webinterface would enforce this. In that case, I DO think there is a bug in the formgenerator since this is so unintuitive... I'm in favour of adding an additional check in the source then to see that if the current value is null and there is no value posted, an exception is thrown.

                              Sigh......

                              • 13. Re: required variables
                                David Roberts Apprentice

                                Code in VariableContainer:

                                public boolean hasVariableLocally(String name) {
                                 return ( (variableInstances!=null)
                                 && (variableInstances.containsKey(name))
                                 );
                                 }


                                The validation uses the method above, and as you can see, it simply checks if the variable exists, and not if it is null.

                                • 14. Re: required variables
                                  Ronald van Kuijk Master

                                  Olivier is 100% right.... I'm afraid....

                                  in the Taskcontroller code:

                                  initializing variables:

                                   if (variableAccess.isReadable()) {
                                   String variableName = variableAccess.getVariableName();
                                   Object value = contextInstance.getVariable(variableName, token);
                                   log.debug("creating task instance variable '"+mappedName+"' from process variable '"+variableName+"', value '"+value+"'");
                                   taskInstance.setVariableLocally(mappedName, value);
                                   } else {
                                  


                                  submitting:
                                  if ( (variableAccess.isRequired())
                                   && (! taskInstance.hasVariableLocally(mappedName))
                                   ) {
                                   ..... some error stuff
                                   }
                                  


                                  So Olivier is correct, but I personally would be in favour of changing the latter to

                                  if ( (variableAccess.isRequired())
                                   && (! taskInstance.hasVariableLocally(mappedName)
                                   && (taskInstance.getVariable(variableAccess.getMappedName()) != null) )
                                   ) {
                                   ..... some error stuff
                                   }
                                  


                                  1 2 Previous Next