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

	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);
		}
	};

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.

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);
		}
	};

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,

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;
	}
...

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

Advertisements

About Vikas Hazrati

Vikas is the Founding Partner @ Knoldus which is a group of software industry veterans who have joined hands to add value to the art of software development. 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). To know more, send a mail to hello@knoldus.com or visit www.knoldus.com
This entry was posted in Cloud, Java and tagged , , , , . Bookmark the permalink.

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

  1. Pingback: Custom Versioning for Google Docs: Handling File Operations on Google App Engine « Inphina Thoughts

  2. Pingback: Building Customized Versioning for Google Docs « Inphina Thoughts

  3. jpoquioma says:

    great tutorial… url of live demo ??

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s