Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8202145

ChangeListener on Property does not fire if an InvalidationListener is removed

    Details

    • Type: Bug
    • Status: Open
    • Priority: P4
    • Resolution: Unresolved
    • Affects Version/s: 8, 9, 10, openjfx11
    • Fix Version/s: tbd
    • Component/s: javafx
    • Subcomponent:
    • CPU:
      x86_64
    • OS:
      generic

      Description

      A DESCRIPTION OF THE PROBLEM :
      When removing an invalidation listener from a property during invalidation a subsequent change listener might not be called if the change listener is (after the removal) the last listener for that property. I noticed that issue when adding a change listener to the showingProperty of a window. By calling hide I expected my change listener to fire but it didn't. I found that the issue lies with the ExpressionHelper when there is a switch from Generic to SingleChange. The currentValue is discarded in favor of the value currently held by the property.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run the attached test case. When you add another change listener as described in the workaround, the code runs without error.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      A registered change listener fires, when the value of the property has changed.
      ACTUAL -
      A registered change listener does not always fire even if the value of the property has changed.

      ---------- BEGIN SOURCE ----------
      import javafx.beans.InvalidationListener;
      import javafx.beans.property.BooleanPropertyBase;
      import javafx.beans.value.ChangeListener;
      import org.junit.Test;

      import java.util.concurrent.atomic.AtomicInteger;

      public class JFXPropertyTest {
        @Test
        public void testJFXProperty() {
          AtomicInteger test = new AtomicInteger(0);
          ChangeListener<Boolean> testListener = (observable, oldValue, newValue) -> test.incrementAndGet();

          assert test.get() == 0;
          TestProperty testProperty = new TestProperty(true, (object) -> {});
          testProperty.addListener(testListener);

          testProperty.setValue(false);
          assert test.get() == 1;
        }

        private static class TestProperty extends BooleanPropertyBase {
          private final InvalidationListener invalidationListener;

          private TestProperty(boolean value, InvalidationListener invalidationListener) {
            super(value);
            this.invalidationListener = invalidationListener;
            addListener(invalidationListener);
          }

          @Override
          protected void invalidated() {
            if (!getValue()) {
              removeListener(invalidationListener);
            }
            super.invalidated();
          }

          @Override
          public Object getBean() {
            return null;
          }

          @Override
          public String getName() {
            return "test";
          }
        }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      - add another dummy change listener so that there are always at least two listeners present.

      FREQUENCY : always


        Attachments

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              webbuggrp Webbug Group
            • Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated: