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

Setters for shared pipeline scripts not getting called

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Open (View Workflow)
    • Priority: Minor
    • Resolution: Unresolved
    • Component/s: workflow-cps-plugin
    • Labels:
      None
    • Environment:
      Jenkins LTS 2.46.3
      Pipeline v2.5
    • Similar Issues:

      Description

      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. 

       

        Attachments

          Issue Links

            Activity

            Hide
            leedega 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.

            Show
            leedega 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.
            Hide
            abayer 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.

            Show
            abayer 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.
            Hide
            abayer 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.

            Show
            abayer 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.
            Hide
            vallon 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
            
            Show
            vallon 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
            Hide
            zuluetar 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.

            Show
            zuluetar 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.
            Hide
            svanoort Sam Van Oort added a comment -

            Andrew Bayer Did the extra info added above help at all?

            Show
            svanoort Sam Van Oort added a comment - Andrew Bayer Did the extra info added above help at all?

              People

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

                Dates

                Created:
                Updated: