Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-45834

Setters for shared pipeline scripts not getting called

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: Minor Minor
    • workflow-cps-plugin
    • None
    • Jenkins LTS 2.46.3
      Pipeline v2.5

      According to the Jenkins docs, one should be able to define getters and setters for global variables in shared pipeline scripts to manipulating fields. This does not seem to work correctly. Take the following example which is a slight variation on the example from the docs:

       

      // tmp_shared/vars/acme.groovy
      def setName(value) {
          echo "Setting name to $value"
          this.@name = value
      }
      def getName() {
          echo "Getting name of " + this.@name
          return this.@name
      }
      
      // Pipeline script
      @Library ('tmp_shared') _
      
      acme.name = 'Alice'  // should print "Setting name to Alice" to the console
      def tmp = acme.name  // should print "Getting name of Alice" to the console

      I'd expect the output from this simple test to look something like this:

      ...
      Setting name to Alice
      Getting name of Alice
      ...

      However I see no console output whatsoever. This indicates that the getter and setter methods are not being invoked at all when defined in the Groovy vars scripts for a shared pipeline library. 

       

          [JENKINS-45834] Setters for shared pipeline scripts not getting called

          I probably should point out this slight variation on the problem outlined above which behaves slightly differently. Consider the following example scripts:

           

          // tmp_shared/var/acme.groovy
          def getName() {
              echo "In getter..."
              return "John"
          }
           //Pipeline script
          @Library('tmp_shared') _
          
          echo "Shared name is " + acme.name
          acme.name = "Jane"
          echo "Shared name is now " + acme.name
          

          The output from this example looks as follows:

          ...
          In getter...
          Shared name is John
          Shared name is now Jane

          It's worth noting that while the getter is called when accessing the field before the value is explicitly set, it is not called after the field is explicitly set (ie: we don't see the "In getter..." output before the "Shared name is now Jane" message). Not sure how / why the behavior would be subtly different in this case but I thought it might be related to the underlying problem.

          Kevin Phillips added a comment - I probably should point out this slight variation on the problem outlined above which behaves slightly differently. Consider the following example scripts:   // tmp_shared/ var /acme.groovy def getName() { echo "In getter..." return "John" }   //Pipeline script @Library( 'tmp_shared' ) _ echo "Shared name is " + acme.name acme.name = "Jane" echo "Shared name is now " + acme.name The output from this example looks as follows: ... In getter... Shared name is John Shared name is now Jane It's worth noting that while the getter is called when accessing the field before the value is explicitly set, it is not called after the field is explicitly set (ie: we don't see the "In getter..." output before the "Shared name is now Jane" message). Not sure how / why the behavior would be subtly different in this case but I thought it might be related to the underlying problem.

          Andrew Bayer added a comment -

          Yeah, it probably is. I'll need to tackle this with a debugger to figure out exactly where the call flow is going.

          Andrew Bayer added a comment - Yeah, it probably is. I'll need to tackle this with a debugger to figure out exactly where the call flow is going.

          Andrew Bayer added a comment -

          First note from investigation is that this doesn't happen in simple CPS-transformed Groovy. It looks to be specific to shared libraries, but I need to dig further.

          Andrew Bayer added a comment - First note from investigation is that this doesn't happen in simple CPS-transformed Groovy. It looks to be specific to shared libraries, but I need to dig further.

          Justin Vallon added a comment -

          I can reproduce this in script console. Version 2.60.1 (old, but might still be broken):

          @groovy.transform.Field
          String _blah = "init";
          
          String getBlah() { println("getBlah"); return _blah; }
          void setBlah(String newBlah) { println("setBlah"); _blah = newBlah; }
          
          String x;
          
          println("start");
          x = this.blah;
          println("after get #1");
          this.blah = "other";
          println("after set");
          x = this.blah;
          println("after get #2");
          

          Output:

          start
          getBlah
          after get #1
          after set
          after get #2
          

          It seems the assignment-to-field writes to the "property map" instead of calling the settor, and then subsequent gets use that value instead of calling the gettor.

          A nested class works correctly:

          class X {
          Object _steps;
          String _blah = "init";
          
            X(Object steps) { _steps = steps; }
          String getBlah() { _steps.println("getBlah"); return _blah; }
          void setBlah(String newBlah) { _steps.println("setBlah"); _blah = newBlah; }
          };
          
          X obj = new X(this);
          String x;
          
          println("start");
          x = obj.blah;
          println("after get #1");
          obj.blah = "other";
          println("after set");
          x = obj.blah;
          println("after get #2");
          

          output:

          start
          getBlah
          after get #1
          setBlah
          after set
          getBlah
          after get #2
          

          Justin Vallon added a comment - I can reproduce this in script console. Version 2.60.1 (old, but might still be broken): @groovy.transform.Field String _blah = "init" ; String getBlah() { println( "getBlah" ); return _blah; } void setBlah( String newBlah) { println( "setBlah" ); _blah = newBlah; } String x; println( "start" ); x = this .blah; println( "after get #1" ); this .blah = "other" ; println( "after set" ); x = this .blah; println( "after get #2" ); Output: start getBlah after get #1 after set after get #2 It seems the assignment-to-field writes to the "property map" instead of calling the settor, and then subsequent gets use that value instead of calling the gettor. A nested class works correctly: class X { Object _steps; String _blah = "init" ; X( Object steps) { _steps = steps; } String getBlah() { _steps.println( "getBlah" ); return _blah; } void setBlah( String newBlah) { _steps.println( "setBlah" ); _blah = newBlah; } }; X obj = new X( this ); String x; println( "start" ); x = obj.blah; println( "after get #1" ); obj.blah = "other" ; println( "after set" ); x = obj.blah; println( "after get #2" ); output: start getBlah after get #1 setBlah after set getBlah after get #2

          Randy Zulueta added a comment -

          This also applies on src modules for shared libraries. It's challenging to write "beanless" libraries. With this bug, we are limited to use constructors, fixed values, and avoid external java libraries that invokes setters/getters.

          Randy Zulueta added a comment - This also applies on src modules for shared libraries. It's challenging to write "beanless" libraries. With this bug, we are limited to use constructors, fixed values, and avoid external java libraries that invokes setters/getters.

          Sam Van Oort added a comment -

          abayer Did the extra info added above help at all?

          Sam Van Oort added a comment - abayer Did the extra info added above help at all?

            Unassigned Unassigned
            leedega Kevin Phillips
            Votes:
            2 Vote for this issue
            Watchers:
            6 Start watching this issue

              Created:
              Updated: