Moved Connector Id -> Connector mapping to Application Moved dirty connector tracking to Root Removed adding oftags/7.0.0.alpha2
@@ -18,6 +18,7 @@ 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; | |||
@@ -47,11 +48,13 @@ 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.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; | |||
@@ -2266,4 +2269,67 @@ public class Application implements Terminal.ErrorListener, Serializable { | |||
public Collection<Root> getRoots() { | |||
return Collections.unmodifiableCollection(roots.values()); | |||
} | |||
private final HashMap<String, Connector> connectorIdToConnector = new HashMap<String, Connector>(); | |||
private int connectorIdSequence = 0; | |||
/** | |||
* Generate an id for the given Connector. Connectors must not call this | |||
* method more than once, the first time they need an id. | |||
* | |||
* @param connector | |||
* A connector that has not yet been assigned an id. | |||
* @return A new id for the connector | |||
*/ | |||
public String createConnectorId(Connector connector) { | |||
// String connectorId = "C_" + connectorIdSequence++; | |||
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 Connector 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(); | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -6,10 +6,6 @@ package com.vaadin.terminal; | |||
import java.io.Serializable; | |||
import java.util.EventObject; | |||
import java.util.List; | |||
import com.vaadin.terminal.gwt.client.communication.SharedState; | |||
import com.vaadin.terminal.gwt.server.ClientMethodInvocation; | |||
/** | |||
* Interface implemented by all classes that can be painted. Classes | |||
@@ -43,30 +39,6 @@ public interface Paintable extends java.util.EventListener, Serializable { | |||
*/ | |||
public void paint(PaintTarget target) throws PaintException; | |||
/** | |||
* Returns the current shared state bean for the paintable. The state (or | |||
* changes to it) is communicated from the server to the client when | |||
* components are painted. | |||
* | |||
* Subclasses can use a more specific return type for this method. | |||
* | |||
* @return shared state instance or null | |||
* | |||
* @since 7.0 | |||
*/ | |||
public SharedState getState(); | |||
/** | |||
* Returns the list of pending server to client RPC calls and clears the | |||
* list. | |||
* | |||
* @return unmodifiable ordered list of pending server to client method | |||
* calls (not null) | |||
* | |||
* @since 7.0 | |||
*/ | |||
public List<ClientMethodInvocation> retrievePendingRpcCalls(); | |||
/** | |||
* Requests that the paintable should be repainted as soon as possible. | |||
*/ |
@@ -82,7 +82,7 @@ public class JsonDecoder { | |||
} else if (JsonEncoder.VTYPE_BOOLEAN.equals(variableType)) { | |||
// TODO handle properly | |||
val = Boolean.valueOf(String.valueOf(value)); | |||
} else if (JsonEncoder.VTYPE_PAINTABLE.equals(variableType)) { | |||
} else if (JsonEncoder.VTYPE_CONNECTOR.equals(variableType)) { | |||
val = idMapper.getConnector(((JSONString) value).stringValue()); | |||
} else { | |||
// object, class name as type |
@@ -14,8 +14,8 @@ import com.google.gwt.json.client.JSONObject; | |||
import com.google.gwt.json.client.JSONString; | |||
import com.google.gwt.json.client.JSONValue; | |||
import com.vaadin.terminal.gwt.client.ApplicationConnection; | |||
import com.vaadin.terminal.gwt.client.Connector; | |||
import com.vaadin.terminal.gwt.client.ConnectorMap; | |||
import com.vaadin.terminal.gwt.client.ServerConnector; | |||
/** | |||
* Encoder for converting RPC parameters and other values to JSON for transfer | |||
@@ -30,7 +30,7 @@ import com.vaadin.terminal.gwt.client.ServerConnector; | |||
*/ | |||
public class JsonEncoder { | |||
public static final String VTYPE_PAINTABLE = "p"; | |||
public static final String VTYPE_CONNECTOR = "c"; | |||
public static final String VTYPE_BOOLEAN = "b"; | |||
public static final String VTYPE_DOUBLE = "d"; | |||
public static final String VTYPE_FLOAT = "f"; | |||
@@ -38,7 +38,7 @@ public class JsonEncoder { | |||
public static final String VTYPE_INTEGER = "i"; | |||
public static final String VTYPE_STRING = "s"; | |||
public static final String VTYPE_ARRAY = "a"; | |||
public static final String VTYPE_STRINGARRAY = "c"; | |||
public static final String VTYPE_STRINGARRAY = "S"; | |||
public static final String VTYPE_MAP = "m"; | |||
public static final String VTYPE_LIST = "L"; | |||
public static final String VTYPE_NULL = "n"; | |||
@@ -88,10 +88,10 @@ public class JsonEncoder { | |||
jsonMap.put(mapKey, encode(mapValue, connectorMap, connection)); | |||
} | |||
return combineTypeAndValue(VTYPE_MAP, jsonMap); | |||
} else if (value instanceof ServerConnector) { | |||
ServerConnector paintable = (ServerConnector) value; | |||
return combineTypeAndValue(VTYPE_PAINTABLE, new JSONString( | |||
connectorMap.getConnectorId(paintable))); | |||
} else if (value instanceof Connector) { | |||
Connector connector = (Connector) value; | |||
return combineTypeAndValue(VTYPE_CONNECTOR, new JSONString( | |||
connector.getConnectorId())); | |||
} else { | |||
String transportType = getTransportType(value); | |||
if (transportType != null) { | |||
@@ -123,8 +123,8 @@ public class JsonEncoder { | |||
return VTYPE_NULL; | |||
} else if (value instanceof String) { | |||
return VTYPE_STRING; | |||
} else if (value instanceof ServerConnector) { | |||
return VTYPE_PAINTABLE; | |||
} else if (value instanceof Connector) { | |||
return VTYPE_CONNECTOR; | |||
} else if (value instanceof Boolean) { | |||
return VTYPE_BOOLEAN; | |||
} else if (value instanceof Integer) { |
@@ -223,7 +223,7 @@ public class VDragAndDropManager { | |||
ENTER, LEAVE, OVER, DROP | |||
} | |||
private static final String DD_SERVICE = "DD"; | |||
public static final String DD_SERVICE = "DD"; | |||
private static VDragAndDropManager instance; | |||
private HandlerRegistration handlerRegistration; |
@@ -472,7 +472,8 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements | |||
/* Handle the request */ | |||
if (requestType == RequestType.FILE_UPLOAD) { | |||
applicationManager.handleFileUpload(request, response); | |||
applicationManager.handleFileUpload(application, request, | |||
response); | |||
return; | |||
} else if (requestType == RequestType.UIDL) { | |||
// Handles AJAX UIDL requests |
@@ -69,9 +69,11 @@ 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.HasComponents; | |||
import com.vaadin.ui.Panel; | |||
import com.vaadin.ui.Root; | |||
import com.vaadin.ui.Window; | |||
/** | |||
* This is a common base class for the server-side implementations of the | |||
@@ -91,7 +93,7 @@ import com.vaadin.ui.Root; | |||
*/ | |||
@SuppressWarnings("serial") | |||
public abstract class AbstractCommunicationManager implements | |||
Paintable.RepaintRequestListener, PaintableIdMapper, Serializable { | |||
Paintable.RepaintRequestListener, Serializable { | |||
private static final String DASHDASH = "--"; | |||
@@ -142,15 +144,16 @@ public abstract class AbstractCommunicationManager implements | |||
// cannot combine with paint queue: | |||
// this can contain dirty components from any Root | |||
private final ArrayList<Paintable> dirtyPaintables = new ArrayList<Paintable>(); | |||
// TODO Remove, it is now in Root | |||
// private final ArrayList<Paintable> dirtyPaintables = new | |||
// ArrayList<Paintable>(); | |||
// queue used during painting to keep track of what still needs to be | |||
// painted within the Root being painted | |||
private LinkedList<Paintable> paintQueue = new LinkedList<Paintable>(); | |||
// private LinkedList<Connector> paintQueue = new LinkedList<Connector>(); | |||
private final HashMap<Paintable, String> paintableIdMap = new HashMap<Paintable, String>(); | |||
private final HashMap<String, Paintable> idPaintableMap = new HashMap<String, Paintable>(); | |||
// private final HashMap<String, Paintable> idPaintableMap = new | |||
// HashMap<String, Paintable>(); | |||
private int idSequence = 0; | |||
@@ -168,6 +171,8 @@ public abstract class AbstractCommunicationManager implements | |||
private int maxInactiveInterval; | |||
private Connector highlightedConnector; | |||
/** | |||
* TODO New constructor - document me! | |||
* | |||
@@ -193,8 +198,6 @@ public abstract class AbstractCommunicationManager implements | |||
private static final String GET_PARAM_HIGHLIGHT_COMPONENT = "highlightComponent"; | |||
private Paintable highLightedPaintable; | |||
private static String readLine(InputStream stream) throws IOException { | |||
ByteArrayOutputStream bout = new ByteArrayOutputStream(); | |||
int readByte = stream.read(); | |||
@@ -220,7 +223,7 @@ public abstract class AbstractCommunicationManager implements | |||
*/ | |||
protected void doHandleSimpleMultipartFileUpload(WrappedRequest request, | |||
WrappedResponse response, StreamVariable streamVariable, | |||
String variableName, VariableOwner owner, String boundary) | |||
String variableName, Connector owner, String boundary) | |||
throws IOException { | |||
// multipart parsing, supports only one file for request, but that is | |||
// fine for our current terminal | |||
@@ -319,7 +322,7 @@ public abstract class AbstractCommunicationManager implements | |||
*/ | |||
protected void doHandleXhrFilePost(WrappedRequest request, | |||
WrappedResponse response, StreamVariable streamVariable, | |||
String variableName, VariableOwner owner, int contentLength) | |||
String variableName, Connector owner, int contentLength) | |||
throws IOException { | |||
// These are unknown in filexhr ATM, maybe add to Accept header that | |||
@@ -536,8 +539,8 @@ public abstract class AbstractCommunicationManager implements | |||
if (request.getParameter(GET_PARAM_HIGHLIGHT_COMPONENT) != null) { | |||
String pid = request | |||
.getParameter(GET_PARAM_HIGHLIGHT_COMPONENT); | |||
highLightedPaintable = idPaintableMap.get(pid); | |||
highlightPaintable(highLightedPaintable); | |||
highlightedConnector = root.getApplication().getConnector(pid); | |||
highlightConnector(highlightedConnector); | |||
} | |||
} | |||
@@ -597,22 +600,35 @@ public abstract class AbstractCommunicationManager implements | |||
paintAfterVariableChanges(request, response, callback, repaintAll, | |||
outWriter, root, analyzeLayouts); | |||
postPaint(root); | |||
} | |||
outWriter.close(); | |||
requestThemeName = null; | |||
} | |||
protected void highlightPaintable(Paintable highLightedPaintable2) { | |||
/** | |||
* Method called after the paint phase while still being synchronized on the | |||
* application | |||
* | |||
* @param root | |||
* | |||
*/ | |||
protected void postPaint(Root root) { | |||
// Remove connectors that have been detached from the application during | |||
// handling of the request | |||
root.getApplication().cleanConnectorMap(); | |||
} | |||
protected void highlightConnector(Connector highlightedConnector) { | |||
StringBuilder sb = new StringBuilder(); | |||
sb.append("*** Debug details of a component: *** \n"); | |||
sb.append("Type: "); | |||
sb.append(highLightedPaintable2.getClass().getName()); | |||
if (highLightedPaintable2 instanceof AbstractComponent) { | |||
AbstractComponent component = (AbstractComponent) highLightedPaintable2; | |||
sb.append(highlightedConnector.getClass().getName()); | |||
if (highlightedConnector instanceof AbstractComponent) { | |||
AbstractComponent component = (AbstractComponent) highlightedConnector; | |||
sb.append("\nId:"); | |||
sb.append(paintableIdMap.get(component)); | |||
sb.append(highlightedConnector.getConnectorId()); | |||
if (component.getCaption() != null) { | |||
sb.append("\nCaption:"); | |||
sb.append(component.getCaption()); | |||
@@ -681,10 +697,6 @@ public abstract class AbstractCommunicationManager implements | |||
final PrintWriter outWriter, Root root, boolean analyzeLayouts) | |||
throws PaintException, IOException { | |||
if (repaintAll) { | |||
makeAllPaintablesDirty(root); | |||
} | |||
// Removes application if it has stopped during variable changes | |||
if (!application.isRunning()) { | |||
endApplication(request, response, application); | |||
@@ -746,126 +758,75 @@ public abstract class AbstractCommunicationManager implements | |||
// for internal use by JsonPaintTarget | |||
public void queuePaintable(Paintable paintable) { | |||
if (!paintQueue.contains(paintable)) { | |||
paintQueue.add(paintable); | |||
} | |||
// if (!paintQueue.contains(paintable)) { | |||
// paintQueue.add((Connector) paintable); | |||
// } | |||
} | |||
public void writeUidlResponse(boolean repaintAll, | |||
final PrintWriter outWriter, Root root, boolean analyzeLayouts) | |||
throws PaintException { | |||
ArrayList<Paintable> paintables = null; | |||
ArrayList<Connector> dirtyVisibleConnectors = new ArrayList<Connector>(); | |||
Application application = root.getApplication(); | |||
// Paints components | |||
DirtyConnectorTracker rootConnectorTracker = root | |||
.getDirtyConnectorTracker(); | |||
System.out.println("* Creating response to client"); | |||
if (repaintAll) { | |||
paintables = new ArrayList<Paintable>(); | |||
paintables.add(root); | |||
System.out.println("Full repaint"); | |||
getClientCache(root).clear(); | |||
rootConnectorTracker.markAllComponentsDirty(); | |||
dirtyVisibleConnectors.addAll(rootConnectorTracker | |||
.getDirtyComponents()); | |||
// Reset sent locales | |||
locales = null; | |||
requireLocale(application.getLocale().toString()); | |||
} else { | |||
// remove detached components from paintableIdMap so they | |||
// can be GC'ed | |||
/* | |||
* TODO figure out if we could move this beyond the painting phase, | |||
* "respond as fast as possible, then do the cleanup". Beware of | |||
* painting the dirty detached components. | |||
*/ | |||
for (Iterator<Paintable> it = paintableIdMap.keySet().iterator(); it | |||
.hasNext();) { | |||
Component p = (Component) it.next(); | |||
if (p.getApplication() == null) { | |||
unregisterPaintable(p); | |||
// Take into account that some other component may have | |||
// reused p's ID by now (this can happen when manually | |||
// assigning IDs with setDebugId().) See #8090. | |||
String pid = paintableIdMap.get(p); | |||
if (idPaintableMap.get(pid) == p) { | |||
idPaintableMap.remove(pid); | |||
} | |||
it.remove(); | |||
dirtyPaintables.remove(p); | |||
} | |||
} | |||
// TODO second list/stack for those whose state needs to be sent? | |||
paintables = getDirtyVisibleComponents(root); | |||
dirtyVisibleConnectors | |||
.addAll(getDirtyVisibleComponents(rootConnectorTracker)); | |||
} | |||
System.out.println("* Found " + dirtyVisibleConnectors.size() | |||
+ " dirty connectors to paint"); | |||
rootConnectorTracker.markAllComponentsClean(); | |||
outWriter.print("\"changes\":["); | |||
List<InvalidLayout> invalidComponentRelativeSizes = null; | |||
JsonPaintTarget paintTarget = new JsonPaintTarget(this, outWriter, | |||
!repaintAll); | |||
ClientCache clientCache = getClientCache(root); | |||
// TODO These seem unnecessary and could be removed/replaced by looping | |||
// through paintQueue without removing paintables from it | |||
LinkedList<Paintable> stateQueue = new LinkedList<Paintable>(); | |||
LinkedList<Paintable> rpcPendingQueue = new LinkedList<Paintable>(); | |||
LinkedList<Paintable> hierarchyPendingQueue = new LinkedList<Paintable>(); | |||
LinkedList<Paintable> connectorTypeQueue = new LinkedList<Paintable>(); | |||
if (paintables != null) { | |||
// clear and rebuild paint queue | |||
paintQueue.clear(); | |||
paintQueue.addAll(paintables); | |||
while (!paintQueue.isEmpty()) { | |||
final Paintable p = paintQueue.removeFirst(); | |||
// for now, all painted components may need a state refresh | |||
stateQueue.push(p); | |||
// TODO This should be optimized. The type only needs to be sent | |||
// once for each connector id + on refresh | |||
connectorTypeQueue.push(p); | |||
// also a hierarchy update | |||
hierarchyPendingQueue.push(p); | |||
// ... and RPC calls to be sent | |||
rpcPendingQueue.push(p); | |||
// // TODO CLEAN | |||
// if (p instanceof Root) { | |||
// final Root r = (Root) p; | |||
// if (r.getTerminal() == null) { | |||
// r.setTerminal(application.getRoot().getTerminal()); | |||
// } | |||
// } | |||
// TODO we may still get changes that have been | |||
// rendered already (changes with only cached flag) | |||
if (paintTarget.needsToBePainted(p)) { | |||
paintTarget.startTag("change"); | |||
final String pid = getPaintableId(p); | |||
paintTarget.addAttribute("pid", pid); | |||
// paints subcomponents as references (via | |||
// JsonPaintTarget.startPaintable()) and defers painting | |||
// their contents to another top-level change (via | |||
// queuePaintable()) | |||
p.paint(paintTarget); | |||
paintTarget.endTag("change"); | |||
} | |||
paintablePainted(p); | |||
for (Connector connector : dirtyVisibleConnectors) { | |||
if (connector instanceof Paintable) { | |||
Paintable p = (Paintable) connector; | |||
paintTarget.startTag("change"); | |||
final String pid = connector.getConnectorId(); | |||
paintTarget.addAttribute("pid", pid); | |||
p.paint(paintTarget); | |||
paintTarget.endTag("change"); | |||
} | |||
} | |||
if (analyzeLayouts) { | |||
Root w = (Root) p; | |||
if (analyzeLayouts) { | |||
// TODO Check if this works | |||
invalidComponentRelativeSizes = ComponentSizeValidator | |||
.validateComponentRelativeSizes(root.getContent(), null, | |||
null); | |||
// Also check any existing subwindows | |||
if (root.getWindows() != null) { | |||
for (Window subWindow : root.getWindows()) { | |||
invalidComponentRelativeSizes = ComponentSizeValidator | |||
.validateComponentRelativeSizes(w.getContent(), | |||
null, null); | |||
// // Also check any existing subwindows | |||
// if (w.getChildWindows() != null) { | |||
// for (Window subWindow : w.getChildWindows()) { | |||
// invalidComponentRelativeSizes = ComponentSizeValidator | |||
// .validateComponentRelativeSizes( | |||
// subWindow.getContent(), | |||
// invalidComponentRelativeSizes, null); | |||
// } | |||
// } | |||
.validateComponentRelativeSizes( | |||
subWindow.getContent(), | |||
invalidComponentRelativeSizes, null); | |||
} | |||
} | |||
} | |||
@@ -873,136 +834,131 @@ public abstract class AbstractCommunicationManager implements | |||
paintTarget.close(); | |||
outWriter.print("], "); // close changes | |||
if (!stateQueue.isEmpty()) { | |||
// send shared state to client | |||
// for now, send the complete state of all modified and new | |||
// components | |||
// Ideally, all this would be sent before "changes", but that causes | |||
// complications with legacy components that create sub-components | |||
// in their paint phase. Nevertheless, this will be processed on the | |||
// client after component creation but before legacy UIDL | |||
// processing. | |||
JSONObject sharedStates = new JSONObject(); | |||
while (!stateQueue.isEmpty()) { | |||
final Paintable p = stateQueue.pop(); | |||
String paintableId = getPaintableId(p); | |||
SharedState state = p.getState(); | |||
if (null != state) { | |||
// encode and send shared state | |||
try { | |||
JSONArray stateJsonArray = JsonCodec | |||
.encode(state, this); | |||
sharedStates.put(paintableId, stateJsonArray); | |||
} catch (JSONException e) { | |||
throw new PaintException( | |||
"Failed to serialize shared state for paintable " | |||
+ paintableId + ": " + e.getMessage()); | |||
} | |||
} | |||
} | |||
outWriter.print("\"state\":"); | |||
outWriter.append(sharedStates.toString()); | |||
outWriter.print(", "); // close states | |||
} | |||
if (!connectorTypeQueue.isEmpty()) { | |||
JSONObject connectorTypes = new JSONObject(); | |||
while (!connectorTypeQueue.isEmpty()) { | |||
final Paintable p = connectorTypeQueue.pop(); | |||
String paintableId = getPaintableId(p); | |||
String connectorType = paintTarget.getTag(p); | |||
// send shared state to client | |||
// for now, send the complete state of all modified and new | |||
// components | |||
// Ideally, all this would be sent before "changes", but that causes | |||
// complications with legacy components that create sub-components | |||
// in their paint phase. Nevertheless, this will be processed on the | |||
// client after component creation but before legacy UIDL | |||
// processing. | |||
JSONObject sharedStates = new JSONObject(); | |||
for (Connector connector : dirtyVisibleConnectors) { | |||
SharedState state = connector.getState(); | |||
if (null != state) { | |||
// encode and send shared state | |||
try { | |||
connectorTypes.put(paintableId, connectorType); | |||
JSONArray stateJsonArray = JsonCodec.encode(state, | |||
application); | |||
sharedStates | |||
.put(connector.getConnectorId(), stateJsonArray); | |||
} catch (JSONException e) { | |||
throw new PaintException( | |||
"Failed to send connector type for paintable " | |||
+ paintableId + ": " + e.getMessage()); | |||
} | |||
} | |||
outWriter.print("\"types\":"); | |||
outWriter.append(connectorTypes.toString()); | |||
outWriter.print(", "); // close states | |||
} | |||
if (!hierarchyPendingQueue.isEmpty()) { | |||
// Send update hierarchy information to the client. | |||
// This could be optimized aswell to send only info if hierarchy has | |||
// actually changed. Much like with the shared state. Note though | |||
// that an empty hierarchy is information aswell (e.g. change from 1 | |||
// child to 0 children) | |||
outWriter.print("\"hierarchy\":"); | |||
JSONObject hierarchyInfo = new JSONObject(); | |||
for (Paintable p : hierarchyPendingQueue) { | |||
if (p instanceof HasComponents) { | |||
HasComponents parent = (HasComponents) p; | |||
String parentConnectorId = paintableIdMap.get(parent); | |||
JSONArray children = new JSONArray(); | |||
for (Component child : getChildComponents(parent)) { | |||
if (isVisible(child)) { | |||
String childConnectorId = getPaintableId(child); | |||
children.put(childConnectorId); | |||
} | |||
} | |||
try { | |||
hierarchyInfo.put(parentConnectorId, children); | |||
} catch (JSONException e) { | |||
throw new PaintException( | |||
"Failed to send hierarchy information about " | |||
+ parentConnectorId | |||
+ " to the client: " + e.getMessage()); | |||
} | |||
"Failed to serialize shared state for connector " | |||
+ connector.getConnectorId() + ": " | |||
+ e.getMessage()); | |||
} | |||
} | |||
outWriter.append(hierarchyInfo.toString()); | |||
outWriter.print(", "); // close hierarchy | |||
} | |||
outWriter.print("\"state\":"); | |||
outWriter.append(sharedStates.toString()); | |||
outWriter.print(", "); // close states | |||
// TODO This should be optimized. The type only needs to be | |||
// sent once for each connector id + on refresh | |||
JSONObject connectorTypes = new JSONObject(); | |||
for (Connector connector : dirtyVisibleConnectors) { | |||
String connectorType = paintTarget.getTag((Paintable) connector); | |||
try { | |||
connectorTypes.put(connector.getConnectorId(), connectorType); | |||
} catch (JSONException e) { | |||
throw new PaintException( | |||
"Failed to send connector type for connector " | |||
+ connector.getConnectorId() + ": " | |||
+ e.getMessage()); | |||
} | |||
} | |||
outWriter.print("\"types\":"); | |||
outWriter.append(connectorTypes.toString()); | |||
outWriter.print(", "); // close states | |||
// send server to client RPC calls for components in the root, in call | |||
// order | |||
if (!rpcPendingQueue.isEmpty()) { | |||
// collect RPC calls from components in the root in the order in | |||
// which they were performed, remove the calls from components | |||
List<ClientMethodInvocation> pendingInvocations = collectPendingRpcCalls(rpcPendingQueue); | |||
JSONArray rpcCalls = new JSONArray(); | |||
for (ClientMethodInvocation invocation : pendingInvocations) { | |||
// add invocation to rpcCalls | |||
try { | |||
JSONArray invocationJson = new JSONArray(); | |||
invocationJson | |||
.put(getPaintableId(invocation.getPaintable())); | |||
invocationJson.put(invocation.getInterfaceName()); | |||
invocationJson.put(invocation.getMethodName()); | |||
JSONArray paramJson = new JSONArray(); | |||
for (int i = 0; i < invocation.getParameters().length; ++i) { | |||
paramJson.put(JsonCodec.encode( | |||
invocation.getParameters()[i], this)); | |||
// Send update hierarchy information to the client. | |||
// This could be optimized aswell to send only info if hierarchy has | |||
// actually changed. Much like with the shared state. Note though | |||
// that an empty hierarchy is information aswell (e.g. change from 1 | |||
// child to 0 children) | |||
outWriter.print("\"hierarchy\":"); | |||
JSONObject hierarchyInfo = new JSONObject(); | |||
for (Connector connector : dirtyVisibleConnectors) { | |||
if (connector instanceof HasComponents) { | |||
HasComponents parent = (HasComponents) connector; | |||
String parentConnectorId = parent.getConnectorId(); | |||
JSONArray children = new JSONArray(); | |||
for (Component child : getChildComponents(parent)) { | |||
if (isVisible(child)) { | |||
children.put(child.getConnectorId()); | |||
} | |||
invocationJson.put(paramJson); | |||
rpcCalls.put(invocationJson); | |||
} | |||
try { | |||
hierarchyInfo.put(parentConnectorId, children); | |||
} catch (JSONException e) { | |||
throw new PaintException( | |||
"Failed to serialize RPC method call parameters for paintable " | |||
+ getPaintableId(invocation.getPaintable()) | |||
+ " method " | |||
+ invocation.getInterfaceName() + "." | |||
+ invocation.getMethodName() + ": " | |||
"Failed to send hierarchy information about " | |||
+ parentConnectorId + " to the client: " | |||
+ e.getMessage()); | |||
} | |||
} | |||
} | |||
outWriter.append(hierarchyInfo.toString()); | |||
outWriter.print(", "); // close hierarchy | |||
// send server to client RPC calls for components in the root, in call | |||
// order | |||
// collect RPC calls from components in the root in the order in | |||
// which they were performed, remove the calls from components | |||
LinkedList<ClientConnector> rpcPendingQueue = new LinkedList<ClientConnector>( | |||
(Collection<? extends ClientConnector>) dirtyVisibleConnectors); | |||
List<ClientMethodInvocation> pendingInvocations = collectPendingRpcCalls(rpcPendingQueue); | |||
if (rpcCalls.length() > 0) { | |||
outWriter.print("\"rpc\" : "); | |||
outWriter.append(rpcCalls.toString()); | |||
outWriter.print(", "); // close rpc | |||
JSONArray rpcCalls = new JSONArray(); | |||
for (ClientMethodInvocation invocation : pendingInvocations) { | |||
// add invocation to rpcCalls | |||
try { | |||
JSONArray invocationJson = new JSONArray(); | |||
invocationJson.put(invocation.getConnector().getConnectorId()); | |||
invocationJson.put(invocation.getInterfaceName()); | |||
invocationJson.put(invocation.getMethodName()); | |||
JSONArray paramJson = new JSONArray(); | |||
for (int i = 0; i < invocation.getParameters().length; ++i) { | |||
paramJson.put(JsonCodec.encode( | |||
invocation.getParameters()[i], application)); | |||
} | |||
invocationJson.put(paramJson); | |||
rpcCalls.put(invocationJson); | |||
} catch (JSONException e) { | |||
throw new PaintException( | |||
"Failed to serialize RPC method call parameters for connector " | |||
+ invocation.getConnector().getConnectorId() | |||
+ " method " + invocation.getInterfaceName() | |||
+ "." + invocation.getMethodName() + ": " | |||
+ e.getMessage()); | |||
} | |||
} | |||
if (rpcCalls.length() > 0) { | |||
outWriter.print("\"rpc\" : "); | |||
outWriter.append(rpcCalls.toString()); | |||
outWriter.print(", "); // close rpc | |||
} | |||
outWriter.print("\"meta\" : {"); | |||
@@ -1027,11 +983,11 @@ public abstract class AbstractCommunicationManager implements | |||
} | |||
outWriter.write("]"); | |||
} | |||
if (highLightedPaintable != null) { | |||
if (highlightedConnector != null) { | |||
outWriter.write(", \"hl\":\""); | |||
outWriter.write(paintableIdMap.get(highLightedPaintable)); | |||
outWriter.write(highlightedConnector.getConnectorId()); | |||
outWriter.write("\""); | |||
highLightedPaintable = null; | |||
highlightedConnector = null; | |||
} | |||
} | |||
@@ -1123,6 +1079,8 @@ public abstract class AbstractCommunicationManager implements | |||
Collection<Class<? extends Paintable>> usedPaintableTypes = paintTarget | |||
.getUsedPaintableTypes(); | |||
boolean typeMappingsOpen = false; | |||
ClientCache clientCache = getClientCache(root); | |||
for (Class<? extends Paintable> class1 : usedPaintableTypes) { | |||
if (clientCache.cache(class1)) { | |||
// client does not know the mapping key for this type, send | |||
@@ -1208,10 +1166,10 @@ public abstract class AbstractCommunicationManager implements | |||
* @return ordered list of pending RPC calls | |||
*/ | |||
private List<ClientMethodInvocation> collectPendingRpcCalls( | |||
LinkedList<Paintable> rpcPendingQueue) { | |||
LinkedList<ClientConnector> rpcPendingQueue) { | |||
List<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>(); | |||
for (Paintable paintable : rpcPendingQueue) { | |||
List<ClientMethodInvocation> paintablePendingRpc = paintable | |||
for (ClientConnector connector : rpcPendingQueue) { | |||
List<ClientMethodInvocation> paintablePendingRpc = connector | |||
.retrievePendingRpcCalls(); | |||
if (null != paintablePendingRpc && !paintablePendingRpc.isEmpty()) { | |||
List<ClientMethodInvocation> oldPendingRpc = pendingInvocations; | |||
@@ -1262,30 +1220,6 @@ public abstract class AbstractCommunicationManager implements | |||
return requestThemeName; | |||
} | |||
public void makeAllPaintablesDirty(Root root) { | |||
// If repaint is requested, clean all ids in this root window | |||
for (final Iterator<String> it = idPaintableMap.keySet().iterator(); it | |||
.hasNext();) { | |||
final Component c = (Component) idPaintableMap.get(it.next()); | |||
if (isChildOf(root, c)) { | |||
it.remove(); | |||
paintableIdMap.remove(c); | |||
} | |||
} | |||
// clean WindowCache | |||
getClientCache(root).clear(); | |||
} | |||
/** | |||
* Called when communication manager stops listening for repaints for given | |||
* component. | |||
* | |||
* @param p | |||
*/ | |||
protected void unregisterPaintable(Component p) { | |||
p.removeListener(this); | |||
} | |||
/** | |||
* Returns false if the cross site request forgery protection is turned off. | |||
* | |||
@@ -1458,20 +1392,14 @@ public abstract class AbstractCommunicationManager implements | |||
if (!ApplicationConnection.UPDATE_VARIABLE_INTERFACE | |||
.equals(interfaceName)) { | |||
// handle other RPC calls than variable changes | |||
applyInvocation(invocation); | |||
applyInvocation(app, invocation); | |||
continue; | |||
} | |||
final VariableOwner owner = getVariableOwner(invocation | |||
.getConnectorId()); | |||
final VariableOwner owner = (VariableOwner) getConnector(app, | |||
invocation.getConnectorId()); | |||
boolean connectorEnabled; | |||
if (owner instanceof Connector) { | |||
connectorEnabled = ((Connector) owner).isConnectorEnabled(); | |||
} else { | |||
// TODO Remove | |||
connectorEnabled = owner.isEnabled(); | |||
} | |||
boolean connectorEnabled = owner.isEnabled(); | |||
if (owner != null && connectorEnabled) { | |||
VariableChange change = new VariableChange(invocation); | |||
@@ -1558,15 +1486,17 @@ public abstract class AbstractCommunicationManager implements | |||
* Execute an RPC call from the client by finding its target and letting the | |||
* RPC mechanism call the correct method for it. | |||
* | |||
* @param app | |||
* | |||
* @param invocation | |||
*/ | |||
protected void applyInvocation(MethodInvocation invocation) { | |||
final RpcTarget target = getRpcTarget(invocation.getConnectorId()); | |||
if (null != target) { | |||
ServerRpcManager.applyInvocation(target, invocation); | |||
protected void applyInvocation(Application app, MethodInvocation invocation) { | |||
Connector c = app.getConnector(invocation.getConnectorId()); | |||
if (c instanceof RpcTarget) { | |||
ServerRpcManager.applyInvocation((RpcTarget) c, invocation); | |||
} else { | |||
// TODO better exception? | |||
throw new RuntimeException("No RPC target for paintable " | |||
throw new RuntimeException("No RPC target for connector " | |||
+ invocation.getConnectorId()); | |||
} | |||
} | |||
@@ -1596,7 +1526,7 @@ public abstract class AbstractCommunicationManager implements | |||
Object[] parameters = new Object[parametersJson.length()]; | |||
for (int j = 0; j < parametersJson.length(); ++j) { | |||
parameters[j] = JsonCodec.decode( | |||
parametersJson.getJSONArray(j), this); | |||
parametersJson.getJSONArray(j), application); | |||
} | |||
MethodInvocation invocation = new MethodInvocation(connectorId, | |||
interfaceName, methodName, parameters); | |||
@@ -1610,34 +1540,17 @@ public abstract class AbstractCommunicationManager implements | |||
owner.changeVariables(source, m); | |||
} | |||
protected VariableOwner getVariableOwner(String string) { | |||
VariableOwner owner = (VariableOwner) idPaintableMap.get(string); | |||
if (owner == null && string.startsWith("DD")) { | |||
protected Connector getConnector(Application app, String connectorId) { | |||
Connector c = app.getConnector(connectorId); | |||
if (c == null | |||
&& connectorId.equals(getDragAndDropService().getConnectorId())) { | |||
return getDragAndDropService(); | |||
} | |||
return owner; | |||
} | |||
/** | |||
* Returns the RPC call target for a paintable ID. | |||
* | |||
* @since 7.0 | |||
* | |||
* @param string | |||
* paintable ID | |||
* @return RPC call target or null if none found | |||
*/ | |||
protected RpcTarget getRpcTarget(String string) { | |||
// TODO improve this - VariableOwner and RpcManager separate? | |||
VariableOwner owner = getVariableOwner(string); | |||
if (owner instanceof RpcTarget) { | |||
return (RpcTarget) owner; | |||
} else { | |||
return null; | |||
} | |||
return c; | |||
} | |||
private VariableOwner getDragAndDropService() { | |||
private DragAndDropService getDragAndDropService() { | |||
if (dragAndDropService == null) { | |||
dragAndDropService = new DragAndDropService(this); | |||
} | |||
@@ -1973,56 +1886,6 @@ public abstract class AbstractCommunicationManager implements | |||
outWriter.print("for(;;);[{"); | |||
} | |||
public Paintable getPaintable(String paintableId) { | |||
return idPaintableMap.get(paintableId); | |||
} | |||
/** | |||
* Gets the Paintable Id. If Paintable has debug id set it will be used | |||
* prefixed with "PID_S". Otherwise a sequenced ID is created. | |||
* | |||
* @param paintable | |||
* @return the paintable Id. | |||
*/ | |||
public String getPaintableId(Paintable paintable) { | |||
String id = paintableIdMap.get(paintable); | |||
if (id == null) { | |||
// use testing identifier as id if set | |||
id = paintable.getDebugId(); | |||
if (id == null) { | |||
id = "PID" + Integer.toString(idSequence++); | |||
} else { | |||
id = "PID_S" + id; | |||
} | |||
Paintable old = idPaintableMap.put(id, paintable); | |||
if (old != null && old != paintable) { | |||
/* | |||
* Two paintables have the same id. We still make sure the old | |||
* one is a component which is still attached to the | |||
* application. This is just a precaution and should not be | |||
* absolutely necessary. | |||
*/ | |||
if (old instanceof Component | |||
&& ((Component) old).getApplication() != null) { | |||
throw new IllegalStateException("Two paintables (" | |||
+ paintable.getClass().getSimpleName() + "," | |||
+ old.getClass().getSimpleName() | |||
+ ") have been assigned the same id: " | |||
+ paintable.getDebugId()); | |||
} | |||
} | |||
paintableIdMap.put(paintable, id); | |||
} | |||
return id; | |||
} | |||
public boolean hasPaintableId(Paintable paintable) { | |||
return paintableIdMap.containsKey(paintable); | |||
} | |||
/** | |||
* Returns dirty components which are in given window. Components in an | |||
* invisible subtrees are omitted. | |||
@@ -2031,65 +1894,26 @@ public abstract class AbstractCommunicationManager implements | |||
* root window for which dirty components is to be fetched | |||
* @return | |||
*/ | |||
private ArrayList<Paintable> getDirtyVisibleComponents(Root r) { | |||
final ArrayList<Paintable> resultset = new ArrayList<Paintable>( | |||
dirtyPaintables); | |||
// TODO mostly unnecessary? | |||
// The following algorithm removes any components that would be painted | |||
// as a direct descendant of other components from the dirty components | |||
// list. The result is that each component should be painted exactly | |||
// once and any unmodified components will be painted as "cached=true". | |||
for (final Iterator<Paintable> i = dirtyPaintables.iterator(); i | |||
.hasNext();) { | |||
final Paintable p = i.next(); | |||
if (p instanceof Component) { | |||
final Component component = (Component) p; | |||
if (component.getApplication() == null) { | |||
// component is detached after requestRepaint is called | |||
resultset.remove(p); | |||
i.remove(); | |||
} else { | |||
Root componentsRoot = component.getRoot(); | |||
if (componentsRoot == null) { | |||
// This should not happen unless somebody has overriden | |||
// getApplication or getWindow in an illegal way. | |||
throw new IllegalStateException( | |||
"component.getWindow() returned null for a component attached to the application"); | |||
} | |||
// if (componentsRoot.getParent() != null) { | |||
// // this is a subwindow | |||
// componentsRoot = componentsRoot.getParent(); | |||
// } | |||
if (componentsRoot != r) { | |||
resultset.remove(p); | |||
} else if (component.getParent() != null | |||
&& !isVisible(component.getParent())) { | |||
/* | |||
* Do not return components in an invisible subtree. | |||
* | |||
* Components that are invisible in visible subree, must | |||
* be rendered (to let client know that they need to be | |||
* hidden). | |||
*/ | |||
resultset.remove(p); | |||
} | |||
} | |||
private ArrayList<Component> getDirtyVisibleComponents( | |||
DirtyConnectorTracker dirtyConnectorTracker) { | |||
ArrayList<Component> dirtyComponents = new ArrayList<Component>(); | |||
for (Component c : dirtyConnectorTracker.getDirtyComponents()) { | |||
if (isVisible(c)) { | |||
dirtyComponents.add(c); | |||
} | |||
} | |||
return resultset; | |||
return dirtyComponents; | |||
} | |||
/** | |||
* @see com.vaadin.terminal.Paintable.RepaintRequestListener#repaintRequested(com.vaadin.terminal.Paintable.RepaintRequestEvent) | |||
*/ | |||
public void repaintRequested(RepaintRequestEvent event) { | |||
final Paintable p = event.getPaintable(); | |||
if (!dirtyPaintables.contains(p)) { | |||
dirtyPaintables.add(p); | |||
} | |||
// final Paintable p = event.getPaintable(); | |||
// if (!dirtyPaintables.contains(p)) { | |||
// dirtyPaintables.add(p); | |||
// } | |||
} | |||
/** | |||
@@ -2099,8 +1923,8 @@ public abstract class AbstractCommunicationManager implements | |||
* @param paintable | |||
*/ | |||
private void paintablePainted(Paintable paintable) { | |||
dirtyPaintables.remove(paintable); | |||
paintable.requestRepaintRequests(); | |||
// dirtyPaintables.remove(paintable); | |||
// paintable.requestRepaintRequests(); | |||
} | |||
/** | |||
@@ -2145,23 +1969,6 @@ public abstract class AbstractCommunicationManager implements | |||
} | |||
} | |||
/** | |||
* Helper method to test if a component contains another | |||
* | |||
* @param parent | |||
* @param child | |||
*/ | |||
private static boolean isChildOf(Component parent, Component child) { | |||
Component p = child.getParent(); | |||
while (p != null) { | |||
if (parent == p) { | |||
return true; | |||
} | |||
p = p.getParent(); | |||
} | |||
return false; | |||
} | |||
protected class InvalidUIDLSecurityKeyException extends | |||
GeneralSecurityException { | |||
@@ -2210,10 +2017,10 @@ public abstract class AbstractCommunicationManager implements | |||
} | |||
abstract String getStreamVariableTargetUrl(VariableOwner owner, | |||
String name, StreamVariable value); | |||
abstract String getStreamVariableTargetUrl(Connector owner, String name, | |||
StreamVariable value); | |||
abstract protected void cleanStreamVariable(VariableOwner owner, String name); | |||
abstract protected void cleanStreamVariable(Connector owner, String name); | |||
/** | |||
* Gets the bootstrap handler that should be used for generating the pages | |||
@@ -2297,7 +2104,6 @@ public abstract class AbstractCommunicationManager implements | |||
protected String getInitialUIDL(WrappedRequest request, Root root) | |||
throws PaintException { | |||
// TODO maybe unify writeUidlResponse()? | |||
makeAllPaintablesDirty(root); | |||
StringWriter sWriter = new StringWriter(); | |||
PrintWriter pWriter = new PrintWriter(sWriter); | |||
pWriter.print("{"); | |||
@@ -2461,4 +2267,13 @@ public abstract class AbstractCommunicationManager implements | |||
return b; | |||
} | |||
} | |||
@Deprecated | |||
public String getPaintableId(Paintable paintable) { | |||
if (paintable instanceof Connector) { | |||
return ((Connector) paintable).getConnectorId(); | |||
} | |||
throw new RuntimeException("Paintable " + paintable | |||
+ " must implement Connector"); | |||
} | |||
} |
@@ -0,0 +1,19 @@ | |||
package com.vaadin.terminal.gwt.server; | |||
import java.util.List; | |||
import com.vaadin.terminal.gwt.client.Connector; | |||
public interface ClientConnector extends Connector { | |||
/** | |||
* Returns the list of pending server to client RPC calls and clears the | |||
* list. | |||
* | |||
* @return unmodifiable ordered list of pending server to client method | |||
* calls (not null) | |||
* | |||
* @since 7.0 | |||
*/ | |||
public List<ClientMethodInvocation> retrievePendingRpcCalls(); | |||
} |
@@ -6,17 +6,15 @@ package com.vaadin.terminal.gwt.server; | |||
import java.io.Serializable; | |||
import com.vaadin.terminal.Paintable; | |||
/** | |||
* Internal class for keeping track of pending server to client method | |||
* invocations for a Paintable. | |||
* invocations for a Connector. | |||
* | |||
* @since 7.0 | |||
*/ | |||
public class ClientMethodInvocation implements Serializable, | |||
Comparable<ClientMethodInvocation> { | |||
private final Paintable paintable; | |||
private final ClientConnector connector; | |||
private final String interfaceName; | |||
private final String methodName; | |||
private final Object[] parameters; | |||
@@ -26,17 +24,17 @@ public class ClientMethodInvocation implements Serializable, | |||
// TODO may cause problems when clustering etc. | |||
private static long counter = 0; | |||
public ClientMethodInvocation(Paintable paintable, String interfaceName, | |||
String methodName, Object[] parameters) { | |||
this.paintable = paintable; | |||
public ClientMethodInvocation(ClientConnector connector, | |||
String interfaceName, String methodName, Object[] parameters) { | |||
this.connector = connector; | |||
this.interfaceName = interfaceName; | |||
this.methodName = methodName; | |||
this.parameters = (null != parameters) ? parameters : new Object[0]; | |||
sequenceNumber = ++counter; | |||
} | |||
public Paintable getPaintable() { | |||
return paintable; | |||
public ClientConnector getConnector() { | |||
return connector; | |||
} | |||
public String getInterfaceName() { |
@@ -8,6 +8,7 @@ import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.net.URL; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.Map; | |||
import java.util.UUID; | |||
@@ -15,12 +16,10 @@ import javax.servlet.ServletContext; | |||
import com.vaadin.Application; | |||
import com.vaadin.terminal.PaintException; | |||
import com.vaadin.terminal.Paintable; | |||
import com.vaadin.terminal.StreamVariable; | |||
import com.vaadin.terminal.VariableOwner; | |||
import com.vaadin.terminal.WrappedRequest; | |||
import com.vaadin.terminal.WrappedResponse; | |||
import com.vaadin.ui.Component; | |||
import com.vaadin.terminal.gwt.client.Connector; | |||
import com.vaadin.ui.Root; | |||
/** | |||
@@ -62,6 +61,8 @@ public class CommunicationManager extends AbstractCommunicationManager { | |||
/** | |||
* Handles file upload request submitted via Upload component. | |||
* | |||
* @param application | |||
* | |||
* @see #getStreamVariableTargetUrl(ReceiverOwner, String, StreamVariable) | |||
* | |||
* @param request | |||
@@ -69,9 +70,9 @@ public class CommunicationManager extends AbstractCommunicationManager { | |||
* @throws IOException | |||
* @throws InvalidUIDLSecurityKeyException | |||
*/ | |||
public void handleFileUpload(WrappedRequest request, | |||
WrappedResponse response) throws IOException, | |||
InvalidUIDLSecurityKeyException { | |||
public void handleFileUpload(Application application, | |||
WrappedRequest request, WrappedResponse response) | |||
throws IOException, InvalidUIDLSecurityKeyException { | |||
/* | |||
* URI pattern: APP/UPLOAD/[PID]/[NAME]/[SECKEY] See #createReceiverUrl | |||
@@ -92,7 +93,7 @@ public class CommunicationManager extends AbstractCommunicationManager { | |||
String secKey = streamVariableToSeckey.get(streamVariable); | |||
if (secKey.equals(parts[2])) { | |||
VariableOwner source = getVariableOwner(paintableId); | |||
Connector source = getConnector(application, paintableId); | |||
String contentType = request.getContentType(); | |||
if (contentType.contains("boundary")) { | |||
// Multipart requests contain boundary string | |||
@@ -113,18 +114,26 @@ public class CommunicationManager extends AbstractCommunicationManager { | |||
} | |||
@Override | |||
protected void unregisterPaintable(Component p) { | |||
/* Cleanup possible receivers */ | |||
protected void postPaint(Root root) { | |||
super.postPaint(root); | |||
Application application = root.getApplication(); | |||
if (pidToNameToStreamVariable != null) { | |||
Map<String, StreamVariable> removed = pidToNameToStreamVariable | |||
.remove(getPaintableId(p)); | |||
if (removed != null) { | |||
for (String key : removed.keySet()) { | |||
streamVariableToSeckey.remove(removed.get(key)); | |||
Iterator<String> iterator = pidToNameToStreamVariable.keySet() | |||
.iterator(); | |||
while (iterator.hasNext()) { | |||
String connectorId = iterator.next(); | |||
if (application.getConnector(connectorId) == null) { | |||
// Owner is no longer attached to the application | |||
Map<String, StreamVariable> removed = pidToNameToStreamVariable | |||
.get(connectorId); | |||
for (String key : removed.keySet()) { | |||
streamVariableToSeckey.remove(removed.get(key)); | |||
} | |||
iterator.remove(); | |||
} | |||
} | |||
} | |||
super.unregisterPaintable(p); | |||
} | |||
@@ -133,7 +142,7 @@ public class CommunicationManager extends AbstractCommunicationManager { | |||
private Map<StreamVariable, String> streamVariableToSeckey; | |||
@Override | |||
String getStreamVariableTargetUrl(VariableOwner owner, String name, | |||
String getStreamVariableTargetUrl(Connector owner, String name, | |||
StreamVariable value) { | |||
/* | |||
* We will use the same APP/* URI space as ApplicationResources but | |||
@@ -147,7 +156,7 @@ public class CommunicationManager extends AbstractCommunicationManager { | |||
* NAME and PID from URI forms a key to fetch StreamVariable when | |||
* handling post | |||
*/ | |||
String paintableId = getPaintableId((Paintable) owner); | |||
String paintableId = owner.getConnectorId(); | |||
String key = paintableId + "/" + name; | |||
if (pidToNameToStreamVariable == null) { | |||
@@ -176,12 +185,12 @@ public class CommunicationManager extends AbstractCommunicationManager { | |||
} | |||
@Override | |||
protected void cleanStreamVariable(VariableOwner owner, String name) { | |||
protected void cleanStreamVariable(Connector owner, String name) { | |||
Map<String, StreamVariable> nameToStreamVar = pidToNameToStreamVariable | |||
.get(getPaintableId((Paintable) owner)); | |||
.get(owner.getConnectorId()); | |||
nameToStreamVar.remove("name"); | |||
if (nameToStreamVar.isEmpty()) { | |||
pidToNameToStreamVariable.remove(getPaintableId((Paintable) owner)); | |||
pidToNameToStreamVariable.remove(owner.getConnectorId()); | |||
} | |||
} | |||
@@ -185,7 +185,7 @@ public class ComponentSizeValidator implements Serializable { | |||
clientJSON.write("{"); | |||
Component parent = component.getParent(); | |||
String paintableId = communicationManager.getPaintableId(component); | |||
String paintableId = component.getConnectorId(); | |||
clientJSON.print("id:\"" + paintableId + "\""); | |||
@@ -18,10 +18,13 @@ import com.vaadin.event.dd.TargetDetailsImpl; | |||
import com.vaadin.event.dd.acceptcriteria.AcceptCriterion; | |||
import com.vaadin.terminal.PaintException; | |||
import com.vaadin.terminal.VariableOwner; | |||
import com.vaadin.terminal.gwt.client.Connector; | |||
import com.vaadin.terminal.gwt.client.communication.SharedState; | |||
import com.vaadin.terminal.gwt.client.ui.dd.VDragAndDropManager; | |||
import com.vaadin.terminal.gwt.client.ui.dd.VDragAndDropManager.DragEventType; | |||
import com.vaadin.ui.Component; | |||
public class DragAndDropService implements VariableOwner { | |||
public class DragAndDropService implements VariableOwner, Connector { | |||
private static final Logger logger = Logger | |||
.getLogger(DragAndDropService.class.getName()); | |||
@@ -177,7 +180,7 @@ public class DragAndDropService implements VariableOwner { | |||
} | |||
public boolean isEnabled() { | |||
return true; | |||
return isConnectorEnabled(); | |||
} | |||
public boolean isImmediate() { | |||
@@ -212,4 +215,18 @@ public class DragAndDropService implements VariableOwner { | |||
} | |||
return false; | |||
} | |||
public SharedState getState() { | |||
// TODO Auto-generated method stub | |||
return null; | |||
} | |||
public String getConnectorId() { | |||
return VDragAndDropManager.DD_SERVICE; | |||
} | |||
public boolean isConnectorEnabled() { | |||
// Drag'n'drop can't be disabled | |||
return true; | |||
} | |||
} |
@@ -16,6 +16,7 @@ import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import com.vaadin.Application; | |||
import com.vaadin.external.json.JSONArray; | |||
import com.vaadin.external.json.JSONException; | |||
import com.vaadin.external.json.JSONObject; | |||
@@ -35,8 +36,7 @@ public class JsonCodec implements Serializable { | |||
static { | |||
registerType(String.class, JsonEncoder.VTYPE_STRING); | |||
registerType(Paintable.class, JsonEncoder.VTYPE_PAINTABLE); | |||
registerType(Connector.class, JsonEncoder.VTYPE_PAINTABLE); | |||
registerType(Connector.class, JsonEncoder.VTYPE_CONNECTOR); | |||
registerType(Boolean.class, JsonEncoder.VTYPE_BOOLEAN); | |||
registerType(Integer.class, JsonEncoder.VTYPE_INTEGER); | |||
registerType(Float.class, JsonEncoder.VTYPE_FLOAT); | |||
@@ -60,27 +60,28 @@ public class JsonCodec implements Serializable { | |||
* | |||
* @param value | |||
* JSON array with two elements | |||
* @param idMapper | |||
* @param application | |||
* mapper between paintable ID and {@link Paintable} objects | |||
* @return converted value (does not contain JSON types) | |||
* @throws JSONException | |||
* if the conversion fails | |||
*/ | |||
public static Object decode(JSONArray value, PaintableIdMapper idMapper) | |||
public static Object decode(JSONArray value, Application application) | |||
throws JSONException { | |||
return decodeVariableValue(value.getString(0), value.get(1), idMapper); | |||
return decodeVariableValue(value.getString(0), value.get(1), | |||
application); | |||
} | |||
private static Object decodeVariableValue(String variableType, | |||
Object value, PaintableIdMapper idMapper) throws JSONException { | |||
Object value, Application application) throws JSONException { | |||
Object val = null; | |||
// TODO type checks etc. | |||
if (JsonEncoder.VTYPE_ARRAY.equals(variableType)) { | |||
val = decodeArray((JSONArray) value, idMapper); | |||
val = decodeArray((JSONArray) value, application); | |||
} else if (JsonEncoder.VTYPE_LIST.equals(variableType)) { | |||
val = decodeList((JSONArray) value, idMapper); | |||
val = decodeList((JSONArray) value, application); | |||
} else if (JsonEncoder.VTYPE_MAP.equals(variableType)) { | |||
val = decodeMap((JSONObject) value, idMapper); | |||
val = decodeMap((JSONObject) value, application); | |||
} else if (JsonEncoder.VTYPE_STRINGARRAY.equals(variableType)) { | |||
val = decodeStringArray((JSONArray) value); | |||
} else if (JsonEncoder.VTYPE_STRING.equals(variableType)) { | |||
@@ -100,27 +101,26 @@ public class JsonCodec implements Serializable { | |||
} else if (JsonEncoder.VTYPE_BOOLEAN.equals(variableType)) { | |||
// TODO handle properly | |||
val = Boolean.valueOf(String.valueOf(value)); | |||
} else if (JsonEncoder.VTYPE_PAINTABLE.equals(variableType)) { | |||
// TODO handle properly | |||
val = idMapper.getPaintable(String.valueOf(value)); | |||
} else if (JsonEncoder.VTYPE_CONNECTOR.equals(variableType)) { | |||
val = application.getConnector(String.valueOf(value)); | |||
} else if (JsonEncoder.VTYPE_NULL.equals(variableType)) { | |||
val = null; | |||
} else { | |||
// Try to decode object using fields | |||
return decodeObject(variableType, (JSONObject) value, idMapper); | |||
return decodeObject(variableType, (JSONObject) value, application); | |||
} | |||
return val; | |||
} | |||
private static Object decodeMap(JSONObject jsonMap, | |||
PaintableIdMapper idMapper) throws JSONException { | |||
private static Object decodeMap(JSONObject jsonMap, Application application) | |||
throws JSONException { | |||
HashMap<String, Object> map = new HashMap<String, Object>(); | |||
Iterator<String> it = jsonMap.keys(); | |||
while (it.hasNext()) { | |||
String key = it.next(); | |||
map.put(key, decode(jsonMap.getJSONArray(key), idMapper)); | |||
map.put(key, decode(jsonMap.getJSONArray(key), application)); | |||
} | |||
return map; | |||
} | |||
@@ -136,18 +136,18 @@ public class JsonCodec implements Serializable { | |||
} | |||
private static Object decodeArray(JSONArray jsonArray, | |||
PaintableIdMapper idMapper) throws JSONException { | |||
List list = decodeList(jsonArray, idMapper); | |||
Application application) throws JSONException { | |||
List list = decodeList(jsonArray, application); | |||
return list.toArray(new Object[list.size()]); | |||
} | |||
private static List decodeList(JSONArray jsonArray, | |||
PaintableIdMapper idMapper) throws JSONException { | |||
private static List decodeList(JSONArray jsonArray, Application application) | |||
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 | |||
JSONArray entryArray = jsonArray.getJSONArray(i); | |||
list.add(decode(entryArray, idMapper)); | |||
list.add(decode(entryArray, application)); | |||
} | |||
return list; | |||
} | |||
@@ -158,19 +158,19 @@ public class JsonCodec implements Serializable { | |||
* | |||
* @param value | |||
* value to convert | |||
* @param idMapper | |||
* @param application | |||
* mapper between paintable ID and {@link Paintable} objects | |||
* @return JSON representation of the value | |||
* @throws JSONException | |||
* if encoding a value fails (e.g. NaN or infinite number) | |||
*/ | |||
public static JSONArray encode(Object value, PaintableIdMapper idMapper) | |||
public static JSONArray encode(Object value, Application application) | |||
throws JSONException { | |||
return encode(value, null, idMapper); | |||
return encode(value, null, application); | |||
} | |||
public static JSONArray encode(Object value, Class<?> valueType, | |||
PaintableIdMapper idMapper) throws JSONException { | |||
Application application) throws JSONException { | |||
if (null == value) { | |||
return combineTypeAndValue(JsonEncoder.VTYPE_NULL, JSONObject.NULL); | |||
@@ -189,20 +189,20 @@ public class JsonCodec implements Serializable { | |||
return combineTypeAndValue(getTransportType(value), value); | |||
} else if (value instanceof List) { | |||
List list = (List) value; | |||
JSONArray jsonArray = encodeList(list, idMapper); | |||
JSONArray jsonArray = encodeList(list, application); | |||
return combineTypeAndValue(JsonEncoder.VTYPE_LIST, jsonArray); | |||
} else if (value instanceof Object[]) { | |||
Object[] array = (Object[]) value; | |||
JSONArray jsonArray = encodeArrayContents(array, idMapper); | |||
JSONArray jsonArray = encodeArrayContents(array, application); | |||
return combineTypeAndValue(JsonEncoder.VTYPE_ARRAY, jsonArray); | |||
} else if (value instanceof Map) { | |||
Map<String, Object> map = (Map<String, Object>) value; | |||
JSONObject jsonMap = encodeMapContents(map, idMapper); | |||
JSONObject jsonMap = encodeMapContents(map, application); | |||
return combineTypeAndValue(JsonEncoder.VTYPE_MAP, jsonMap); | |||
} else if (value instanceof Paintable) { | |||
Paintable paintable = (Paintable) value; | |||
return combineTypeAndValue(JsonEncoder.VTYPE_PAINTABLE, | |||
idMapper.getPaintableId(paintable)); | |||
} else if (value instanceof Connector) { | |||
Connector connector = (Connector) value; | |||
return combineTypeAndValue(JsonEncoder.VTYPE_CONNECTOR, | |||
connector.getConnectorId()); | |||
} else if (getTransportType(value) != null) { | |||
return combineTypeAndValue(getTransportType(value), | |||
String.valueOf(value)); | |||
@@ -214,11 +214,11 @@ public class JsonCodec implements Serializable { | |||
} | |||
return combineTypeAndValue(valueType.getCanonicalName(), | |||
encodeObject(value, idMapper)); | |||
encodeObject(value, application)); | |||
} | |||
} | |||
private static Object encodeObject(Object value, PaintableIdMapper idMapper) | |||
private static Object encodeObject(Object value, Application application) | |||
throws JSONException { | |||
JSONObject jsonMap = new JSONObject(); | |||
@@ -232,7 +232,8 @@ public class JsonCodec implements Serializable { | |||
} | |||
Method getterMethod = pd.getReadMethod(); | |||
Object fieldValue = getterMethod.invoke(value, (Object[]) null); | |||
jsonMap.put(fieldName, encode(fieldValue, fieldType, idMapper)); | |||
jsonMap.put(fieldName, | |||
encode(fieldValue, fieldType, application)); | |||
} | |||
} catch (Exception e) { | |||
// TODO: Should exceptions be handled in a different way? | |||
@@ -242,7 +243,7 @@ public class JsonCodec implements Serializable { | |||
} | |||
private static Object decodeObject(String type, | |||
JSONObject serializedObject, PaintableIdMapper idMapper) | |||
JSONObject serializedObject, Application application) | |||
throws JSONException { | |||
Class<?> cls; | |||
@@ -260,7 +261,7 @@ public class JsonCodec implements Serializable { | |||
JSONArray encodedObject = serializedObject | |||
.getJSONArray(fieldName); | |||
pd.getWriteMethod().invoke(decodedObject, | |||
decode(encodedObject, idMapper)); | |||
decode(encodedObject, application)); | |||
} | |||
return decodedObject; | |||
@@ -280,32 +281,32 @@ public class JsonCodec implements Serializable { | |||
} | |||
private static JSONArray encodeArrayContents(Object[] array, | |||
PaintableIdMapper idMapper) throws JSONException { | |||
Application application) throws JSONException { | |||
JSONArray jsonArray = new JSONArray(); | |||
for (Object o : array) { | |||
// TODO handle object graph loops? | |||
jsonArray.put(encode(o, idMapper)); | |||
jsonArray.put(encode(o, application)); | |||
} | |||
return jsonArray; | |||
} | |||
private static JSONArray encodeList(List list, PaintableIdMapper idMapper) | |||
private static JSONArray encodeList(List list, Application application) | |||
throws JSONException { | |||
JSONArray jsonArray = new JSONArray(); | |||
for (Object o : list) { | |||
// TODO handle object graph loops? | |||
jsonArray.put(encode(o, idMapper)); | |||
jsonArray.put(encode(o, application)); | |||
} | |||
return jsonArray; | |||
} | |||
private static JSONObject encodeMapContents(Map<String, Object> map, | |||
PaintableIdMapper idMapper) throws JSONException { | |||
Application application) throws JSONException { | |||
JSONObject jsonMap = new JSONObject(); | |||
for (String mapKey : map.keySet()) { | |||
// TODO handle object graph loops? | |||
Object mapValue = map.get(mapKey); | |||
jsonMap.put(mapKey, encode(mapValue, idMapper)); | |||
jsonMap.put(mapKey, encode(mapValue, application)); | |||
} | |||
return jsonMap; | |||
} |
@@ -34,9 +34,9 @@ import com.vaadin.terminal.Resource; | |||
import com.vaadin.terminal.StreamVariable; | |||
import com.vaadin.terminal.ThemeResource; | |||
import com.vaadin.terminal.VariableOwner; | |||
import com.vaadin.terminal.gwt.client.Connector; | |||
import com.vaadin.ui.Alignment; | |||
import com.vaadin.ui.ClientWidget; | |||
import com.vaadin.ui.Component; | |||
import com.vaadin.ui.CustomLayout; | |||
import com.vaadin.ui.Root; | |||
@@ -89,7 +89,7 @@ public class JsonPaintTarget implements PaintTarget { | |||
private final Collection<Paintable> paintedComponents = new HashSet<Paintable>(); | |||
private Collection<Paintable> identifiersCreatedDueRefPaint; | |||
// private Collection<Paintable> identifiersCreatedDueRefPaint; | |||
private Collection<Paintable> deferredPaintables; | |||
@@ -687,49 +687,46 @@ public class JsonPaintTarget implements PaintTarget { | |||
*/ | |||
public PaintStatus startPaintable(Paintable paintable, String tagName) | |||
throws PaintException { | |||
boolean topLevelPaintable = openPaintables.isEmpty(); | |||
System.out.println("startPaintable for " | |||
+ paintable.getClass().getName() + "@" | |||
+ Integer.toHexString(paintable.hashCode())); | |||
startTag(tagName, true); | |||
final boolean isPreviouslyPainted = manager.hasPaintableId(paintable) | |||
&& (identifiersCreatedDueRefPaint == null || !identifiersCreatedDueRefPaint | |||
.contains(paintable)) | |||
&& !deferredPaintables.contains(paintable); | |||
openPaintables.push(paintable); | |||
openPaintableTags.push(tagName); | |||
final String id = manager.getPaintableId(paintable); | |||
paintable.addListener(manager); | |||
addAttribute("id", id); | |||
// queue for painting later if already painting a paintable | |||
boolean topLevelPaintableTag = openPaintables.isEmpty(); | |||
openPaintables.push(paintable); | |||
openPaintableTags.push(tagName); | |||
if (!topLevelPaintableTag) { | |||
if (!topLevelPaintable) { | |||
// if (!deferredPaintables.contains(paintable)) { | |||
// notify manager: add to paint queue instead of painting now | |||
manager.queuePaintable(paintable); | |||
deferredPaintables.add(paintable); | |||
// manager.queuePaintable(paintable); | |||
// deferredPaintables.add(paintable); | |||
// } | |||
return PaintStatus.DEFER; | |||
} else if (cacheEnabled && isPreviouslyPainted) { | |||
// cached (unmodified) paintable, paint the it now | |||
paintedComponents.add(paintable); | |||
deferredPaintables.remove(paintable); | |||
return PaintStatus.CACHED; | |||
} else { | |||
// not a nested paintable, paint the it now | |||
paintedComponents.add(paintable); | |||
deferredPaintables.remove(paintable); | |||
} | |||
if (paintable instanceof CustomLayout) { | |||
customLayoutArgumentsOpen = true; | |||
} | |||
return PaintStatus.PAINTING; | |||
// not a nested paintable, paint the it now | |||
paintedComponents.add(paintable); | |||
// deferredPaintables.remove(paintable); | |||
if (paintable instanceof CustomLayout) { | |||
customLayoutArgumentsOpen = true; | |||
} | |||
return PaintStatus.PAINTING; | |||
} | |||
public void endPaintable(Paintable paintable) throws PaintException { | |||
Paintable openPaintable = openPaintables.peek(); | |||
if (paintable != openPaintable) { | |||
throw new PaintException("Invalid UIDL: closing wrong paintable: '" | |||
+ getPaintIdentifier(paintable) + "' expected: '" | |||
+ getPaintIdentifier(openPaintable) + "'."); | |||
+ manager.getPaintableId(paintable) + "' expected: '" | |||
+ manager.getPaintableId(openPaintable) + "'."); | |||
} | |||
// remove paintable from the stack | |||
openPaintables.pop(); | |||
@@ -738,12 +735,12 @@ public class JsonPaintTarget implements PaintTarget { | |||
} | |||
public String getPaintIdentifier(Paintable paintable) throws PaintException { | |||
if (!manager.hasPaintableId(paintable)) { | |||
if (identifiersCreatedDueRefPaint == null) { | |||
identifiersCreatedDueRefPaint = new HashSet<Paintable>(); | |||
} | |||
identifiersCreatedDueRefPaint.add(paintable); | |||
} | |||
// if (!manager.hasPaintableId(paintable)) { | |||
// if (identifiersCreatedDueRefPaint == null) { | |||
// identifiersCreatedDueRefPaint = new HashSet<Paintable>(); | |||
// } | |||
// identifiersCreatedDueRefPaint.add(paintable); | |||
// } | |||
return manager.getPaintableId(paintable); | |||
} | |||
@@ -1021,22 +1018,22 @@ public class JsonPaintTarget implements PaintTarget { | |||
return usedResources; | |||
} | |||
/** | |||
* Method to check if paintable is already painted into this target. | |||
* | |||
* @param p | |||
* @return true if is not yet painted into this target and is connected to | |||
* app | |||
*/ | |||
public boolean needsToBePainted(Paintable p) { | |||
if (paintedComponents.contains(p)) { | |||
return false; | |||
} else if (((Component) p).getApplication() == null) { | |||
return false; | |||
} else { | |||
return true; | |||
} | |||
} | |||
// /** | |||
// * Method to check if paintable is already painted into this target. | |||
// * | |||
// * @param p | |||
// * @return true if is not yet painted into this target and is connected to | |||
// * app | |||
// */ | |||
// public boolean needsToBePainted(Paintable p) { | |||
// if (paintedComponents.contains(p)) { | |||
// return false; | |||
// } else if (((Component) p).getApplication() == null) { | |||
// return false; | |||
// } else { | |||
// return true; | |||
// } | |||
// } | |||
private static final Map<Class<? extends Paintable>, Class<? extends Paintable>> widgetMappingCache = new HashMap<Class<? extends Paintable>, Class<? extends Paintable>>(); | |||
@@ -1195,7 +1192,8 @@ public class JsonPaintTarget implements PaintTarget { | |||
public void addVariable(VariableOwner owner, String name, | |||
StreamVariable value) throws PaintException { | |||
String url = manager.getStreamVariableTargetUrl(owner, name, value); | |||
String url = manager.getStreamVariableTargetUrl((Connector) owner, | |||
name, value); | |||
if (url != null) { | |||
addVariable(owner, name, url); | |||
} // else { //NOP this was just a cleanup by component } |
@@ -6,6 +6,7 @@ package com.vaadin.terminal.gwt.server; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.Map; | |||
import javax.portlet.MimeResponse; | |||
@@ -22,12 +23,10 @@ import com.vaadin.external.json.JSONException; | |||
import com.vaadin.external.json.JSONObject; | |||
import com.vaadin.terminal.DeploymentConfiguration; | |||
import com.vaadin.terminal.PaintException; | |||
import com.vaadin.terminal.Paintable; | |||
import com.vaadin.terminal.StreamVariable; | |||
import com.vaadin.terminal.VariableOwner; | |||
import com.vaadin.terminal.WrappedRequest; | |||
import com.vaadin.terminal.WrappedResponse; | |||
import com.vaadin.ui.Component; | |||
import com.vaadin.terminal.gwt.client.Connector; | |||
import com.vaadin.ui.Root; | |||
/** | |||
@@ -50,26 +49,36 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { | |||
String contentType = request.getContentType(); | |||
String name = request.getParameter("name"); | |||
String ownerId = request.getParameter("rec-owner"); | |||
VariableOwner variableOwner = getVariableOwner(ownerId); | |||
StreamVariable streamVariable = ownerToNameToStreamVariable.get( | |||
variableOwner).get(name); | |||
Connector owner = getConnector(getApplication(), ownerId); | |||
StreamVariable streamVariable = ownerToNameToStreamVariable.get(owner) | |||
.get(name); | |||
if (contentType.contains("boundary")) { | |||
doHandleSimpleMultipartFileUpload(request, response, | |||
streamVariable, name, variableOwner, | |||
streamVariable, name, owner, | |||
contentType.split("boundary=")[1]); | |||
} else { | |||
doHandleXhrFilePost(request, response, streamVariable, name, | |||
variableOwner, request.getContentLength()); | |||
doHandleXhrFilePost(request, response, streamVariable, name, owner, | |||
request.getContentLength()); | |||
} | |||
} | |||
@Override | |||
protected void unregisterPaintable(Component p) { | |||
super.unregisterPaintable(p); | |||
protected void postPaint(Root root) { | |||
super.postPaint(root); | |||
Application application = root.getApplication(); | |||
if (ownerToNameToStreamVariable != null) { | |||
ownerToNameToStreamVariable.remove(p); | |||
Iterator<Connector> iterator = ownerToNameToStreamVariable.keySet() | |||
.iterator(); | |||
while (iterator.hasNext()) { | |||
Connector owner = iterator.next(); | |||
if (application.getConnector(owner.getConnectorId()) == null) { | |||
// Owner is no longer attached to the application | |||
iterator.remove(); | |||
} | |||
} | |||
} | |||
} | |||
@@ -83,13 +92,13 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { | |||
currentUidlResponse = null; | |||
} | |||
private Map<VariableOwner, Map<String, StreamVariable>> ownerToNameToStreamVariable; | |||
private Map<Connector, Map<String, StreamVariable>> ownerToNameToStreamVariable; | |||
@Override | |||
String getStreamVariableTargetUrl(VariableOwner owner, String name, | |||
String getStreamVariableTargetUrl(Connector owner, String name, | |||
StreamVariable value) { | |||
if (ownerToNameToStreamVariable == null) { | |||
ownerToNameToStreamVariable = new HashMap<VariableOwner, Map<String, StreamVariable>>(); | |||
ownerToNameToStreamVariable = new HashMap<Connector, Map<String, StreamVariable>>(); | |||
} | |||
Map<String, StreamVariable> nameToReceiver = ownerToNameToStreamVariable | |||
.get(owner); | |||
@@ -101,14 +110,14 @@ public class PortletCommunicationManager extends AbstractCommunicationManager { | |||
ResourceURL resurl = currentUidlResponse.createResourceURL(); | |||
resurl.setResourceID("UPLOAD"); | |||
resurl.setParameter("name", name); | |||
resurl.setParameter("rec-owner", getPaintableId((Paintable) owner)); | |||
resurl.setParameter("rec-owner", owner.getConnectorId()); | |||
resurl.setProperty("name", name); | |||
resurl.setProperty("rec-owner", getPaintableId((Paintable) owner)); | |||
resurl.setProperty("rec-owner", owner.getConnectorId()); | |||
return resurl.toString(); | |||
} | |||
@Override | |||
protected void cleanStreamVariable(VariableOwner owner, String name) { | |||
protected void cleanStreamVariable(Connector owner, String name) { | |||
Map<String, StreamVariable> map = ownerToNameToStreamVariable | |||
.get(owner); | |||
map.remove(name); |
@@ -147,6 +147,8 @@ public abstract class AbstractComponent implements Component, MethodEventSource | |||
*/ | |||
private ArrayList<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>(); | |||
private String connectorId; | |||
/* Constructor */ | |||
/** | |||
@@ -651,6 +653,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource | |||
* interface. | |||
*/ | |||
public void attach() { | |||
getRoot().componentAttached(this); | |||
requestRepaint(); | |||
if (!getState().isVisible()) { | |||
/* | |||
@@ -676,6 +679,7 @@ public abstract class AbstractComponent implements Component, MethodEventSource | |||
// compiler happy | |||
actionManager.setViewer((Root) null); | |||
} | |||
getRoot().componentDetached(this); | |||
} | |||
/** | |||
@@ -1752,7 +1756,13 @@ public abstract class AbstractComponent implements Component, MethodEventSource | |||
} | |||
public String getConnectorId() { | |||
throw new RuntimeException( | |||
"TODO: Move connector id handling to AbstractComponent"); | |||
if (connectorId == null) { | |||
if (getApplication() == null) { | |||
throw new RuntimeException( | |||
"Component must be attached to an application when getConnectorId() is called for the first time"); | |||
} | |||
connectorId = getApplication().createConnectorId(this); | |||
} | |||
return connectorId; | |||
} | |||
} |
@@ -18,7 +18,7 @@ import com.vaadin.terminal.Resource; | |||
import com.vaadin.terminal.Sizeable; | |||
import com.vaadin.terminal.VariableOwner; | |||
import com.vaadin.terminal.gwt.client.ComponentState; | |||
import com.vaadin.terminal.gwt.client.Connector; | |||
import com.vaadin.terminal.gwt.server.ClientConnector; | |||
import com.vaadin.terminal.gwt.server.RpcTarget; | |||
/** | |||
@@ -54,7 +54,7 @@ import com.vaadin.terminal.gwt.server.RpcTarget; | |||
* @VERSION@ | |||
* @since 3.0 | |||
*/ | |||
public interface Component extends Connector, Paintable, VariableOwner, | |||
public interface Component extends ClientConnector, Paintable, VariableOwner, | |||
Sizeable, Serializable, RpcTarget { | |||
/** |
@@ -0,0 +1,102 @@ | |||
package com.vaadin.ui; | |||
import java.util.Collection; | |||
import java.util.HashSet; | |||
import java.util.Set; | |||
import java.util.logging.Logger; | |||
import com.vaadin.terminal.Paintable.RepaintRequestEvent; | |||
import com.vaadin.terminal.Paintable.RepaintRequestListener; | |||
public class DirtyConnectorTracker implements RepaintRequestListener { | |||
private Set<Component> dirtyComponents = new HashSet<Component>(); | |||
private Root root; | |||
public static Logger getLogger() { | |||
return Logger.getLogger(DirtyConnectorTracker.class.getName()); | |||
} | |||
public DirtyConnectorTracker(Root root) { | |||
this.root = root; | |||
} | |||
public void repaintRequested(RepaintRequestEvent event) { | |||
markDirty((Component) event.getPaintable()); | |||
} | |||
public void componentAttached(Component component) { | |||
component.addListener(this); | |||
markDirty(component); | |||
} | |||
private void markDirty(Component component) { | |||
// TODO Remove debug info | |||
if (!dirtyComponents.contains(component)) { | |||
debug(component, "is now dirty"); | |||
} | |||
dirtyComponents.add(component); | |||
} | |||
private void debug(Component component, String string) { | |||
getLogger().info(getDebugInfo(component) + " " + string); | |||
} | |||
private void markClean(Component component) { | |||
// TODO Remove debug info | |||
if (dirtyComponents.contains(component)) { | |||
debug(component, "is no longer dirty"); | |||
} | |||
dirtyComponents.remove(component); | |||
// TODO .... WTF .... | |||
component.requestRepaintRequests(); | |||
} | |||
private String getDebugInfo(Component component) { | |||
String message = getObjectString(component); | |||
if (component.getParent() != null) { | |||
message += " (parent: " + getObjectString(component.getParent()) | |||
+ ")"; | |||
} | |||
return message; | |||
} | |||
private String getObjectString(Object component) { | |||
return component.getClass().getName() + "@" | |||
+ Integer.toHexString(component.hashCode()); | |||
} | |||
public void componentDetached(Component component) { | |||
component.removeListener(this); | |||
markClean(component); | |||
} | |||
public void markAllComponentsDirty() { | |||
markComponentsDirtyRecursively(root); | |||
System.out.println("All components are now dirty"); | |||
} | |||
public void markAllComponentsClean() { | |||
dirtyComponents.clear(); | |||
System.out.println("All components are now clean"); | |||
} | |||
private void markComponentsDirtyRecursively(Component c) { | |||
markDirty(c); | |||
if (c instanceof HasComponents) { | |||
HasComponents container = (HasComponents) c; | |||
for (Component child : container) { | |||
markComponentsDirtyRecursively(child); | |||
} | |||
} | |||
} | |||
public Collection<Component> getDirtyComponents() { | |||
return dirtyComponents; | |||
} | |||
} |
@@ -409,6 +409,9 @@ public abstract class Root extends AbstractComponentContainer implements | |||
/** Identifies the click event */ | |||
private static final String CLICK_EVENT_ID = VView.CLICK_EVENT_ID; | |||
private DirtyConnectorTracker dirtyConnectorTracker = new DirtyConnectorTracker( | |||
this); | |||
/** | |||
* Creates a new empty root without a caption. This root will have a | |||
* {@link VerticalLayout} with margins enabled as its content. | |||
@@ -1567,4 +1570,17 @@ public abstract class Root extends AbstractComponentContainer implements | |||
// TODO How can a Root be invisible? What does it mean? | |||
return isVisible() && isEnabled(); | |||
} | |||
public DirtyConnectorTracker getDirtyConnectorTracker() { | |||
return dirtyConnectorTracker; | |||
} | |||
public void componentAttached(Component component) { | |||
getDirtyConnectorTracker().componentAttached(component); | |||
} | |||
public void componentDetached(Component component) { | |||
getDirtyConnectorTracker().componentDetached(component); | |||
} | |||
} |