]> source.dussan.org Git - vaadin-framework.git/commitdiff
Fixed #6155: stop listening to Property value change and read only status change...
authorJohannes Dahlström <johannes.dahlstrom@vaadin.com>
Thu, 5 Apr 2012 10:38:28 +0000 (10:38 +0000)
committerJohannes Dahlström <johannes.dahlstrom@vaadin.com>
Thu, 5 Apr 2012 10:38:28 +0000 (10:38 +0000)
svn changeset:23406/svn branch:6.8

src/com/vaadin/ui/AbstractField.java
tests/server-side/com/vaadin/tests/server/component/abstractfield/RemoveListenersOnDetach.java [new file with mode: 0644]

index 9ea9bbba2ef6b548bde87f745e272aafec7510d5..ac3e14b2b6a83540ca192cada6c57c6f2286371e 100644 (file)
@@ -139,6 +139,11 @@ public abstract class AbstractField extends AbstractComponent implements Field,
 
     private boolean valueWasModifiedByDataSourceDuringCommit;
 
+    /**
+     * Whether this field currently listens to Property events.
+     */
+    private boolean isListening = false;
+
     /* Component basics */
 
     /*
@@ -593,18 +598,8 @@ public abstract class AbstractField extends AbstractComponent implements Field,
         // Saves the old value
         final Object oldValue = value;
 
-        // Stops listening the old data source changes
-        if (dataSource != null
-                && Property.ValueChangeNotifier.class
-                        .isAssignableFrom(dataSource.getClass())) {
-            ((Property.ValueChangeNotifier) dataSource).removeListener(this);
-        }
-        if (dataSource != null
-                && Property.ReadOnlyStatusChangeNotifier.class
-                        .isAssignableFrom(dataSource.getClass())) {
-            ((Property.ReadOnlyStatusChangeNotifier) dataSource)
-                    .removeListener(this);
-        }
+        // Stop listening to the old data source
+        removePropertyListeners();
 
         // Sets the new data source
         dataSource = newDataSource;
@@ -622,14 +617,8 @@ public abstract class AbstractField extends AbstractComponent implements Field,
             modified = true;
         }
 
-        // Listens the new data source if possible
-        if (dataSource instanceof Property.ValueChangeNotifier) {
-            ((Property.ValueChangeNotifier) dataSource).addListener(this);
-        }
-        if (dataSource instanceof Property.ReadOnlyStatusChangeNotifier) {
-            ((Property.ReadOnlyStatusChangeNotifier) dataSource)
-                    .addListener(this);
-        }
+        // Listen to new data source if possible
+        addPropertyListeners();
 
         // Copy the validators from the data source
         if (dataSource instanceof Validatable) {
@@ -1121,6 +1110,8 @@ public abstract class AbstractField extends AbstractComponent implements Field,
         if (actionManager != null) {
             actionManager.setViewer(getWindow());
         }
+        // No-op if listeners already registered
+        addPropertyListeners();
     }
 
     @Override
@@ -1129,6 +1120,9 @@ public abstract class AbstractField extends AbstractComponent implements Field,
         if (actionManager != null) {
             actionManager.setViewer((Window) null);
         }
+        // Stop listening to data source events on detach to avoid a potential
+        // memory leak. See #6155.
+        removePropertyListeners();
     }
 
     /**
@@ -1329,4 +1323,31 @@ public abstract class AbstractField extends AbstractComponent implements Field,
             focusable.focus();
         }
     }
+
+    private void addPropertyListeners() {
+        if (!isListening) {
+            if (dataSource instanceof Property.ValueChangeNotifier) {
+                ((Property.ValueChangeNotifier) dataSource).addListener(this);
+            }
+            if (dataSource instanceof Property.ReadOnlyStatusChangeNotifier) {
+                ((Property.ReadOnlyStatusChangeNotifier) dataSource)
+                        .addListener(this);
+            }
+            isListening = true;
+        }
+    }
+
+    private void removePropertyListeners() {
+        if (isListening) {
+            if (dataSource instanceof Property.ValueChangeNotifier) {
+                ((Property.ValueChangeNotifier) dataSource)
+                        .removeListener(this);
+            }
+            if (dataSource instanceof Property.ReadOnlyStatusChangeNotifier) {
+                ((Property.ReadOnlyStatusChangeNotifier) dataSource)
+                        .removeListener(this);
+            }
+            isListening = false;
+        }
+    }
 }
\ No newline at end of file
diff --git a/tests/server-side/com/vaadin/tests/server/component/abstractfield/RemoveListenersOnDetach.java b/tests/server-side/com/vaadin/tests/server/component/abstractfield/RemoveListenersOnDetach.java
new file mode 100644 (file)
index 0000000..32b80e0
--- /dev/null
@@ -0,0 +1,72 @@
+package com.vaadin.tests.server.component.abstractfield;
+
+import static org.junit.Assert.assertEquals;
+
+import com.vaadin.data.Property;
+import com.vaadin.data.util.AbstractProperty;
+import com.vaadin.ui.AbstractField;
+
+import org.junit.Test;
+
+public class RemoveListenersOnDetach {
+
+    int numValueChanges = 0;
+    int numReadOnlyChanges = 0;
+
+    AbstractField field = new AbstractField() {
+        @Override
+        public Class<?> getType() {
+            return null;
+        }
+
+        @Override
+        public void valueChange(Property.ValueChangeEvent event) {
+            super.valueChange(event);
+            numValueChanges++;
+        }
+
+        @Override
+        public void readOnlyStatusChange(
+                Property.ReadOnlyStatusChangeEvent event) {
+            super.readOnlyStatusChange(event);
+            numReadOnlyChanges++;
+        }
+    };
+
+    Property property = new AbstractProperty() {
+        public Object getValue() {
+            return null;
+        }
+
+        public void setValue(Object newValue) throws ReadOnlyException,
+                ConversionException {
+            fireValueChange();
+        }
+
+        public Class<?> getType() {
+            return null;
+        }
+    };
+
+    @Test
+    public void testAttachDetach() {
+        field.setPropertyDataSource(property);
+
+        property.setValue(null);
+        property.setReadOnly(true);
+        assertEquals(1, numValueChanges);
+        assertEquals(1, numReadOnlyChanges);
+
+        field.attach();
+        property.setValue(null);
+        property.setReadOnly(false);
+        assertEquals(2, numValueChanges);
+        assertEquals(2, numReadOnlyChanges);
+
+        field.detach();
+        property.setValue(null);
+        property.setReadOnly(true);
+        assertEquals(2, numValueChanges);
+        assertEquals(2, numReadOnlyChanges);
+    }
+}