svn changeset:2381/svn branch:trunktags/6.7.0.beta1
@@ -74,7 +74,7 @@ public class ApplicationConnection implements FocusListener { | |||
return re.test(uri); | |||
}-*/; | |||
private native String getAppUri()/*-{ | |||
public native String getAppUri()/*-{ | |||
return $wnd.itmtk.appUri; | |||
}-*/; | |||
@@ -20,6 +20,7 @@ import com.itmill.toolkit.terminal.gwt.client.ui.IOrderedLayoutVertical; | |||
import com.itmill.toolkit.terminal.gwt.client.ui.IPanel; | |||
import com.itmill.toolkit.terminal.gwt.client.ui.IPasswordField; | |||
import com.itmill.toolkit.terminal.gwt.client.ui.IPopupCalendar; | |||
import com.itmill.toolkit.terminal.gwt.client.ui.IProgressIndicator; | |||
import com.itmill.toolkit.terminal.gwt.client.ui.IScrollTable; | |||
import com.itmill.toolkit.terminal.gwt.client.ui.ISelect; | |||
import com.itmill.toolkit.terminal.gwt.client.ui.ISlider; | |||
@@ -151,7 +152,11 @@ public class DefaultWidgetSet implements WidgetSet { | |||
} else if ("com.itmill.toolkit.terminal.gwt.client.ui.IFilterSelect" | |||
.equals(className)) { | |||
return new IFilterSelect(); | |||
} else if ("com.itmill.toolkit.terminal.gwt.client.ui.IProgressIndicator" | |||
.equals(className)) { | |||
return new IProgressIndicator(); | |||
} | |||
return new IUnknownComponent(); | |||
/* | |||
@@ -241,6 +246,8 @@ public class DefaultWidgetSet implements WidgetSet { | |||
return "com.itmill.toolkit.terminal.gwt.client.ui.ISplitPanelHorizontal"; | |||
} else if ("vsplitpanel".equals(tag)) { | |||
return "com.itmill.toolkit.terminal.gwt.client.ui.ISplitPanelVertical"; | |||
} else if ("progressindicator".equals(tag)) { | |||
return "com.itmill.toolkit.terminal.gwt.client.ui.IProgressIndicator"; | |||
} | |||
return "com.itmill.toolkit.terminal.gwt.client.ui.IUnknownComponent"; |
@@ -0,0 +1,54 @@ | |||
package com.itmill.toolkit.terminal.gwt.client.ui; | |||
import com.google.gwt.user.client.DOM; | |||
import com.google.gwt.user.client.Element; | |||
import com.google.gwt.user.client.Timer; | |||
import com.google.gwt.user.client.ui.Widget; | |||
import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection; | |||
import com.itmill.toolkit.terminal.gwt.client.Paintable; | |||
import com.itmill.toolkit.terminal.gwt.client.UIDL; | |||
public class IProgressIndicator extends Widget implements Paintable { | |||
private static final String CLASSNAME = "i-progressindicator"; | |||
Element wrapper = DOM.createDiv(); | |||
Element indicator = DOM.createDiv(); | |||
private ApplicationConnection client; | |||
private Poller poller; | |||
public IProgressIndicator() { | |||
setElement(wrapper); | |||
setStyleName(CLASSNAME); | |||
DOM.appendChild(wrapper, indicator); | |||
poller = new Poller(); | |||
} | |||
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { | |||
poller.cancel(); | |||
this.client = client; | |||
if(client.updateComponent(this, uidl, true)) | |||
return; | |||
boolean indeterminate = uidl.getBooleanAttribute("indeterminate"); | |||
if(indeterminate) { | |||
// TODO put up some image or something | |||
} else { | |||
try { | |||
float f = Float.parseFloat(uidl.getStringAttribute("state")); | |||
int size = Math.round(100*f); | |||
DOM.setStyleAttribute(indicator, "width", size + "%"); | |||
} catch (Exception e) { | |||
} | |||
} | |||
poller.scheduleRepeating(uidl.getIntAttribute("pollinginterval")); | |||
} | |||
class Poller extends Timer { | |||
public void run() { | |||
client.sendPendingVariableChanges(); | |||
} | |||
} | |||
} |
@@ -1,5 +1,6 @@ | |||
package com.itmill.toolkit.terminal.gwt.client.ui; | |||
import com.google.gwt.user.client.Timer; | |||
import com.google.gwt.user.client.ui.Button; | |||
import com.google.gwt.user.client.ui.ClickListener; | |||
import com.google.gwt.user.client.ui.FileUpload; | |||
@@ -8,58 +9,68 @@ import com.google.gwt.user.client.ui.FormHandler; | |||
import com.google.gwt.user.client.ui.FormPanel; | |||
import com.google.gwt.user.client.ui.FormSubmitCompleteEvent; | |||
import com.google.gwt.user.client.ui.FormSubmitEvent; | |||
import com.google.gwt.user.client.ui.Frame; | |||
import com.google.gwt.user.client.ui.Label; | |||
import com.google.gwt.user.client.ui.Panel; | |||
import com.google.gwt.user.client.ui.Widget; | |||
import com.itmill.toolkit.terminal.gwt.client.ApplicationConnection; | |||
import com.itmill.toolkit.terminal.gwt.client.Paintable; | |||
import com.itmill.toolkit.terminal.gwt.client.UIDL; | |||
public class IUpload extends FormPanel implements Paintable, ClickListener { | |||
public class IUpload extends FormPanel implements Paintable, ClickListener, | |||
FormHandler { | |||
/** | |||
* FileUpload component that opens native OS dialog to select file. | |||
*/ | |||
FileUpload fu = new FileUpload(); | |||
Panel panel = new FlowPanel(); | |||
ApplicationConnection client; | |||
private String paintableId; | |||
/** | |||
* Button that initiates uploading | |||
*/ | |||
private Button b; | |||
/** | |||
* When expecting big files, programmer may initiate some UI changes when | |||
* uploading the file starts. Bit after submitting file we'll visit the | |||
* server to check possible changes. | |||
*/ | |||
private Timer t; | |||
/** | |||
* some browsers tries to send form twice if submit is called in button | |||
* click handler, some don't submit at all without it, so we need to track | |||
* if form is already being submitted | |||
*/ | |||
private boolean submitted = false; | |||
public IUpload() { | |||
super(); | |||
setEncoding(FormPanel.ENCODING_MULTIPART); | |||
setMethod(FormPanel.METHOD_POST); | |||
setMethod(FormPanel.METHOD_POST); | |||
setWidget(panel); | |||
panel.add(new Label("UPLOAD component incomplete")); | |||
panel.add(fu); | |||
b = new Button("Upload"); | |||
// TODO | |||
b = new Button("Click to Upload"); | |||
b.addClickListener(this); | |||
panel.add(b); | |||
addFormHandler(new FormHandler() { | |||
public void onSubmitComplete(FormSubmitCompleteEvent event) { | |||
if(client != null) { | |||
// request update | |||
client.sendPendingVariableChanges(); | |||
} | |||
} | |||
public void onSubmit(FormSubmitEvent event) { | |||
if (fu.getFilename().length() == 0) { | |||
event.setCancelled(true); | |||
} | |||
} | |||
}); | |||
addFormHandler(this); | |||
} | |||
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { | |||
this.client = client; | |||
this.paintableId = uidl.getId(); | |||
if(uidl.hasAttribute("caption")) | |||
setAction(client.getAppUri()); | |||
if (uidl.hasAttribute("caption")) | |||
b.setText(uidl.getStringAttribute("caption")); | |||
fu.setName(paintableId + "_file"); | |||
} | |||
@@ -67,5 +78,49 @@ public class IUpload extends FormPanel implements Paintable, ClickListener { | |||
this.submit(); | |||
} | |||
public void onSubmit(FormSubmitEvent event) { | |||
if (fu.getFilename().length() == 0 || submitted) { | |||
event.setCancelled(true); | |||
ApplicationConnection.getConsole().log( | |||
"Submit cancelled (no file or already submitted)"); | |||
return; | |||
} | |||
submitted = true; | |||
ApplicationConnection.getConsole().log("Submitted form"); | |||
disableUpload(); | |||
/* | |||
* visit server after upload to see possible changes from UploadStarted | |||
* event | |||
*/ | |||
t = new Timer() { | |||
public void run() { | |||
client.sendPendingVariableChanges(); | |||
} | |||
}; | |||
t.schedule(800); | |||
} | |||
protected void disableUpload() { | |||
b.setEnabled(false); | |||
fu.setVisible(false); | |||
} | |||
protected void enableUploaod() { | |||
b.setEnabled(true); | |||
fu.setVisible(true); | |||
} | |||
public void onSubmitComplete(FormSubmitCompleteEvent event) { | |||
if (client != null) { | |||
if (t != null) | |||
t.cancel(); | |||
ApplicationConnection.getConsole().log("Submit complete"); | |||
client.sendPendingVariableChanges(); | |||
} | |||
submitted = false; | |||
enableUploaod(); | |||
} | |||
} |
@@ -0,0 +1,8 @@ | |||
.i-progressindicator { | |||
background: red; | |||
height: 20px; | |||
} | |||
.i-progressindicator div { | |||
background: green; | |||
height:100%; | |||
} |
@@ -12,4 +12,5 @@ | |||
@import "tree/tree.css"; | |||
@import "splitpanel/splitpanel.css"; | |||
@import "select/filterselect.css"; | |||
@import "progressindicator/progressindicator.css"; | |||
@@ -52,6 +52,7 @@ import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
import javax.servlet.http.HttpSession; | |||
import org.apache.commons.fileupload.servlet.ServletFileUpload; | |||
import org.xml.sax.SAXException; | |||
import com.itmill.toolkit.Application; | |||
@@ -309,6 +310,13 @@ public class ApplicationServlet extends HttpServlet { | |||
Application application = null; | |||
try { | |||
// handle file upload if multipart request | |||
if(ServletFileUpload.isMultipartContent(request)) { | |||
application = getApplication(request); | |||
getApplicationManager(application).handleFileUpload(request, response); | |||
return; | |||
} | |||
// Update browser details | |||
WebBrowser browser = WebApplicationContext.getApplicationContext( | |||
request.getSession()).getBrowser(); | |||
@@ -330,11 +338,7 @@ public class ApplicationServlet extends HttpServlet { | |||
// Is this a download request from application | |||
DownloadStream download = null; | |||
// The rest of the process is synchronized with the application | |||
// in order to guarantee that no parallel variable handling is | |||
// made | |||
synchronized (application) { | |||
// Handles AJAX UIDL requests | |||
String resourceId = request.getPathInfo(); | |||
if (resourceId != null && resourceId.startsWith(AJAX_UIDL_URI)) { | |||
@@ -385,7 +389,6 @@ public class ApplicationServlet extends HttpServlet { | |||
writeAjaxPage(request, response, window, themeName); | |||
} | |||
} | |||
// For normal requests, transform the window | |||
if (download != null) |
@@ -59,16 +59,25 @@ import javax.servlet.ServletOutputStream; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
import org.apache.commons.fileupload.FileItemIterator; | |||
import org.apache.commons.fileupload.FileItemStream; | |||
import org.apache.commons.fileupload.FileUploadException; | |||
import org.apache.commons.fileupload.ProgressListener; | |||
import org.apache.commons.fileupload.servlet.ServletFileUpload; | |||
import org.apache.commons.fileupload.util.Streams; | |||
import com.itmill.toolkit.Application; | |||
import com.itmill.toolkit.Application.WindowAttachEvent; | |||
import com.itmill.toolkit.Application.WindowDetachEvent; | |||
import com.itmill.toolkit.terminal.DownloadStream; | |||
import com.itmill.toolkit.terminal.Paintable; | |||
import com.itmill.toolkit.terminal.URIHandler; | |||
import com.itmill.toolkit.terminal.UploadStream; | |||
import com.itmill.toolkit.terminal.VariableOwner; | |||
import com.itmill.toolkit.terminal.Paintable.RepaintRequestEvent; | |||
import com.itmill.toolkit.ui.Component; | |||
import com.itmill.toolkit.ui.FrameWindow; | |||
import com.itmill.toolkit.ui.Upload; | |||
import com.itmill.toolkit.ui.Window; | |||
/** | |||
@@ -135,10 +144,93 @@ public class CommunicationManager implements Paintable.RepaintRequestListener, | |||
application.removeListener((Application.WindowDetachListener) this); | |||
} | |||
/** | |||
* Handles file upload request submitted via Upload component. | |||
* | |||
* @param request | |||
* @param response | |||
* @throws IOException | |||
*/ | |||
public void handleFileUpload(HttpServletRequest request, | |||
HttpServletResponse response) throws IOException { | |||
// Create a new file upload handler | |||
ServletFileUpload upload = new ServletFileUpload(); | |||
UploadProgressListener pl = new UploadProgressListener(); | |||
upload.setProgressListener(pl); | |||
// Parse the request | |||
FileItemIterator iter; | |||
try { | |||
iter = upload.getItemIterator(request); | |||
/* ATM this loop is run only once as we are uploading one file per | |||
* request. | |||
*/ | |||
while (iter.hasNext()) { | |||
FileItemStream item = iter.next(); | |||
String name = item.getFieldName(); | |||
final String filename = item.getName(); | |||
final String mimeType = item.getContentType(); | |||
final InputStream stream = item.openStream(); | |||
if (item.isFormField()) { | |||
// ignored, upload requests contian only files | |||
} else { | |||
String pid = name.split("_")[0]; | |||
Upload uploadComponent = (Upload) idPaintableMap.get(pid); | |||
if (uploadComponent == null) { | |||
throw new FileUploadException( | |||
"Upload component not found"); | |||
} | |||
synchronized (application) { | |||
// put upload component into receiving state | |||
uploadComponent.startUpload(); | |||
} | |||
UploadStream upstream = new UploadStream() { | |||
public String getContentName() { | |||
return filename; | |||
} | |||
public String getContentType() { | |||
return mimeType; | |||
} | |||
public InputStream getStream() { | |||
return stream; | |||
} | |||
public String getStreamName() { | |||
return "stream"; | |||
} | |||
}; | |||
// tell UploadProgressListener which component is receiving file | |||
pl.setUpload(uploadComponent); | |||
uploadComponent.receiveUpload(upstream); | |||
} | |||
} | |||
} catch (FileUploadException e) { | |||
e.printStackTrace(); | |||
} | |||
// Send short response to acknowledge client that request was done | |||
response.setContentType("text/html"); | |||
OutputStream out = response.getOutputStream(); | |||
PrintWriter outWriter = new PrintWriter(new BufferedWriter( | |||
new OutputStreamWriter(out, "UTF-8"))); | |||
outWriter.print("<html><body>download handled</body></html>"); | |||
outWriter.flush(); | |||
out.close(); | |||
} | |||
public void handleUidlRequest(HttpServletRequest request, | |||
HttpServletResponse response) throws IOException { | |||
// repaint requested or sesssion has timed out and new one is created | |||
// repaint requested or session has timed out and new one is created | |||
boolean repaintAll = (request.getParameter(GET_PARAM_REPAINT_ALL) != null) | |||
|| request.getSession().isNew(); | |||
@@ -146,10 +238,6 @@ public class CommunicationManager implements Paintable.RepaintRequestListener, | |||
PrintWriter outWriter = new PrintWriter(new BufferedWriter( | |||
new OutputStreamWriter(out, "UTF-8"))); | |||
// TODO Move dirt elsewhere | |||
outWriter.print(")/*{"); // some dirt to prevent cross site scripting | |||
// vulnerabilities | |||
try { | |||
// Is this a download request from application | |||
@@ -193,6 +281,9 @@ public class CommunicationManager implements Paintable.RepaintRequestListener, | |||
// Sets the response type | |||
response.setContentType("application/json; charset=UTF-8"); | |||
// some dirt to prevent cross site scripting | |||
outWriter.print(")/*{"); | |||
outWriter.print("\"changes\":["); | |||
paintTarget = new JsonPaintTarget(this, outWriter); | |||
@@ -206,7 +297,7 @@ public class CommunicationManager implements Paintable.RepaintRequestListener, | |||
// Reset sent locales | |||
locales = null; | |||
requireLocale(application.getLocale().toString()); | |||
} else | |||
paintables = getDirtyComponents(); | |||
if (paintables != null) { | |||
@@ -267,17 +358,14 @@ public class CommunicationManager implements Paintable.RepaintRequestListener, | |||
w.setTerminal(application.getMainWindow() | |||
.getTerminal()); | |||
} | |||
/* This does not seem to happen in tk5, but remember this case: | |||
else if (p instanceof Component) { | |||
if (((Component) p).getParent() == null | |||
|| ((Component) p).getApplication() == null) { | |||
// Component requested repaint, but is no | |||
// longer attached: skip | |||
paintablePainted(p); | |||
continue; | |||
} | |||
} | |||
*/ | |||
/* | |||
* This does not seem to happen in tk5, but remember | |||
* this case: else if (p instanceof Component) { if | |||
* (((Component) p).getParent() == null || | |||
* ((Component) p).getApplication() == null) { // | |||
* Component requested repaint, but is no // longer | |||
* attached: skip paintablePainted(p); continue; } } | |||
*/ | |||
paintTarget.startTag("change"); | |||
paintTarget.addAttribute("format", "uidl"); | |||
String pid = getPaintableId(p); | |||
@@ -318,8 +406,9 @@ public class CommunicationManager implements Paintable.RepaintRequestListener, | |||
if (request.getParameter("theme") != null) { | |||
themeName = request.getParameter("theme"); | |||
} | |||
if (themeName == null) themeName = "default"; | |||
if (themeName == null) | |||
themeName = "default"; | |||
// TODO We should only precache the layouts that are not | |||
// cached already | |||
int resourceIndex = 0; | |||
@@ -413,14 +502,15 @@ public class CommunicationManager implements Paintable.RepaintRequestListener, | |||
Map m; | |||
if (i + 2 >= ca.length | |||
|| !vid[0].equals(ca[i + 2].split("_")[0])) { | |||
if(ca.length > i + 1) { | |||
m = new SingleValueMap(vid[1], convertVariableValue( | |||
vid[2].charAt(0), ca[++i])); | |||
if (ca.length > i + 1) { | |||
m = new SingleValueMap(vid[1], | |||
convertVariableValue(vid[2].charAt(0), | |||
ca[++i])); | |||
} else { | |||
m = new SingleValueMap(vid[1], convertVariableValue(vid[2].charAt(0), "")); | |||
m = new SingleValueMap(vid[1], | |||
convertVariableValue(vid[2].charAt(0), "")); | |||
} | |||
} | |||
else { | |||
} else { | |||
m = new HashMap(); | |||
m.put(vid[1], convertVariableValue(vid[2].charAt(0), | |||
ca[++i])); | |||
@@ -465,8 +555,9 @@ public class CommunicationManager implements Paintable.RepaintRequestListener, | |||
val = Boolean.valueOf(strValue); | |||
break; | |||
} | |||
System.out.println("result: " + val + " of type " + (val == null ? "-" : val.getClass().toString())); | |||
System.out.println("result: " + val + " of type " | |||
+ (val == null ? "-" : val.getClass().toString())); | |||
return val; | |||
} | |||
@@ -605,7 +696,7 @@ public class CommunicationManager implements Paintable.RepaintRequestListener, | |||
// Find the window where the request is handled | |||
String path = request.getPathInfo(); | |||
// Remove UIDL from the path | |||
path = path.substring("/UIDL".length()); | |||
@@ -779,8 +870,7 @@ public class CommunicationManager implements Paintable.RepaintRequestListener, | |||
new OutputStreamWriter(out, "UTF-8"))); | |||
outWriter.print(")/*{"); | |||
outWriter.print("\"redirect\":{"); | |||
outWriter.write("\"url\":\"" + logoutUrl | |||
+ "\"}"); | |||
outWriter.write("\"url\":\"" + logoutUrl + "\"}"); | |||
outWriter.flush(); | |||
outWriter.close(); | |||
out.flush(); | |||
@@ -1067,4 +1157,26 @@ public class CommunicationManager implements Paintable.RepaintRequestListener, | |||
else | |||
return new Locale(temp[0], temp[1], temp[2]); | |||
} | |||
/* | |||
* Upload progress listener notifies upload component once when Jakarta | |||
* FileUpload can determine content length. Used to detect files total size, | |||
* uploads progress can be tracked inside upload. | |||
*/ | |||
private class UploadProgressListener implements ProgressListener { | |||
Upload uploadComponent; | |||
boolean updated = false; | |||
public void setUpload(Upload u) { | |||
uploadComponent = u; | |||
} | |||
public void update(long bytesRead, long contentLength, int items) { | |||
if (!updated && uploadComponent != null) { | |||
uploadComponent.setUploadSize(contentLength); | |||
updated = true; | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,207 @@ | |||
package com.itmill.toolkit.tests; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import com.itmill.toolkit.terminal.StreamResource; | |||
import com.itmill.toolkit.ui.Button; | |||
import com.itmill.toolkit.ui.CustomComponent; | |||
import com.itmill.toolkit.ui.Label; | |||
import com.itmill.toolkit.ui.Layout; | |||
import com.itmill.toolkit.ui.Link; | |||
import com.itmill.toolkit.ui.OrderedLayout; | |||
import com.itmill.toolkit.ui.Panel; | |||
import com.itmill.toolkit.ui.ProgressIndicator; | |||
import com.itmill.toolkit.ui.Upload; | |||
import com.itmill.toolkit.ui.Button.ClickEvent; | |||
import com.itmill.toolkit.ui.Upload.FailedEvent; | |||
import com.itmill.toolkit.ui.Upload.FailedListener; | |||
import com.itmill.toolkit.ui.Upload.FinishedEvent; | |||
import com.itmill.toolkit.ui.Upload.FinishedListener; | |||
import com.itmill.toolkit.ui.Upload.StartedEvent; | |||
import com.itmill.toolkit.ui.Upload.StartedListener; | |||
import com.itmill.toolkit.ui.Upload.SucceededEvent; | |||
import com.itmill.toolkit.ui.Upload.SucceededListener; | |||
public class TestForUpload extends CustomComponent implements | |||
Upload.FinishedListener, FailedListener,SucceededListener, Upload.ProgressListener, StartedListener { | |||
Layout main = new OrderedLayout(); | |||
Buffer buffer = new Buffer(); | |||
Panel status = new Panel("Uploaded file:"); | |||
private Upload up; | |||
private Label l; | |||
private ProgressIndicator pi = new ProgressIndicator(); | |||
public TestForUpload() { | |||
setCompositionRoot(main); | |||
main.addComponent( new Label( | |||
"This is a simple test for upload application. " | |||
+ "Upload should work with big files and concurrent " | |||
+ "requests should not be blocked. Button 'b' reads " | |||
+ "current state into label below it. TODO make " | |||
+ "streaming example/test where upload contents " | |||
+ "is read but not saved and memory consumption is " | |||
+ "verified low. TODO make test where contents is " | |||
+ "written to disk and verifiy low memory consumption.")); | |||
main.addComponent(new Label("Clicking on button b updates information about upload components status.")); | |||
up = new Upload("Upload", buffer); | |||
up.setImmediate(true); | |||
up.addListener((FinishedListener)this); | |||
up.addListener((FailedListener) this); | |||
up.addListener((SucceededListener) this); | |||
up.addListener((StartedListener) this); | |||
up.setProgressListener(this); | |||
Button b = new Button("b", this, "readState"); | |||
main.addComponent(b); | |||
main.addComponent(up); | |||
l = new Label("Idle"); | |||
main.addComponent(l); | |||
pi.setVisible(false); | |||
pi.setPollingInterval(1000); | |||
main.addComponent(pi); | |||
status.setVisible(false); | |||
main.addComponent(status); | |||
Button restart = new Button("R"); | |||
restart.addListener(new Button.ClickListener() { | |||
public void buttonClick(ClickEvent event) { | |||
getApplication().close(); | |||
} | |||
}); | |||
main.addComponent(restart); | |||
} | |||
public void readState() { | |||
StringBuffer sb = new StringBuffer(); | |||
if (up.isUploading()) { | |||
sb.append("Uploading..."); | |||
sb.append(up.getBytesRead()); | |||
sb.append("/"); | |||
sb.append(up.getUploadSize()); | |||
sb.append(" "); | |||
sb.append(Math.round(100 * up.getBytesRead() | |||
/ (double) up.getUploadSize())); | |||
sb.append("%"); | |||
} else { | |||
sb.append("Idle"); | |||
} | |||
l.setValue(sb.toString()); | |||
} | |||
public void uploadFinished(FinishedEvent event) { | |||
status.removeAllComponents(); | |||
if (buffer.getStream() == null) | |||
status.addComponent(new Label( | |||
"Upload finished, but output buffer is null!!")); | |||
else { | |||
status | |||
.addComponent(new Label("<b>Name:</b> " | |||
+ event.getFilename(), Label.CONTENT_XHTML)); | |||
status.addComponent(new Label("<b>Mimetype:</b> " | |||
+ event.getMIMEType(), Label.CONTENT_XHTML)); | |||
status.addComponent(new Label("<b>Size:</b> " + event.getLength() | |||
+ " bytes.", Label.CONTENT_XHTML)); | |||
status.addComponent(new Link("Download " + buffer.getFileName(), | |||
new StreamResource(buffer, buffer.getFileName(), | |||
getApplication()))); | |||
status.setVisible(true); | |||
} | |||
} | |||
public class Buffer implements StreamResource.StreamSource, Upload.Receiver { | |||
ByteArrayOutputStream outputBuffer = null; | |||
String mimeType; | |||
String fileName; | |||
public Buffer() { | |||
} | |||
public InputStream getStream() { | |||
if (outputBuffer == null) | |||
return null; | |||
return new ByteArrayInputStream(outputBuffer.toByteArray()); | |||
} | |||
/** | |||
* @see com.itmill.toolkit.ui.Upload.Receiver#receiveUpload(String, | |||
* String) | |||
*/ | |||
public OutputStream receiveUpload(String filename, String MIMEType) { | |||
fileName = filename; | |||
mimeType = MIMEType; | |||
outputBuffer = new ByteArrayOutputStream(); | |||
return outputBuffer; | |||
} | |||
/** | |||
* Returns the fileName. | |||
* | |||
* @return String | |||
*/ | |||
public String getFileName() { | |||
return fileName; | |||
} | |||
/** | |||
* Returns the mimeType. | |||
* | |||
* @return String | |||
*/ | |||
public String getMimeType() { | |||
return mimeType; | |||
} | |||
} | |||
public void uploadFailed(FailedEvent event) { | |||
System.out.println(event); | |||
System.out.println(event.getSource()); | |||
} | |||
public void uploadSucceeded(SucceededEvent event) { | |||
pi.setVisible(false); | |||
l.setValue("Finished upload, idle"); | |||
System.out.println(event); | |||
} | |||
public void updateProgress(long readBytes, long contentLenght) { | |||
pi.setValue(new Float(readBytes/(float)contentLenght)); | |||
} | |||
public void uploadStarted(StartedEvent event) { | |||
pi.setVisible(true); | |||
l.setValue("Started uploading file " + event.getFilename()); | |||
} | |||
} |
@@ -45,7 +45,7 @@ import com.itmill.toolkit.terminal.PaintTarget; | |||
* @author IT Mill Ltd. | |||
* @version | |||
* @VERSION@ | |||
* @since 3.1 | |||
* @since 4 | |||
*/ | |||
public class ProgressIndicator extends AbstractField implements Property, | |||
Property.Viewer, Property.ValueChangeListener { |
@@ -71,6 +71,18 @@ public class Upload extends AbstractComponent implements Component.Focusable { | |||
private long focusableId = -1; | |||
private boolean isUploading; | |||
private long contentLength = -1; | |||
private int totalBytes; | |||
/** | |||
* ProgressListener to which information about progress is sent during | |||
* upload | |||
*/ | |||
private ProgressListener progressListener; | |||
/* TODO: Add a default constructor, receive to temp file. */ | |||
/** | |||
@@ -95,25 +107,16 @@ public class Upload extends AbstractComponent implements Component.Focusable { | |||
return "upload"; | |||
} | |||
/** | |||
* Invoked when the value of a variable has changed. | |||
* | |||
* @see com.itmill.toolkit.ui.AbstractComponent#changeVariables(java.lang.Object, | |||
* java.util.Map) | |||
*/ | |||
public void changeVariables(Object source, Map variables) { | |||
// Checks the variable name | |||
if (!variables.containsKey("stream")) | |||
return; | |||
// Gets the upload stream | |||
UploadStream upload = (UploadStream) variables.get("stream"); | |||
public void receiveUpload(UploadStream upload) { | |||
if (!isUploading) | |||
throw new IllegalStateException("uploading not started"); | |||
// Gets file properties | |||
String filename = upload.getContentName(); | |||
String type = upload.getContentType(); | |||
fireStarted(filename, type); | |||
// Gets the output target stream | |||
OutputStream out = receiver.receiveUpload(filename, type); | |||
if (out == null) | |||
@@ -121,32 +124,53 @@ public class Upload extends AbstractComponent implements Component.Focusable { | |||
"Error getting outputstream from upload receiver"); | |||
InputStream in = upload.getStream(); | |||
if (null == in) { | |||
// No file, for instance non-existent filename in html upload | |||
fireUploadInterrupted(filename, type, 0); | |||
endUpload(); | |||
return; | |||
} | |||
byte buffer[] = new byte[BUFFER_SIZE]; | |||
int bytesRead = 0; | |||
long totalBytes = 0; | |||
totalBytes = 0; | |||
try { | |||
while ((bytesRead = in.read(buffer)) > 0) { | |||
out.write(buffer, 0, bytesRead); | |||
totalBytes += bytesRead; | |||
if (progressListener != null && contentLength > 0) { | |||
// update progress if listener set and contentLength | |||
// received | |||
progressListener.updateProgress(totalBytes, contentLength); | |||
} | |||
} | |||
// Download successfull | |||
// upload successful | |||
out.close(); | |||
fireUploadSuccess(filename, type, totalBytes); | |||
endUpload(); | |||
requestRepaint(); | |||
} catch (IOException e) { | |||
// Download interrupted | |||
fireUploadInterrupted(filename, type, totalBytes); | |||
endUpload(); | |||
} | |||
} | |||
/** | |||
* Invoked when the value of a variable has changed. | |||
* | |||
* @see com.itmill.toolkit.ui.AbstractComponent#changeVariables(java.lang.Object, | |||
* java.util.Map) | |||
*/ | |||
public void changeVariables(Object source, Map variables) { | |||
// NOP | |||
} | |||
/** | |||
* Paints the content of this component. | |||
* | |||
@@ -164,6 +188,10 @@ public class Upload extends AbstractComponent implements Component.Focusable { | |||
if (this.tabIndex >= 0) | |||
target.addAttribute("tabindex", this.tabIndex); | |||
target.addAttribute("state", isUploading); | |||
target.addVariable(this, "fake", true); | |||
target.addUploadStreamVariable(this, "stream"); | |||
} | |||
@@ -198,12 +226,16 @@ public class Upload extends AbstractComponent implements Component.Focusable { | |||
private static final Method UPLOAD_SUCCEEDED_METHOD; | |||
private static final Method UPLOAD_STARTED_METHOD; | |||
static { | |||
try { | |||
UPLOAD_FINISHED_METHOD = FinishedListener.class.getDeclaredMethod( | |||
"uploadFinished", new Class[] { FinishedEvent.class }); | |||
UPLOAD_FAILED_METHOD = FailedListener.class.getDeclaredMethod( | |||
"uploadFailed", new Class[] { FailedEvent.class }); | |||
UPLOAD_STARTED_METHOD = StartedListener.class.getDeclaredMethod( | |||
"uploadStarted", new Class[] { StartedEvent.class }); | |||
UPLOAD_SUCCEEDED_METHOD = SucceededListener.class | |||
.getDeclaredMethod("uploadSucceeded", | |||
new Class[] { SucceededEvent.class }); | |||
@@ -282,21 +314,21 @@ public class Upload extends AbstractComponent implements Component.Focusable { | |||
} | |||
/** | |||
* Gets the length of the file. | |||
* Gets the MIME Type of the file. | |||
* | |||
* @return the length. | |||
* @return the MIME type. | |||
*/ | |||
public long getLength() { | |||
return length; | |||
public String getMIMEType() { | |||
return type; | |||
} | |||
/** | |||
* Gets the MIME Type of the file. | |||
* Gets the length of the file. | |||
* | |||
* @return the MIME type. | |||
* @return the length. | |||
*/ | |||
public String getMIMEType() { | |||
return type; | |||
public long getLength() { | |||
return length; | |||
} | |||
} | |||
@@ -360,6 +392,85 @@ public class Upload extends AbstractComponent implements Component.Focusable { | |||
} | |||
/** | |||
* Upload.Started event is sent when the upload is started to received. | |||
* | |||
* @author IT Mill Ltd. | |||
* @version | |||
* @VERSION@ | |||
* @since 5.0 | |||
*/ | |||
public class StartedEvent extends Component.Event { | |||
/** | |||
* Serial generated by eclipse. | |||
*/ | |||
private static final long serialVersionUID = -3984393770487403525L; | |||
private String filename; | |||
private String type; | |||
/** | |||
* | |||
* @param source | |||
* @param filename | |||
* @param MIMEType | |||
* @param length | |||
*/ | |||
public StartedEvent(Upload source, String filename, String MIMEType) { | |||
super(source); | |||
this.filename = filename; | |||
this.type = MIMEType; | |||
} | |||
/** | |||
* Uploads where the event occurred. | |||
* | |||
* @return the Source of the event. | |||
*/ | |||
public Upload getUpload() { | |||
return (Upload) getSource(); | |||
} | |||
/** | |||
* Gets the file name. | |||
* | |||
* @return the filename. | |||
*/ | |||
public String getFilename() { | |||
return filename; | |||
} | |||
/** | |||
* Gets the MIME Type of the file. | |||
* | |||
* @return the MIME type. | |||
*/ | |||
public String getMIMEType() { | |||
return type; | |||
} | |||
} | |||
/** | |||
* Receives the events when the upload starts. | |||
* | |||
* @author IT Mill Ltd. | |||
* @version | |||
* @VERSION@ | |||
* @since 5.0 | |||
*/ | |||
public interface StartedListener { | |||
/** | |||
* Upload has started. | |||
* | |||
* @param event | |||
* the Upload started event. | |||
*/ | |||
public void uploadStarted(StartedEvent event); | |||
} | |||
/** | |||
* Receives the events when the uploads are ready. | |||
* | |||
@@ -417,6 +528,26 @@ public class Upload extends AbstractComponent implements Component.Focusable { | |||
public void uploadSucceeded(SucceededEvent event); | |||
} | |||
/** | |||
* Adds the upload started event listener. | |||
* | |||
* @param listener | |||
* the Listener to be added. | |||
*/ | |||
public void addListener(StartedListener listener) { | |||
addListener(StartedEvent.class, listener, UPLOAD_STARTED_METHOD); | |||
} | |||
/** | |||
* Removes the upload started event listener. | |||
* | |||
* @param listener | |||
* the Listener to be removed. | |||
*/ | |||
public void removeListener(StartedListener listener) { | |||
removeListener(FinishedEvent.class, listener, UPLOAD_STARTED_METHOD); | |||
} | |||
/** | |||
* Adds the upload received event listener. | |||
* | |||
@@ -477,6 +608,17 @@ public class Upload extends AbstractComponent implements Component.Focusable { | |||
removeListener(SucceededEvent.class, listener, UPLOAD_SUCCEEDED_METHOD); | |||
} | |||
/** | |||
* Emit upload received event. | |||
* | |||
* @param filename | |||
* @param MIMEType | |||
* @param length | |||
*/ | |||
protected void fireStarted(String filename, String MIMEType) { | |||
fireEvent(new Upload.StartedEvent(this, filename, MIMEType)); | |||
} | |||
/** | |||
* Emit upload received event. | |||
* | |||
@@ -572,4 +714,77 @@ public class Upload extends AbstractComponent implements Component.Focusable { | |||
return this.focusableId; | |||
} | |||
/** | |||
* 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. | |||
*/ | |||
public void startUpload() { | |||
if (isUploading) | |||
throw new IllegalStateException("uploading already started"); | |||
isUploading = true; | |||
} | |||
/** | |||
* Go into state where new uploading can begin. | |||
*/ | |||
public void endUpload() { | |||
isUploading = false; | |||
contentLength = -1; | |||
} | |||
public boolean isUploading() { | |||
return isUploading; | |||
} | |||
/** | |||
* Gets read bytes of the file currently being uploaded. | |||
* | |||
* @return bytes | |||
*/ | |||
public long getBytesRead() { | |||
return totalBytes; | |||
} | |||
/** | |||
* Returns size of file currently being uploaded. Value sane only during | |||
* upload. | |||
* | |||
* @return size in bytes | |||
*/ | |||
public long getUploadSize() { | |||
return contentLength; | |||
} | |||
/** | |||
* Sets listener to track progress of upload. | |||
* | |||
* @param progressListener | |||
*/ | |||
public void setProgressListener(ProgressListener progressListener) { | |||
this.progressListener = progressListener; | |||
} | |||
/** | |||
* ProgressListener receives events to track progress of upload. | |||
*/ | |||
public interface ProgressListener { | |||
/** | |||
* Updates progress to listener | |||
* | |||
* @param readBytes | |||
* bytes transferred | |||
* @param contentLength | |||
* total size of file currently being uploaded, -1 if unknown | |||
*/ | |||
public void updateProgress(long readBytes, long contentLength); | |||
} | |||
} |