aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--client/src/com/vaadin/client/communication/MessageHandler.java289
-rw-r--r--server/src/com/vaadin/server/communication/ConnectorHierarchyWriter.java25
-rw-r--r--server/src/com/vaadin/server/communication/SharedStateWriter.java15
-rw-r--r--server/src/com/vaadin/server/communication/UidlWriter.java6
4 files changed, 198 insertions, 137 deletions
diff --git a/client/src/com/vaadin/client/communication/MessageHandler.java b/client/src/com/vaadin/client/communication/MessageHandler.java
index e87928fc57..7ff8820563 100644
--- a/client/src/com/vaadin/client/communication/MessageHandler.java
+++ b/client/src/com/vaadin/client/communication/MessageHandler.java
@@ -1122,142 +1122,42 @@ public class MessageHandler {
ConnectorHierarchyUpdateResult result = new ConnectorHierarchyUpdateResult();
getLogger().info(" * Updating connector hierarchy");
- if (!json.containsKey("hierarchy")) {
- return result;
- }
Profiler.enter("updateConnectorHierarchy");
FastStringSet maybeDetached = FastStringSet.create();
+ FastStringSet hasHierarchy = FastStringSet.create();
- ValueMap hierarchies = json.getValueMap("hierarchy");
- JsArrayString hierarchyKeys = hierarchies.getKeyArray();
- for (int i = 0; i < hierarchyKeys.length(); i++) {
- try {
- Profiler.enter("updateConnectorHierarchy hierarchy entry");
-
+ // Process regular hierarchy data
+ if (json.containsKey("hierarchy")) {
+ ValueMap hierarchies = json.getValueMap("hierarchy");
+ JsArrayString hierarchyKeys = hierarchies.getKeyArray();
+ for (int i = 0; i < hierarchyKeys.length(); i++) {
String connectorId = hierarchyKeys.get(i);
- ServerConnector parentConnector = getConnectorMap()
- .getConnector(connectorId);
JsArrayString childConnectorIds = hierarchies
.getJSStringArray(connectorId);
- int childConnectorSize = childConnectorIds.length();
-
- Profiler.enter("updateConnectorHierarchy find new connectors");
-
- List<ServerConnector> newChildren = new ArrayList<ServerConnector>();
- List<ComponentConnector> newComponents = new ArrayList<ComponentConnector>();
- for (int connectorIndex = 0; connectorIndex < childConnectorSize; connectorIndex++) {
- String childConnectorId = childConnectorIds
- .get(connectorIndex);
- ServerConnector childConnector = getConnectorMap()
- .getConnector(childConnectorId);
- if (childConnector == null) {
- getLogger()
- .severe("Hierarchy claims that "
- + childConnectorId
- + " is a child for "
- + connectorId
- + " ("
- + parentConnector.getClass()
- .getName()
- + ") but no connector with id "
- + childConnectorId
- + " has been registered. "
- + "More information might be available in the server-side log if assertions are enabled");
- continue;
- }
- newChildren.add(childConnector);
- if (childConnector instanceof ComponentConnector) {
- newComponents
- .add((ComponentConnector) childConnector);
- } else if (!(childConnector instanceof AbstractExtensionConnector)) {
- throw new IllegalStateException(
- Util.getConnectorString(childConnector)
- + " is not a ComponentConnector nor an AbstractExtensionConnector");
- }
- if (childConnector.getParent() != parentConnector) {
- childConnector.setParent(parentConnector);
- result.parentChangedIds.add(childConnectorId);
- // Not detached even if previously removed from
- // parent
- maybeDetached.remove(childConnectorId);
- }
- }
-
- Profiler.leave("updateConnectorHierarchy find new connectors");
-
- // TODO This check should be done on the server side in
- // the future so the hierarchy update is only sent when
- // something actually has changed
- List<ServerConnector> oldChildren = parentConnector
- .getChildren();
- boolean actuallyChanged = !Util.collectionsEquals(
- oldChildren, newChildren);
-
- if (!actuallyChanged) {
- continue;
- }
-
- Profiler.enter("updateConnectorHierarchy handle HasComponentsConnector");
-
- if (parentConnector instanceof HasComponentsConnector) {
- HasComponentsConnector ccc = (HasComponentsConnector) parentConnector;
- List<ComponentConnector> oldComponents = ccc
- .getChildComponents();
- if (!Util.collectionsEquals(oldComponents,
- newComponents)) {
- // Fire change event if the hierarchy has
- // changed
- ConnectorHierarchyChangeEvent event = GWT
- .create(ConnectorHierarchyChangeEvent.class);
- event.setOldChildren(oldComponents);
- event.setConnector(parentConnector);
- ccc.setChildComponents(newComponents);
- result.events.add(event);
- }
- } else if (!newComponents.isEmpty()) {
- getLogger()
- .severe("Hierachy claims "
- + Util.getConnectorString(parentConnector)
- + " has component children even though it isn't a HasComponentsConnector");
- }
+ hasHierarchy.add(connectorId);
- Profiler.leave("updateConnectorHierarchy handle HasComponentsConnector");
-
- Profiler.enter("updateConnectorHierarchy setChildren");
- parentConnector.setChildren(newChildren);
- Profiler.leave("updateConnectorHierarchy setChildren");
+ updateConnectorHierarchy(connectorId,
+ childConnectorIds, maybeDetached, result);
+ }
+ }
- Profiler.enter("updateConnectorHierarchy find removed children");
+ // Assume empty hierarchy for connectors with state updates but
+ // no hierarchy data
+ if (json.containsKey("state")) {
+ JsArrayString stateKeys = json.getValueMap("state")
+ .getKeyArray();
- /*
- * Find children removed from this parent and mark for
- * removal unless they are already attached to some
- * other parent.
- */
- for (ServerConnector oldChild : oldChildren) {
- if (oldChild.getParent() != parentConnector) {
- // Ignore if moved to some other connector
- continue;
- }
+ JsArrayString emptyArray = JavaScriptObject.createArray()
+ .cast();
- if (!newChildren.contains(oldChild)) {
- /*
- * Consider child detached for now, will be
- * cleared if it is later on added to some other
- * parent.
- */
- maybeDetached.add(oldChild.getConnectorId());
- }
+ for (int i = 0; i < stateKeys.length(); i++) {
+ String connectorId = stateKeys.get(i);
+ if (!hasHierarchy.contains(connectorId)) {
+ updateConnectorHierarchy(connectorId, emptyArray,
+ maybeDetached, result);
}
-
- Profiler.leave("updateConnectorHierarchy find removed children");
- } catch (final Throwable e) {
- getLogger().log(Level.SEVERE,
- "Error updating connector hierarchy", e);
- } finally {
- Profiler.leave("updateConnectorHierarchy hierarchy entry");
}
}
@@ -1287,6 +1187,149 @@ public class MessageHandler {
}
+ /**
+ * Updates the hierarchy for a connector
+ *
+ * @since
+ * @param connectorId
+ * the id of the connector to update
+ * @param childConnectorIds
+ * array of child connector ids
+ * @param maybeDetached
+ * set of connectors that are maybe detached
+ * @param result
+ * the hierarchy update result
+ */
+ private void updateConnectorHierarchy(String connectorId,
+ JsArrayString childConnectorIds,
+ FastStringSet maybeDetached,
+ ConnectorHierarchyUpdateResult result) {
+ try {
+ Profiler.enter("updateConnectorHierarchy hierarchy entry");
+
+ ConnectorMap connectorMap = getConnectorMap();
+
+ ServerConnector parentConnector = connectorMap
+ .getConnector(connectorId);
+ int childConnectorSize = childConnectorIds.length();
+
+ Profiler.enter("updateConnectorHierarchy find new connectors");
+
+ List<ServerConnector> newChildren = new ArrayList<ServerConnector>();
+ List<ComponentConnector> newComponents = new ArrayList<ComponentConnector>();
+ for (int connectorIndex = 0; connectorIndex < childConnectorSize; connectorIndex++) {
+ String childConnectorId = childConnectorIds
+ .get(connectorIndex);
+ ServerConnector childConnector = connectorMap
+ .getConnector(childConnectorId);
+ if (childConnector == null) {
+ getLogger()
+ .severe("Hierarchy claims that "
+ + childConnectorId
+ + " is a child for "
+ + connectorId
+ + " ("
+ + parentConnector.getClass()
+ .getName()
+ + ") but no connector with id "
+ + childConnectorId
+ + " has been registered. "
+ + "More information might be available in the server-side log if assertions are enabled");
+ continue;
+ }
+ newChildren.add(childConnector);
+ if (childConnector instanceof ComponentConnector) {
+ newComponents
+ .add((ComponentConnector) childConnector);
+ } else if (!(childConnector instanceof AbstractExtensionConnector)) {
+ throw new IllegalStateException(
+ Util.getConnectorString(childConnector)
+ + " is not a ComponentConnector nor an AbstractExtensionConnector");
+ }
+ if (childConnector.getParent() != parentConnector) {
+ childConnector.setParent(parentConnector);
+ result.parentChangedIds.add(childConnectorId);
+ // Not detached even if previously removed from
+ // parent
+ maybeDetached.remove(childConnectorId);
+ }
+ }
+
+ Profiler.leave("updateConnectorHierarchy find new connectors");
+
+ // TODO This check should be done on the server side in
+ // the future so the hierarchy update is only sent when
+ // something actually has changed
+ List<ServerConnector> oldChildren = parentConnector
+ .getChildren();
+ boolean actuallyChanged = !Util.collectionsEquals(
+ oldChildren, newChildren);
+
+ if (!actuallyChanged) {
+ return;
+ }
+
+ Profiler.enter("updateConnectorHierarchy handle HasComponentsConnector");
+
+ if (parentConnector instanceof HasComponentsConnector) {
+ HasComponentsConnector ccc = (HasComponentsConnector) parentConnector;
+ List<ComponentConnector> oldComponents = ccc
+ .getChildComponents();
+ if (!Util.collectionsEquals(oldComponents,
+ newComponents)) {
+ // Fire change event if the hierarchy has
+ // changed
+ ConnectorHierarchyChangeEvent event = GWT
+ .create(ConnectorHierarchyChangeEvent.class);
+ event.setOldChildren(oldComponents);
+ event.setConnector(parentConnector);
+ ccc.setChildComponents(newComponents);
+ result.events.add(event);
+ }
+ } else if (!newComponents.isEmpty()) {
+ getLogger()
+ .severe("Hierachy claims "
+ + Util.getConnectorString(parentConnector)
+ + " has component children even though it isn't a HasComponentsConnector");
+ }
+
+ Profiler.leave("updateConnectorHierarchy handle HasComponentsConnector");
+
+ Profiler.enter("updateConnectorHierarchy setChildren");
+ parentConnector.setChildren(newChildren);
+ Profiler.leave("updateConnectorHierarchy setChildren");
+
+ Profiler.enter("updateConnectorHierarchy find removed children");
+
+ /*
+ * Find children removed from this parent and mark for
+ * removal unless they are already attached to some other
+ * parent.
+ */
+ for (ServerConnector oldChild : oldChildren) {
+ if (oldChild.getParent() != parentConnector) {
+ // Ignore if moved to some other connector
+ continue;
+ }
+
+ if (!newChildren.contains(oldChild)) {
+ /*
+ * Consider child detached for now, will be cleared
+ * if it is later on added to some other parent.
+ */
+ maybeDetached.add(oldChild.getConnectorId());
+ }
+ }
+
+ Profiler.leave("updateConnectorHierarchy find removed children");
+ } catch (final Throwable e) {
+ getLogger().log(Level.SEVERE,
+ "Error updating connector hierarchy", e);
+ } finally {
+ Profiler.leave("updateConnectorHierarchy hierarchy entry");
+ }
+ }
+
private void recursivelyDetach(ServerConnector connector,
JsArrayObject<ConnectorHierarchyChangeEvent> events,
FastStringSet detachedConnectors) {
diff --git a/server/src/com/vaadin/server/communication/ConnectorHierarchyWriter.java b/server/src/com/vaadin/server/communication/ConnectorHierarchyWriter.java
index 1c1a220b5d..503bf8c0ae 100644
--- a/server/src/com/vaadin/server/communication/ConnectorHierarchyWriter.java
+++ b/server/src/com/vaadin/server/communication/ConnectorHierarchyWriter.java
@@ -20,6 +20,7 @@ import java.io.IOException;
import java.io.Serializable;
import java.io.Writer;
import java.util.Collection;
+import java.util.Set;
import com.vaadin.server.AbstractClientConnector;
import com.vaadin.server.ClientConnector;
@@ -49,10 +50,13 @@ public class ConnectorHierarchyWriter implements Serializable {
* The {@link UI} whose hierarchy to write.
* @param writer
* The {@link Writer} used to write the JSON.
+ * @param stateUpdateConnectors
+ * connector ids with state changes
* @throws IOException
* If the serialization fails.
*/
- public void write(UI ui, Writer writer) throws IOException {
+ public void write(UI ui, Writer writer, Set<String> stateUpdateConnectors)
+ throws IOException {
Collection<ClientConnector> dirtyVisibleConnectors = ui
.getConnectorTracker().getDirtyVisibleConnectors();
@@ -69,13 +73,18 @@ public class ConnectorHierarchyWriter implements Serializable {
children.set(children.length(), child.getConnectorId());
}
}
- try {
- hierarchyInfo.put(connectorId, children);
- } catch (JsonException e) {
- throw new PaintException(
- "Failed to send hierarchy information about "
- + connectorId + " to the client: "
- + e.getMessage(), e);
+
+ // Omit for leaf nodes with state changes
+ if (children.length() > 0
+ || !stateUpdateConnectors.contains(connectorId)) {
+ try {
+ hierarchyInfo.put(connectorId, children);
+ } catch (JsonException e) {
+ throw new PaintException(
+ "Failed to send hierarchy information about "
+ + connectorId + " to the client: "
+ + e.getMessage(), e);
+ }
}
}
writer.write(JsonUtil.stringify(hierarchyInfo));
diff --git a/server/src/com/vaadin/server/communication/SharedStateWriter.java b/server/src/com/vaadin/server/communication/SharedStateWriter.java
index 6ef02955f7..06b59ad4cc 100644
--- a/server/src/com/vaadin/server/communication/SharedStateWriter.java
+++ b/server/src/com/vaadin/server/communication/SharedStateWriter.java
@@ -20,6 +20,8 @@ import java.io.IOException;
import java.io.Serializable;
import java.io.Writer;
import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
import com.vaadin.server.ClientConnector;
import com.vaadin.server.PaintException;
@@ -47,31 +49,36 @@ public class SharedStateWriter implements Serializable {
* The UI whose state changes should be written.
* @param writer
* The writer to use.
+ * @return a set of connector ids with state changes
* @throws IOException
* If the serialization fails.
*/
- public void write(UI ui, Writer writer) throws IOException {
+ public Set<String> write(UI ui, Writer writer) throws IOException {
Collection<ClientConnector> dirtyVisibleConnectors = ui
.getConnectorTracker().getDirtyVisibleConnectors();
+ Set<String> writtenConnectors = new HashSet<String>();
JsonObject sharedStates = Json.createObject();
for (ClientConnector connector : dirtyVisibleConnectors) {
// encode and send shared state
+ String connectorId = connector.getConnectorId();
try {
JsonObject stateJson = connector.encodeState();
if (stateJson != null && stateJson.keys().length != 0) {
- sharedStates.put(connector.getConnectorId(), stateJson);
+ sharedStates.put(connectorId, stateJson);
+ writtenConnectors.add(connectorId);
}
} catch (JsonException e) {
throw new PaintException(
"Failed to serialize shared state for connector "
+ connector.getClass().getName() + " ("
- + connector.getConnectorId() + "): "
- + e.getMessage(), e);
+ + connectorId + "): " + e.getMessage(), e);
}
}
writer.write(JsonUtil.stringify(sharedStates));
+
+ return writtenConnectors;
}
}
diff --git a/server/src/com/vaadin/server/communication/UidlWriter.java b/server/src/com/vaadin/server/communication/UidlWriter.java
index 25b1bdaaf9..b117cb4b4d 100644
--- a/server/src/com/vaadin/server/communication/UidlWriter.java
+++ b/server/src/com/vaadin/server/communication/UidlWriter.java
@@ -159,7 +159,8 @@ public class UidlWriter implements Serializable {
// processing.
writer.write("\"state\":");
- new SharedStateWriter().write(ui, writer);
+ Set<String> stateUpdateConnectors = new SharedStateWriter().write(
+ ui, writer);
writer.write(", "); // close states
// TODO This should be optimized. The type only needs to be
@@ -179,7 +180,8 @@ public class UidlWriter implements Serializable {
// child to 0 children)
writer.write("\"hierarchy\":");
- new ConnectorHierarchyWriter().write(ui, writer);
+ new ConnectorHierarchyWriter().write(ui, writer,
+ stateUpdateConnectors);
writer.write(", "); // close hierarchy
// send server to client RPC calls for components in the UI, in call