package com.smartgwt.client.widgets; import com.google.gwt.core.client.JavaScriptObject; import com.smartgwt.client.data.DataSource; import com.smartgwt.client.widgets.form.fields.FormItem; /** * Handles the upload, validation, review and saving of a dataset expressed in CSV or other upload formats. * * <p><em><b>Requires Smart GWT Enterprise, Power or Eval.</b></em> * * <p>By default, a <code>BatchUploader</code> consists of a single {@link com.smartgwt.client.widgets.form.fields.FileItem} * form field. This form field will upload a file using the special "batchUpload" built-in DataSource. * The uploaded file data will be parsed and validated using the {@link #getUploadDataSource() uploadDataSource}, * then streamed back to the browser, along with any errors, for display in a {@link com.smartgwt.client.widgets.grid.ListGrid}. * * <p>The user can then correct any errors and submit the final dataset, which will be added to * the DataSource via a series of "add" {@link com.smartgwt.client.data.DSRequest}s, all submitted * as a single HTTP request via request queuing. * * <p>Additional form fields can be added to the form that uploads the data file via {@link #setUploadFormItems(FormItem...) uploadFormItems}. * Values entered into these fields are not included in the "add" DSRequests used to store the * uploaded records. Instead, they are stored as HttpSession attributes with the names corresponding * to the names of the specified uploadFormItems (optionally with a prefix applied, in case this * is necessary to avoid name collisions in the session). This allows any custom logic for the * "add" operation to access these additional fields via httpSession.getAttribute(). * * <p>Because all records are saved in a single HTTP request, a similar strategy of storing data * as servletRequest or session attributes allows reuse of objects required to perform the "add" * operations (such as field values common to all added records, or a SQL connection or transaction manager). * * <p>If uploadFieldName is set on any of the uploadDataSource's fields, the <code>BatchUploader</code> * will use that name to map the uploaded file's content. * * <p>A couple of server-side techniques are interesting in conjunction with the <code>BatchUploader</code>. * One is to set the DataSource.serverConstructor property to point at your own class that inherits * from <code>com.isomorphic.datasource.DataSource</code>. The most interesting reason for doing * this is to override the validate method and provide complete custom validation - for example, * checking relations to other tables. * * <p>Another technique is to handle the initial SmartClient call in your own servlet, by setting * the {@link #getDataURL() dataURL} property. You then handle the add requests with a combination * of your own code and SmartClient server API calls. This is a good way to add special pre- and * post-processing to the normal server-side flow. * * <p>The BatchUploader sample, provided as one of the databinding examples, demonstrates both * of these techniques. We use a special subclass of DataSource and override the validate method * as described above; we also set dataURL to point at a JSP that wraps the queue of SmartClient * requests inside a single database transaction (a servlet would be more usual, but we use a * JSP for ease of modification) * * <p><b>Note:</b> The special "batchUpload" DataSource, which should reside in the <code>shared/ds</code> * folder of your application's webroot (see Deploying SmartClient), is not part of your application's * data flow, and it has nothing to do with the uploadDataSource you use to actually persist * the validated and error-corrected data; it is simply the means for uploading the raw data * in the first place. Normally, you should simply ignore its presence and treat it as an internal * detail of the SmartClient framework. * * <p>However, there are circumstances in which you may wish to change it to achieve specific aims. * For example, you may wish to override the Java class it invokes, in order to insert your own * security or other validation logic into the initial upload flow. This is entirely in keeping * with the design, but we regard it as an out-of-the-ordinary use-case; normal usage is simply * to ignore the presence of the batchUpload DataSource. */ public class BatchUploader extends Canvas { public static BatchUploader getOrCreateRef(JavaScriptObject jsObj) { if (jsObj == null) return null; BaseWidget obj = BaseWidget.getRef(jsObj); if (obj != null) { return (BatchUploader) obj; } else { // Instantiate `BatchUploader' in the com.smartgwtee package to prevent possible // `ClassCastException's until officially removed. return new com.smartgwtee.client.widgets.BatchUploader(jsObj); } } public BatchUploader() { setDsURL("/ds/batchUpload.ds.xml"); } public BatchUploader(JavaScriptObject jsObj) { super(jsObj); } public native JavaScriptObject create() /*-{ if (!$wnd.isc.BatchUploader) { var errorMessage = "Attempt to create BatchUploader. This class requires Smart GWT Enterprise or Eval."; @com.smartgwt.client.util.SC::logWarn(Ljava/lang/String;)(errorMessage); throw @java.lang.UnsupportedOperationException::new(Ljava/lang/String;)(errorMessage); } var config = this.@com.smartgwt.client.widgets.BaseWidget::getConfig()(); var widget = $wnd.isc.BatchUploader.create(config); this.@com.smartgwt.client.widgets.BaseWidget::internalSetID(Ljava/lang/String;Z)(widget.getID(), true); this.@com.smartgwt.client.widgets.BaseWidget::doInit()(); return widget; }-*/; public void setUploadFormItems(FormItem... items) { setAttribute("uploadFormFields", items, false); } public void setUploadDataSource(DataSource dataSource) { setAttribute("uploadDataSource", dataSource.getOrCreateJsObj(), true); } public DataSource getUploadDataSource() { return DataSource.getOrCreateRef(getAttributeAsJavaScriptObject("uploadDataSource")); } /** * Setting dataURL causes every "add" operation issued by the BatchUploader to use that dataURL. * * @param dataURL the data URL */ public void setDataURL(String dataURL) { setAttribute("dataURL", dataURL, true); } public String getDataURL() { return getAttributeAsString("dataURL"); } /** * The location of batchUpload.ds.xml. Defaults to /ds/batchUpload.ds.xml * * @param dsURL the batchUploader datasource URL */ public void setDsURL(String dsURL) { setAttribute("dsURL", dsURL, true); } }