summaryrefslogtreecommitdiffstats
path: root/src/com/vaadin/ui
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/vaadin/ui')
-rw-r--r--src/com/vaadin/ui/DragAndDropWrapper.java208
-rw-r--r--src/com/vaadin/ui/Html5File.java137
-rw-r--r--src/com/vaadin/ui/Upload.java251
3 files changed, 345 insertions, 251 deletions
diff --git a/src/com/vaadin/ui/DragAndDropWrapper.java b/src/com/vaadin/ui/DragAndDropWrapper.java
index 87ff571f36..afcb70506b 100644
--- a/src/com/vaadin/ui/DragAndDropWrapper.java
+++ b/src/com/vaadin/ui/DragAndDropWrapper.java
@@ -3,10 +3,6 @@
*/
package com.vaadin.ui;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
@@ -19,23 +15,34 @@ import com.vaadin.event.dd.TargetDetails;
import com.vaadin.event.dd.TargetDetailsImpl;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.UploadStream;
+import com.vaadin.terminal.Receiver;
+import com.vaadin.terminal.ReceiverOwner;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
import com.vaadin.terminal.gwt.client.ui.VDragAndDropWrapper;
import com.vaadin.terminal.gwt.client.ui.dd.HorizontalDropLocation;
import com.vaadin.terminal.gwt.client.ui.dd.VerticalDropLocation;
-import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
-import com.vaadin.ui.DragAndDropWrapper.WrapperTransferable.Html5File;
-import com.vaadin.ui.Upload.Receiver;
-import com.vaadin.ui.Upload.UploadException;
+import com.vaadin.ui.Html5File.ProxyReceiver;
@SuppressWarnings("serial")
@ClientWidget(VDragAndDropWrapper.class)
public class DragAndDropWrapper extends CustomComponent implements DropTarget,
- DragSource {
+ DragSource, ReceiverOwner {
public class WrapperTransferable extends TransferableImpl {
+ /**
+ * @deprecated this class is made top level in recent version. Use
+ * com.vaadin.ui.Html5File instead
+ */
+ @Deprecated
+ private class Html5File extends com.vaadin.ui.Html5File {
+
+ Html5File(String name, long size, String mimeType) {
+ super(name, size, mimeType);
+ }
+
+ }
+
private Html5File[] files;
public WrapperTransferable(Component sourceComponent,
@@ -45,13 +52,14 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget,
if (fc != null) {
files = new Html5File[fc];
for (int i = 0; i < fc; i++) {
- Html5File file = new Html5File();
+ Html5File file = new Html5File(
+ (String) rawVariables.get("fn" + i), // name
+ (Integer) rawVariables.get("fs" + i), // size
+ (String) rawVariables.get("ft" + i)); // mime
String id = (String) rawVariables.get("fi" + i);
- file.name = (String) rawVariables.get("fn" + i);
- file.size = (Integer) rawVariables.get("fs" + i);
- file.type = (String) rawVariables.get("ft" + i);
files[i] = file;
receivers.put(id, file);
+ requestRepaint(); // paint Receivers
}
}
}
@@ -96,50 +104,6 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget,
return data;
}
- /**
- * {@link DragAndDropWrapper} can receive also files from client
- * computer if appropriate HTML 5 features are supported on client side.
- * This class wraps information about dragged file on server side.
- */
- public class Html5File implements Serializable {
-
- public String name;
- private int size;
- private Receiver receiver;
- private String type;
-
- public String getFileName() {
- return name;
- }
-
- public int getFileSize() {
- return size;
- }
-
- public String getType() {
- return type;
- }
-
- /**
- * Sets the {@link Receiver} that into which the file contents will
- * be written. Usage of Reveiver is similar to {@link Upload}
- * component.
- *
- * <p>
- * <em>Note!</em> receiving file contents is experimental feature
- * depending on HTML 5 API's. It is supported only by Firefox 3.6 at
- * this time.
- *
- * @param receiver
- * the callback that returns stream where the
- * implementation writes the file contents as it arrives.
- */
- public void setReceiver(Receiver receiver) {
- this.receiver = receiver;
- }
-
- }
-
}
private Map<String, Html5File> receivers = new HashMap<String, Html5File>();
@@ -220,9 +184,23 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget,
if (getDropHandler() != null) {
getDropHandler().getAcceptCriterion().paint(target);
}
+ if (receivers != null && receivers.size() > 0) {
+ for (String id : receivers.keySet()) {
+ Html5File html5File = receivers.get(id);
+ if (html5File.getReceiver() != null) {
+ target.addVariable(this, "rec-" + id,
+ html5File.getProxyReceiver());
+ } else {
+ // instructs the client side not to send the file
+ target.addVariable(this, "rec-" + id, (String) null);
+ }
+ }
+ }
}
private DropHandler dropHandler;
+ private Html5File currentlyUploadedFile;
+ private boolean listenProgressOfUploadedFile;
public DropHandler getDropHandler() {
return dropHandler;
@@ -251,38 +229,96 @@ public class DragAndDropWrapper extends CustomComponent implements DropTarget,
return dragStartMode;
}
- /**
- * This method should only be used by Vaadin terminal implementation. This
- * is not end user api.
- *
- * TODO should fire progress events + end/succes events like upload. Not
- * critical until we have a wider browser support for HTML5 File API
- *
- * @param upstream
- * @param fileId
- * @throws UploadException
+ /*
+ * Single controller is enough for atm as files are transferred in serial.
+ * If parallel transfer is needed, this logic needs to go to Html5File
*/
- public void receiveFile(UploadStream upstream, String fileId)
- throws UploadException {
- Html5File file = receivers.get(fileId);
- if (file != null && file.receiver != null) {
- OutputStream receiveUpload = file.receiver.receiveUpload(
- file.getFileName(), "TODO");
-
- InputStream stream = upstream.getStream();
- byte[] buf = new byte[AbstractApplicationServlet.MAX_BUFFER_SIZE];
- int bytesRead;
- try {
- while ((bytesRead = stream.read(buf)) != -1) {
- receiveUpload.write(buf, 0, bytesRead);
+ private ReceivingController controller = new ReceivingController() {
+ /*
+ * With XHR2 file posts we can't provide as much information from the
+ * terminal as with multipart request. This helper class wraps the
+ * terminal event and provides the lacking information from the
+ * Html5File.
+ */
+ class ReceivingEventWrapper implements ReceivingFailedEvent,
+ ReceivingEndedEvent, ReceivingStartedEvent,
+ ReceivingProgressedEvent {
+ private ReceivingEvent wrappedEvent;
+
+ ReceivingEventWrapper(ReceivingEvent e) {
+ wrappedEvent = e;
+ }
+
+ public String getMimeType() {
+ return currentlyUploadedFile.getType();
+ }
+
+ public String getFileName() {
+ return currentlyUploadedFile.getFileName();
+ }
+
+ public long getContentLength() {
+ return currentlyUploadedFile.getFileSize();
+ }
+
+ public Receiver getReceiver() {
+ return currentlyUploadedFile.getReceiver();
+ }
+
+ public Exception getException() {
+ if (wrappedEvent instanceof ReceivingFailedEvent) {
+ return ((ReceivingFailedEvent) wrappedEvent).getException();
}
- receiveUpload.close();
- } catch (IOException e) {
- throw new UploadException(e);
+ return null;
+ }
+
+ public long getBytesReceived() {
+ return wrappedEvent.getBytesReceived();
+ }
+ }
+
+ public boolean listenProgress() {
+ return listenProgressOfUploadedFile;
+ }
+
+ public void onProgress(ReceivingProgressedEvent event) {
+ currentlyUploadedFile.getUploadListener().onProgress(
+ new ReceivingEventWrapper(event));
+ }
+
+ public void uploadStarted(ReceivingStartedEvent event) {
+ currentlyUploadedFile = ((ProxyReceiver) event.getReceiver())
+ .getFile();
+ listenProgressOfUploadedFile = currentlyUploadedFile
+ .getUploadListener() != null;
+ if (listenProgressOfUploadedFile) {
+ currentlyUploadedFile.getUploadListener().uploadStarted(
+ new ReceivingEventWrapper(event));
+ }
+ }
+
+ public void uploadFinished(ReceivingEndedEvent event) {
+ if (listenProgressOfUploadedFile) {
+ currentlyUploadedFile.getUploadListener().uploadFinished(
+ new ReceivingEventWrapper(event));
}
- // clean up the reference when file is downloaded
- receivers.remove(fileId);
}
+ public void uploadFailed(final ReceivingFailedEvent event) {
+ if (listenProgressOfUploadedFile) {
+ currentlyUploadedFile.getUploadListener().uploadFailed(
+ new ReceivingEventWrapper(event));
+ }
+ }
+
+ public boolean isInterrupted() {
+ return currentlyUploadedFile.isInterrupted();
+ }
+
+ };
+
+ public ReceivingController getReceivingController(Receiver receiver) {
+ return controller;
}
+
}
diff --git a/src/com/vaadin/ui/Html5File.java b/src/com/vaadin/ui/Html5File.java
new file mode 100644
index 0000000000..988bf534b9
--- /dev/null
+++ b/src/com/vaadin/ui/Html5File.java
@@ -0,0 +1,137 @@
+package com.vaadin.ui;
+
+import java.io.OutputStream;
+import java.io.Serializable;
+
+import com.vaadin.event.dd.DropHandler;
+import com.vaadin.terminal.Receiver;
+import com.vaadin.terminal.ReceiverOwner.ReceivingEndedEvent;
+import com.vaadin.terminal.ReceiverOwner.ReceivingFailedEvent;
+import com.vaadin.terminal.ReceiverOwner.ReceivingProgressedEvent;
+import com.vaadin.terminal.ReceiverOwner.ReceivingStartedEvent;
+
+/**
+ * {@link DragAndDropWrapper} can receive also files from client computer if
+ * appropriate HTML 5 features are supported on client side. This class wraps
+ * information about dragged file on server side.
+ */
+public class Html5File implements Serializable {
+
+ final class ProxyReceiver implements Receiver {
+ public OutputStream receiveUpload(String filename, String MIMEType) {
+ if (receiver == null) {
+ return null;
+ }
+ return receiver.receiveUpload(filename, MIMEType);
+ }
+
+ Html5File getFile() {
+ return Html5File.this;
+ }
+ }
+
+ private String name;
+ private long size;
+ private Receiver receiver;
+ private String type;
+
+ Html5File(String name, long size, String mimeType) {
+ this.name = name;
+ this.size = size;
+ type = mimeType;
+ }
+
+ /**
+ * The receiver that is registered to the terminal. Wraps the actual
+ * Receiver set later by Html5File user.
+ */
+ private ProxyReceiver proxyReceiver = new ProxyReceiver();
+ private boolean interrupted = false;
+ private Html5FileUploadListener listener;;
+
+ public String getFileName() {
+ return name;
+ }
+
+ public long getFileSize() {
+ return size;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * Sets the {@link Receiver} that into which the file contents will be
+ * written. Usage of Reveiver is similar to {@link Upload} component.
+ * <p>
+ * If the {@link Receiver} is not set in the {@link DropHandler} the file
+ * contents will not be sent to server.
+ * <p>
+ * <em>Note!</em> receiving file contents is experimental feature depending
+ * on HTML 5 API's. It is supported only by modern web brosers like Firefox
+ * 3.6 and above and recent webkit based browsers (Safari 5, Chrome 6) at
+ * this time.
+ *
+ * @param receiver
+ * the callback that returns stream where the implementation
+ * writes the file contents as it arrives.
+ */
+ public void setReceiver(Receiver receiver) {
+ this.receiver = receiver;
+ }
+
+ public Receiver getReceiver() {
+ return receiver;
+ }
+
+ ProxyReceiver getProxyReceiver() {
+ return proxyReceiver;
+ }
+
+ /**
+ * Gets the {@link Html5FileUploadListener} that is used to track the progress of
+ * streaming the file contents to given {@link Receiver}.
+ *
+ * @return
+ */
+ public Html5FileUploadListener getUploadListener() {
+ return listener;
+ }
+
+ /**
+ * Sets the {@link Html5FileUploadListener} that can be used to track the progress of
+ * streaming the file contents to given {@link Receiver}.
+ *
+ * @param listener
+ * @see #setReceiver(Receiver)
+ */
+ public void setUploadListener(Html5FileUploadListener listener) {
+ this.listener = listener;
+ }
+
+ public boolean isInterrupted() {
+ return interrupted;
+ }
+
+ /**
+ * Interrupts uploading this file.
+ *
+ * @param interrupted
+ */
+ public void setInterrupted(boolean interrupted) {
+ this.interrupted = interrupted;
+ }
+
+ public interface Html5FileUploadListener {
+
+ void onProgress(ReceivingProgressedEvent event);
+
+ void uploadStarted(ReceivingStartedEvent event);
+
+ void uploadFinished(ReceivingEndedEvent event);
+
+ void uploadFailed(ReceivingFailedEvent event);
+ }
+
+} \ No newline at end of file
diff --git a/src/com/vaadin/ui/Upload.java b/src/com/vaadin/ui/Upload.java
index b281d90b09..1acc91031e 100644
--- a/src/com/vaadin/ui/Upload.java
+++ b/src/com/vaadin/ui/Upload.java
@@ -4,20 +4,18 @@
package com.vaadin.ui;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
-import com.vaadin.Application;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
-import com.vaadin.terminal.UploadStream;
+import com.vaadin.terminal.ReceiverOwner;
import com.vaadin.terminal.gwt.client.ui.VUpload;
+import com.vaadin.terminal.gwt.server.NoInputStreamException;
+import com.vaadin.terminal.gwt.server.NoOutputStreamException;
import com.vaadin.ui.ClientWidget.LoadStyle;
/**
@@ -62,12 +60,8 @@ import com.vaadin.ui.ClientWidget.LoadStyle;
*/
@SuppressWarnings("serial")
@ClientWidget(value = VUpload.class, loadStyle = LoadStyle.LAZY)
-public class Upload extends AbstractComponent implements Component.Focusable {
-
- /**
- * Upload buffer size.
- */
- private static final int BUFFER_SIZE = 64 * 1024; // 64k
+public class Upload extends AbstractComponent implements Component.Focusable,
+ ReceiverOwner {
/**
* Should the field be focused on next repaint?
@@ -131,105 +125,6 @@ public class Upload extends AbstractComponent implements Component.Focusable {
}
/**
- * This method is called by terminal when upload is received.
- *
- * Note, this method is called outside synchronized (Application) block, so
- * overriding this may be dangerous.
- *
- * @param upload
- */
- public void receiveUpload(UploadStream upload) throws UploadException {
- if (receiver == null) {
- throw new IllegalStateException(
- "Receiver not set for the Upload component");
- }
-
- if (!isUploading) {
- throw new IllegalStateException("uploading not started");
- }
-
- // Gets file properties
- final String filename = upload.getContentName();
- final String type = upload.getContentType();
-
- final Application application = getApplication();
-
- synchronized (application) {
- fireStarted(filename, type);
- }
-
- // Gets the output target stream
- final OutputStream out = receiver.receiveUpload(filename, type);
- if (out == null) {
- synchronized (application) {
- fireNoOutputStream(filename, type, 0);
- endUpload();
- }
- return;
- }
-
- final InputStream in = upload.getStream();
-
- if (null == in) {
- // No file, for instance non-existent filename in html upload
- synchronized (application) {
- fireNoInputStream(filename, type, 0);
- endUpload();
- }
- return;
- }
-
- final byte buffer[] = new byte[BUFFER_SIZE];
- int bytesRead = 0;
- totalBytes = 0;
- try {
- while ((bytesRead = in.read(buffer)) > 0) {
- out.write(buffer, 0, bytesRead);
- totalBytes += bytesRead;
- if (contentLength > 0
- && (progressListeners != null || progressListener != null)) {
- // update progress if listener set and contentLength
- // received
- synchronized (application) {
- fireUpdateProgress(totalBytes, contentLength);
- }
- }
- if (interrupted) {
- throw new UploadInterruptedException();
- }
- }
-
- // upload successful
- out.close();
- synchronized (application) {
- fireUploadSuccess(filename, type, totalBytes);
- endUpload();
- requestRepaint();
- }
-
- } catch (final Exception e) {
- synchronized (application) {
- if (e instanceof UploadInterruptedException) {
- // Download interrupted
- try {
- // still try to close output stream
- out.close();
- } catch (IOException e1) {
- // NOP
- }
- }
- fireUploadInterrupted(filename, type, totalBytes, e);
- endUpload();
- interrupted = false;
- if (!(e instanceof UploadInterruptedException)) {
- // throw exception for terminal to be handled
- throw new UploadException(e);
- }
- }
- }
- }
-
- /**
* Invoked when the value of a variable has changed.
*
* @see com.vaadin.ui.AbstractComponent#changeVariables(java.lang.Object,
@@ -278,6 +173,9 @@ public class Upload extends AbstractComponent implements Component.Focusable {
target.addAttribute("nextid", nextid);
+ // Post file to this receiver
+ target.addVariable(this, "action", receiver);
+
}
/**
@@ -288,20 +186,11 @@ public class Upload extends AbstractComponent implements Component.Focusable {
* @version
* @VERSION@
* @since 3.0
+ * @deprecated use {@link com.vaadin.terminal.Receiver} instead. A "copy"
+ * here is kept for backwards compatibility.
*/
- public interface Receiver extends Serializable {
-
- /**
- * Invoked when a new upload arrives.
- *
- * @param filename
- * the desired filename of the upload, usually as specified
- * by the client.
- * @param MIMEType
- * the MIME type of the uploaded file.
- * @return Stream to which the uploaded file should be written.
- */
- public OutputStream receiveUpload(String filename, String MIMEType);
+ @Deprecated
+ public interface Receiver extends com.vaadin.terminal.Receiver {
}
/* Upload events */
@@ -332,19 +221,6 @@ public class Upload extends AbstractComponent implements Component.Focusable {
}
}
- private class UploadInterruptedException extends Exception {
- public UploadInterruptedException() {
- super("Upload interrupted by other thread");
- }
-
- }
-
- public static class UploadException extends Exception {
- public UploadException(Exception e) {
- super("Upload failed", e);
- }
- }
-
/**
* Upload.Received event is sent when the upload receives a file, regardless
* of whether the reception was successful or failed. If you wish to
@@ -356,7 +232,7 @@ public class Upload extends AbstractComponent implements Component.Focusable {
* @VERSION@
* @since 3.0
*/
- public class FinishedEvent extends Component.Event {
+ public static class FinishedEvent extends Component.Event {
/**
* Length of the received file.
@@ -439,7 +315,7 @@ public class Upload extends AbstractComponent implements Component.Focusable {
* @VERSION@
* @since 3.0
*/
- public class FailedEvent extends FinishedEvent {
+ public static class FailedEvent extends FinishedEvent {
private Exception reason = null;
@@ -484,7 +360,7 @@ public class Upload extends AbstractComponent implements Component.Focusable {
/**
* FailedEvent that indicates that an output stream could not be obtained.
*/
- public class NoOutputStreamEvent extends FailedEvent {
+ public static class NoOutputStreamEvent extends FailedEvent {
/**
*
@@ -502,7 +378,7 @@ public class Upload extends AbstractComponent implements Component.Focusable {
/**
* FailedEvent that indicates that an input stream could not be obtained.
*/
- public class NoInputStreamEvent extends FailedEvent {
+ public static class NoInputStreamEvent extends FailedEvent {
/**
*
@@ -526,7 +402,7 @@ public class Upload extends AbstractComponent implements Component.Focusable {
* @VERSION@
* @since 3.0
*/
- public class SucceededEvent extends FinishedEvent {
+ public static class SucceededEvent extends FinishedEvent {
/**
*
@@ -550,10 +426,14 @@ public class Upload extends AbstractComponent implements Component.Focusable {
* @VERSION@
* @since 5.0
*/
- public class StartedEvent extends Component.Event {
+ public static class StartedEvent extends Component.Event {
private final String filename;
private final String type;
+ /**
+ * Length of the received file.
+ */
+ private final long length;
/**
*
@@ -562,10 +442,12 @@ public class Upload extends AbstractComponent implements Component.Focusable {
* @param MIMEType
* @param length
*/
- public StartedEvent(Upload source, String filename, String MIMEType) {
+ public StartedEvent(Upload source, String filename, String MIMEType,
+ long contentLength) {
super(source);
this.filename = filename;
type = MIMEType;
+ length = contentLength;
}
/**
@@ -595,6 +477,13 @@ public class Upload extends AbstractComponent implements Component.Focusable {
return type;
}
+ /**
+ * @return the length of the file that is being uploaded
+ */
+ public long getContentLength() {
+ return length;
+ }
+
}
/**
@@ -786,19 +675,8 @@ public class Upload extends AbstractComponent implements Component.Focusable {
* @param length
*/
protected void fireStarted(String filename, String MIMEType) {
- fireEvent(new Upload.StartedEvent(this, filename, MIMEType));
- }
-
- /**
- * Emit upload finished event.
- *
- * @param filename
- * @param MIMEType
- * @param length
- */
- protected void fireUploadReceived(String filename, String MIMEType,
- long length) {
- fireEvent(new Upload.FinishedEvent(this, filename, MIMEType, length));
+ fireEvent(new Upload.StartedEvent(this, filename, MIMEType,
+ contentLength));
}
/**
@@ -914,15 +792,6 @@ public class Upload extends AbstractComponent implements Component.Focusable {
}
/**
- * Sets the size of the file currently being uploaded.
- *
- * @param contentLength
- */
- public void setUploadSize(long contentLength) {
- this.contentLength = contentLength;
- }
-
- /**
* Go into upload state. This is to prevent double uploading on same
* component.
*
@@ -959,6 +828,8 @@ public class Upload extends AbstractComponent implements Component.Focusable {
private void endUpload() {
isUploading = false;
contentLength = -1;
+ interrupted = false;
+ requestRepaint();
}
public boolean isUploading() {
@@ -1046,4 +917,54 @@ public class Upload extends AbstractComponent implements Component.Focusable {
this.buttonCaption = buttonCaption;
}
+ /*
+ * Handle to terminal via Upload monitors and controls the upload during it
+ * is being streamed.
+ */
+ private final ReceivingController controller = new ReceivingController() {
+ public boolean listenProgress() {
+ return (progressListeners != null || progressListener != null);
+ }
+
+ public void onProgress(ReceivingProgressedEvent event) {
+ fireUpdateProgress(event.getBytesReceived(),
+ event.getContentLength());
+ }
+
+ public void uploadStarted(ReceivingStartedEvent event) {
+ startUpload();
+ contentLength = event.getContentLength();
+ fireStarted(event.getFileName(), event.getMimeType());
+ }
+
+ public void uploadFinished(ReceivingEndedEvent event) {
+ fireUploadSuccess(event.getFileName(), event.getMimeType(),
+ event.getContentLength());
+ endUpload();
+ requestRepaint();
+ }
+
+ public void uploadFailed(ReceivingFailedEvent event) {
+ Exception exception = event.getException();
+ if (exception instanceof NoInputStreamException) {
+ fireNoInputStream(event.getFileName(), event.getMimeType(), 0);
+ } else if (exception instanceof NoOutputStreamException) {
+ fireNoOutputStream(event.getFileName(), event.getMimeType(), 0);
+ } else {
+ fireUploadInterrupted(event.getFileName(), event.getMimeType(),
+ 0, exception);
+ }
+ endUpload();
+ }
+
+ public boolean isInterrupted() {
+ return interrupted;
+ }
+ };
+
+ public final ReceivingController getReceivingController(
+ com.vaadin.terminal.Receiver receiver) {
+ return controller;
+ }
+
}