aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorcaalador <mikael.grankvist@gmail.com>2018-04-04 15:18:57 +0300
committerIlia Motornyi <elmot@vaadin.com>2018-04-04 15:18:57 +0300
commitd1864557a59c66f4e0f7d6fd5b6e10e5bba1a0fe (patch)
tree9562eb3e6378ae4f637967e4cab784ede9024808 /server
parent1999e411fc608f3610269419432606454e90fbc5 (diff)
downloadvaadin-framework-d1864557a59c66f4e0f7d6fd5b6e10e5bba1a0fe.tar.gz
vaadin-framework-d1864557a59c66f4e0f7d6fd5b6e10e5bba1a0fe.zip
Added possibility to add listener for connectorMarkedDirty (#10773)
Diffstat (limited to 'server')
-rw-r--r--server/src/main/java/com/vaadin/event/MarkedAsDirtyConnectorEvent.java44
-rw-r--r--server/src/main/java/com/vaadin/event/MarkedAsDirtyListener.java33
-rw-r--r--server/src/main/java/com/vaadin/ui/ConnectorTracker.java47
-rw-r--r--server/src/test/java/com/vaadin/tests/event/MarkAsDirtyListenerTest.java155
4 files changed, 277 insertions, 2 deletions
diff --git a/server/src/main/java/com/vaadin/event/MarkedAsDirtyConnectorEvent.java b/server/src/main/java/com/vaadin/event/MarkedAsDirtyConnectorEvent.java
new file mode 100644
index 0000000000..7a18cbd16c
--- /dev/null
+++ b/server/src/main/java/com/vaadin/event/MarkedAsDirtyConnectorEvent.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2000-2018 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.event;
+
+import com.vaadin.server.ClientConnector;
+import com.vaadin.ui.UI;
+
+/**
+ * Event which is fired for all registered MarkDirtyListeners when a
+ * connector is marked as dirty.
+ *
+ * @since 8.4
+ */
+public class MarkedAsDirtyConnectorEvent extends ConnectorEvent {
+
+ private final UI ui;
+
+ public MarkedAsDirtyConnectorEvent(ClientConnector source, UI ui) {
+ super(source);
+ this.ui = ui;
+ }
+
+ /**
+ * Get the UI for which the connector event was fired
+ *
+ * @return target ui for event
+ */
+ public UI getUi() {
+ return ui;
+ }
+}
diff --git a/server/src/main/java/com/vaadin/event/MarkedAsDirtyListener.java b/server/src/main/java/com/vaadin/event/MarkedAsDirtyListener.java
new file mode 100644
index 0000000000..f973de322a
--- /dev/null
+++ b/server/src/main/java/com/vaadin/event/MarkedAsDirtyListener.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2000-2018 Vaadin Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.vaadin.event;
+
+/**
+ * An interface used for listening to marked as dirty events.
+ *
+ * @since 8.4
+ */
+@FunctionalInterface
+public interface MarkedAsDirtyListener extends ConnectorEventListener {
+
+ /**
+ * Method called when a client connector has been marked as dirty.
+ *
+ * @param event
+ * marked as dirty connector event object
+ */
+ void connectorMarkedAsDirty(MarkedAsDirtyConnectorEvent event);
+}
diff --git a/server/src/main/java/com/vaadin/ui/ConnectorTracker.java b/server/src/main/java/com/vaadin/ui/ConnectorTracker.java
index 10ba15b6a6..0c331ae8ee 100644
--- a/server/src/main/java/com/vaadin/ui/ConnectorTracker.java
+++ b/server/src/main/java/com/vaadin/ui/ConnectorTracker.java
@@ -25,12 +25,15 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
+import com.vaadin.event.MarkedAsDirtyConnectorEvent;
+import com.vaadin.event.MarkedAsDirtyListener;
import com.vaadin.server.AbstractClientConnector;
import com.vaadin.server.ClientConnector;
import com.vaadin.server.DragAndDropService;
@@ -40,6 +43,7 @@ import com.vaadin.server.StreamVariable;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinService;
import com.vaadin.server.communication.ConnectorHierarchyWriter;
+import com.vaadin.shared.Registration;
import elemental.json.Json;
import elemental.json.JsonException;
@@ -71,6 +75,8 @@ public class ConnectorTracker implements Serializable {
private final Set<ClientConnector> dirtyConnectors = new HashSet<>();
private final Set<ClientConnector> uninitializedConnectors = new HashSet<>();
+ private List<MarkedAsDirtyListener> markedDirtyListeners = new ArrayList<>(0);
+
/**
* Connectors that have been unregistered and should be cleaned up the next
* time {@link #cleanConnectorMap(boolean)} is invoked unless they have been
@@ -502,7 +508,8 @@ public class ConnectorTracker implements Serializable {
}
/**
- * Mark the connector as dirty. This should not be done while the response
+ * Mark the connector as dirty and notifies any marked as dirty listeners.
+ * This should not be done while the response
* is being written.
*
* @see #getDirtyConnectors()
@@ -518,12 +525,16 @@ public class ConnectorTracker implements Serializable {
}
if (getLogger().isLoggable(Level.FINE)) {
- if (!dirtyConnectors.contains(connector)) {
+ if (!isDirty(connector)) {
getLogger().log(Level.FINE, "{0} is now dirty",
getConnectorAndParentInfo(connector));
}
}
+ if(!isDirty(connector)) {
+ notifyMarkedAsDirtyListeners(connector);
+ }
+
dirtyConnectors.add(connector);
}
@@ -899,4 +910,36 @@ public class ConnectorTracker implements Serializable {
public int getCurrentSyncId() {
return currentSyncId;
}
+
+ /**
+ * Add a marked as dirty listener that will be called when a client
+ * connector is marked as dirty.
+ *
+ * @param listener
+ * listener to add
+ * @since 8.4
+ * @return registration for removing listener registration
+ */
+ public Registration addMarkedAsDirtyListener(
+ MarkedAsDirtyListener listener) {
+ markedDirtyListeners.add(listener);
+ return () -> markedDirtyListeners.remove(listener);
+ }
+
+ /**
+ * Notify all registered MarkedAsDirtyListeners the given client connector
+ * has been marked as dirty.
+ *
+ * @param connector
+ * client connector marked as dirty
+ * @since 8.4
+ */
+ public void notifyMarkedAsDirtyListeners(ClientConnector connector) {
+ MarkedAsDirtyConnectorEvent event = new MarkedAsDirtyConnectorEvent(
+ connector, uI);
+ new ArrayList<>(markedDirtyListeners).forEach(listener -> {
+ listener.connectorMarkedAsDirty(event);
+ });
+ }
+
}
diff --git a/server/src/test/java/com/vaadin/tests/event/MarkAsDirtyListenerTest.java b/server/src/test/java/com/vaadin/tests/event/MarkAsDirtyListenerTest.java
new file mode 100644
index 0000000000..461ab74bcf
--- /dev/null
+++ b/server/src/test/java/com/vaadin/tests/event/MarkAsDirtyListenerTest.java
@@ -0,0 +1,155 @@
+package com.vaadin.tests.event;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.vaadin.event.MarkedAsDirtyConnectorEvent;
+import com.vaadin.server.ClientConnector;
+import com.vaadin.tests.util.MockUI;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.ComponentTest;
+import com.vaadin.ui.ConnectorTracker;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.TextField;
+import com.vaadin.ui.UI;
+
+/**
+ * Test for mark as dirty listener functionality.
+ */
+public class MarkAsDirtyListenerTest {
+
+ @Test
+ public void fire_event_when_ui_marked_dirty() {
+ UI ui = new MockUI();
+
+ AtomicReference<MarkedAsDirtyConnectorEvent> events = new AtomicReference<>();
+ ui.getConnectorTracker().addMarkedAsDirtyListener(event -> Assert
+ .assertTrue("No reference should have been registered",
+ events.compareAndSet(null, event)));
+ // UI is marked dirty on creation and when adding a listener
+ ComponentTest.syncToClient(ui);
+
+ ui.getConnectorTracker().markDirty(ui);
+
+ Assert.assertNotNull("Mark as dirty event should have fired",
+ events.get());
+ Assert.assertEquals("Event contains wrong ui", ui,
+ events.get().getUi());
+ Assert.assertEquals("Found wrong connector in event", ui,
+ events.get().getConnector());
+ }
+
+ @Test
+ public void fire_event_for_setContent() {
+ List<MarkedAsDirtyConnectorEvent> events = new ArrayList<>();
+ UI ui = new MockUI() {{
+ getConnectorTracker().addMarkedAsDirtyListener(event -> events.add(event));
+ }};
+ ComponentTest.syncToClient(ui);
+
+ Button button = new Button("Button");
+ ui.setContent(button);
+
+ Assert.assertEquals("Mark as dirty events should have fired", 2,
+ events.size());
+ Assert.assertEquals("Expected button to inform first for creation",
+ button, events.get(0).getConnector());
+ Assert.assertEquals("Expected UI marked as dirty for setContent", ui,
+ events.get(1).getConnector());
+ }
+
+ @Test
+ public void fire_event_for_component_stateChange() {
+ UI ui = new MockUI();
+ Button button = new Button("empty");
+ ui.setContent(button);
+ ComponentTest.syncToClient(button);
+
+ AtomicReference<MarkedAsDirtyConnectorEvent> events = new AtomicReference<>();
+ ui.getConnectorTracker().addMarkedAsDirtyListener(event -> Assert
+ .assertTrue("No reference should have been registered",
+ events.compareAndSet(null, event)));
+
+ button.setIconAlternateText("alternate");
+
+ Assert.assertNotNull("Mark as dirty event should have fired",
+ events.get());
+ Assert.assertEquals("Event contains wrong ui", ui,
+ events.get().getUi());
+ Assert.assertEquals("Found wrong connector in event", button,
+ events.get().getConnector());
+ }
+
+ @Test
+ public void fire_events_for_each_component() {
+ List<MarkedAsDirtyConnectorEvent> events = new ArrayList<>();
+ UI ui = new MockUI() {{
+ getConnectorTracker().addMarkedAsDirtyListener(event -> events.add(event));
+ }};
+
+ HorizontalLayout layout = new HorizontalLayout();
+ // UI initially marked as dirty so should not show as event.
+ ui.setContent(layout);
+ TextField field = new TextField("Name");
+ Button button = new Button("say hello");
+ layout.addComponents(field, button);
+
+ Assert.assertFalse("Mark as dirty event should have fired",
+ events.isEmpty());
+ Assert.assertEquals("Unexpected amount of connector events", 3,
+ events.size());
+
+ Set<ClientConnector> connectors = events.stream()
+ .map(MarkedAsDirtyConnectorEvent::getConnector)
+ .collect(Collectors.toSet());
+
+ Assert.assertTrue(
+ "HorizontalLayout should have fired an markedAsDirty event",
+ connectors.contains(layout));
+ Assert.assertTrue("TextField should have fired an markedAsDirty event",
+ connectors.contains(field));
+ Assert.assertTrue("Button should have fired an markedAsDirty event",
+ connectors.contains(button));
+ }
+
+ @Test
+ public void event_should_only_fire_once_for_an_connector_per_roundtrip() {
+ UI ui = new MockUI();
+ Button button = new Button("empty");
+ ui.setContent(button);
+ ComponentTest.syncToClient(button);
+
+ AtomicReference<MarkedAsDirtyConnectorEvent> events = new AtomicReference<>();
+ ui.getConnectorTracker().addMarkedAsDirtyListener(event -> Assert
+ .assertTrue("Only one event should have registered",
+ events.compareAndSet(null, event)));
+
+ button.setIconAlternateText("alternate");
+ button.setCaption("Update");
+ button.setDisableOnClick(true);
+
+ Assert.assertNotNull("Mark as dirty event should have fired",
+ events.get());
+ Assert.assertEquals("Event contains wrong ui", ui,
+ events.get().getUi());
+ Assert.assertEquals("Found wrong connector in event", button,
+ events.get().getConnector());
+
+ events.set(null);
+ ComponentTest.syncToClient(button);
+
+ button.setCaption("new caption");
+
+ Assert.assertNotNull("Mark as dirty event should have fired",
+ events.get());
+ Assert.assertEquals("Found wrong connector in event", button,
+ events.get().getConnector());
+ }
+
+}