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;
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;
return Collections.unmodifiableCollection(roots.values());
}
- private final HashMap<String, ClientConnector> connectorIdToConnector = new HashMap<String, ClientConnector>();
-
private int connectorIdSequence = 0;
/**
* @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() {
public void requestRepaint() {
Root root = getRoot();
if (root != null) {
- root.getDirtyConnectorTracker().markDirty(this);
+ root.getConnectorTracker().markDirty(this);
}
}
public void attach() {
requestRepaint();
+ getRoot().getConnectorTracker().registerConnector(this);
+
for (ClientConnector connector : getAllChildrenIterable(this)) {
connector.attach();
}
+
}
/**
for (ClientConnector connector : getAllChildrenIterable(this)) {
connector.detach();
}
+
+ getRoot().getConnectorTracker().unregisterConnector(this);
}
public boolean isConnectorEnabled() {
/* Handle the request */
if (requestType == RequestType.FILE_UPLOAD) {
- applicationManager.handleFileUpload(wrappedRequest,
+ applicationManager.handleFileUpload(root, wrappedRequest,
wrappedResponse);
return;
} else if (requestType == RequestType.BROWSER_DETAILS) {
/* 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);
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;
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;
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);
}
}
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) {
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();
}
}
Object stateJson = JsonCodec.encode(state, referenceState,
- stateType, application);
+ stateType, root.getConnectorTracker());
sharedStates.put(connector.getConnectorId(), stateJson);
} catch (JSONException e) {
// }
paramJson.put(JsonCodec.encode(
invocation.getParameters()[i], referenceParameter,
- parameterType, application));
+ parameterType, root.getConnectorTracker()));
}
invocationJson.put(paramJson);
rpcCalls.put(invocationJson);
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
* 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()) {
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) {
errorComponent = (Component) dropHandlerOwner;
}
}
- handleChangeVariablesError(app, errorComponent, e,
- changes);
+ handleChangeVariablesError(root.getApplication(),
+ errorComponent, e, changes);
}
}
}
* 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);
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
}
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);
return parseLegacyChangeVariablesInvocation(connectorId,
interfaceName, methodName,
(LegacyChangeVariablesInvocation) previousInvocation,
- parametersJson);
+ parametersJson, connectorTracker);
} else {
return parseServerRpcInvocation(connectorId, interfaceName,
- methodName, parametersJson);
+ methodName, parametersJson, connectorTracker);
}
}
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 "
}
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();
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());
Object parameterValue = parametersJson.get(j);
Type parameterType = declaredRpcMethodParameterTypes[j];
parameters[j] = JsonCodec.decodeInternalOrCustomType(parameterType,
- parameterValue, application);
+ parameterValue, connectorTracker);
}
invocation.setParameters(parameters);
return invocation;
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();
* @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);
}
/**
* Handles file upload request submitted via Upload component.
*
- * @param application
+ * @param root
+ * The root for this request
*
* @see #getStreamVariableTargetUrl(ReceiverOwner, String, StreamVariable)
*
* @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
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
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);
// 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";
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;
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
}
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");
|| targetType == JSONArray.class) {
return value;
} else {
- return decodeObject(targetType, (JSONObject) value, application);
+ return decodeObject(targetType, (JSONObject) value,
+ connectorTracker);
}
}
*/
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.");
// 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);
String stringValue = String.valueOf(encodedJsonValue);
if (JsonEncoder.VTYPE_CONNECTOR.equals(transportType)) {
- return application.getConnector(stringValue);
+ return connectorTracker.getConnector(stringValue);
}
// Standard Java types
}
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);
}
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.
.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>();
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);
}
}
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();
}
*/
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();
}
}
}
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;
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;
}
}
private static Object decodeObject(Type targetType,
- JSONObject serializedObject, Application application)
+ JSONObject serializedObject, ConnectorTracker connectorTracker)
throws JSONException {
Class<?> targetClass = getClassForType(targetType);
Object encodedFieldValue = serializedObject.get(fieldName);
Type fieldType = pd.getReadMethod().getGenericReturnType();
Object decodedFieldValue = decodeInternalOrCustomType(
- fieldType, encodedFieldValue, application);
+ fieldType, encodedFieldValue, connectorTracker);
pd.getWriteMethod().invoke(decodedObject, decodedFieldValue);
}
}
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");
} 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;
}
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);
}
}
}
private static Object encodeObject(Object value, Object referenceValue,
- Application application) throws JSONException {
+ ConnectorTracker connectorTracker) throws JSONException {
JSONObject jsonMap = new JSONObject();
try {
jsonMap.put(
fieldName,
encode(fieldValue, referenceFieldValue, fieldType,
- application));
+ connectorTracker));
// } else {
// System.out.println("Skipping field " + fieldName
// + " of type " + fieldType.getName()
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) {
}
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);
}
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);
}
}
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);
}
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);
.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();
}
--- /dev/null
+/*
+@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;
+ }
+
+}
+++ /dev/null
-/*
-@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;
- }
-
-}
/** 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);
return isVisible() && isEnabled();
}
- public DirtyConnectorTracker getDirtyConnectorTracker() {
- return dirtyConnectorTracker;
+ public ConnectorTracker getConnectorTracker() {
+ return connectorTracker;
}
public Page getPage() {