• Icon: Bug Bug
    • Resolution: Not A Defect
    • Icon: Minor Minor
    • job-dsl-plugin
    • Job DSL Plugin 1.48
      Jenkins 2.7.4

      I am using the "configure" command to directly modify the XML since the Template Plugin is not supported directly.

      This is simple groovy code that produces (in my opinion) a false ArrayIndexOutOfBoundsException:

      job("Template Output Job") {
        String[] templates = ["Template1", "Template2"]
        for (int i = 0; i < templates.length; i++) {
          configure {
            it / "builders" << "hudson.plugins.templateproject.ProxyBuilder" {
              "projectName"(templates[i]);
            }
          }
        }
      }
      

      I can easily fix this by using a temporary variable:

      job("Template Output Job") {
        String[] templates = ["Template1", "Template2"]
        for (int i = 0; i < templates.length; i++) {
          String template = templates[i];
          configure {
            it / "builders" << "hudson.plugins.templateproject.ProxyBuilder" {
              "projectName"(template);
            }
          }
        }
      }
      

      There may be a good reason for this, but it is very strange from an end user point of view...

          [JENKINS-38747] Job DSL: strange behavior from "configure"

          This is actually a feature. The configure block takes a closure argument (the block with the curly braces). That block is not evaluated immediately but when the config XML generated after the script has been processed.

          When a Groovy closure is defined, it captures the variables but not their values. In your case it captures it i variable. And since the value of i is 2 after the loop (when the closure is actually called), you get an ArrayIndexOutOfBoundsException.

          You can test this behavior by running this script in the Jenkins Script Console:

          def a = 1
          
          def c = {
            println a
          }
          
          c()
          
          a = 2
          
          c()
          
          a = 3
          
          c()
          

          The output is

          1
          2
          3
          

          To avoid this problem you could iterate through the array using each (which is also Groovyier):

          job('Template Output Job') {
            String[] templates = ['Template1', 'Template2']
            templates.each { String template ->
              configure {
                it / builders << 'hudson.plugins.templateproject.ProxyBuilder' {
                  projectName(template)
                }
              }
            }
          }
          

          Daniel Spilker added a comment - This is actually a feature. The configure block takes a closure argument (the block with the curly braces). That block is not evaluated immediately but when the config XML generated after the script has been processed. When a Groovy closure is defined, it captures the variables but not their values. In your case it captures it i variable. And since the value of i is 2 after the loop (when the closure is actually called), you get an ArrayIndexOutOfBoundsException . You can test this behavior by running this script in the Jenkins Script Console: def a = 1 def c = { println a } c() a = 2 c() a = 3 c() The output is 1 2 3 To avoid this problem you could iterate through the array using each (which is also Groovyier): job( 'Template Output Job' ) { String [] templates = [ 'Template1' , 'Template2' ] templates.each { String template -> configure { it / builders << 'hudson.plugins.templateproject.ProxyBuilder' { projectName(template) } } } }

          I appreciate the explanation, and can certainly update to use an iterator instead.

          The only thing I don't quite get is why my second code example works correctly. Based on your explanation, when the XML is finally generated, wouldn't I expect to see my second array element used twice? If both configure calls only capture the "template" variable but not the value, then shouldn't my XML have two calls to "Template 2"?

          This was changed from a bug report to a Groovy lesson...

          Christopher Shannon added a comment - I appreciate the explanation, and can certainly update to use an iterator instead. The only thing I don't quite get is why my second code example works correctly. Based on your explanation, when the XML is finally generated, wouldn't I expect to see my second array element used twice? If both configure calls only capture the "template" variable but not the value, then shouldn't my XML have two calls to "Template 2"? This was changed from a bug report to a Groovy lesson...

          The second example works because the template variable is defined in the loop. So it's a new variable in every iteration that does not change it's value. If you define the variable outside of the loop, you would see the last element twice:

          job("Template Output Job") {
            String[] templates = ["Template1", "Template2"]
            String template
            for (int i = 0; i < templates.length; i++) {      
              template = templates[i];
              configure {
                it / "builders" << "hudson.plugins.templateproject.ProxyBuilder" {
                  "projectName"(template);
                }
              }
            }
          }
          

          Daniel Spilker added a comment - The second example works because the template variable is defined in the loop. So it's a new variable in every iteration that does not change it's value. If you define the variable outside of the loop, you would see the last element twice: job( "Template Output Job" ) { String [] templates = [ "Template1" , "Template2" ] String template for ( int i = 0; i < templates.length; i++) { template = templates[i]; configure { it / "builders" << "hudson.plugins.templateproject.ProxyBuilder" { "projectName" (template); } } } }

          Ah, makes perfect sense. Thanks for the explanation!

          Christopher Shannon added a comment - Ah, makes perfect sense. Thanks for the explanation!

            daspilker Daniel Spilker
            w60001 Christopher Shannon
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: