]> source.dussan.org Git - vaadin-framework.git/commitdiff
Moved connector tracking from Application to Root
authorArtur Signell <artur@vaadin.com>
Thu, 21 Jun 2012 07:35:38 +0000 (10:35 +0300)
committerArtur Signell <artur@vaadin.com>
Thu, 21 Jun 2012 14:11:28 +0000 (17:11 +0300)
This should fix issues with connectors being detached and reattached and
also make the connector map cleanup method unnecessary as long as
AbstractConnect.attach and detach always are called (#8943)

12 files changed:
src/com/vaadin/Application.java
src/com/vaadin/terminal/AbstractClientConnector.java
src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java
src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java
src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
src/com/vaadin/terminal/gwt/server/CommunicationManager.java
src/com/vaadin/terminal/gwt/server/Constants.java
src/com/vaadin/terminal/gwt/server/JsonCodec.java
src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java
src/com/vaadin/ui/ConnectorTracker.java [new file with mode: 0644]
src/com/vaadin/ui/DirtyConnectorTracker.java [deleted file]
src/com/vaadin/ui/Root.java

index dbf71408a9876608fef74326b73f91c5aa59929b..3da314a11ebe7182a2aa9de4fd7d767cfcfe864e 100644 (file)
@@ -18,7 +18,6 @@ import java.util.EventObject;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
-import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.Locale;
 import java.util.Map;
@@ -48,14 +47,12 @@ import com.vaadin.terminal.WrappedRequest;
 import com.vaadin.terminal.WrappedRequest.BrowserDetails;
 import com.vaadin.terminal.WrappedResponse;
 import com.vaadin.terminal.gwt.client.ApplicationConnection;
-import com.vaadin.terminal.gwt.client.Connector;
 import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
 import com.vaadin.terminal.gwt.server.ChangeVariablesErrorEvent;
 import com.vaadin.terminal.gwt.server.ClientConnector;
 import com.vaadin.terminal.gwt.server.WebApplicationContext;
 import com.vaadin.ui.AbstractComponent;
 import com.vaadin.ui.AbstractField;
-import com.vaadin.ui.Component;
 import com.vaadin.ui.Root;
 import com.vaadin.ui.Table;
 import com.vaadin.ui.Window;
@@ -2359,8 +2356,6 @@ public class Application implements Terminal.ErrorListener, Serializable {
         return Collections.unmodifiableCollection(roots.values());
     }
 
-    private final HashMap<String, ClientConnector> connectorIdToConnector = new HashMap<String, ClientConnector>();
-
     private int connectorIdSequence = 0;
 
     /**
@@ -2372,53 +2367,7 @@ public class Application implements Terminal.ErrorListener, Serializable {
      * @return A new id for the connector
      */
     public String createConnectorId(ClientConnector connector) {
-        String connectorId = String.valueOf(connectorIdSequence++);
-        Connector oldReference = connectorIdToConnector.put(connectorId,
-                connector);
-        if (oldReference != null) {
-            throw new RuntimeException(
-                    "An error occured while generating connector ids. A connector with id "
-                            + connectorId + " was already found!");
-        }
-        return connectorId;
-    }
-
-    /**
-     * Gets a connector by its id.
-     * 
-     * @param connectorId
-     *            The connector id to look for
-     * @return The connector with the given id or null if no connector has the
-     *         given id
-     */
-    public ClientConnector getConnector(String connectorId) {
-        return connectorIdToConnector.get(connectorId);
-    }
-
-    /**
-     * Cleans the connector map from all connectors that are no longer attached
-     * to the application. This should only be called by the framework.
-     */
-    public void cleanConnectorMap() {
-        // remove detached components from paintableIdMap so they
-        // can be GC'ed
-        Iterator<String> iterator = connectorIdToConnector.keySet().iterator();
-
-        while (iterator.hasNext()) {
-            String connectorId = iterator.next();
-            Connector connector = connectorIdToConnector.get(connectorId);
-            if (connector instanceof Component) {
-                Component component = (Component) connector;
-                if (component.getApplication() != this) {
-                    // If component is no longer part of this application,
-                    // remove it from the map. If it is re-attached to the
-                    // application at some point it will be re-added to this
-                    // collection when sent to the client.
-                    iterator.remove();
-                }
-            }
-        }
-
+        return String.valueOf(connectorIdSequence++);
     }
 
     private static final Logger getLogger() {
index 6a87f58c71710816586314e99998afd87f7be53c..9de444d70ee65ac6d3efe0db808b5f2201aebf38 100644 (file)
@@ -72,7 +72,7 @@ public abstract class AbstractClientConnector implements ClientConnector {
     public void requestRepaint() {
         Root root = getRoot();
         if (root != null) {
-            root.getDirtyConnectorTracker().markDirty(this);
+            root.getConnectorTracker().markDirty(this);
         }
     }
 
@@ -455,9 +455,12 @@ public abstract class AbstractClientConnector implements ClientConnector {
     public void attach() {
         requestRepaint();
 
+        getRoot().getConnectorTracker().registerConnector(this);
+
         for (ClientConnector connector : getAllChildrenIterable(this)) {
             connector.attach();
         }
+
     }
 
     /**
@@ -472,6 +475,8 @@ public abstract class AbstractClientConnector implements ClientConnector {
         for (ClientConnector connector : getAllChildrenIterable(this)) {
             connector.detach();
         }
+
+        getRoot().getConnectorTracker().unregisterConnector(this);
     }
 
     public boolean isConnectorEnabled() {
index bf29144cc1043e6d3ef45ad121e07e25a17ce90a..b7be6fd394d62e293288f39f4dd0095139b078c3 100644 (file)
@@ -638,7 +638,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
 
                 /* Handle the request */
                 if (requestType == RequestType.FILE_UPLOAD) {
-                    applicationManager.handleFileUpload(wrappedRequest,
+                    applicationManager.handleFileUpload(root, wrappedRequest,
                             wrappedResponse);
                     return;
                 } else if (requestType == RequestType.BROWSER_DETAILS) {
index 2179761d313cbc6d5855e7a72515c65c4d4aebe5..0e548f61c80127d215be2b236d654c1f1b26414a 100644 (file)
@@ -421,25 +421,28 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
 
             /* Handle the request */
             if (requestType == RequestType.FILE_UPLOAD) {
-                applicationManager.handleFileUpload(application, request,
-                        response);
+                Root root = application.getRootForRequest(request);
+                if (root == null) {
+                    throw new ServletException(ERROR_NO_ROOT_FOUND);
+                }
+                applicationManager.handleFileUpload(root, request, response);
                 return;
             } else if (requestType == RequestType.UIDL) {
-                // Handles AJAX UIDL requests
                 Root root = application.getRootForRequest(request);
                 if (root == null) {
-                    throw new ServletException(ERROR_NO_WINDOW_FOUND);
-                }
+                    throw new ServletException(ERROR_NO_ROOT_FOUND);
+                }// Handles AJAX UIDL requests
                 applicationManager.handleUidlRequest(request, response,
                         servletWrapper, root);
                 return;
             } else if (requestType == RequestType.BROWSER_DETAILS) {
+                // Browser details - not related to a specific root
                 applicationManager.handleBrowserDetailsRequest(request,
                         response, application);
                 return;
             }
 
-            // Removes application if it has stopped (mayby by thread or
+            // Removes application if it has stopped (maybe by thread or
             // transactionlistener)
             if (!application.isRunning()) {
                 endApplication(request, response, application);
index ecd4fdd1ad6e0d738806b12f4d422cbc71c98f25..797f8f756c3e7a4aaf7e02f54870369ad327c8ea 100644 (file)
@@ -74,7 +74,7 @@ import com.vaadin.terminal.gwt.server.ComponentSizeValidator.InvalidLayout;
 import com.vaadin.ui.AbstractComponent;
 import com.vaadin.ui.AbstractField;
 import com.vaadin.ui.Component;
-import com.vaadin.ui.DirtyConnectorTracker;
+import com.vaadin.ui.ConnectorTracker;
 import com.vaadin.ui.HasComponents;
 import com.vaadin.ui.Root;
 import com.vaadin.ui.Window;
@@ -134,6 +134,9 @@ public abstract class AbstractCommunicationManager implements Serializable {
 
     private static final String GET_PARAM_ANALYZE_LAYOUTS = "analyzeLayouts";
 
+    /**
+     * The application this communication manager is used for
+     */
     private final Application application;
 
     private List<String> locales;
@@ -517,7 +520,8 @@ public abstract class AbstractCommunicationManager implements Serializable {
             if (request.getParameter(GET_PARAM_HIGHLIGHT_COMPONENT) != null) {
                 String pid = request
                         .getParameter(GET_PARAM_HIGHLIGHT_COMPONENT);
-                highlightedConnector = root.getApplication().getConnector(pid);
+                highlightedConnector = root.getConnectorTracker().getConnector(
+                        pid);
                 highlightConnector(highlightedConnector);
             }
         }
@@ -617,7 +621,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
     protected void postPaint(Root root) {
         // Remove connectors that have been detached from the application during
         // handling of the request
-        root.getApplication().cleanConnectorMap();
+        root.getConnectorTracker().cleanConnectorMap();
     }
 
     protected void highlightConnector(Connector highlightedConnector) {
@@ -763,8 +767,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
         ArrayList<ClientConnector> dirtyVisibleConnectors = new ArrayList<ClientConnector>();
         Application application = root.getApplication();
         // Paints components
-        DirtyConnectorTracker rootConnectorTracker = root
-                .getDirtyConnectorTracker();
+        ConnectorTracker rootConnectorTracker = root.getConnectorTracker();
         getLogger().log(Level.FINE, "* Creating response to client");
         if (repaintAll) {
             getClientCache(root).clear();
@@ -848,7 +851,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
                         }
                     }
                     Object stateJson = JsonCodec.encode(state, referenceState,
-                            stateType, application);
+                            stateType, root.getConnectorTracker());
 
                     sharedStates.put(connector.getConnectorId(), stateJson);
                 } catch (JSONException e) {
@@ -950,7 +953,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
                     // }
                     paramJson.put(JsonCodec.encode(
                             invocation.getParameters()[i], referenceParameter,
-                            parameterType, application));
+                            parameterType, root.getConnectorTracker()));
                 }
                 invocationJson.put(paramJson);
                 rpcCalls.put(invocationJson);
@@ -1411,7 +1414,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
             for (int bi = 1; bi < bursts.length; bi++) {
                 // unescape any encoded separator characters in the burst
                 final String burst = unescapeBurst(bursts[bi]);
-                success &= handleBurst(request, application2, burst);
+                success &= handleBurst(request, root, burst);
 
                 // In case that there were multiple bursts, we know that this is
                 // a special synchronous case for closing window. Thus we are
@@ -1453,22 +1456,23 @@ public abstract class AbstractCommunicationManager implements Serializable {
      * directly.
      * 
      * @param source
-     * @param app
-     *            application receiving the burst
+     * @param root
+     *            the root receiving the burst
      * @param burst
      *            the content of the burst as a String to be parsed
      * @return true if the processing of the burst was successful and there were
      *         no messages to non-existent components
      */
-    public boolean handleBurst(Object source, Application app,
+    public boolean handleBurst(WrappedRequest source, Root root,
             final String burst) {
         boolean success = true;
         try {
             Set<Connector> enabledConnectors = new HashSet<Connector>();
 
-            List<MethodInvocation> invocations = parseInvocations(burst);
+            List<MethodInvocation> invocations = parseInvocations(
+                    root.getConnectorTracker(), burst);
             for (MethodInvocation invocation : invocations) {
-                final ClientConnector connector = getConnector(app,
+                final ClientConnector connector = getConnector(root,
                         invocation.getConnectorId());
 
                 if (connector != null && connector.isConnectorEnabled()) {
@@ -1479,7 +1483,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
             for (int i = 0; i < invocations.size(); i++) {
                 MethodInvocation invocation = invocations.get(i);
 
-                final ClientConnector connector = getConnector(app,
+                final ClientConnector connector = getConnector(root,
                         invocation.getConnectorId());
 
                 if (connector == null) {
@@ -1557,8 +1561,8 @@ public abstract class AbstractCommunicationManager implements Serializable {
                                 errorComponent = (Component) dropHandlerOwner;
                             }
                         }
-                        handleChangeVariablesError(app, errorComponent, e,
-                                changes);
+                        handleChangeVariablesError(root.getApplication(),
+                                errorComponent, e, changes);
                     }
                 }
             }
@@ -1577,12 +1581,15 @@ public abstract class AbstractCommunicationManager implements Serializable {
      * Parse a message burst from the client into a list of MethodInvocation
      * instances.
      * 
+     * @param root
+     *            The root for this request
      * @param burst
      *            message string (JSON)
      * @return list of MethodInvocation to perform
      * @throws JSONException
      */
-    private List<MethodInvocation> parseInvocations(final String burst)
+    private List<MethodInvocation> parseInvocations(
+            ConnectorTracker connectorTracker, final String burst)
             throws JSONException {
         JSONArray invocationsJson = new JSONArray(burst);
 
@@ -1595,7 +1602,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
             JSONArray invocationJson = invocationsJson.getJSONArray(i);
 
             MethodInvocation invocation = parseInvocation(invocationJson,
-                    previousInvocation);
+                    previousInvocation, connectorTracker);
             if (invocation != null) {
                 // Can be null iff the invocation was a legacy invocation and it
                 // was merged with the previous one
@@ -1607,7 +1614,8 @@ public abstract class AbstractCommunicationManager implements Serializable {
     }
 
     private MethodInvocation parseInvocation(JSONArray invocationJson,
-            MethodInvocation previousInvocation) throws JSONException {
+            MethodInvocation previousInvocation,
+            ConnectorTracker connectorTracker) throws JSONException {
         String connectorId = invocationJson.getString(0);
         String interfaceName = invocationJson.getString(1);
         String methodName = invocationJson.getString(2);
@@ -1623,10 +1631,10 @@ public abstract class AbstractCommunicationManager implements Serializable {
             return parseLegacyChangeVariablesInvocation(connectorId,
                     interfaceName, methodName,
                     (LegacyChangeVariablesInvocation) previousInvocation,
-                    parametersJson);
+                    parametersJson, connectorTracker);
         } else {
             return parseServerRpcInvocation(connectorId, interfaceName,
-                    methodName, parametersJson);
+                    methodName, parametersJson, connectorTracker);
         }
 
     }
@@ -1634,7 +1642,8 @@ public abstract class AbstractCommunicationManager implements Serializable {
     private LegacyChangeVariablesInvocation parseLegacyChangeVariablesInvocation(
             String connectorId, String interfaceName, String methodName,
             LegacyChangeVariablesInvocation previousInvocation,
-            JSONArray parametersJson) throws JSONException {
+            JSONArray parametersJson, ConnectorTracker connectorTracker)
+            throws JSONException {
         if (parametersJson.length() != 2) {
             throw new JSONException(
                     "Invalid parameters in legacy change variables call. Expected 2, was "
@@ -1642,7 +1651,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
         }
         String variableName = parametersJson.getString(0);
         UidlValue uidlValue = (UidlValue) JsonCodec.decodeInternalType(
-                UidlValue.class, true, parametersJson.get(1), application);
+                UidlValue.class, true, parametersJson.get(1), connectorTracker);
 
         Object value = uidlValue.getValue();
 
@@ -1658,7 +1667,8 @@ public abstract class AbstractCommunicationManager implements Serializable {
 
     private ServerRpcMethodInvocation parseServerRpcInvocation(
             String connectorId, String interfaceName, String methodName,
-            JSONArray parametersJson) throws JSONException {
+            JSONArray parametersJson, ConnectorTracker connectorTracker)
+            throws JSONException {
         ServerRpcMethodInvocation invocation = new ServerRpcMethodInvocation(
                 connectorId, interfaceName, methodName, parametersJson.length());
 
@@ -1670,7 +1680,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
             Object parameterValue = parametersJson.get(j);
             Type parameterType = declaredRpcMethodParameterTypes[j];
             parameters[j] = JsonCodec.decodeInternalOrCustomType(parameterType,
-                    parameterValue, application);
+                    parameterValue, connectorTracker);
         }
         invocation.setParameters(parameters);
         return invocation;
@@ -1681,8 +1691,9 @@ public abstract class AbstractCommunicationManager implements Serializable {
         owner.changeVariables(source, m);
     }
 
-    protected ClientConnector getConnector(Application app, String connectorId) {
-        ClientConnector c = app.getConnector(connectorId);
+    protected ClientConnector getConnector(Root root, String connectorId) {
+        ClientConnector c = root.getConnectorTracker()
+                .getConnector(connectorId);
         if (c == null
                 && connectorId.equals(getDragAndDropService().getConnectorId())) {
             return getDragAndDropService();
@@ -2037,9 +2048,9 @@ public abstract class AbstractCommunicationManager implements Serializable {
      * @return
      */
     private ArrayList<ClientConnector> getDirtyVisibleConnectors(
-            DirtyConnectorTracker dirtyConnectorTracker) {
+            ConnectorTracker connectorTracker) {
         ArrayList<ClientConnector> dirtyConnectors = new ArrayList<ClientConnector>();
-        for (ClientConnector c : dirtyConnectorTracker.getDirtyConnectors()) {
+        for (ClientConnector c : connectorTracker.getDirtyConnectors()) {
             if (isVisible(c)) {
                 dirtyConnectors.add(c);
             }
index 3dd2eb97fd74348e13ab146fa72243d17a30f171..cc2981dc45bc26c21c6e8a62d34bc6a246c1062b 100644 (file)
@@ -61,7 +61,8 @@ public class CommunicationManager extends AbstractCommunicationManager {
     /**
      * Handles file upload request submitted via Upload component.
      * 
-     * @param application
+     * @param root
+     *            The root for this request
      * 
      * @see #getStreamVariableTargetUrl(ReceiverOwner, String, StreamVariable)
      * 
@@ -70,9 +71,9 @@ public class CommunicationManager extends AbstractCommunicationManager {
      * @throws IOException
      * @throws InvalidUIDLSecurityKeyException
      */
-    public void handleFileUpload(Application application,
-            WrappedRequest request, WrappedResponse response)
-            throws IOException, InvalidUIDLSecurityKeyException {
+    public void handleFileUpload(Root root, WrappedRequest request,
+            WrappedResponse response) throws IOException,
+            InvalidUIDLSecurityKeyException {
 
         /*
          * URI pattern: APP/UPLOAD/[PID]/[NAME]/[SECKEY] See #createReceiverUrl
@@ -86,14 +87,14 @@ public class CommunicationManager extends AbstractCommunicationManager {
         String uppUri = pathInfo.substring(startOfData);
         String[] parts = uppUri.split("/", 3); // 0 = pid, 1= name, 2 = sec key
         String variableName = parts[1];
-        String paintableId = parts[0];
+        String connectorId = parts[0];
 
         StreamVariable streamVariable = pidToNameToStreamVariable.get(
-                paintableId).get(variableName);
+                connectorId).get(variableName);
         String secKey = streamVariableToSeckey.get(streamVariable);
         if (secKey.equals(parts[2])) {
 
-            Connector source = getConnector(application, paintableId);
+            Connector source = getConnector(root, connectorId);
             String contentType = request.getContentType();
             if (contentType.contains("boundary")) {
                 // Multipart requests contain boundary string
@@ -117,13 +118,12 @@ public class CommunicationManager extends AbstractCommunicationManager {
     protected void postPaint(Root root) {
         super.postPaint(root);
 
-        Application application = root.getApplication();
         if (pidToNameToStreamVariable != null) {
             Iterator<String> iterator = pidToNameToStreamVariable.keySet()
                     .iterator();
             while (iterator.hasNext()) {
                 String connectorId = iterator.next();
-                if (application.getConnector(connectorId) == null) {
+                if (root.getConnectorTracker().getConnector(connectorId) == null) {
                     // Owner is no longer attached to the application
                     Map<String, StreamVariable> removed = pidToNameToStreamVariable
                             .get(connectorId);
index 7c467aa7f428345b68f7b459d901eb0e1f4509e0..9e6b2c775b980502939c7e1e55ee1728af73cf21 100644 (file)
@@ -68,7 +68,7 @@ public interface Constants {
     // Widget set parameter name
     static final String PARAMETER_WIDGETSET = "widgetset";
 
-    static final String ERROR_NO_WINDOW_FOUND = "No window found. Did you remember to setMainWindow()?";
+    static final String ERROR_NO_ROOT_FOUND = "Application did not return a root for the request and did not request extra information either. Something is wrong.";
 
     static final String DEFAULT_THEME_NAME = "reindeer";
 
index 71e4727164cfec8f3bf00e74e173b3d19422ce8c..4b470f72151174e0da0316b2b9755d706e5006c0 100644 (file)
@@ -23,7 +23,6 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 
-import com.vaadin.Application;
 import com.vaadin.external.json.JSONArray;
 import com.vaadin.external.json.JSONException;
 import com.vaadin.external.json.JSONObject;
@@ -31,6 +30,7 @@ import com.vaadin.terminal.gwt.client.Connector;
 import com.vaadin.terminal.gwt.client.communication.JsonEncoder;
 import com.vaadin.terminal.gwt.client.communication.UidlValue;
 import com.vaadin.ui.Component;
+import com.vaadin.ui.ConnectorTracker;
 
 /**
  * Decoder for converting RPC parameters and other values from JSON in transfer
@@ -105,16 +105,18 @@ public class JsonCodec implements Serializable {
     }
 
     public static Object decodeInternalOrCustomType(Type targetType,
-            Object value, Application application) throws JSONException {
+            Object value, ConnectorTracker connectorTracker)
+            throws JSONException {
         if (isInternalType(targetType)) {
-            return decodeInternalType(targetType, false, value, application);
+            return decodeInternalType(targetType, false, value,
+                    connectorTracker);
         } else {
-            return decodeCustomType(targetType, value, application);
+            return decodeCustomType(targetType, value, connectorTracker);
         }
     }
 
     public static Object decodeCustomType(Type targetType, Object value,
-            Application application) throws JSONException {
+            ConnectorTracker connectorTracker) throws JSONException {
         if (isInternalType(targetType)) {
             throw new JSONException("decodeCustomType cannot be used for "
                     + targetType + ", which is an internal type");
@@ -127,7 +129,8 @@ public class JsonCodec implements Serializable {
                 || targetType == JSONArray.class) {
             return value;
         } else {
-            return decodeObject(targetType, (JSONObject) value, application);
+            return decodeObject(targetType, (JSONObject) value,
+                    connectorTracker);
         }
     }
 
@@ -157,7 +160,7 @@ public class JsonCodec implements Serializable {
      */
     public static Object decodeInternalType(Type targetType,
             boolean restrictToInternalTypes, Object encodedJsonValue,
-            Application application) throws JSONException {
+            ConnectorTracker connectorTracker) throws JSONException {
         if (!isInternalType(targetType)) {
             throw new JSONException("Type " + targetType
                     + " is not a supported internal type.");
@@ -170,26 +173,27 @@ public class JsonCodec implements Serializable {
 
         // UidlValue
         if (targetType == UidlValue.class) {
-            return decodeUidlValue((JSONArray) encodedJsonValue, application);
+            return decodeUidlValue((JSONArray) encodedJsonValue,
+                    connectorTracker);
         }
 
         // Collections
         if (JsonEncoder.VTYPE_LIST.equals(transportType)) {
             return decodeList(targetType, restrictToInternalTypes,
-                    (JSONArray) encodedJsonValue, application);
+                    (JSONArray) encodedJsonValue, connectorTracker);
         } else if (JsonEncoder.VTYPE_SET.equals(transportType)) {
             return decodeSet(targetType, restrictToInternalTypes,
-                    (JSONArray) encodedJsonValue, application);
+                    (JSONArray) encodedJsonValue, connectorTracker);
         } else if (JsonEncoder.VTYPE_MAP.equals(transportType)) {
             return decodeMap(targetType, restrictToInternalTypes,
-                    encodedJsonValue, application);
+                    encodedJsonValue, connectorTracker);
         }
 
         // Arrays
         if (JsonEncoder.VTYPE_ARRAY.equals(transportType)) {
 
             return decodeObjectArray(targetType, (JSONArray) encodedJsonValue,
-                    application);
+                    connectorTracker);
 
         } else if (JsonEncoder.VTYPE_STRINGARRAY.equals(transportType)) {
             return decodeStringArray((JSONArray) encodedJsonValue);
@@ -200,7 +204,7 @@ public class JsonCodec implements Serializable {
         String stringValue = String.valueOf(encodedJsonValue);
 
         if (JsonEncoder.VTYPE_CONNECTOR.equals(transportType)) {
-            return application.getConnector(stringValue);
+            return connectorTracker.getConnector(stringValue);
         }
 
         // Standard Java types
@@ -223,11 +227,11 @@ public class JsonCodec implements Serializable {
     }
 
     private static UidlValue decodeUidlValue(JSONArray encodedJsonValue,
-            Application application) throws JSONException {
+            ConnectorTracker connectorTracker) throws JSONException {
         String type = encodedJsonValue.getString(0);
 
         Object decodedValue = decodeInternalType(getType(type), true,
-                encodedJsonValue.get(1), application);
+                encodedJsonValue.get(1), connectorTracker);
         return new UidlValue(decodedValue);
     }
 
@@ -248,7 +252,7 @@ public class JsonCodec implements Serializable {
 
     private static Map<Object, Object> decodeMap(Type targetType,
             boolean restrictToInternalTypes, Object jsonMap,
-            Application application) throws JSONException {
+            ConnectorTracker connectorTracker) throws JSONException {
         if (jsonMap instanceof JSONArray) {
             // Client-side has no declared type information to determine
             // encoding method for empty maps, so these are handled separately.
@@ -266,22 +270,22 @@ public class JsonCodec implements Serializable {
                     .getActualTypeArguments()[1];
             if (keyType == String.class) {
                 return decodeStringMap(valueType, (JSONObject) jsonMap,
-                        application);
+                        connectorTracker);
             } else if (keyType == Connector.class) {
                 return decodeConnectorMap(valueType, (JSONObject) jsonMap,
-                        application);
+                        connectorTracker);
             } else {
                 return decodeObjectMap(keyType, valueType, (JSONArray) jsonMap,
-                        application);
+                        connectorTracker);
             }
         } else {
             return decodeStringMap(UidlValue.class, (JSONObject) jsonMap,
-                    application);
+                    connectorTracker);
         }
     }
 
     private static Map<Object, Object> decodeObjectMap(Type keyType,
-            Type valueType, JSONArray jsonMap, Application application)
+            Type valueType, JSONArray jsonMap, ConnectorTracker connectorTracker)
             throws JSONException {
         Map<Object, Object> map = new HashMap<Object, Object>();
 
@@ -292,9 +296,9 @@ public class JsonCodec implements Serializable {
 
         for (int i = 0; i < keys.length(); i++) {
             Object key = decodeInternalOrCustomType(keyType, keys.get(i),
-                    application);
+                    connectorTracker);
             Object value = decodeInternalOrCustomType(valueType, values.get(i),
-                    application);
+                    connectorTracker);
 
             map.put(key, value);
         }
@@ -303,30 +307,32 @@ public class JsonCodec implements Serializable {
     }
 
     private static Map<Object, Object> decodeConnectorMap(Type valueType,
-            JSONObject jsonMap, Application application) throws JSONException {
+            JSONObject jsonMap, ConnectorTracker connectorTracker)
+            throws JSONException {
         Map<Object, Object> map = new HashMap<Object, Object>();
 
         for (Iterator<?> iter = jsonMap.keys(); iter.hasNext();) {
             String key = (String) iter.next();
             Object value = decodeInternalOrCustomType(valueType,
-                    jsonMap.get(key), application);
+                    jsonMap.get(key), connectorTracker);
             if (valueType == UidlValue.class) {
                 value = ((UidlValue) value).getValue();
             }
-            map.put(application.getConnector(key), value);
+            map.put(connectorTracker.getConnector(key), value);
         }
 
         return map;
     }
 
     private static Map<Object, Object> decodeStringMap(Type valueType,
-            JSONObject jsonMap, Application application) throws JSONException {
+            JSONObject jsonMap, ConnectorTracker connectorTracker)
+            throws JSONException {
         Map<Object, Object> map = new HashMap<Object, Object>();
 
         for (Iterator<?> iter = jsonMap.keys(); iter.hasNext();) {
             String key = (String) iter.next();
             Object value = decodeInternalOrCustomType(valueType,
-                    jsonMap.get(key), application);
+                    jsonMap.get(key), connectorTracker);
             if (valueType == UidlValue.class) {
                 value = ((UidlValue) value).getValue();
             }
@@ -349,17 +355,18 @@ public class JsonCodec implements Serializable {
      */
     private static Object decodeParametrizedType(Type targetType,
             boolean restrictToInternalTypes, int typeIndex, Object value,
-            Application application) throws JSONException {
+            ConnectorTracker connectorTracker) throws JSONException {
         if (!restrictToInternalTypes && targetType instanceof ParameterizedType) {
             Type childType = ((ParameterizedType) targetType)
                     .getActualTypeArguments()[typeIndex];
             // Only decode the given type
-            return decodeInternalOrCustomType(childType, value, application);
+            return decodeInternalOrCustomType(childType, value,
+                    connectorTracker);
         } else {
             // Only UidlValue when not enforcing a given type to avoid security
             // issues
             UidlValue decodeInternalType = (UidlValue) decodeInternalType(
-                    UidlValue.class, true, value, application);
+                    UidlValue.class, true, value, connectorTracker);
             return decodeInternalType.getValue();
         }
     }
@@ -380,20 +387,21 @@ public class JsonCodec implements Serializable {
     }
 
     private static Object[] decodeObjectArray(Type targetType,
-            JSONArray jsonArray, Application application) throws JSONException {
-        List list = decodeList(List.class, true, jsonArray, application);
+            JSONArray jsonArray, ConnectorTracker connectorTracker)
+            throws JSONException {
+        List list = decodeList(List.class, true, jsonArray, connectorTracker);
         return list.toArray(new Object[list.size()]);
     }
 
     private static List<Object> decodeList(Type targetType,
             boolean restrictToInternalTypes, JSONArray jsonArray,
-            Application application) throws JSONException {
+            ConnectorTracker connectorTracker) throws JSONException {
         List<Object> list = new ArrayList<Object>();
         for (int i = 0; i < jsonArray.length(); ++i) {
             // each entry always has two elements: type and value
             Object encodedValue = jsonArray.get(i);
             Object decodedChild = decodeParametrizedType(targetType,
-                    restrictToInternalTypes, 0, encodedValue, application);
+                    restrictToInternalTypes, 0, encodedValue, connectorTracker);
             list.add(decodedChild);
         }
         return list;
@@ -401,10 +409,10 @@ public class JsonCodec implements Serializable {
 
     private static Set<Object> decodeSet(Type targetType,
             boolean restrictToInternalTypes, JSONArray jsonArray,
-            Application application) throws JSONException {
+            ConnectorTracker connectorTracker) throws JSONException {
         HashSet<Object> set = new HashSet<Object>();
         set.addAll(decodeList(targetType, restrictToInternalTypes, jsonArray,
-                application));
+                connectorTracker));
         return set;
     }
 
@@ -430,7 +438,7 @@ public class JsonCodec implements Serializable {
     }
 
     private static Object decodeObject(Type targetType,
-            JSONObject serializedObject, Application application)
+            JSONObject serializedObject, ConnectorTracker connectorTracker)
             throws JSONException {
 
         Class<?> targetClass = getClassForType(targetType);
@@ -451,7 +459,7 @@ public class JsonCodec implements Serializable {
                 Object encodedFieldValue = serializedObject.get(fieldName);
                 Type fieldType = pd.getReadMethod().getGenericReturnType();
                 Object decodedFieldValue = decodeInternalOrCustomType(
-                        fieldType, encodedFieldValue, application);
+                        fieldType, encodedFieldValue, connectorTracker);
 
                 pd.getWriteMethod().invoke(decodedObject, decodedFieldValue);
             }
@@ -471,7 +479,8 @@ public class JsonCodec implements Serializable {
     }
 
     public static Object encode(Object value, Object referenceValue,
-            Type valueType, Application application) throws JSONException {
+            Type valueType, ConnectorTracker connectorTracker)
+            throws JSONException {
 
         if (valueType == null) {
             throw new IllegalArgumentException("type must be defined");
@@ -497,15 +506,15 @@ public class JsonCodec implements Serializable {
         } else if (value instanceof Collection) {
             Collection<?> collection = (Collection<?>) value;
             JSONArray jsonArray = encodeCollection(valueType, collection,
-                    application);
+                    connectorTracker);
             return jsonArray;
         } else if (value instanceof Object[]) {
             Object[] array = (Object[]) value;
-            JSONArray jsonArray = encodeArrayContents(array, application);
+            JSONArray jsonArray = encodeArrayContents(array, connectorTracker);
             return jsonArray;
         } else if (value instanceof Map) {
             Object jsonMap = encodeMap(valueType, (Map<?, ?>) value,
-                    application);
+                    connectorTracker);
             return jsonMap;
         } else if (value instanceof Connector) {
             Connector connector = (Connector) value;
@@ -516,13 +525,13 @@ public class JsonCodec implements Serializable {
             }
             return connector.getConnectorId();
         } else if (value instanceof Enum) {
-            return encodeEnum((Enum<?>) value, application);
+            return encodeEnum((Enum<?>) value, connectorTracker);
         } else if (value instanceof JSONArray || value instanceof JSONObject) {
             return value;
         } else {
             // Any object that we do not know how to encode we encode by looping
             // through fields
-            return encodeObject(value, referenceValue, application);
+            return encodeObject(value, referenceValue, connectorTracker);
         }
     }
 
@@ -531,7 +540,7 @@ public class JsonCodec implements Serializable {
     }
 
     private static Object encodeObject(Object value, Object referenceValue,
-            Application application) throws JSONException {
+            ConnectorTracker connectorTracker) throws JSONException {
         JSONObject jsonMap = new JSONObject();
 
         try {
@@ -565,7 +574,7 @@ public class JsonCodec implements Serializable {
                     jsonMap.put(
                             fieldName,
                             encode(fieldValue, referenceFieldValue, fieldType,
-                                    application));
+                                    connectorTracker));
                     // } else {
                     // System.out.println("Skipping field " + fieldName
                     // + " of type " + fieldType.getName()
@@ -599,44 +608,44 @@ public class JsonCodec implements Serializable {
         return false;
     }
 
-    private static String encodeEnum(Enum<?> e, Application application)
-            throws JSONException {
+    private static String encodeEnum(Enum<?> e,
+            ConnectorTracker connectorTracker) throws JSONException {
         return e.name();
     }
 
     private static JSONArray encodeArrayContents(Object[] array,
-            Application application) throws JSONException {
+            ConnectorTracker connectorTracker) throws JSONException {
         JSONArray jsonArray = new JSONArray();
         for (Object o : array) {
-            jsonArray.put(encode(o, null, null, application));
+            jsonArray.put(encode(o, null, null, connectorTracker));
         }
         return jsonArray;
     }
 
     private static JSONArray encodeCollection(Type targetType,
-            Collection collection, Application application)
+            Collection collection, ConnectorTracker connectorTracker)
             throws JSONException {
         JSONArray jsonArray = new JSONArray();
         for (Object o : collection) {
-            jsonArray.put(encodeChild(targetType, 0, o, application));
+            jsonArray.put(encodeChild(targetType, 0, o, connectorTracker));
         }
         return jsonArray;
     }
 
     private static Object encodeChild(Type targetType, int typeIndex, Object o,
-            Application application) throws JSONException {
+            ConnectorTracker connectorTracker) throws JSONException {
         if (targetType instanceof ParameterizedType) {
             Type childType = ((ParameterizedType) targetType)
                     .getActualTypeArguments()[typeIndex];
             // Encode using the given type
-            return encode(o, null, childType, application);
+            return encode(o, null, childType, connectorTracker);
         } else {
             throw new JSONException("Collection is missing generics");
         }
     }
 
     private static Object encodeMap(Type mapType, Map<?, ?> map,
-            Application application) throws JSONException {
+            ConnectorTracker connectorTracker) throws JSONException {
         Type keyType, valueType;
 
         if (mapType instanceof ParameterizedType) {
@@ -653,24 +662,25 @@ public class JsonCodec implements Serializable {
         }
 
         if (keyType == String.class) {
-            return encodeStringMap(valueType, map, application);
+            return encodeStringMap(valueType, map, connectorTracker);
         } else if (keyType == Connector.class) {
-            return encodeConnectorMap(valueType, map, application);
+            return encodeConnectorMap(valueType, map, connectorTracker);
         } else {
-            return encodeObjectMap(keyType, valueType, map, application);
+            return encodeObjectMap(keyType, valueType, map, connectorTracker);
         }
     }
 
     private static JSONArray encodeObjectMap(Type keyType, Type valueType,
-            Map<?, ?> map, Application application) throws JSONException {
+            Map<?, ?> map, ConnectorTracker connectorTracker)
+            throws JSONException {
         JSONArray keys = new JSONArray();
         JSONArray values = new JSONArray();
 
         for (Entry<?, ?> entry : map.entrySet()) {
             Object encodedKey = encode(entry.getKey(), null, keyType,
-                    application);
+                    connectorTracker);
             Object encodedValue = encode(entry.getValue(), null, valueType,
-                    application);
+                    connectorTracker);
 
             keys.put(encodedKey);
             values.put(encodedValue);
@@ -680,13 +690,13 @@ public class JsonCodec implements Serializable {
     }
 
     private static JSONObject encodeConnectorMap(Type valueType, Map<?, ?> map,
-            Application application) throws JSONException {
+            ConnectorTracker connectorTracker) throws JSONException {
         JSONObject jsonMap = new JSONObject();
 
         for (Entry<?, ?> entry : map.entrySet()) {
             Connector key = (Connector) entry.getKey();
             Object encodedValue = encode(entry.getValue(), null, valueType,
-                    application);
+                    connectorTracker);
             jsonMap.put(key.getConnectorId(), encodedValue);
         }
 
@@ -694,13 +704,13 @@ public class JsonCodec implements Serializable {
     }
 
     private static JSONObject encodeStringMap(Type valueType, Map<?, ?> map,
-            Application application) throws JSONException {
+            ConnectorTracker connectorTracker) throws JSONException {
         JSONObject jsonMap = new JSONObject();
 
         for (Entry<?, ?> entry : map.entrySet()) {
             String key = (String) entry.getKey();
             Object encodedValue = encode(entry.getValue(), null, valueType,
-                    application);
+                    connectorTracker);
             jsonMap.put(key, encodedValue);
         }
 
index 55f15da3d993e23a03d005c9768fd648d47912e7..d3fbf4d988b13e6a8d2cb75e9e22b2b02bbd9749 100644 (file)
@@ -43,12 +43,12 @@ public class PortletCommunicationManager extends AbstractCommunicationManager {
         super(application);
     }
 
-    public void handleFileUpload(WrappedRequest request,
+    public void handleFileUpload(Root root, WrappedRequest request,
             WrappedResponse response) throws IOException {
         String contentType = request.getContentType();
         String name = request.getParameter("name");
         String ownerId = request.getParameter("rec-owner");
-        Connector owner = getConnector(getApplication(), ownerId);
+        Connector owner = getConnector(root, ownerId);
         StreamVariable streamVariable = ownerToNameToStreamVariable.get(owner)
                 .get(name);
 
@@ -73,7 +73,7 @@ public class PortletCommunicationManager extends AbstractCommunicationManager {
                     .iterator();
             while (iterator.hasNext()) {
                 Connector owner = iterator.next();
-                if (application.getConnector(owner.getConnectorId()) == null) {
+                if (getConnector(root, owner.getConnectorId()) == null) {
                     // Owner is no longer attached to the application
                     iterator.remove();
                 }
diff --git a/src/com/vaadin/ui/ConnectorTracker.java b/src/com/vaadin/ui/ConnectorTracker.java
new file mode 100644 (file)
index 0000000..75a75ad
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+@VaadinApache2LicenseForJavaFiles@
+ */
+package com.vaadin.ui;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.vaadin.terminal.AbstractClientConnector;
+import com.vaadin.terminal.gwt.client.ServerConnector;
+import com.vaadin.terminal.gwt.server.ClientConnector;
+
+/**
+ * A class which takes care of book keeping of {@link ClientConnector}s for one
+ * Root.
+ * <p>
+ * Provides {@link #getConnector(String)} which can be used to lookup a
+ * connector from its id. This is for framework use only and should not be
+ * needed in applications.
+ * </p>
+ * <p>
+ * Tracks which {@link ClientConnector}s are dirty so they can be updated to the
+ * client when the following response is sent. A connector is dirty when an
+ * operation has been performed on it on the server and as a result of this
+ * operation new information needs to be sent to its {@link ServerConnector}.
+ * </p>
+ * 
+ * @author Vaadin Ltd
+ * @version @VERSION@
+ * @since 7.0.0
+ * 
+ */
+public class ConnectorTracker implements Serializable {
+
+    private final HashMap<String, ClientConnector> connectorIdToConnector = new HashMap<String, ClientConnector>();
+    private Set<ClientConnector> dirtyConnectors = new HashSet<ClientConnector>();
+
+    private Root root;
+
+    /**
+     * Gets a logger for this class
+     * 
+     * @return A logger instance for logging within this class
+     * 
+     */
+    public static Logger getLogger() {
+        return Logger.getLogger(ConnectorTracker.class.getName());
+    }
+
+    public ConnectorTracker(Root root) {
+        this.root = root;
+    }
+
+    /**
+     * Register the given connector.
+     * <p>
+     * The lookup method {@link #getConnector(String)} only returns registered
+     * connectors.
+     * </p>
+     * 
+     * @param connector
+     *            The connector to register.
+     */
+    public void registerConnector(ClientConnector connector) {
+        String connectorId = connector.getConnectorId();
+        ClientConnector previouslyRegistered = connectorIdToConnector
+                .get(connectorId);
+        if (previouslyRegistered == null) {
+            connectorIdToConnector.put(connectorId, connector);
+            getLogger().fine(
+                    "Registered " + connector.getClass().getSimpleName() + " ("
+                            + connectorId + ")");
+        } else if (previouslyRegistered != connector) {
+            throw new RuntimeException("A connector with id " + connectorId
+                    + " is already registered!");
+        } else {
+            getLogger().warning(
+                    "An already registered connector was registered again: "
+                            + connector.getClass().getSimpleName() + " ("
+                            + connectorId + ")");
+        }
+
+    }
+
+    /**
+     * Unregister the given connector.
+     * 
+     * <p>
+     * The lookup method {@link #getConnector(String)} only returns registered
+     * connectors.
+     * </p>
+     * 
+     * @param connector
+     *            The connector to unregister
+     */
+    public void unregisterConnector(ClientConnector connector) {
+        String connectorId = connector.getConnectorId();
+        if (!connectorIdToConnector.containsKey(connectorId)) {
+            getLogger().warning(
+                    "Tried to unregister "
+                            + connector.getClass().getSimpleName() + " ("
+                            + connectorId + ") which is not registered");
+            return;
+        }
+        if (connectorIdToConnector.get(connectorId) != connector) {
+            throw new RuntimeException("The given connector with id "
+                    + connectorId
+                    + " is not the one that was registered for that id");
+        }
+
+        getLogger().fine(
+                "Unregistered " + connector.getClass().getSimpleName() + " ("
+                        + connectorId + ")");
+        connectorIdToConnector.remove(connectorId);
+    }
+
+    /**
+     * Gets a connector by its id.
+     * 
+     * @param connectorId
+     *            The connector id to look for
+     * @return The connector with the given id or null if no connector has the
+     *         given id
+     */
+    public ClientConnector getConnector(String connectorId) {
+        return connectorIdToConnector.get(connectorId);
+    }
+
+    /**
+     * Cleans the connector map from all connectors that are no longer attached
+     * to the application. This should only be called by the framework.
+     */
+    public void cleanConnectorMap() {
+        // remove detached components from paintableIdMap so they
+        // can be GC'ed
+        Iterator<String> iterator = connectorIdToConnector.keySet().iterator();
+
+        while (iterator.hasNext()) {
+            String connectorId = iterator.next();
+            ClientConnector connector = connectorIdToConnector.get(connectorId);
+            if (connector instanceof Component) {
+                Component component = (Component) connector;
+                if (component.getRoot() != root) {
+                    // If component is no longer part of this application,
+                    // remove it from the map. If it is re-attached to the
+                    // application at some point it will be re-added through
+                    // registerConnector(connector)
+                    iterator.remove();
+                }
+            }
+        }
+
+    }
+
+    public void markDirty(ClientConnector connector) {
+        if (getLogger().isLoggable(Level.FINE)) {
+            if (!dirtyConnectors.contains(connector)) {
+                getLogger()
+                        .fine(getDebugInfo(connector) + " " + "is now dirty");
+            }
+        }
+
+        dirtyConnectors.add(connector);
+    }
+
+    public void markClean(ClientConnector connector) {
+        if (getLogger().isLoggable(Level.FINE)) {
+            if (dirtyConnectors.contains(connector)) {
+                getLogger().fine(
+                        getDebugInfo(connector) + " " + "is no longer dirty");
+            }
+        }
+
+        dirtyConnectors.remove(connector);
+    }
+
+    private String getDebugInfo(ClientConnector connector) {
+        String message = getObjectString(connector);
+        if (connector.getParent() != null) {
+            message += " (parent: " + getObjectString(connector.getParent())
+                    + ")";
+        }
+        return message;
+    }
+
+    private String getObjectString(Object connector) {
+        return connector.getClass().getName() + "@"
+                + Integer.toHexString(connector.hashCode());
+    }
+
+    public void markAllConnectorsDirty() {
+        markConnectorsDirtyRecursively(root);
+        getLogger().fine("All connectors are now dirty");
+    }
+
+    public void markAllConnectorsClean() {
+        dirtyConnectors.clear();
+        getLogger().fine("All connectors are now clean");
+    }
+
+    /**
+     * Marks all visible connectors dirty, starting from the given connector and
+     * going downwards in the hierarchy.
+     * 
+     * @param c
+     *            The component to start iterating downwards from
+     */
+    private void markConnectorsDirtyRecursively(ClientConnector c) {
+        if (c instanceof Component && !((Component) c).isVisible()) {
+            return;
+        }
+        markDirty(c);
+        for (ClientConnector child : AbstractClientConnector
+                .getAllChildrenIterable(c)) {
+            markConnectorsDirtyRecursively(child);
+        }
+    }
+
+    public Collection<ClientConnector> getDirtyConnectors() {
+        return dirtyConnectors;
+    }
+
+}
diff --git a/src/com/vaadin/ui/DirtyConnectorTracker.java b/src/com/vaadin/ui/DirtyConnectorTracker.java
deleted file mode 100644 (file)
index ae47d87..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
-@VaadinApache2LicenseForJavaFiles@
- */
-package com.vaadin.ui;
-
-import java.io.Serializable;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.vaadin.terminal.AbstractClientConnector;
-import com.vaadin.terminal.gwt.server.ClientConnector;
-
-/**
- * A class that tracks dirty {@link ClientConnector}s. A {@link ClientConnector}
- * is dirty when an operation has been performed on it on the server and as a
- * result of this operation new information needs to be sent to its client side
- * counterpart.
- * 
- * @author Vaadin Ltd
- * @version @VERSION@
- * @since 7.0.0
- * 
- */
-public class DirtyConnectorTracker implements Serializable {
-    private Set<ClientConnector> dirtyConnectors = new HashSet<ClientConnector>();
-    private Root root;
-
-    /**
-     * Gets a logger for this class
-     * 
-     * @return A logger instance for logging within this class
-     * 
-     */
-    public static Logger getLogger() {
-        return Logger.getLogger(DirtyConnectorTracker.class.getName());
-    }
-
-    public DirtyConnectorTracker(Root root) {
-        this.root = root;
-    }
-
-    public void markDirty(ClientConnector connector) {
-        if (getLogger().isLoggable(Level.FINE)) {
-            if (!dirtyConnectors.contains(connector)) {
-                getLogger()
-                        .fine(getDebugInfo(connector) + " " + "is now dirty");
-            }
-        }
-
-        dirtyConnectors.add(connector);
-    }
-
-    public void markClean(ClientConnector connector) {
-        if (getLogger().isLoggable(Level.FINE)) {
-            if (dirtyConnectors.contains(connector)) {
-                getLogger().fine(
-                        getDebugInfo(connector) + " " + "is no longer dirty");
-            }
-        }
-
-        dirtyConnectors.remove(connector);
-    }
-
-    private String getDebugInfo(ClientConnector connector) {
-        String message = getObjectString(connector);
-        if (connector.getParent() != null) {
-            message += " (parent: " + getObjectString(connector.getParent())
-                    + ")";
-        }
-        return message;
-    }
-
-    private String getObjectString(Object connector) {
-        return connector.getClass().getName() + "@"
-                + Integer.toHexString(connector.hashCode());
-    }
-
-    public void markAllConnectorsDirty() {
-        markConnectorsDirtyRecursively(root);
-        getLogger().fine("All connectors are now dirty");
-    }
-
-    public void markAllConnectorsClean() {
-        dirtyConnectors.clear();
-        getLogger().fine("All connectors are now clean");
-    }
-
-    /**
-     * Marks all visible connectors dirty, starting from the given connector and
-     * going downwards in the hierarchy.
-     * 
-     * @param c
-     *            The component to start iterating downwards from
-     */
-    private void markConnectorsDirtyRecursively(ClientConnector c) {
-        if (c instanceof Component && !((Component) c).isVisible()) {
-            return;
-        }
-        markDirty(c);
-        for (ClientConnector child : AbstractClientConnector
-                .getAllChildrenIterable(c)) {
-            markConnectorsDirtyRecursively(child);
-        }
-    }
-
-    public Collection<ClientConnector> getDirtyConnectors() {
-        return dirtyConnectors;
-    }
-
-}
index 50ad99571ee5bd3ca94574623a0332ad5f693aaa..3727d6f251850228bac0a48b3b0713b6123d0ef5 100644 (file)
@@ -560,8 +560,7 @@ public abstract class Root extends AbstractComponentContainer implements
     /** Identifies the click event */
     private static final String CLICK_EVENT_ID = VRoot.CLICK_EVENT_ID;
 
-    private DirtyConnectorTracker dirtyConnectorTracker = new DirtyConnectorTracker(
-            this);
+    private ConnectorTracker connectorTracker = new ConnectorTracker(this);
 
     private Page page = new Page(this);
 
@@ -1208,8 +1207,8 @@ public abstract class Root extends AbstractComponentContainer implements
         return isVisible() && isEnabled();
     }
 
-    public DirtyConnectorTracker getDirtyConnectorTracker() {
-        return dirtyConnectorTracker;
+    public ConnectorTracker getConnectorTracker() {
+        return connectorTracker;
     }
 
     public Page getPage() {