Custom Versioning for Google Docs: Building GWT Gadget to Upload on App Engine

In my previous post, we talked about the the business case for building a custom versioning gadget for Google Docs. In this post, we would talk about the GWT gadget which we built for the purpose.

The custom gadget looks like this

As you would notice, we have a file uploader which can upload multiple files at once (of course you have to select them individually). Then there is an ajax enabled feedback which tells you the status of the upload and once it is done.

There are also 4 fields which are custom meta data that needs to be tagged with every document. Note that this is not static tagging just like what we can do with Google Collections (the rechristened folders in Google Docs). The difference here is that, these tags are dynamic. Which means that we can have dynamic values in them. For example a document could have an expiry date of 30th May, 30th June or whatever you intend it to be.

How did we do it?

Luckily, there is an exciting project called GWTUpload which took more than half of our pains away. The idea behind this component is that there is a GWT component and then there is a backing servlet which would get the file. Depending on whether you would be able to save your file in the filesystem (which you cannot on the Google App Engine), you would be able to manipulate and work on the file. The excellent getting started guide for GWTUpload is present here. We had custom meta data too as a part of the gadget, let us see what does the code look like now

[sourcecode language=”java”]
public void onModuleLoad() {
// Attach the image viewer to the document
RootPanel.get("thumbnails").add(panelImages);

// Create a new uploader panel and attach it to the document
MultiUploader defaultUploader = new MultiUploader();
RootPanel.get("default").add(defaultUploader);

defaultUploader.addOnStartUploadHandler(onStartUploaderHandler);

// Add Date boxes
DateTimeFormat dateFormat = DateTimeFormat.getLongDateFormat();
expiryDateBox.setFormat(new DateBox.DefaultFormat(dateFormat));
RootPanel.get("expiryDate").add(expiryDateBox);

reminderDateBox.setFormat(new DateBox.DefaultFormat(dateFormat));
RootPanel.get("reminderDate").add(reminderDateBox);

// Add List Boxes
coeListBox.addItem("Application To Disimbursement",
"Application To Disimbursement");
coeListBox.addItem("Customer Service", "Customer Service");
coeListBox.addItem("Others", "Others");
RootPanel.get("coeListBox").add(coeListBox);

geographyListBox.addItem("North", "North");
geographyListBox.addItem("East", "East");
geographyListBox.addItem("West", "West");
geographyListBox.addItem("South", "South");
RootPanel.get("geographyListBox").add(geographyListBox);

// Add a finish handler which will load the image once the upload
// finishes
defaultUploader.addOnFinishUploadHandler(onFinishUploaderHandler);
}

private IUploader.OnStartUploaderHandler onStartUploaderHandler = new IUploader.OnStartUploaderHandler() {

@Override
public void onStart(IUploader uploader) {
String path = uploader.getServletPath()
+ "?expiryDate="
+ expiryDateBox.getTextBox().getText()
+ "&reminderDate="
+ reminderDateBox.getTextBox().getText()
+ "&coe="
+ coeListBox.getValue(coeListBox.getSelectedIndex())
+ "&geography="
+ geographyListBox.getValue(geographyListBox
.getSelectedIndex());

System.out.println("The path made: " + path);
uploader.setServletPath(path);
}
};
[/sourcecode]

As you would notice in the code, we have added the GWTUpload component as MultiUploader. Apart from that, we have added a few more fields as a part of the custom meta data. These are expiry date, reminder one, coe and geography. These are standard GWT date picker and list boxes.

Notice that we have defined a start uploader handler which GWTUpload would honor as soon as the upload begins.

[sourcecode language=”java”]
private IUploader.OnStartUploaderHandler onStartUploaderHandler = new IUploader.OnStartUploaderHandler() {

@Override
public void onStart(IUploader uploader) {
String path = uploader.getServletPath()
+ "?expiryDate="
+ expiryDateBox.getTextBox().getText()
+ "&reminderDate="
+ reminderDateBox.getTextBox().getText()
+ "&coe="
+ coeListBox.getValue(coeListBox.getSelectedIndex())
+ "&geography="
+ geographyListBox.getValue(geographyListBox
.getSelectedIndex());

System.out.println("The path made: " + path);
uploader.setServletPath(path);
}
};
[/sourcecode]

What we are doing in this is that we are passing all the values as a part of the request object to the server where they can be fetched along with the file.

Since for us the application is deployed on the app engine, we cannot make use of files. Luckily, again, GWTUpload has a servlet specifically for the app engine. We need to extend that and write our custom code as shown,

[sourcecode language=”java”]
public class GoogleDocsGAEUploadServlet extends AppEngineUploadAction {

@Override
public String executeAction(HttpServletRequest request, List sessionFiles) throws UploadActionException {
String response = "";
List<FileItem> itemsTobeUploaded = new ArrayList();
String description = getDescription(request);

System.out.println("The Description created is: " + description);

for (FileItem item : sessionFiles) {
if (item.isFormField() == false) {
itemsTobeUploaded.add(item);
}
}

for (FileItem fileItem : itemsTobeUploaded) {
try {
documentService.uploadFileToGoogleDocs(fileItem, description);
} catch (Exception e) {
// exception handling code here
}
}
// Remove files from session because we have a copy of them
removeSessionFileItems(request);

// Send your customized message to the client.
return response;
}

private String getDescription(HttpServletRequest request) {
String expiryDate = request.getParameter("expiryDate");
String reminderDate = request.getParameter("reminderDate");
String coe = request.getParameter("coe");
String geography = request.getParameter("geography");
return "EXPIRY="+expiryDate + "::" + "REMINDER-ONE=" + reminderDate + "::" + "COE=" + coe + "::" + "GEOGRAPHY=" + geography;
}

[/sourcecode]

The advantage of using the AppEngineUploadAction is that, instead of standard UploadAction is that it circumvents the following limitations of the app engine

  1. It doesn’t support writing to file-system, so this servlet stores fileItems in memcache using CacheableFileItem
  2. The request size is limited to 512 KB, so this servlet hardcodes the maxSize to 512
  3. The limit for session and cache objects is 1024 KB
  4. The time spent to process a request is limited, so this servlet limits the sleep delay to a maximum of 50ms

Also, we are fetching all the fields as a part of the request and forming a description out of it which we are finally passing to the document service. We would get into the internals of the document service in our next post when we talk about handling file operations on Google App Engine.

Inphina, as an expert on Google App Engine and Google Apps has helped more than 10 medium to large organizations help take advantage of the cloud by building, migrating or re-engineering their complex line of business applications to the cloud thereby making significant reductions in their capex expenditure. If you are interested in an assessment send us a mail at cloud@inphina.com

Written by 

Vikas is the CEO and Co-Founder of Knoldus Inc. Knoldus does niche Reactive and Big Data product development on Scala, Spark, and Functional Java. Knoldus has a strong focus on software craftsmanship which ensures high-quality software development. It partners with the best in the industry like Lightbend (Scala Ecosystem), Databricks (Spark Ecosystem), Confluent (Kafka) and Datastax (Cassandra). Vikas has been working in the cutting edge tech industry for 20+ years. He was an ardent fan of Java with multiple high load enterprise systems to boast of till he met Scala. His current passions include utilizing the power of Scala, Akka and Play to make Reactive and Big Data systems for niche startups and enterprises who would like to change the way software is developed. To know more, send a mail to hello@knoldus.com or visit www.knoldus.com

3 thoughts on “Custom Versioning for Google Docs: Building GWT Gadget to Upload on App Engine

Leave a Reply

%d bloggers like this: