From: Matti Tahvonen Date: Thu, 11 Nov 2010 20:43:18 +0000 (+0000) Subject: #5741 : modified stream variables so that component developers can use the same... X-Git-Tag: 6.7.0.beta1~885 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=bee1212d66233330aa1716d197793490faff3edf;p=vaadin-framework.git #5741 : modified stream variables so that component developers can use the same post target several times without repaint. Bit more complex implementation, but on the other hand makes component developers job bit easier in some situations (eg. synchronizations, third party integrations). Still need to verify that this works at least in liferay and gatein portals. EasyUploads with flash multiple file upload fallback for IE will verify tomorrow. svn changeset:15971/svn branch:6.5 --- diff --git a/src/com/vaadin/terminal/PaintTarget.java b/src/com/vaadin/terminal/PaintTarget.java index 76ebd19a7d..a81c637de2 100644 --- a/src/com/vaadin/terminal/PaintTarget.java +++ b/src/com/vaadin/terminal/PaintTarget.java @@ -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. *

- * 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: + *

+ * 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 diff --git a/src/com/vaadin/terminal/StreamVariable.java b/src/com/vaadin/terminal/StreamVariable.java index d711a8f1c8..92dc057fd9 100644 --- a/src/com/vaadin/terminal/StreamVariable.java +++ b/src/com/vaadin/terminal/StreamVariable.java @@ -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 { } diff --git a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java index 5b691438f3..e8af424935 100644 --- a/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java @@ -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); + } diff --git a/src/com/vaadin/terminal/gwt/server/CommunicationManager.java b/src/com/vaadin/terminal/gwt/server/CommunicationManager.java index ae4ec16c2a..111f43b31f 100644 --- a/src/com/vaadin/terminal/gwt/server/CommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/CommunicationManager.java @@ -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 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(); } - 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 nameToStreamVar = pidToNameToStreamVariable + .get(getPaintableId((Paintable) owner)); + nameToStreamVar.remove("name"); + if (nameToStreamVar.isEmpty()) { + pidToNameToStreamVariable.remove(getPaintableId((Paintable) owner)); + } + } + } diff --git a/src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java b/src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java index d25619282c..8c8ab1e1be 100644 --- a/src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java +++ b/src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java @@ -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 } + } } diff --git a/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java b/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java index 69e7b0ef27..0a8b7a78a5 100644 --- a/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java +++ b/src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java @@ -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> ownerToNameToStreamVariable; @Override - String createStreamVariableTargetUrl(VariableOwner owner, String name, StreamVariable value) { + String getStreamVariableTargetUrl(VariableOwner owner, String name, + StreamVariable value) { if (ownerToNameToStreamVariable == null) { ownerToNameToStreamVariable = new HashMap>(); } - Map nameToReceiver = ownerToNameToStreamVariable.get(owner); + Map nameToReceiver = ownerToNameToStreamVariable + .get(owner); if (nameToReceiver == null) { nameToReceiver = new HashMap(); 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 map = ownerToNameToStreamVariable + .get(owner); + map.remove(name); + if (map.isEmpty()) { + ownerToNameToStreamVariable.remove(owner); + } + } + } diff --git a/src/com/vaadin/terminal/gwt/server/StreamingStartEventImpl.java b/src/com/vaadin/terminal/gwt/server/StreamingStartEventImpl.java index fedd2bd110..113c69cc08 100644 --- a/src/com/vaadin/terminal/gwt/server/StreamingStartEventImpl.java +++ b/src/com/vaadin/terminal/gwt/server/StreamingStartEventImpl.java @@ -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; + } + } diff --git a/src/com/vaadin/ui/DragAndDropWrapper.java b/src/com/vaadin/ui/DragAndDropWrapper.java index 4dc31cc34e..aebdde975a 100644 --- a/src/com/vaadin/ui/DragAndDropWrapper.java +++ b/src/com/vaadin/ui/DragAndDropWrapper.java @@ -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() { + + } } }