Browse Source

#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
tags/6.7.0.beta1
Matti Tahvonen 13 years ago
parent
commit
bee1212d66

+ 15
- 5
src/com/vaadin/terminal/PaintTarget.java View 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

+ 12
- 6
src/com/vaadin/terminal/StreamVariable.java View 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 {
}

+ 33
- 13
src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java View 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);

}

+ 26
- 12
src/com/vaadin/terminal/gwt/server/CommunicationManager.java View 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));
}
}

}

+ 8
- 5
src/com/vaadin/terminal/gwt/server/JsonPaintTarget.java View 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 }

}

}

+ 19
- 10
src/com/vaadin/terminal/gwt/server/PortletCommunicationManager.java View 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);
}
}

}

+ 10
- 0
src/com/vaadin/terminal/gwt/server/StreamingStartEventImpl.java View 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;
}

}

+ 11
- 0
src/com/vaadin/ui/DragAndDropWrapper.java View 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() {

}
}

}

Loading…
Cancel
Save