diff options
4 files changed, 251 insertions, 65 deletions
diff --git a/server/src/com/vaadin/server/AbstractCommunicationManager.java b/server/src/com/vaadin/server/AbstractCommunicationManager.java index 0a3dff8120..af9118547f 100644 --- a/server/src/com/vaadin/server/AbstractCommunicationManager.java +++ b/server/src/com/vaadin/server/AbstractCommunicationManager.java @@ -165,10 +165,6 @@ public abstract class AbstractCommunicationManager implements Serializable { private Map<String, Class<?>> publishedFileContexts = new HashMap<String, Class<?>>(); - private Map<String, Map<String, StreamVariable>> pidToNameToStreamVariable; - - private Map<StreamVariable, String> streamVariableToSeckey; - /** * TODO New constructor - document me! * @@ -644,23 +640,6 @@ public abstract class AbstractCommunicationManager implements Serializable { // Remove connectors that have been detached from the session during // handling of the request uI.getConnectorTracker().cleanConnectorMap(); - - if (pidToNameToStreamVariable != null) { - Iterator<String> iterator = pidToNameToStreamVariable.keySet() - .iterator(); - while (iterator.hasNext()) { - String connectorId = iterator.next(); - if (uI.getConnectorTracker().getConnector(connectorId) == null) { - // Owner is no longer attached to the session - Map<String, StreamVariable> removed = pidToNameToStreamVariable - .get(connectorId); - for (String key : removed.keySet()) { - streamVariableToSeckey.remove(removed.get(key)); - } - iterator.remove(); - } - } - } } protected void highlightConnector(ClientConnector highlightedConnector) { @@ -2269,28 +2248,13 @@ public abstract class AbstractCommunicationManager implements Serializable { * handling post */ String paintableId = owner.getConnectorId(); - int uiId = owner.getUI().getUIId(); + UI ui = owner.getUI(); + int uiId = ui.getUIId(); String key = uiId + "/" + paintableId + "/" + name; - if (pidToNameToStreamVariable == null) { - pidToNameToStreamVariable = new HashMap<String, Map<String, StreamVariable>>(); - } - Map<String, StreamVariable> nameToStreamVariable = pidToNameToStreamVariable - .get(paintableId); - if (nameToStreamVariable == null) { - nameToStreamVariable = new HashMap<String, StreamVariable>(); - pidToNameToStreamVariable.put(paintableId, nameToStreamVariable); - } - nameToStreamVariable.put(name, value); - - if (streamVariableToSeckey == null) { - streamVariableToSeckey = new HashMap<StreamVariable, String>(); - } - String seckey = streamVariableToSeckey.get(value); - if (seckey == null) { - seckey = UUID.randomUUID().toString(); - streamVariableToSeckey.put(value, seckey); - } + ConnectorTracker connectorTracker = ui.getConnectorTracker(); + connectorTracker.addStreamVariable(paintableId, name, value); + String seckey = connectorTracker.getSeckey(value); return ApplicationConstants.APP_PROTOCOL_PREFIX + ServletPortletHelper.UPLOAD_URL_PREFIX + key + "/" + seckey; @@ -2298,12 +2262,8 @@ public abstract class AbstractCommunicationManager implements Serializable { } public void cleanStreamVariable(ClientConnector owner, String name) { - Map<String, StreamVariable> nameToStreamVar = pidToNameToStreamVariable - .get(owner.getConnectorId()); - nameToStreamVar.remove(name); - if (nameToStreamVar.isEmpty()) { - pidToNameToStreamVariable.remove(owner.getConnectorId()); - } + owner.getUI().getConnectorTracker() + .cleanStreamVariable(owner.getConnectorId(), name); } /** @@ -2683,9 +2643,9 @@ public abstract class AbstractCommunicationManager implements Serializable { UI uI = session.getUIById(Integer.parseInt(uiId)); UI.setCurrent(uI); - StreamVariable streamVariable = getStreamVariable(connectorId, - variableName); - String secKey = streamVariableToSeckey.get(streamVariable); + StreamVariable streamVariable = uI.getConnectorTracker() + .getStreamVariable(connectorId, variableName); + String secKey = uI.getConnectorTracker().getSeckey(streamVariable); if (secKey.equals(parts[3])) { ClientConnector source = getConnector(uI, connectorId); @@ -2741,17 +2701,6 @@ public abstract class AbstractCommunicationManager implements Serializable { } } - public StreamVariable getStreamVariable(String connectorId, - String variableName) { - Map<String, StreamVariable> map = pidToNameToStreamVariable - .get(connectorId); - if (map == null) { - return null; - } - StreamVariable streamVariable = map.get(variableName); - return streamVariable; - } - /** * Stream that extracts content from another stream until the boundary * string is encountered. diff --git a/server/src/com/vaadin/ui/ConnectorTracker.java b/server/src/com/vaadin/ui/ConnectorTracker.java index 8b1a940c4b..91f9ac451c 100644 --- a/server/src/com/vaadin/ui/ConnectorTracker.java +++ b/server/src/com/vaadin/ui/ConnectorTracker.java @@ -23,6 +23,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; +import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; @@ -33,6 +34,7 @@ import com.vaadin.server.AbstractClientConnector; import com.vaadin.server.AbstractCommunicationManager; import com.vaadin.server.ClientConnector; import com.vaadin.server.GlobalResourceHandler; +import com.vaadin.server.StreamVariable; /** * A class which takes care of book keeping of {@link ClientConnector}s for a @@ -65,6 +67,11 @@ public class ConnectorTracker implements Serializable { private UI uI; private transient Map<ClientConnector, JSONObject> diffStates = new HashMap<ClientConnector, JSONObject>(); + /** Maps connectorIds to a map of named StreamVariables */ + private Map<String, Map<String, StreamVariable>> pidToNameToStreamVariable; + + private Map<StreamVariable, String> streamVariableToSeckey; + /** * Gets a logger for this class * @@ -259,6 +266,7 @@ public class ConnectorTracker implements Serializable { } } + cleanStreamVariables(); } /** @@ -497,4 +505,111 @@ public class ConnectorTracker implements Serializable { } } + + /** + * Checks if the indicated connector has a StreamVariable of the given name + * and returns the variable if one is found. + * + * @param connectorId + * @param variableName + * @return variable if a matching one exists, otherwise null + */ + public StreamVariable getStreamVariable(String connectorId, + String variableName) { + if (pidToNameToStreamVariable == null) { + return null; + } + Map<String, StreamVariable> map = pidToNameToStreamVariable + .get(connectorId); + if (map == null) { + return null; + } + StreamVariable streamVariable = map.get(variableName); + return streamVariable; + } + + /** + * Adds a StreamVariable of the given name to the indicated connector. + * + * @param connectorId + * @param variableName + * @param variable + */ + public void addStreamVariable(String connectorId, String variableName, + StreamVariable variable) { + if (pidToNameToStreamVariable == null) { + pidToNameToStreamVariable = new HashMap<String, Map<String, StreamVariable>>(); + } + Map<String, StreamVariable> nameToStreamVariable = pidToNameToStreamVariable + .get(connectorId); + if (nameToStreamVariable == null) { + nameToStreamVariable = new HashMap<String, StreamVariable>(); + pidToNameToStreamVariable.put(connectorId, nameToStreamVariable); + } + nameToStreamVariable.put(variableName, variable); + + if (streamVariableToSeckey == null) { + streamVariableToSeckey = new HashMap<StreamVariable, String>(); + } + String seckey = streamVariableToSeckey.get(variable); + if (seckey == null) { + seckey = UUID.randomUUID().toString(); + streamVariableToSeckey.put(variable, seckey); + } + } + + /** + * Removes StreamVariables that belong to connectors that are no longer + * attached to the session. + */ + private void cleanStreamVariables() { + if (pidToNameToStreamVariable != null) { + Iterator<String> iterator = pidToNameToStreamVariable.keySet() + .iterator(); + while (iterator.hasNext()) { + String connectorId = iterator.next(); + if (uI.getConnectorTracker().getConnector(connectorId) == null) { + // Owner is no longer attached to the session + Map<String, StreamVariable> removed = pidToNameToStreamVariable + .get(connectorId); + for (String key : removed.keySet()) { + streamVariableToSeckey.remove(removed.get(key)); + } + iterator.remove(); + } + } + } + } + + /** + * Removes any StreamVariable of the given name from the indicated + * connector. + * + * @param connectorId + * @param variableName + */ + public void cleanStreamVariable(String connectorId, String variableName) { + if (pidToNameToStreamVariable == null) { + return; + } + Map<String, StreamVariable> nameToStreamVar = pidToNameToStreamVariable + .get(connectorId); + nameToStreamVar.remove(variableName); + if (nameToStreamVar.isEmpty()) { + pidToNameToStreamVariable.remove(connectorId); + } + } + + /** + * Returns the security key associated with the given StreamVariable. + * + * @param variable + * @return matching security key if one exists, null otherwise + */ + public String getSeckey(StreamVariable variable) { + if (streamVariableToSeckey == null) { + return null; + } + return streamVariableToSeckey.get(variable); + } } diff --git a/server/tests/src/com/vaadin/tests/server/TestStreamVariableMapping.java b/server/tests/src/com/vaadin/tests/server/TestStreamVariableMapping.java index f8dceca363..f24638e6a7 100644 --- a/server/tests/src/com/vaadin/tests/server/TestStreamVariableMapping.java +++ b/server/tests/src/com/vaadin/tests/server/TestStreamVariableMapping.java @@ -8,6 +8,7 @@ import com.vaadin.server.CommunicationManager; import com.vaadin.server.StreamVariable; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinSession; +import com.vaadin.ui.ConnectorTracker; import com.vaadin.ui.UI; import com.vaadin.ui.Upload; @@ -51,18 +52,21 @@ public class TestStreamVariableMapping extends TestCase { streamVariable); assertTrue(targetUrl.startsWith("app://APP/UPLOAD/-1/1/myName/")); - StreamVariable streamVariable2 = cm.getStreamVariable( + ConnectorTracker tracker = UI.getCurrent().getConnectorTracker(); + StreamVariable streamVariable2 = tracker.getStreamVariable( owner.getConnectorId(), variableName); assertSame(streamVariable, streamVariable2); } public void testRemoverVariable() { + ConnectorTracker tracker = UI.getCurrent().getConnectorTracker(); cm.getStreamVariableTargetUrl(owner, variableName, streamVariable); - assertNotNull(cm - .getStreamVariable(owner.getConnectorId(), variableName)); + assertNotNull(tracker.getStreamVariable(owner.getConnectorId(), + variableName)); cm.cleanStreamVariable(owner, variableName); - assertNull(cm.getStreamVariable(owner.getConnectorId(), variableName)); + assertNull(tracker.getStreamVariable(owner.getConnectorId(), + variableName)); } private CommunicationManager createCommunicationManager() { diff --git a/uitest/src/com/vaadin/tests/components/ui/MultipleUIUploadTest.java b/uitest/src/com/vaadin/tests/components/ui/MultipleUIUploadTest.java new file mode 100644 index 0000000000..f92e22d06b --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/ui/MultipleUIUploadTest.java @@ -0,0 +1,118 @@ +package com.vaadin.tests.components.ui; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +import com.vaadin.server.StreamResource; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.Notification; +import com.vaadin.ui.Upload; +import com.vaadin.ui.VerticalLayout; + +public class MultipleUIUploadTest extends AbstractTestUI { + + private MemoryBuffer buffer = new MemoryBuffer(); + private Upload upload; + + @Override + protected String getTestDescription() { + return "Using Upload with multiple UIs causes NPE." + + " Open test in first browser window and open the file selection window." + + " Then open test in second browser window (without ?restartApplication) and click the notification button." + + " Then go back to the first window, select a file, and click Upload." + + " Click notification button to ensure the upload was received successfully."; + } + + @Override + protected Integer getTicketNumber() { + return 10112; + } + + @Override + protected void setup(VaadinRequest request) { + final VerticalLayout layout = new VerticalLayout(); + layout.setMargin(true); + layout.setSpacing(true); + setContent(layout); + + upload = new Upload(null, buffer); + upload.setId("upload"); + layout.addComponent(upload); + + Button button = new Button("show notification"); + button.setId("notify"); + button.addClickListener(new ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + Notification.show("uploaded: " + buffer.getFileName()); + } + }); + layout.addComponent(button); + + } + + public class MemoryBuffer implements StreamResource.StreamSource, + Upload.Receiver { + ByteArrayOutputStream outputBuffer = null; + + String mimeType; + + String fileName; + + public MemoryBuffer() { + + } + + @Override + public InputStream getStream() { + if (outputBuffer == null) { + return null; + } + return new ByteArrayInputStream(outputBuffer.toByteArray()); + } + + /** + * @see com.vaadin.ui.Upload.Receiver#receiveUpload(String, String) + */ + @Override + public OutputStream receiveUpload(String filename, String MIMEType) { + fileName = filename; + mimeType = MIMEType; + outputBuffer = new ByteArrayOutputStream() { + @Override + public synchronized void write(byte[] b, int off, int len) { + super.write(b, off, len); + } + + }; + return outputBuffer; + } + + /** + * Returns the fileName. + * + * @return String + */ + public String getFileName() { + return fileName; + } + + /** + * Returns the mimeType. + * + * @return String + */ + public String getMimeType() { + return mimeType; + } + + } + +} |