]> source.dussan.org Git - vaadin-framework.git/commitdiff
#5741 : modified stream variables so that component developers can use the same...
authorMatti Tahvonen <matti.tahvonen@itmill.com>
Thu, 11 Nov 2010 20:43:18 +0000 (20:43 +0000)
committerMatti Tahvonen <matti.tahvonen@itmill.com>
Thu, 11 Nov 2010 20:43:18 +0000 (20:43 +0000)
svn changeset:15971/svn branch:6.5

src/com/vaadin/terminal/PaintTarget.java
src/com/vaadin/terminal/StreamVariable.java
src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java
src/com/vaadin/terminal/gwt/server/CommunicationManager.java
src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java
src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java
src/com/vaadin/terminal/gwt/server/StreamingStartEventImpl.java
src/com/vaadin/ui/DragAndDropWrapper.java

index 76ebd19a7d9fb8153bb7ec85b115885f5d1b8eef..a81c637de2b87265d9fd9c8b234d420f7022e801 100644 (file)
@@ -7,6 +7,8 @@ package com.vaadin.terminal;
 import java.io.Serializable;
 import java.util.Map;
 
+import com.vaadin.terminal.StreamVariable.StreamingStartEvent;
+
 /**
  * This interface defines the methods for painting XML to the UIDL stream.
  * 
@@ -156,9 +158,17 @@ public interface PaintTarget extends Serializable {
      * terminals Receivers are typically rendered for the client side as URLs,
      * where the client side implementation can do an http post request.
      * <p>
-     * Note that a StreamVariable can only be used once per "paint". The same StreamVariable
-     * can be used several times, but it must be repainted before the next
-     * stream can be received.
+     * Note that in current terminal implementation StreamVariables are cleaned
+     * from the terminal only when:
+     * <ul>
+     * <li>a StreamVariable with same name replaces an old one
+     * <li>the variable owner is no more attached
+     * <li>the developer signals this by calling
+     * {@link StreamingStartEvent#disposeStreamVariable()}
+     * </ul>
+     * Most commonly a component developer can just ignore this issue, but with
+     * strict memory requirements and lots of StreamVariables implementations
+     * that reserve a lot of memory this may be a critical issue.
      * 
      * @param owner
      *            the ReceiverOwner that can track the progress of streaming to
@@ -171,8 +181,8 @@ public interface PaintTarget extends Serializable {
      * @throws PaintException
      *             if the paint operation failed.
      */
-    public void addVariable(VariableOwner owner, String name, StreamVariable value)
-            throws PaintException;
+    public void addVariable(VariableOwner owner, String name,
+            StreamVariable value) throws PaintException;
 
     /**
      * Adds a long attribute to component. Atributes must be added before any
index d711a8f1c8ea3da9a2e1aa8aeb6954d0b2ef7685..92dc057fd955999a862463d471003dc28a0fdf85 100644 (file)
@@ -45,8 +45,8 @@ public interface StreamVariable extends Serializable {
      * {@link #onProgress(long, long)} is called in a synchronized block when
      * the content is being received. This is potentially bit slow, so we are
      * calling that method only if requested. The value is requested after the
-     * {@link #uploadStarted(StreamingStartEvent)} event, but not after
-     * reading each buffer.
+     * {@link #uploadStarted(StreamingStartEvent)} event, but not after reading
+     * each buffer.
      * 
      * @return true if this {@link StreamVariable} wants to by notified during
      *         the upload of the progress of streaming.
@@ -108,15 +108,21 @@ public interface StreamVariable extends Serializable {
     }
 
     /**
-     * Event passed to {@link #uploadStarted(StreamingStartEvent)} method
-     * before the streaming of the content to {@link StreamVariable} starts.
+     * Event passed to {@link #uploadStarted(StreamingStartEvent)} method before
+     * the streaming of the content to {@link StreamVariable} starts.
      */
     public interface StreamingStartEvent extends StreamingEvent {
+        /**
+         * The owner of the StreamVariable can call this method to inform the
+         * terminal implementation that this StreamVariable will not be used to
+         * accept more post.
+         */
+        public void disposeStreamVariable();
     }
 
     /**
-     * Event passed to {@link #onProgress(StreamingProgressEvent)} method
-     * during the streaming progresses.
+     * Event passed to {@link #onProgress(StreamingProgressEvent)} method during
+     * the streaming progresses.
      */
     public interface StreamingProgressEvent extends StreamingEvent {
     }
index 5b691438f3d1bc622e47f01c1107fb1d669a3404..e8af424935fec047ae3522eb366760fbf3716c25 100644 (file)
@@ -54,7 +54,6 @@ import com.vaadin.terminal.Paintable.RepaintRequestEvent;
 import com.vaadin.terminal.StreamVariable;
 import com.vaadin.terminal.StreamVariable.StreamingEndEvent;
 import com.vaadin.terminal.StreamVariable.StreamingErrorEvent;
-import com.vaadin.terminal.StreamVariable.StreamingStartEvent;
 import com.vaadin.terminal.Terminal.ErrorEvent;
 import com.vaadin.terminal.Terminal.ErrorListener;
 import com.vaadin.terminal.URIHandler;
@@ -379,7 +378,8 @@ public abstract class AbstractCommunicationManager implements
      */
     protected void doHandleSimpleMultipartFileUpload(Request request,
             Response response, StreamVariable streamVariable,
-            VariableOwner owner, String boundary) throws IOException {
+            String variableName, VariableOwner owner, String boundary)
+            throws IOException {
         boundary = CRLF + "--" + boundary + "--";
 
         // multipart parsing, supports only one file for request, but that is
@@ -528,8 +528,11 @@ public abstract class AbstractCommunicationManager implements
                 throw new UploadException(
                         "Warning: file upload ignored because the componente was read-only");
             }
-            streamToReceiver(simpleMultiPartReader, streamVariable, filename,
-                    mimeType, contentLength);
+            boolean forgetVariable = streamToReceiver(simpleMultiPartReader,
+                    streamVariable, filename, mimeType, contentLength);
+            if (forgetVariable) {
+                cleanStreamVariable(owner, variableName);
+            }
         } catch (Exception e) {
             synchronized (application) {
                 handleChangeVariablesError(application, (Component) owner, e,
@@ -551,8 +554,8 @@ public abstract class AbstractCommunicationManager implements
      * @throws IOException
      */
     protected void doHandleXhrFilePost(Request request, Response response,
-            StreamVariable streamVariable, VariableOwner owner,
-            int contentLength) throws IOException {
+            StreamVariable streamVariable, String variableName,
+            VariableOwner owner, int contentLength) throws IOException {
 
         // These are unknown in filexhr ATM, maybe add to Accept header that
         // is accessible in portlets
@@ -569,8 +572,11 @@ public abstract class AbstractCommunicationManager implements
                 throw new UploadException(
                         "Warning: file upload ignored because the component was read-only");
             }
-            streamToReceiver(stream, streamVariable, filename, mimeType,
-                    contentLength);
+            boolean forgetVariable = streamToReceiver(stream, streamVariable,
+                    filename, mimeType, contentLength);
+            if (forgetVariable) {
+                cleanStreamVariable(owner, variableName);
+            }
         } catch (Exception e) {
             synchronized (application) {
                 handleChangeVariablesError(application, (Component) owner, e,
@@ -580,7 +586,17 @@ public abstract class AbstractCommunicationManager implements
         sendUploadResponse(request, response);
     }
 
-    protected final void streamToReceiver(final InputStream in,
+    /**
+     * @param in
+     * @param streamVariable
+     * @param filename
+     * @param type
+     * @param contentLength
+     * @return true if the streamvariable has informed that the terminal can
+     *         forget this variable
+     * @throws UploadException
+     */
+    protected final boolean streamToReceiver(final InputStream in,
             StreamVariable streamVariable, String filename, String type,
             int contentLength) throws UploadException {
         if (streamVariable == null) {
@@ -592,11 +608,11 @@ public abstract class AbstractCommunicationManager implements
 
         OutputStream out = null;
         int totalBytes = 0;
+        StreamingStartEventImpl startedEvent = new StreamingStartEventImpl(
+                filename, type, contentLength);
         try {
             boolean listenProgress;
             synchronized (application) {
-                StreamingStartEvent startedEvent = new StreamingStartEventImpl(
-                        filename, type, contentLength);
                 streamVariable.streamingStarted(startedEvent);
                 out = streamVariable.getOutputStream();
                 listenProgress = streamVariable.listenProgress();
@@ -662,6 +678,7 @@ public abstract class AbstractCommunicationManager implements
                 throw new UploadException(e);
             }
         }
+        return startedEvent.isDisposed();
     }
 
     private void tryToCloseStream(OutputStream out) {
@@ -2181,6 +2198,9 @@ public abstract class AbstractCommunicationManager implements
 
     }
 
-    abstract String createStreamVariableTargetUrl(VariableOwner owner, String name,
-            StreamVariable value);
+    abstract String getStreamVariableTargetUrl(VariableOwner owner,
+            String name, StreamVariable value);
+
+    abstract protected void cleanStreamVariable(VariableOwner owner, String name);
+
 }
index ae4ec16c2aedb3d9b2f3527db95f30fc32f4a5b9..111f43b31f54411d96ab0d4d0af73a6fa04d6781 100644 (file)
@@ -208,8 +208,7 @@ public class CommunicationManager extends AbstractCommunicationManager {
     /**
      * Handles file upload request submitted via Upload component.
      * 
-     * @see #createStreamVariableTargetUrl(ReceiverOwner, String,
-     *      StreamVariable)
+     * @see #getStreamVariableTargetUrl(ReceiverOwner, String, StreamVariable)
      * 
      * @param request
      * @param response
@@ -231,27 +230,30 @@ public class CommunicationManager extends AbstractCommunicationManager {
                 + AbstractApplicationServlet.UPLOAD_URL_PREFIX.length();
         String uppUri = pathInfo.substring(startOfData);
         String[] parts = uppUri.split("/", 3); // 0 = pid, 1= name, 2 = sec key
+        String variableName = parts[1];
+        String paintableId = parts[0];
 
-        StreamVariable streamVariable = pidToNameToStreamVariable.get(parts[0])
-                .remove(parts[1]);
-        String secKey = streamVariableToSeckey.remove(streamVariable);
+        StreamVariable streamVariable = pidToNameToStreamVariable.get(
+                paintableId).get(variableName);
+        String secKey = streamVariableToSeckey.get(streamVariable);
         if (secKey.equals(parts[2])) {
 
-            VariableOwner source = getVariableOwner(parts[0]);
+            VariableOwner source = getVariableOwner(paintableId);
             String contentType = request.getContentType();
             if (request.getContentType().contains("boundary")) {
                 // Multipart requests contain boundary string
                 doHandleSimpleMultipartFileUpload(
                         new HttpServletRequestWrapper(request),
                         new HttpServletResponseWrapper(response),
-                        streamVariable, source,
+                        streamVariable, variableName, source,
                         contentType.split("boundary=")[1]);
             } else {
                 // if boundary string does not exist, the posted file is from
                 // XHR2.post(File)
                 doHandleXhrFilePost(new HttpServletRequestWrapper(request),
                         new HttpServletResponseWrapper(response),
-                        streamVariable, source, request.getContentLength());
+                        streamVariable, variableName, source,
+                        request.getContentLength());
             }
         } else {
             throw new InvalidUIDLSecurityKeyException(
@@ -357,9 +359,8 @@ public class CommunicationManager extends AbstractCommunicationManager {
     private Map<StreamVariable, String> streamVariableToSeckey;
 
     @Override
-    String createStreamVariableTargetUrl(VariableOwner owner, String name,
+    String getStreamVariableTargetUrl(VariableOwner owner, String name,
             StreamVariable value) {
-
         /*
          * We will use the same APP/* URI space as ApplicationResources but
          * prefix url with UPLOAD
@@ -389,8 +390,11 @@ public class CommunicationManager extends AbstractCommunicationManager {
         if (streamVariableToSeckey == null) {
             streamVariableToSeckey = new HashMap<StreamVariable, String>();
         }
-        String seckey = UUID.randomUUID().toString();
-        streamVariableToSeckey.put(value, seckey);
+        String seckey = streamVariableToSeckey.get(value);
+        if (seckey == null) {
+            seckey = UUID.randomUUID().toString();
+            streamVariableToSeckey.put(value, seckey);
+        }
 
         return getApplication().getURL()
                 + AbstractApplicationServlet.UPLOAD_URL_PREFIX + key + "/"
@@ -398,4 +402,14 @@ public class CommunicationManager extends AbstractCommunicationManager {
 
     }
 
+    @Override
+    protected void cleanStreamVariable(VariableOwner owner, String name) {
+        Map<String, StreamVariable> nameToStreamVar = pidToNameToStreamVariable
+                .get(getPaintableId((Paintable) owner));
+        nameToStreamVar.remove("name");
+        if (nameToStreamVar.isEmpty()) {
+            pidToNameToStreamVariable.remove(getPaintableId((Paintable) owner));
+        }
+    }
+
 }
index d25619282c90d85f66907484430aa4aba0c900b4..8c8ab1e1be0a5c449ba2152459a3642dc0ddd79a 100644 (file)
@@ -29,8 +29,8 @@ import com.vaadin.terminal.ExternalResource;
 import com.vaadin.terminal.PaintException;
 import com.vaadin.terminal.PaintTarget;
 import com.vaadin.terminal.Paintable;
-import com.vaadin.terminal.StreamVariable;
 import com.vaadin.terminal.Resource;
+import com.vaadin.terminal.StreamVariable;
 import com.vaadin.terminal.ThemeResource;
 import com.vaadin.terminal.VariableOwner;
 import com.vaadin.ui.Alignment;
@@ -1111,10 +1111,13 @@ public class JsonPaintTarget implements PaintTarget {
         return usedPaintableTypes;
     }
 
-    public void addVariable(VariableOwner owner, String name, StreamVariable value)
-            throws PaintException {
-        String url = manager.createStreamVariableTargetUrl(owner, name, value);
-        addVariable(owner, name, url);
+    public void addVariable(VariableOwner owner, String name,
+            StreamVariable value) throws PaintException {
+        String url = manager.getStreamVariableTargetUrl(owner, name, value);
+        if (url != null) {
+            addVariable(owner, name, url);
+        } // else { //NOP this was just a cleanup by component }
+
     }
 
 }
index 69e7b0ef27ea7b9cf2a3d59fefc4d18d1788c06c..0a8b7a78a54299f2dbe4bf22634ebd77956b41e8 100644 (file)
@@ -197,21 +197,18 @@ public class PortletCommunicationManager extends AbstractCommunicationManager {
         String contentType = request.getContentType();
         String name = request.getParameter("name");
         String ownerId = request.getParameter("rec-owner");
-        VariableOwner variableOwner = (VariableOwner) getVariableOwner(ownerId);
-        StreamVariable streamVariable = ownerToNameToStreamVariable.get(variableOwner).remove(
-                name);
-
-        // clean up, may be re added on next paint
-        ownerToNameToStreamVariable.get(variableOwner).remove(name);
+        VariableOwner variableOwner = getVariableOwner(ownerId);
+        StreamVariable streamVariable = ownerToNameToStreamVariable.get(
+                variableOwner).get(name);
 
         if (contentType.contains("boundary")) {
             doHandleSimpleMultipartFileUpload(
                     new PortletRequestWrapper(request),
-                    new PortletResponseWrapper(response), streamVariable,
+                    new PortletResponseWrapper(response), streamVariable, name,
                     variableOwner, contentType.split("boundary=")[1]);
         } else {
             doHandleXhrFilePost(new PortletRequestWrapper(request),
-                    new PortletResponseWrapper(response), streamVariable,
+                    new PortletResponseWrapper(response), streamVariable, name,
                     variableOwner, request.getContentLength());
         }
 
@@ -274,11 +271,13 @@ public class PortletCommunicationManager extends AbstractCommunicationManager {
     private Map<VariableOwner, Map<String, StreamVariable>> ownerToNameToStreamVariable;
 
     @Override
-    String createStreamVariableTargetUrl(VariableOwner owner, String name, StreamVariable value) {
+    String getStreamVariableTargetUrl(VariableOwner owner, String name,
+            StreamVariable value) {
         if (ownerToNameToStreamVariable == null) {
             ownerToNameToStreamVariable = new HashMap<VariableOwner, Map<String, StreamVariable>>();
         }
-        Map<String, StreamVariable> nameToReceiver = ownerToNameToStreamVariable.get(owner);
+        Map<String, StreamVariable> nameToReceiver = ownerToNameToStreamVariable
+                .get(owner);
         if (nameToReceiver == null) {
             nameToReceiver = new HashMap<String, StreamVariable>();
             ownerToNameToStreamVariable.put(owner, nameToReceiver);
@@ -293,4 +292,14 @@ public class PortletCommunicationManager extends AbstractCommunicationManager {
         return resurl.toString();
     }
 
+    @Override
+    protected void cleanStreamVariable(VariableOwner owner, String name) {
+        Map<String, StreamVariable> map = ownerToNameToStreamVariable
+                .get(owner);
+        map.remove(name);
+        if (map.isEmpty()) {
+            ownerToNameToStreamVariable.remove(owner);
+        }
+    }
+
 }
index fedd2bd1107488dd549ccecbf87f158c43623479..113c69cc08af9f8b374a1b58a64c9aa6a022b049 100644 (file)
@@ -6,9 +6,19 @@ import com.vaadin.terminal.StreamVariable.StreamingStartEvent;
 final class StreamingStartEventImpl extends AbstractStreamingEvent implements
         StreamingStartEvent {
 
+    private boolean disposed;
+
     public StreamingStartEventImpl(final String filename, final String type,
             long contentLength) {
         super(filename, type, contentLength, 0);
     }
 
+    public void disposeStreamVariable() {
+        disposed = true;
+    }
+
+    boolean isDisposed() {
+        return disposed;
+    }
+
 }
index 4dc31cc34e6be3e523a9274800f859a719cbba3a..aebdde975a4a6bcdd0d676f230be09a9e87ba85b 100644 (file)
@@ -256,6 +256,9 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget,
             }
             // no need tell to the client about this receiver on next paint
             receivers.remove(file);
+            // let the terminal GC the streamvariable and not to accept other
+            // file uploads to this variable
+            event.disposeStreamVariable();
         }
 
         public void streamingFinished(StreamingEndEvent event) {
@@ -317,6 +320,14 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget,
             public long getBytesReceived() {
                 return wrappedEvent.getBytesReceived();
             }
+
+            /**
+             * Calling this method has no effect. DD files are receive only once
+             * anyway.
+             */
+            public void disposeStreamVariable() {
+
+            }
         }
 
     }