diff options
12 files changed, 378 insertions, 285 deletions
diff --git a/src/com/vaadin/Application.java b/src/com/vaadin/Application.java index dbf71408a9..3da314a11e 100644 --- a/src/com/vaadin/Application.java +++ b/src/com/vaadin/Application.java @@ -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() { diff --git a/src/com/vaadin/terminal/AbstractClientConnector.java b/src/com/vaadin/terminal/AbstractClientConnector.java index 6a87f58c71..9de444d70e 100644 --- a/src/com/vaadin/terminal/AbstractClientConnector.java +++ b/src/com/vaadin/terminal/AbstractClientConnector.java @@ -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() { diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java index bf29144cc1..b7be6fd394 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java @@ -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) { diff --git a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java index 2179761d31..0e548f61c8 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java @@ -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); diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java index ecd4fdd1ad..797f8f756c 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java @@ -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); } diff --git a/src/com/vaadin/terminal/gwt/server/CommunicationManager.java b/src/com/vaadin/terminal/gwt/server/CommunicationManager.java index 3dd2eb97fd..cc2981dc45 100644 --- a/src/com/vaadin/terminal/gwt/server/CommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/CommunicationManager.java @@ -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); diff --git a/src/com/vaadin/terminal/gwt/server/Constants.java b/src/com/vaadin/terminal/gwt/server/Constants.java index 7c467aa7f4..9e6b2c775b 100644 --- a/src/com/vaadin/terminal/gwt/server/Constants.java +++ b/src/com/vaadin/terminal/gwt/server/Constants.java @@ -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"; diff --git a/src/com/vaadin/terminal/gwt/server/JsonCodec.java b/src/com/vaadin/terminal/gwt/server/JsonCodec.java index 71e4727164..4b470f7215 100644 --- a/src/com/vaadin/terminal/gwt/server/JsonCodec.java +++ b/src/com/vaadin/terminal/gwt/server/JsonCodec.java @@ -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); } diff --git a/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java index 55f15da3d9..d3fbf4d988 100644 --- a/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java @@ -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 index 0000000000..75a75ad22a --- /dev/null +++ b/src/com/vaadin/ui/ConnectorTracker.java @@ -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 index ae47d87eae..0000000000 --- a/src/com/vaadin/ui/DirtyConnectorTracker.java +++ /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; - } - -} diff --git a/src/com/vaadin/ui/Root.java b/src/com/vaadin/ui/Root.java index 50ad99571e..3727d6f251 100644 --- a/src/com/vaadin/ui/Root.java +++ b/src/com/vaadin/ui/Root.java @@ -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() { |