Help The Victims of the 8.9 Earthquake in Japan by Spreading Awareness and Aid. Visit http://goo.gl/wjZQz to donate. /* ]]> */

Google App Engine recently announced version 1.5.1 of its App Engine SDK. One of the features that interests me is that of the new header added in every client request.

The official announcement from Google is given below:

App Engine will now include a header with every client request that represents a best-effort attempt to identify the country from which the request originated. This header X-AppEngine-country can allow you to customize content based on the origin of the user.

Location is a big thing in most applications. Typically you can classify Location Based Applications into two types:

  • One that requires coarse grained information. What this means is that it is enough to know if the user is from a particular city or country. There is no need to know the exact co-ordinates.
  • The other type requires access to accurate location information and this typically means getting information about the latitude and longitude. The lat, lon are then used to further fine tune the results or actions.

The X-AppEngine-country is fantastic for applications that need coarse grained location information and would definitely help to avoid some network calls.

To test out, I wrote a simple Google App Engine Java project which has a single page (index.jsp) that simply sniffs the value of this header and prints it out. I was more interested in seeing what it returns. Here is the snippet of code that prints a greeting and the country from where you are accessing the page:

<h2>Hello My Friend from <%=request.getHeader("X-AppEngine-country") %></h2>

Since I live in India, it does a good job and prints out the following (as shown in the screenshot below):

You can try it too.

Some observations/comments:

While this is a good start, here are some points:

  • The country code that it provides are standard country abbreviations. Here is a list. If your application does not deal directly with the country code and requires some other value (e.g. full country name) — you might still have to do a lookup.
  • Several public APIs that require a coarse grained location might still not work with just the country code given here. So some additional translation might be due.
  • I still think this is a great start. And my suggestion to the Google Team if they haven’t already implemented it is to implement the a  Header that I shall take the liberty of calling X-AppEngine-city.
So go ahead and give the Location Test a try from your browser.

 


		

I had spoken at IndicThreads Mobile conference which was held in Pune on November 19-20, 2010. One of the talks I gave was Using Google App Engine to power your Mobile Applications. This was the second talk that I gave at the conference, the first one was on using HTML5 to write Mobile Web applications.

This presentation covered Google App Engine via a simple example along with an Android Native Application and a generic HTML5 based Mobile Web Application that were both powered by the Service in the Cloud.



I wrote a blog post at my company’s site titled “Architecture considerations for software products on cloud”. Though the article is general in nature, I do feel that a lot of the points apply if you are planning to move your product to GAE.

Click here for the blog post.

There were 2 articles posted in Google App Engine experiments blog.

1. Episode 7: Writing your First Google Wave Robot (http://rominirani.com/2009/11/04/episode-7-writing-your-first-google-wave-robot/)

2. Episode 11: Develop Simple Google Wave Robots using the WadRobotFramework (http://rominirani.wordpress.com/2009/12/03/episode-11-develop-simple-google-wave-robots-using-the-wadrobotframework/)

I have updated both of these articles. The reasons for updating them are:

1. Google released a newer version of the Robots API : Version 2.0. They have recommended that all Robots written with the earlier API (which the articles covered) should be ported to use the new Version 2.0 API. The older API will be deprecated by end June.

2. WadRobotFramework (http://code.google.com/p/wadrobotframework) has also undergone an update to use the newer version 2.0 of the Robot API internally. Along with that, WadRobotFramework has also introduced several new features including a generator (so you almost end up writing as few lines as possible, the concept of obedience, etc.).

You will find both these articles updated at my new Blog (Google Wave Experiments) that is focused on Google Wave programming. Please find the articles over here:

Episode 1 : Writing your First Google Wave Robot using Robot API v2

Episode 2 : Writing a Wave Robot using WadRobotFramework

Hope you like the articles. I am looking forward to your feedback and want to know what you would like to see covered vis-a-vis Google Wave Programming so that I can conduct my experiments accordingly.

Welcome to Episode 16. In this episode we shall cover basic usage of the Datastore API in Google App Engine. The Datastore API is used to persist and retrieve data. Google App Engine uses the BigTable database as its underlying datastore and provides abstraction for using it via the Datastore API. There are currently two options available to developers i.e. via the Java Data Objects (JDO) and Java Persistence Architecture (JPA) APIs.

In this episode, we shall cover the following items:

  • Persist a simple record to the datastore using JDO. The intention is to limit it to a single record in this episode and not address relationships and complex structures. That could be the basis of future episodes.
  • Retrieve the persisted records by firing some queries. In the process, we shall see how to create parameterized queries and execute them.
  • Discuss some nuances about indexes and what steps you need to do to make sure that the same application that you use locally will work fine when deployed to the Google App Engine cloud.

The underlying theme will not be to present a comprehensive tutorial about the Datastore API. There are some excellent references available for that. The official documentation and well as the GAE Persistence Blog. The focus will be on getting up and running with the Datastore API ASAP and seeing it work when deployed to the cloud.

What we shall build

In this episode, we shall build the following:

  1. Create a simple Object that we shall persist to the underlying datastore. The Object will be a Health Report and will have about 4-5 attributes that we would like to save.
  2. Write the Save method that takes an instance of the above Health Report Record and persists it using the JDO API.
  3. Write a Search method that will query for several Health Reports using several filter parameters.
  4. Look at the datastore_indexes.xml file that is required when you deploy the application to the cloud.

Please note that the focus will be on the server side and not on building a pretty GUI. All server side actions will be invoked via a REST like request (HTTP GET) — so that we can test the functionality in the browser itself.

Developing our Application

The first thing to do is to create a New Google Web Application Project. Follow these steps:

1. Either click on File –> New –> Other or press Ctrl-N to create a new project. Select Google and then Web Application project. Alternately you could also click on the New Web Application Project Toolbar icon as part of the Google Eclipse plugin.
2. In the New Web Application Project dialog, deselect the Use Google Web Toolkit and give a name to your project. I have named mine GAEJExperiments. I suggest you go with the same name so that things are consistent with the rest of the article, but I leave that to you. In case you are following the series, you could simply use the same project and skip all these steps altogether. You can go straight to the Servlet Development section.
3. Click on Finish.

This will generate the project and also create a sample Hello World Servlet for you. But we will be writing our own Servlet.

Few things to note first:

Quite a few things are enabled for you by default as far as the database support is concerned. They are as follows:

a. Several JAR files are added to the CLASSPATH by default. Take a look and you will see several JARs *jpa*.jar, *datanucleus*.jar, etc.
b. In the src/META-INF folder, you will find a jdoconfig.xml file. There is a default Persistence Manager Factory class in that that we shall be using in the rest of the article. For the purposes of this article we do not need to do anything to this file.
c. GAEJ uses the DataNucleus library to abstract the BigTable store. The DataNucleaus library provides the JDO and JPA interfaces so that you do not have to deal with the underlying low level API. You will also find a logging.properties file present in war/WEB-INF folder. You will find several log levels mentioned for the DataNucleus classes. You can tweak them to lower levels like DEBUG/INFO to see more debug level statements of what happens when you are using these APIs. I have found it very helpful to set the debug levels to DEBUG/INFO especially when facing a problem.

PMF.java

The first class that we shall write is a simple utility class that shall get us the underlying Persistence Manager factory instance. This class is important since all other methods like saving a record, querying records, etc will work on the instance of the PersistenceManagerFactory.

The code is shown below and wherever we need an instance of the class, we shall simply invoke the get() method below:

package com.gaejexperiments.db;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;

public final class PMF {
private static final PersistenceManagerFactory pmfInstance =
JDOHelper.getPersistenceManagerFactory("transactions-optional");

private PMF() {}

public static PersistenceManagerFactory get() {
return pmfInstance;
}
}

HealthReport.java

Next is the Health Report. As mentioned in the beginning of the article, we shall be saving a Health Report record. The Health Report will have 4 attributes and they are explained below:

  1. Key : This is a unique key that is used to persist/identify the record in the datastore. We shall leave its implementation/generation to the Google App Engine implementation.
  2. PinCode : This is similar to a ZipCode. This is a string that shall contain the value of the zipcode (Area) where the Health Incident has occured.
  3. HealthIncident: This is a string value that contains the health incident name. For e.g. Flu, Cough, Cold, etc. In this simple application — we shall be concerned only with 3 health incidents i.e. Flu, Cough and Cold.
  4. Status : This is a string value that specifies if the record is ACTIVE or INACTIVE. Only records with ACTIVE status shall be used in determining any statistics / data reports. We shall set this value to ACTIVE at the time of saving the record.
  5. ReportDateTime : This is a  Date field that shall contain the date/time that the record was created.

Shown below is the listing for the HealthReport.java class. In addition to the above attributes and getter/setter methods for them, note the following additions to make sure that your class can be persisted using JDO.

1. We need to have a constructor that contains all the fields except for the Key field.
2. All fields that need to be persisted are annotated with the @Persistent annotation.
3. The class is declared as being persistable via the @PersistenceCapable annotation and we are leaving the identity to the Application.
4. The Primary Key field i.e. Key is declared via the @PrimaryKey annotation and we are using an available Generator for the ID instead of rolling our own.

 

package com.gaejexperiments.db;

import java.util.Date;
import com.google.appengine.api.datastore.Key;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class HealthReport {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;

private String pinCode;
@Persistent
private String healthIncident;
@Persistent
private String status;
@Persistent
private Date reportDateTime;

public HealthReport(String pinCode, String healthIncident,String status, Date reportDateTime) {
super();
this.pinCode = pinCode;
this.healthIncident = healthIncident;
this.status = status;
this.reportDateTime = reportDateTime;
}

public Key getKey() {
return key;
}

public void setKey(Key key) {
this.key = key;
}

public String getPinCode() {
return pinCode;
}

public void setPinCode(String pinCode) {
this.pinCode = pinCode;
}

public String getHealthIncident() {
return healthIncident;
}

public void setHealthIncident(String healthIncident) {
this.healthIncident = healthIncident;
}

public String getStatus() {
return status;
}

public void setStatus(String status) {
this.status = status;
}

public Date getReportDateTime() {
return reportDateTime;
}

public void setReportDateTime(Date reportDateTime) {
this.reportDateTime = reportDateTime;
}

}

 

PostHealthIncidentServlet.java

We shall now look at how to persist the above Health Record. Since we are not going to build a UI for it, we shall simply invoke a servlet (HTTP GET) with the required parameters. It would almost be like a FORM submitting these values to the Servlet. Before we write this Servlet code, let us look at how we will invoke it. Given below is a screenshot of the browser where I punch in the URL : http://localhost:8888/posthealthincident?healthincident=Flu&pincode=400101


As you can see, I am running the application on my local development server and invoke the servlet (which we shall see in a while) providing two key parameters healthincident and pincode. These two parameters are two key fields of the HealthReport class that we saw above. The other fields like ReportDateTime and Status are determined automatically by the application. Similarly the Key value of the record in the underlying datastore will be generated by App Engine infrastructure itself.

Let us now look at the PostHealthIncidentServlet.java code shown below:

 

package com.gaejexperiments.db;

import java.io.IOException;
import java.util.Date;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.*;
@SuppressWarnings("serial")
public class PostHealthIncidentServlet extends HttpServlet {
public static final Logger _logger = Logger.getLogger(PostHealthIncidentServlet.class.getName());

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doPost(req, resp);
}

public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
resp.setContentType("text/plain");
String strResponse = "";
String strHealthIncident = "";
String strPinCode = "";
try {

//DO ALL YOUR REQUIRED VALIDATIONS HERE AND THROW EXCEPTION IF NEEDED

strHealthIncident = (String)req.getParameter("healthincident");
strPinCode = (String)req.getParameter("pincode");

String strRecordStatus = "ACTIVE";

Date dt = new Date();
HealthReport HR = new HealthReport(strPinCode,
strHealthIncident,
strRecordStatus,
dt);
DBUtils.saveHealthReport(HR);
strResponse = "Your Health Incident has been reported successfully.";
}
catch (Exception ex) {
_logger.severe("Error in saving Health Record : " + strHealthIncident + "," + strPinCode +  " : " + ex.getMessage());
strResponse = "Error in saving Health Record via web. Reason : " + ex.getMessage();
}
resp.getWriter().println(strResponse);
}
}

The main points of the code are :

1) We extract out the HealthIncident and PinCode request parameters. We do not do any particular validations but you could do all of that depending on your application requirement.
2. We generate the two other field values i.e. Date (ReportDate) and Status (ACTIVE).
3. Finally, we create a new instance of the HealthReport class, providing the values in the constructor. And then call the DBUtils.saveHealthReport(…) method that persists the record to the underlying datastore.
4. We display back a successfull message if all is well, which is what was visible in the screenshot above.

Let us look at the DBUtils.java class now. Please note that we have simply separated out the code into this file but you can manage/partition your code in any which way you like.

DBUtils.java

The DBUtils.java source code is listed below. Focus first on the saveHealthReport() method which was invoked by our servlet earlier. The other method, we shall come to that later on in the article.

Key Points are :

1. The saveHealthReport() method first gets the instance of the PersistenceManager through the PMF.java class that we wrote earlier.
2. It simply invoke the makePersistent() method on it. The makePersistent() method will take as a parameter the object that you want to persist. In our case it is the HealthReport.java class instance that we have created in the servlet. This method will persist the record and in the process also assign it a unique key.
3. Finally, we need to close the PersistenceManager instance by invoking the close() method.

The entire code listing is shown below:

 

package com.gaejexperiments.db;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

public class DBUtils {
public static final Logger _logger = Logger.getLogger(DBUtils.class.getName());

//Currently we are hardcoding this list. But this could also be retrieved from
//database
public static String getHealthIncidentMasterList() throws Exception {
return "Flu,Cough,Cold";
}

/**
* This method persists a record to the database.
*/
public static void saveHealthReport(HealthReport healthReport)
throws Exception {
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
pm.makePersistent(healthReport);
_logger.log(Level.INFO, "Health Report has been saved");
} catch (Exception ex) {
_logger.log(Level.SEVERE,
"Could not save the Health Report. Reason : "
+ ex.getMessage());
throw ex;
} finally {
pm.close();
}
}

/**
* This method gets the count all health incidents in an area (Pincode/Zipcode) for the current month
* @param healthIncident
* @param pinCode
* @return A Map containing the health incident name and the number of cases reported for it in the current month
*/
public static Map<String, Integer> getHealthIncidentCountForCurrentMonth(String healthIncident, String pinCode) {
Map<String, Integer> _healthReport = new HashMap<String, Integer>();

PersistenceManager pm = null;

//Get the current month and year
Calendar c = Calendar.getInstance();
int CurrentMonth = c.get(Calendar.MONTH);
int CurrentYear = c.get(Calendar.YEAR);

try {
//Determine if we need to generate data for only one health Incident or ALL
String[] healthIncidents = {};
if (healthIncident.equalsIgnoreCase("ALL")) {
String strHealthIncidents = getHealthIncidentMasterList();
healthIncidents = strHealthIncidents.split(",");
}
else {
healthIncidents =  new String[]{healthIncident};
}

pm = PMF.get().getPersistenceManager();
Query query = null;

//If Pincode (Zipcode) is ALL, we need to retrieve all the records irrespective of Pincode
if (pinCode.equalsIgnoreCase("ALL")) {
//Form the query
query = pm.newQuery(HealthReport.class, " healthIncident == paramHealthIncident && reportDateTime >= paramStartDate && reportDateTime < paramEndDate && status == paramStatus");

// declare parameters used above
query.declareParameters("String paramHealthIncident, java.util.Date paramStartDate, java.util.Date paramEndDate, String paramStatus");
}
else {
query = pm.newQuery(HealthReport.class, " healthIncident == paramHealthIncident && pinCode == paramPinCode && reportDateTime >= paramStartDate && reportDateTime <paramEndDate && status == paramStatus");

// declare params used above
query.declareParameters("String paramHealthIncident, String paramPinCode, java.util.Date paramStartDate, java.util.Date paramEndDate, String paramStatus");
}

//For each health incident (i.e. Cold Flu Cough), retrieve the records

for (int i = 0; i < healthIncidents.length; i++) {
int healthIncidentCount = 0;
//Set the From and To Dates i.e. 1st of the month and 1st day of next month
Calendar _cal1 = Calendar.getInstance();
_cal1.set(CurrentYear, CurrentMonth, 1);
Calendar _cal2 = Calendar.getInstance();
_cal2.set(CurrentYear,CurrentMonth+1,1);

List<HealthReport> codes = null;
if (pinCode.equalsIgnoreCase("ALL")) {
//Execute the query by passing in actual data for the filters
codes = (List<HealthReport>) query.executeWithArray(healthIncidents[i],_cal1.getTime(),_cal2.getTime(),"ACTIVE");
}
else {
codes = (List<HealthReport>) query.executeWithArray(healthIncidents[i], pinCode, _cal1.getTime(),_cal2.getTime(),"ACTIVE");
}

//Iterate through the results and increment the count
for (Iterator iterator = codes.iterator(); iterator.hasNext();) {
HealthReport _report = (HealthReport) iterator.next();
healthIncidentCount++;
}

//Put the record in the Map data structure
_healthReport.put(healthIncidents[i], new Integer(healthIncidentCount));
}
return _healthReport;
} catch (Exception ex) {
return null;
} finally {
pm.close();
}
}
}

Assuming that your application is running, you can view the data records that are being persisted. If you navigate to http://localhost:<YourPort>/_ah/admin in your browser, you will see a screenshot similar to the one shown below:

The screen above shows the Entity types that are currently having some data records. In our case it is the HealthReport entity of which we have saved one record so far. If you click on the List Entities button, you can see the records that are currently persisted for the HealthReport Entity Type. A sample screenshot from my system after saving my first record is shown below:

Go ahead and populate a few more records in the database for different HealthIncidents like Cough, Cold, Flu (only). This is needed so that we can get some more data when we cover how to query persistent data in the next section.

ReportsServlet.java

Before we look at this servlet, let us look at the output that this servlet produces, so that it becomes easier to follow the code later. This is assuming  that you have added atleast 4-5 records using the /posthealthincident servlet that we covered above.

Shown below is the screenshot of the servlet output when I provide the following url:

http://localhost:8888/reports?type=HEALTHINCIDENTCOUNT_CURRENT_MONTH&healthincident=Flu&pincode=ALL
What we are asking for here is a report that gets all health incidents in the current month (type = HEALTHINCIDENTCOUNT_CURRENT_MONTH) for the healthincident = Flu and where pincode = ALL (irrespective of pincode)

Shown below is the screenshot of the servlet output when I provide the following url:

http://localhost:8888/reports?type=HEALTHINCIDENTCOUNT_CURRENT_MONTH&healthincident=ALL&pincode=ALL
What we are asking for here is a report that gets all health incidents in the current month (type = HEALTHINCIDENTCOUNT_CURRENT_MONTH) for the healthincident = ALL (irrespective of health incident which means all of them)  and where pincode = ALL (irrespective of pincode)

So what we have effectively done here is to query the set of Health Records that are present in our database using a variety of parameters (filters). In other words, if we take a SQL like aspect to it, we are saying something like this:

SELECT * FROM HEALTHREPORTS WHERE PINCODE = %1 AND HEALTHINCIDENT = %2 AND REPORTDATE >= %3 AND REPORTDATE < %4 AND STATUS = ACTIVE , etc.

The above SQL statement is just representative of the queries that we want to execute. So let us look at the code for the servlet first.

 

package com.gaejexperiments.db;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
public class ReportsServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
resp.setContentType("text/xml");
String strResult = "";
String strData   = "";
try {

String type = (String)req.getParameter("type");
if (type == null) {
strResult =  "No Report Type specified.";
throw new Exception(strResult);
}
else if (type.equals("HEALTHINCIDENTCOUNT_CURRENT_MONTH")) {
String strHealthIncident = (String)req.getParameter("healthincident");
String strPinCode = (String)req.getParameter("pincode");
Map<String,Integer> _healthReports = DBUtils.getHealthIncidentCountForCurrentMonth(strHealthIncident,strPinCode);
if (_healthReports == null) {
}
else {
Iterator<String> it = _healthReports.keySet().iterator();
while (it.hasNext()) {
String healthIncident = (String)it.next();
int healthIncidentCount = 0;
Integer healthIncidentCountObject = _healthReports.get(healthIncident);
if (healthIncidentCountObject == null) {
healthIncidentCount = 0;
}
else {
healthIncidentCount = healthIncidentCountObject.intValue();
}
if (healthIncidentCount > 0)
strData += "<HealthIncident><name>" + healthIncident + "</name>" + "<count>" + healthIncidentCount + "</count></HealthIncident>";
}
}
strResult = "<Response><Status>success</Status><StatusDescription></StatusDescription><Result>" + strData + "</Result></Response>";
}
}
catch (Exception ex) {
strResult = "<Response><Status>fail</Status><StatusDescription>"+ "Error in executing operation : " + ex.getMessage() + "</StatusDescription></Response>";
}
resp.getWriter().println(strResult);
}

} </pre>
<pre>

 

The Servlet code is straightforward:

1. Currently it has only one type of report i.e. HEALTHINCIDENTCOUNT_CURRENT_MONTH
2. Next we extract out the Pincode and the HealthIncident request parameter values.
3. Then we invoke the DBUtils.getHealthIncidentCountForCurrentMonth method. This takes two parameters : pincode and healthincident that we have got from above step.
4. The method will return us a map where each record in the map will contain the key (String) containing the healthincident name and the value containing the count of incidents reported for that month. So something like [ {"Flu","20"} , {"Cough", "30"} ,  {"Cold","10"} ]
5. Finally we simply format that into a XML output so that it can be returned to the client. And this is the exact output that we see in the browser.

Analyzing the DBUtils.getHealthIncidentCountForCurrentMonth method

I reproduce here the method from the DBUtils.java class that was listed before:

 

/**
* This method gets the count all health incidents in an area (Pincode/Zipcode) for the current month
* @param healthIncident
* @param pinCode
* @return A Map containing the health incident name and the number of cases reported for it in the current month
*/
public static Map<String, Integer> getHealthIncidentCountForCurrentMonth(String healthIncident, String pinCode) {
Map<String, Integer> _healthReport = new HashMap<String, Integer>();

PersistenceManager pm = null;

//Get the current month and year
Calendar c = Calendar.getInstance();
int CurrentMonth = c.get(Calendar.MONTH);
int CurrentYear = c.get(Calendar.YEAR);

try {
//Determine if we need to generate data for only one health Incident or ALL
String[] healthIncidents = {};
if (healthIncident.equalsIgnoreCase("ALL")) {
String strHealthIncidents = getHealthIncidentMasterList();
healthIncidents = strHealthIncidents.split(",");
}
else {
healthIncidents =  new String[]{healthIncident};
}

pm = PMF.get().getPersistenceManager();
Query query = null;

//If Pincode (Zipcode) is ALL, we need to retrieve all the records irrespective of Pincode
if (pinCode.equalsIgnoreCase("ALL")) {
//Form the query
query = pm.newQuery(HealthReport.class, " healthIncident == paramHealthIncident && reportDateTime >= paramStartDate && reportDateTime < paramEndDate && status == paramStatus");

// declare parameters used above
query.declareParameters("String paramHealthIncident, java.util.Date paramStartDate, java.util.Date paramEndDate, String paramStatus");
}
else {
query = pm.newQuery(HealthReport.class, " healthIncident == paramHealthIncident && pinCode == paramPinCode && reportDateTime >= paramStartDate && reportDateTime <paramEndDate && status == paramStatus");

// declare params used above
query.declareParameters("String paramHealthIncident, String paramPinCode, java.util.Date paramStartDate, java.util.Date paramEndDate, String paramStatus");
}

//For each health incident (i.e. Cold Flu Cough), retrieve the records

for (int i = 0; i < healthIncidents.length; i++) {
int healthIncidentCount = 0;
//Set the From and To Dates i.e. 1st of the month and 1st day of next month
Calendar _cal1 = Calendar.getInstance();
_cal1.set(CurrentYear, CurrentMonth, 1);
Calendar _cal2 = Calendar.getInstance();
_cal2.set(CurrentYear,CurrentMonth+1,1);

List<HealthReport> codes = null;
if (pinCode.equalsIgnoreCase("ALL")) {
//Execute the query by passing in actual data for the filters
codes = (List<HealthReport>) query.executeWithArray(healthIncidents[i],_cal1.getTime(),_cal2.getTime(),"ACTIVE");
}
else {
codes = (List<HealthReport>) query.executeWithArray(healthIncidents[i], pinCode, _cal1.getTime(),_cal2.getTime(),"ACTIVE");
}

//Iterate through the results and increment the count
for (Iterator iterator = codes.iterator(); iterator.hasNext();) {
HealthReport _report = (HealthReport) iterator.next();
healthIncidentCount++;
}

//Put the record in the Map data structure
_healthReport.put(healthIncidents[i], new Integer(healthIncidentCount));
}
return _healthReport;
} catch (Exception ex) {
return null;
} finally {
pm.close();
}
}

I have attempted to provide comments so that you can follow the code but I will list down the important parts here:

1. We are going to deal with the following classes : PersistenceManager and Query from the javax.jdo package.
2. We get the PersistenceManager instance via the PMF.java class that we wrote earlier.
3. We are using the Query class here to first build the query. For e.g.
query = pm.newQuery(HealthReport.class, ” healthIncident == paramHealthIncident && reportDateTime >= paramStartDate && reportDateTime < paramEndDate && status == paramStatus”);

What this means is that we are creating a query instance where we wish to get all records for the HealthReport class. Additionally we are passing a criteria string. Notice that the lefthand side are the fields of the HealthReport class (healthIncident, reportDateTime, status) and the right hand side are parameters which will define and then pass the values for to execute the query.

4. We define the parameters next as shown below:
// declare parameters used above
query.declareParameters(“String paramHealthIncident, java.util.Date paramStartDate, java.util.Date paramEndDate, String paramStatus”);
5. Finally we use the query.executeWithArray(…) method which takes as parameter an array that contains all the values for the above parameters that you have declared.
6. The executeWithArray(…) will return a List<> of HealthReport class instances that you can then iterate through the populate your result. In our code, we simply compute the total number for each of the health incidents (Flu, Cough, Cold).

Servlet Configuration

To complete our Servlet development, we will also need to add the <servlet/> and <servlet-mapping/> entry to the web.xml file. This file is present in the WEB-INF folder of the project. The necessary fragment to be added to your web.xml file are shown below. Please note that you can use your own namespace and servlet class. Just modify it accordingly if you do so.

 

<servlet>
<servlet-name>PostHealthIncidentServlet</servlet-name>
<servlet-class>com.gaejexperiments.db.PostHealthIncidentServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>ReportsServlet</servlet-name>
<servlet-class>com.gaejexperiments.db.ReportsServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>PostHealthIncidentServlet</servlet-name>
<url-pattern>/posthealthincident</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ReportsServlet</servlet-name>
<url-pattern>/reports</url-pattern>
</servlet-mapping>

 

Running the application locally

I am assuming that you have already created a new Google Web Application Project and have created the above Servlets, web.xml , etc. So assuming that all is well, we will run our application, by right-clicking on the project and selecting Run As –> Web Application. Launch the browser on your local machine and try out the URLs that we have covered so far like :

1. Adding a Health Report record :

http://localhost:8888/posthealthincident?healthincident=Flu&pincode=400101

2. Reports

http://localhost:8888/reports?type=HEALTHINCIDENTCOUNT_CURRENT_MONTH&healthincident=Flu&pincode=ALL

http://localhost:8888/reports?type=HEALTHINCIDENTCOUNT_CURRENT_MONTH&healthincident=ALL&pincode=ALL

Please replace the port 8888 with your local port number.

datastore_indexes.xml

If you run your application locally, you will notice that everything is working fine. However, if you deploy this application the Google App Engine, you will not get any results where you query the reports. Why is that? It is because there are no indexes defined for your application.

If you visit your Developer Console at http://appengine.google.com and for your particular application, you will find no indexes are defined for the Datastore indexes link as shown below:

What are these indexes ? Indexes are generated for every possible query that you fire in your program. This is Google’s way of retrieving your data results efficiently. However when you run in local mode, these indexes are generated for your automatically. If you look in war/WEB-INF directory, you will find a directory named appengine-generated. Inside this directory is a file that is constantly updated called datastore-indexes-auto.xml. The contents of this file for the reports that we have run so far is shown below:

 

<datastore-indexes>

<datastore-index kind="HealthReport" ancestor="false" source="auto">
<property name="healthIncident" direction="asc"/>
<property name="status" direction="asc"/>
<property name="reportDateTime" direction="asc"/>
</datastore-index>

<datastore-index kind="HealthReport" ancestor="false" source="auto">
<property name="healthIncident" direction="asc"/>
<property name="pinCode" direction="asc"/>
<property name="status" direction="asc"/>
<property name="reportDateTime" direction="asc"/>
</datastore-index>

</datastore-indexes>

 

As you can see, there are two indexes generated for the searches that we fired and each of them contains the parameters that we queried the Health Reports on.

To get your deployed application to function correctly, you will need to copy these indexes to a file named datastore_indexes.xml.

You  will need to place this file in the war/WEB-INF folder of your application project.

Now deploy your project again. On successful deploy, revist the Developer Console –> Datastore indexes. You will find that the indexes have got created and are buing built as shown below:

Wait for a while. The building process goes through your current data and then on completion, you will see the screen as shown below (A refresh needed!):

Try out the queries now on your deployed application and they should work fine. You can also manual create the datastore_indexes.xml file but the local development server has this nice feature of auto generating out for you, so that you dont end up making mistakes. But remember to upload the updated datastore_indexes.xml as part of  your deployment others the queries will silently fail.

Conclusion

We conclude this episode in which we covered how you can persist and query data using the JDO API. I hope that it has given you enough inputs to start incorporating persistence into your applications. It is by no means a simple exercise especially if you are coming in from a SQL world. Not everything SQL-like can be converted as is to a NoSQL world so refer to the several excellent sources available on the web in your persistence journey. I highly recommend the official documentation along with the GAE Persistence Blog mentioned at the beginning of the article.

Cloud Computing has arrived and it provides significant benefits to developers who wish to write applications in the cloud and make use of large infrastructure that has been setup by Platform As A Service (PAAS) providers like Amazon and Google.

Google App Engine is one of the popular PAAS providers that allows you to deploy Java and/or Python based applications to the same infrastructure that hosts their services. I have covered several episodes at this blog that teach you the various services that are available in the Google App Engine to start writing your applications today.

To make it easier for everyone, I have combined all the episodes into a single and free eBook.

The GAEJ Experiments eBook contains

  • All 15 episodes published at the site
  • A Bonus Chapter 16: Using the Datastore API

I thank all readers and my friends who made production of the eBook possible.

Download the eBook and distribute. It is free of cost.

Download it here.

Cheers
Romin

Welcome to Episode 15. In this episode, we shall cover how to incorporate a CAPTCHA in your application. The CAPTCHA solution that I will be demonstrating over here is the ReCAPTCHA project found here. To quote from its Wikipedia entry, a CAPTCHA is a type of challenge-response test used in computing to ensure that the response is not generated by the computer. Usually, one sees one or two words shown to us that we need to enter and they need to match before our request submission is accepted by the server.

See it in Action

 

Let us first see the application that we shall be developing in its final form. The main screen is a dummy form that accepts one field and displays the CAPTCHA to the user as shown below:

If the CAPTCHA response is entered correctly and the submit button is clicked, the response is sent to the server. The server side validates the CAPTCHA challenge-response and will display an “all is well” message as shown below:

If the CAPTCHA response is not entered correctly, the server side will display an “all is not well” message as shown below:

ReCAPTCHA Project

 

We shall be using the ReCAPTCHAProject present at http://recaptcha.net/. The ReCAPTCHAproject is comprehensive and provides support for a variety of server side platforms, hence I have gone with it. I do not have a particular preference for it, except that most people recommended this to me and hence I decided to try it out. I was able to integrate it easily and quickly into a recent App Engine application that is currently live, so I can vouch for its ease of integration and which is what I shall demonstrate in this experiment.

To get started with ReCAPTCHA, we need to do 2 things:

1. Get your private/public keys for the site name hat your application will be running on.

 

Follow these steps:

  1. First get yourself a ReCAPTCHAaccount. Go to http://recaptcha.net/whyrecaptcha.html and click on the Sign up Now! button.
  2. This will bring up a login form, but you click on the “New to reCaptcha? Sign up” link. Here you can register by giving a username/password/email . Click on the Sign up now button.
  3. On successful registration, you will signed in automatically and led to a Create a reCAPTCHA key page. Here you need to enter the site name that you will be using ReCAPTCHAon. Remember you can get ReCAPTCHAkeys for more than 1 site and you can manage it all from your account itself. For e.g. you can get a key for localhost since that will be what we need to test on first before deploying to the Google App Engine cloud. And then you will atleast need one based on the Application ID of your Google App Engine application. So for e.g. if my application ID is gaejexperiments, then my full site url is http://gaejexperiments.appspot.com. So you will need to enter gaejexperiments.appspot.com in the Domain form field.
  4. Click on Create Key. For e.g. shown below is the series of screens that I got when I wanted keys for localhost. You will need to do the same if you wish to test the code first on your local system i.e. localhost.

 

Then when you click Create Key, you will be presented the Public Key and Private Key as shown below:

Please note down the Public and Private Key since you will need to use them in your application. The usage is straightforward and mentioned on the screen and I will be repeat it here:

  • Use the Public Key in your HTML page (Javascript) to communicate with the ReCAPTCHA server to get the challenge (image).
  • Use the Private Key in your server side Java code that will be used to communicate with the Server to verify the challenge + response submitted by the form in the above step.

2. Download the JAR file that we shall be using at the Server side to verify the CAPTCHA challenge and response.

 

To do the server side validation, you need to download a JAR  file that is available from the following url : http://code.google.com/p/recaptcha/downloads/list?q=label:java-Latest. Go to the URL and you shall be presented with a screen as shown below:

The JAR file is required and it encapsulates all communication between our application and the ReCAPTCHA Servers.

Download the zip file, expand it in  an appropriate folder and you will find the recaptcha4j-0.0.7.jar present in that.  We will need it later on to be referenced in your App Engine Eclipse Project.

Developing our Application

 

The first thing to do is to create a New Google Web Application Project. Follow these steps:

1. Either click on File –> New –> Other or press Ctrl-N to create a new project. Select Google and then Web Application project. Alternately you could also click on the New Web Application Project Toolbar icon as part of the Google Eclipse plugin.
2. In the New Web Application Project dialog, deselect the Use Google Web Toolkit and give a name to your project. I have named mine GAEJExperiments. I suggest you go with the same name so that things are consistent with the rest of the article, but I leave that to you. In case you are following the series, you could simply use the same project and skip all these steps altogether. You can go straight to the Servlet Development section.
3. Click on Finish.

This will generate the project and also create a sample Hello World Servlet for you. But we will be writing our own Servlet.

Add the recaptcha4j-0.0.7.jar file to your Project classpath

 

The recaptcha4j-0.0.7.jar file that we downloaded needs to be added to the runtime and compile time CLASSPATH of your App Engine Eclipse Project. Follow these steps:
a) Copy recaptcha4j-0.0.7.jar to the WEB-INFlib folder of your project. This folder is present in the war sub folder of your main project structure.
b) Go to Project properties –> Java Build Path and reference the JAR file (recaptcha4j-0.0.7.jar) in the Java Build Path of your application as shown below:

The Front end HTML form [captcha.html]

 

<html xmlns="<a href="http://www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml</a>" lang="en" xml:lang="en">
<head>
<title>ReCaptcha Integration</title>
<script type="text/javascript" src="<a href="http://api.recaptcha.net/js/recaptcha_ajax.js%22%3E%3C/script">http://api.recaptcha.net/js/recaptcha_ajax.js"></script</a>>
<script type="text/javascript">
function PreloadCaptcha() {
showRecaptcha();
}

function showRecaptcha() {
Recaptcha.create("YOUR_PUBLIC_KEY", "dynamic_recaptcha_1", {
theme: "white",
callback: Recaptcha.focus_response_field
});
}

var xmlhttp;
function submitFormData(name)
{

//alert("Message");
xmlhttp=null;
if (window.XMLHttpRequest)
{// code for IE7, Firefox, Opera, etc.
xmlhttp=new XMLHttpRequest();
}
else if (window.ActiveXObject)
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
if (xmlhttp!=null)
{
xmlhttp.onreadystatechange=state_Change;
var url = "postformdata";
var params = "name="+name+"&recaptcha_challenge_field=" + Recaptcha.get_challenge() + "&recaptcha_response_field="+Recaptcha.get_response();
var status = document.getElementById("status");
status.innerHTML = "<img src='img/ajax-loader.gif'><b>Submitting your data. Please wait...</b>";
xmlhttp.open("POST",url,true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.setRequestHeader("Content-length", params.length);
xmlhttp.setRequestHeader("Connection", "close");
xmlhttp.send(params);
}
else
{
alert("Your browser does not support XMLHTTP.");
}
}

function state_Change()
{
if (xmlhttp.readyState==4)
{// 4 = "loaded"
if (xmlhttp.status==200)
{
// 200 = "OK"
var status = document.getElementById("status");
status.innerHTML = xmlhttp.responseText;;
Recaptcha.reload();
setTimeout(function() {
status.innerHTML = "";
}, 3000);
}
else {
var status = document.getElementById("status");
status.innerHTML = xmlhttp.responseText;;
Recaptcha.reload();
setTimeout(function() {
status.innerHTML = "";
}, 3000);
}
}
}
</script>
</head>

<body onload="PreloadCaptcha()">
<FORM NAME="dataform">
<TABLE>
<TR>
<TD><b>Your name:</b></TD>
<TD><INPUT NAME="txtName"/></TD>
</TR>
<TR>
<TD colspan="2"><div id="dynamic_recaptcha_1"></div></TD>
</TR>
<TR>
<TD colspan="2"><INPUT type="button" value="Submit Data" name="btnSubmitData" onClick="submitFormData(txtName.value); return true"></TD>
</TR>
<TR>
<TD colspan="2"><div id="status"/></TD>
</TR>
</TABLE>
</FORM>
</body>
</html>

 

Let us go through the important parts of the code:

1. The first part to notice is that I have referenced the javascript file for the ReCAPTCHA code as shown :

<script type="text/javascript" src="http://api.recaptcha.net/js/recaptcha_ajax.js"></script>

2. There is a javascript function call being made on the load of the body (<body onload=”PreloadCaptcha()”>). This invokes the function showRecaptcha() that is shown below:

 

Recaptcha.create("YOUR_PUBLIC_KEY", "dynamic_recaptcha_1", {
theme: "white",
callback: Recaptcha.focus_response_field
});

 

 

The Recaptcha class is available from the javascript file and we use the create method. The first parameter is the PUBLIC_KEY that you get when you registered at ReCaptcha for  your site name. If you are testing it locally, this will be the Public Key for your localhost site. The second parameter is a DIV element that is present in the form.

The create() method on successful invocation will populate the DIV element with the CAPTCHA challenge/response fields.

3. The rest of the code is standard AJAX stuff. The submit button invokes the submitDataForm() that does the following:

  • It will invoke the URL : /postformdata that will be our servlet that does the verification. The servlet code is discussed in the next section
  • It will form the request parameter string as shown below:
    var params = “name=”+name+”&recaptcha_challenge_field=” + Recaptcha.get_challenge() + “&recaptcha_response_field=”+Recaptcha.get_response();
  • Note the two fields : recaptcha_challenge_field and recaptcha_challenge_response_field. We get the two values from the Recaptcha class methods, get_challenge() and get_response() respectively. The get_challenge() is what was provided by the ReCAPTCHA Server and the get_response() is what was entered by the user.
  • Finally we do a POST to the server and collect the response. The response is that displayed in another DIV element “status”.

Please note that the code is just for demonstration purpose and may not represent the best practices or most efficient way of writing JavaScript, AJAX, etc.

The Servlet [PostFormDataServlet.java]

 

package com.gaejexperiments.captcha;

import java.io.IOException;

import javax.servlet.http.*;

//RECAPTCHA
import net.tanesha.recaptcha.ReCaptchaImpl;
import net.tanesha.recaptcha.ReCaptchaResponse;
@SuppressWarnings("serial")
public class PostFormDataServlet extends HttpServlet {

public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
resp.setContentType("text/plain");
String strResponse = "";
try {

/*
* CAPTCHA CHECK
*
*
*/
//First validate the captcha, if not -- just get out of the loop
String challenge = (String) req.getParameter("recaptcha_challenge_field");
String response = (String) req.getParameter("recaptcha_response_field");
if ((challenge == null) || (response == null)) {
throw new Exception("Your words did not match. Please try submitting again.");
}

String remoteAddr = req.getRemoteAddr();
ReCaptchaImpl reCaptcha = new ReCaptchaImpl();

reCaptcha.setPrivateKey("YOUR_PRIVATE_KEY");

ReCaptchaResponse reCaptchaResponse =
reCaptcha.checkAnswer(remoteAddr, challenge, response);

if (!reCaptchaResponse.isValid()) {
//RECAPTCHA VALIDATION FAILED
throw new Exception("Your words did not match. Please try submitting again.");
}

strResponse = "Your record has been accepted and you did a good job entering the two words. Thank you";
}
catch (Exception ex) {
strResponse = "You record was not accepted. Reason : " + ex.getMessage();
}
resp.getWriter().println(strResponse);
}
}

Let us go through the important parts in the code:

1. Notice that we are importing  2 classes : net.tanesha.recaptcha.ReCaptchaImpl and net.tanesha.recaptcha.ReCaptchaResponse at the beginning. These are the implementation classes that encapsulate the verification with the ReCAPTCHA Server and the response from the ReCAPTCHA Server respectively.

2. We first extract out the challenge and the response fields as passed by our form.

 

String challenge = (String) req.getParameter("recaptcha_challenge_field");
String response = (String) req.getParameter("recaptcha_response_field");

 

3. We also need the Remote IP Address that is making the request.

 

String remoteAddr = req.getRemoteAddr();

 

4. We then instantiate an instance of the ReCaptchaImpl class. We set the Private Key that we got during our registration for the site localhost. Please use your key over here. And then we make a call to their server by invoking the checkAnswer method. The checkAnswer method takes 3 parameters : challenge, response and the Remote Address.

 

ReCaptchaImpl reCaptcha = new ReCaptchaImpl();

reCaptcha.setPrivateKey("YOUR_PRIVATE_KEY");

ReCaptchaResponse reCaptchaResponse =
reCaptcha.checkAnswer(remoteAddr, challenge, response);

 

5. We will receive an instance of the ReCaptchaResponse class. We simply use a utility method isValid() to determine if the response entered for the challenge was correct. And depending on that we send back an appropriate response back to the browser, which is then displayed to the user.

Servlet Configuration

To complete our Servlet development, we will also need to add the <servlet/> and <servlet-mapping/> entry to the web.xml file. This file is present in the WEB-INF folder of the project. The necessary fragment to be added to your web.xml file are shown below. Please note that you can use your own namespace and servlet class. Just modify it accordingly if you do so.

 

<servlet>
<servlet-name>PostFormDataServlet</servlet-name>
<servlet-class>com.gaejexperiments.captcha.PostFormDataServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>PostFormDataServlet</servlet-name>
<url-pattern>/postformdata</url-pattern>
</servlet-mapping>

 

Running the application locally

I am assuming that you have already created a new Google Web Application Project and have created the above Servlets, web.xml , etc. We shall be running this episode within our local development server only since the keys that I have used are for localhost. Before you deploy the application to the actual Google App Engine infrastructure using your Application ID, do remember to get yourself the appropriate public/private ReCAPTCHA keys for your site name.

So assuming that all is well, we will run our application, by right-clicking on the project and selecting Run As –> Web Application. Launch the browser on your local machine and navigate to http://localhost:<YourLocalPort>/captcha.html

Conclusion

In this episode, we saw how to incorporate a CAPTCHA in our Google App Engine application. Please remember that you will need to determine if you really need a CAPTCHA in your application. There are some good points mentioned in this article on various other techniques that can be used to address the same problems.

Till the next episode ….

Welcome to Episode 14. In this episode we shall cover how you can get started with writing Google Chrome Extensions. We shall also drive the Extension via our App Engine application. We have already seen in earlier episodes how your App Engine applications participated within Google Wave, Google Talk, etc. So this episode takes it further and shows how you can write a Google Chrome Extension whose data will be provided by our App Engine Application.

Google Chrome Extension

Google Chrome browser since its release has got a fair amount of developer mind share. Recently they addressed extensibility of the Chrome browser by announcing support for writing your own extensions. So the focus is now on the developer to take this extension mechanism, harness it and produce some great extensions that extend the capabilities of the browser. You can read here the official announcement for Extension support over here.

Google Chrome Dev Channel version

Note: The first thing that you need to do is download a copy of the Chrome Browser from their Dev Channel because extensions are currently supported there till they come into the mainstream release. So given that, make sure that you download that version of Google Chrome browser.

The Beta channel or the Dev channel is available at : http://www.chromium.org/getting-involved/dev-channel.

Depending on your Operation System, download the appropriate version and install it. To check that you have extension enabled correctly, launch the Chrome Browser and click on the Settings icon as shown below. You should see the Extensions menu item available.

It is a good idea to take a look at the existing Chrome Extensions gallery that lists several excellent extensions that you can start using today inside of your Google Chrome browser. The gallery is available at Gallery : https://chrome.google.com/extensions. This page also contains a link where you can submit your extension too for the world to use!

See it in Action

Let us see our Google Chrome Extension that we shall build in this episode. The extension is a simple Reminder application. It shows the current Reminders that you have set up for the month. All the data is saved and managed by our App Engine application and the Chrome Extension simply requests and fetches the data for the current month.

Shown below is the Chrome Browser screen with our Extension available as a little red icon in the Toolbar.

When we click the icon, it will fetch the reminders that I have setup in the current month (December) and display them as shown below:

Sounds good? We shall split up the details into two parts:

Part I : The first part will deal with developing/deploying our Server side application. This application will be deployed as a Google App Engine Java application. We shall keep the implementation simple and not do any database operations, etc. The focus is on the second part where we need to understand what it takes to get a simple Chrome Extension up and running, and which is powered by this Google App Engine application.

Part II: The second part will deal with how you can write a Google Chrome Extension. We will consider the bare minimum that you need to do to get your extension working. I will leave the design and look/feel aspects to you.

Alright then, let’s get started.

Developing our Java Web Application  : MyReminderApp

The first thing to do is to create a New Google Web Application Project. Follow these steps:

1. Either click on File –> New –> Other or press Ctrl-N to create a new project. Select Google and then Web Application project. Alternately you could also click on the New Web Application Project Toolbar icon as part of the Google Eclipse plugin.

2. In the New Web Application Project dialog, deselect the Use Google Web Toolkit and give a name to your project. I have named mine MyReminderApp. I suggest you go with the same name so that things are consistent with the rest of the article.

3. Click on Finish. This will generate the project and also create a sample Servlet named MyReminderAppServlet for you. We shall be modifying this Servlet code appropriately.

Design discussion

Now that the project template is ready, let me discuss in brief what we shall be building:

1. A simple Reminder App that contains a list of Reminders. Each Reminder is kept simple and has 3 attributed only: The day, month and the description. For e.g. [25,12,Christmas Day], [19,12,My Birthday], [1,1,New Year Day], [31,7,Pay Taxes] and so on.

2. So we will define a Reminder.java class that simply has getter/setters for the 3 attributes : day, month and description

3. We will then write a ReminderService.java class (A Singleton) that implements two simple functions:

  • ArrayList<Reminder> findRemindersByMonth(String month)
    This method returns the reminders for a particular month. We pass in “1″ and we get reminders defined for January, etc.
  • ArrayList<Reminder> findCurrentMonthReminders()
    This method returns the reminders for the current month. The method determines which month is currently on and retrieves reminders for that month only. And this is precisely the method that will be invoked when our extension asks for the current reminders.

4. Finally, we shall modify the MyReminderAppServlet.java so that it implements a simple REST like interface for retrieving the reminders. If the request stream contains a parameter named month, then the reminders will be returned for that month by invoking the findRemindersByMonth method. If the request stream does not contain a parameter named month, then the current month reminders will be returned by invoking the findCurrentMonthReminders() method. The response will be returned in XML format.

Given next are the code listings, that should be straight forward to understand now:

Reminder.java

 

package com.reminderapp;

public class Reminder {

private String day;
private String month;
private String event;

public Reminder(String day, String month, String event) {
super();
this.day = day;
this.month = month;
this.event = event;
}
public String getDay() {
return day;
}
public void setDay(String day) {
this.day = day;
}
public String getMonth() {
return month;
}
public void setMonth(String month) {
this.month = month;
}
public String getEvent() {
return event;
}
public void setEvent(String event) {
this.event = event;
}
}

 

The Reminder class simply has 3 attributes : day , month and year. There are getter/setter for each of these attributes and a constructor that takes in all the 3 attributes. We are keeping at as String for now to keep things simple.

ReminderService.java

 

package com.reminderapp;

import java.util.ArrayList;
import java.util.Calendar;

public class ReminderService {

private static ReminderService _instance = null;

private ArrayList<Reminder> _reminderList = null;

private ReminderService() {
initializeData();
}

public static ReminderService getInstance() {
if (_instance == null) {
_instance = new ReminderService();
}
return _instance;
}

public ArrayList<Reminder> findRemindersByMonth(String month) {
ArrayList<Reminder> _results = new ArrayList<Reminder>();
for (Reminder reminder : _reminderList) {
if (reminder.getMonth().equals(month)) {
_results.add(reminder);
}
}
return _results;
}

public ArrayList<Reminder> findCurrentMonthReminders() {
ArrayList<Reminder> _results = new ArrayList<Reminder>();
String currentMonth = "" + (Calendar.getInstance().get(Calendar.MONTH)+1);
for (Reminder reminder : _reminderList) {
if (reminder.getMonth().equals(currentMonth)) {
_results.add(reminder);
}
}
return _results;
}

private void initializeData() {
_reminderList = new ArrayList<Reminder>();
Reminder R1 = new Reminder("1","1","New Year Day");
Reminder R2 = new Reminder("26","1","Republic Day");
Reminder R3 = new Reminder("15","8","Independence Day");
Reminder R4 = new Reminder("25","12","Christmas Day");
Reminder R5 = new Reminder("31","12","New Year Eve");

_reminderList.add(R1);
_reminderList.add(R2);
_reminderList.add(R3);
_reminderList.add(R4);
_reminderList.add(R5);

}

}

 

The ReminderService is a singleton class. And we are initializing some data here via the initializeData() call. We are not complicating things by using a database here but this is something that should ideally be there in a real application. As you can notice we add 5 sample records and for the current month i.e. December (12), we have two events. The two methods findRemindersByMonth and findCurrentMonthReminders are straightforward in which it goes through the reminderList and matches the records as appropriate.

MyReminderAppServlet.java

 

package com.reminderapp;

import java.io.IOException;
import java.util.ArrayList;

import javax.servlet.http.*;

@SuppressWarnings("serial")
public class MyReminderAppServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
resp.setContentType("text/xml");
//resp.getWriter().println("Hello, world");

String month = (String)req.getParameter("month");
ArrayList<Reminder> _results = null;
if (month == null) {
_results = ReminderService.getInstance().findCurrentMonthReminders();
}
else {
_results = ReminderService.getInstance().findRemindersByMonth(month);
}

StringBuilder SB = new StringBuilder();
SB.append("<?xml version="1.0"?>");
SB.append("<ReminderList>");
for (Reminder reminder : _results) {
SB.append("<Reminder>"+reminder.getDay()+"/"+reminder.getMonth()+" : " + reminder.getEvent()+"</Reminder>");
}
SB.append("</ReminderList>");
resp.getWriter().println(SB.toString());
}
}

 

Let us go through the code for the MyReminderAppServlet in brief:

1. It implements the doGet(…) method and inspects if a request parameter named month is passed or not.

2. If the month parameter is passed, its value is extracted and the findRemindersByMonth method is invoked. Else the findCurrentMonthReminders is invoked.

3. The result is then formatted into a simple XML structure and then it is returned back in the response stream.

Configuring the Servlet

We need to make sure that the entry for  <servlet/> and <servlet-mapping/> in the web.xml file. is there for MyReminderAppServlet. This file is present in the WEB-INF folder of the project. The necessary fragment to be added to your web.xml file are shown below.

 

<servlet>
<servlet-name>MyReminderApp</servlet-name>
<servlet-class>com.reminderapp.MyReminderAppServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyReminderApp</servlet-name>
<url-pattern>/myreminderapp</url-pattern>
</servlet-mapping>

 

Deploying the Application

To deploy the application, you will need to first create your Application ID. The Application Identifier can be created by logging in at http://appengine.google.com with your Google Account. You will see a list of application identifiers already registered under your account (or none if you are just getting started). To create a new Application, click on the Create Application button and provide the Application Identifier as requested. Please note this name down since you will be using it for deployment.

For e.g. I have registered an application identifier named itsareminder.

To deploy the application, follow these steps (they should be familiar to you now):

  1. Click on the Deploy Icon in the Toolbar.
  2. In the Deploy dialog, provide your Email and Password. Do not click on Deploy button yet.
  3. Click on the App Engine Project settings link. This will lead you to a dialog, where you need to enter your Application ID [For e.g. my Application Identifier itsareminder]
  4. Click on OK. You will be lead back to the previous screen, where you can click on theDeploy button. This will start deploying your application to the GAEJ cloud. You should see several messages in the Console window as the application is being deployed.
  5. Finally, you should see the message “Deployment completed successfully”.

Testing our Reminder Service

To test our Reminder Service, we can use the http://<AppId>.appspot.com/myreminderapp URL. You can replace it with your AppId and endpoint as appropriate to your application. For e.g. I have hosted it at http://itsareminder.appspot.com/myreminderapp and putting this in the browser returns me the Reminders that are set for the current Month i.e. Month of December. I get the following response XML:

 

<ReminderList><Reminder>25/12 : Christmas Day</Reminder><Reminder>31/12 : New Year Eve</Reminder></ReminderList>

 

Now that the development of the Reminder Service is complete, we can move on to the next part i.e. developing the Chrome Extension.

Developing the Reminder Service Chrome Extension

The official documentation on developing a Chrome Extension is quite good and you can refer it for more details. I will keep things to a minimum here and cover only what is required to get the Chrome Extension up and running. Follow these steps:

1. Create a folder on your machine. For e.g. I have created the following folder on my machine in C:. It is c:MyReminderExtension. All the necessary files that we create for the extension will reside in this folder.

2. To create an extension, we need at the minimum 3 files. The three files are an icon file for your extension. This icon will be visible in the toolbar as we saw earlier. The second file is a manifest file called manifest.json. The manifest provides important metadata about the extension like name,version, permissions, action, etc. And the final file is the html file in which you will keep the core of your extensions code written in Javascript.

3. So the first thing that I do is to create a manifest file for the extension. The manifest.json file for the Reminder Service is shown below:

 

{
"name": "My Reminder Extension",
"version": "1.0",
"description": "Reminders based on month",
"browser_action": {
"default_icon": "myreminder.png",
"popup": "reminder.html"
},
"permissions": [
"http://itsareminder.appspot.com/"
]
}

 

As you can see this is pretty simple. There is the standard name/version/description that you can give as per your preferences. The browser_action has two important attributes. The default_icon refers to the icon file that contains the icon for our extension. So this means that we will have the myreminder.png file present in the same folder. The popup attribute indicates which HTML file to be invoked when the icon is clicked. The permissions attribute indicates which domain the extension will be accessing to retrieve the data. In our case it is the http://itsareminder.appspot.com domain at which we hosted our application.

4. As shown above, we will create a file named myreminder.png in the same folder i.e. C:MyReminderExtension.

5. Finally, we have the reminder.html file that will contain the code of the extension. It is listed below:

 

<style>
body {
min-width:200px;
overflow-x:hidden;
}

</style>

<script>
var req = new XMLHttpRequest();
req.open(
"GET",
"http://itsareminder.appspot.com/myreminderapp",
true);
req.onload = showReminders;
req.send(null);

function showReminders() {
var reminders = req.responseXML.getElementsByTagName("Reminder");

for (var i = 0, reminder; reminder = reminders[i]; i++) {
var reminderDIV = document.createElement("div");
reminderDIV.innerHTML = reminder.textContent;
document.body.appendChild(reminderDIV);
}
}

</script>

 

The code above is focussed around the <script> tag. It requires knowledge about AJAX and the XMLHttpRequest object along with some Javascript basics. The core pieces are mentioned below:

1. We use the XMLHttpRequest to make a call to our Reminder Service. The end point is where our servlet is present to serve the requests i.e. http://itsareminder.appspot.com/myreminderapp.

2. When we get the response XML, we call the showReminders() method. This method simply parses the <Reminder> tags and for each tag, it creates a <div> element in which the text content is the Reminder Event Text returned. Its appends each of the <div/> elements to the document.body as shown.

Simple isnt it? Now let us see how to install this extension into your Chrome browser to test it out.

Installing the Reminder Chrome Extension

At this point in time, I will assume that you have the following 3 files created in the C:MyReminder folder:

  • manifest.json
  • myreminder.png
  • reminder.html

You could have created different files but just remember that you need the icon, manifest and your html file that implements the Chrome extension code.

Once you have that, follow these steps:

1. Launch Google Chrome Browser. You need to go the Extensions page that allows you to view the current extensions installed and from where you can either install new extensions/reload/uninstall,etc. There are two ways to go to that page:

  • In the address, type in chrome://extensions
  • From the settings option as mentioned in the start of this article, click on the “Extensions” menu option.

2. You should see a screen that looks something like this:

What this means is that there are no extensions currently installed in my Chrome browser. If there were any, they would be listed over here.

3. Click on the Load unpacked extension button. This will bring up a dialog box from where you can navigate to the folder where your extension files are present. In our case, it was the C:MyReminderExtension and so I navigate to that and click on OK as shown below:

4. This will install the Chrome Extension in the browser as shown below:

5. Finally, we can test out the browser by clicking on the icon and it will display the list of Reminders for the current month.

If you have newer versions, all you need to do is simply click on Reload option for the extension. Alternately, you can also Uninstall and load it again.

Some points to note:

1. You have not completely installed this extension. What this means is that if you close the Chrome Browser and launch it again, this Extension will not be visible.

2. The next step would be to visit the official documentation at http://code.google.com/chrome/extensions/getstarted.html and also go to the gallery and submit your Extension. The gallery is present at : https://chrome.google.com/extensions and you will find a link to submit your extension. Click that and follow the steps to submit your extension. All you will need to provide from your side will be a ZIP file of the folder that we mentioned above.

Conclusion

This concludes the final episode of the year 2009. In this episode we covered how to write a Google Chrome Extension and power it via a Google App Engine application.

I hope that the new year takes you further into writing Cloud applications using Google App Engine. I will continue to explore tools/languages/APIs/extensions that work in coherence with the Google App Engine. The journey or might I say “Experiments” are far from over. Have a great 2010 and thanks for reading this blog. Its been a joy to share my experiments with all of you.

Welcome to Episode 13 of this series. It has been a while since the last episode and to compensate I thought why not cover something that has just been released. In keeping with that line, this episode will cover the recently released Blobstore API in the 1.3.0 release of the Google App Engine SDK.

Using the Blobstore API, we shall cover how to build an application that mimics several sites that allow you to upload a picture and send a Tweet. The Tweet will then contain a link back to the image that you uploaded. Blobstore API now makes all this possible along with several other applications that you could envision like file sharing, document management, etc.

What is the Blobstore API?

The Blobstore API allows your application to store blobs of information, where each blob can be upto 50MB in size. These blobs are typically large files like video and images. This is different from the datastore API that does not allow for saving objects upto this large size. The API documention for the Blobstore API is provided here.

The Blobstore API provides two types of functions:

  • An ability to upload and save the blob automaticallyThe BlobstoreService which is provided by the com.google.appengine.api.blobstore.BlobstoreService allows us to specify a URL where users can upload their large files. You can think of this url as the action element in the HTML form. The implementation at this URL is internal to the BlobstoreService. But what it does is significant. It will extract out the file contents that you uploaded and store it as a Blob in the database. Each blob that is stored in the database is associated with the a Blob Key. This Blob key is then provided to your url and you can then use the Blob Key to do anything within your application. In our case, we form a url that is tweeted to the users who can then view the picture that we uploaded.
  • An ability to serve or retrieve the blob.The BlobstoreService also provides an ability to serve or retrieve the blob that was saved successfully. It will provide the blob as a response that could then use as a source in an <img> element for example. All you need to do is provide it the Blob Key and the response stream and in return, it will provide the content properly encoded as per its type that you could then use.

It will be clear as we work out the application in the next few sections.

Note: The Blobstore API is enabled in your application only if you enable “billing” for your applications. What this means is that you can try out the API with your local development server but if you deploy the same on the App Engine cloud, your application will not be able to use the API if it has not been enabled for “billing”. So keep that in mind before leaping with joy that your new application enabled with large file uploads, etc is now ready to start functioning in the cloud. In case you try to use any Blobstore APIs without enabling billing in your application, you will get a FeatureNotSupported exception and the text as: “The Blobstore API will be enabled for this application once billing has been enabled in the admin console.”

Prerequisites

Google announced the release of version 1.3.0 of the App Engine SDK for both Python and Java. We need to set our development environment with this release of the SDK since the Blobstore API was released in this version.

So the first thing that you need to do is to download the latest release 1.3.0 of the App Engine SDK and upgrade your Eclipse development environment with that. There are two ways you can do that:

a. Use the Update features of Eclipse to download and install the latest SDK

b. Download the SDK from the following link. Make sure you download the Java SDK. Further to the download, inside of your Eclipse IDE, go to Windows -> Preferences and then Google -> App Engine SDK. Click on Add and provide the path to the expanded 1.3.0 distribution on your machine.

I had covered in detail how to upgrade your SDK for your Eclipse IDE when new releases come out. This was covered in Episode 5, in which we upgraded from 1.2.5 -> 1.2.6. Readers are advised to take a look at it in case they are not familiar. The process remains the same.

Tweet My Picture in Action

The application that we are going to develop is called Tweet My Picture. There are several sites now that allow you to upload a picture to their site and then send a Tweet. The Tweet contains a simple description and the url to the picture. An example is TwitPic.  I thought the Blobstore API now actually makes an application of this kind way too easy to create, so why not demonstrate that. We will not be creating a polished looking application but rather demonstrating the API and showing that it works. Remember that all the instructions later on deploying and running this application will happen in the local development server and not the live App Engine site because I need to enable billing in order to utilize the Blobstore Service. But the best part is that the local development server works fine, so we can do our complete development and testing on it. Then you are the best judge of writing your own application that utilizes the Blobstore API and making it a billable application when you go live.

Before we get down to code, we should see the end result.

Assuming that I have started my local development server, I navigate to the index.jsp page that is shown below:

This will display a simple page, where you provide the following information:

  • Your Twitter UserId and Password
  • The image file that you wish to upload. Simply click the Choose File button and select a file to upload.

Then when you click the Upload Picture button, what happens is the following:

1. The file contents that  you uploaded will be stored by the Blobstore API into the datastore automatically for you. It will provide you with the Blob Key for the blob that was saved.

2. We use the Blob Key to create a simple Tweet message containing the link. And then we use Twitter4J to send the Tweet.

3. Finally a page is displayed indicating the status of the Tweet, the Tweet contents and it also shows the image that you uploaded. This page is shown below:

Now, switch to your Twitter client and you will notice that the Tweet has been posted. A sample screenshot is shown below.The url is pointing to the localhost but that is because I wish to do my testing locally and I have not enabled billing for my live application at the appspot.com domain.

Clicking on the Tweet link, takes you back to an page that shows you the picture as below:

Nice, isn’t it? Do not limit yourself only to this example. You can now start thinking on creating serious document management, file sharing, etc applications and use the App Engine infrastructure to host it. So let’s get coding now.

Implementing Tweet My Picture

The first thing to do is to create a New Google Web Application Project. Follow these steps:

1. Either click on File –> New –> Other or press Ctrl-N to create a new project. Select Google and then Web Application project. Alternately you could also click on the New Web Application Project Toolbar icon as part of the Google Eclipse plugin. Please make sure that you have installed the latest 1.3.0 version of the App Engine SDK and are using that as the API for this version. Shown below is the New Project screenshot where you should see an additional SDK 1.3.0 and need to select that.

2. In the New Web Application Project dialog, deselect the Use Google Web Toolkit and give a name to your project. I have named mine GAEJExperiments. I suggest you go with the same name so that things are consistent with the rest of the article, but I leave that to you. In case you are following the series, you could simply use the same project and skip all these steps altogether. You can simply go to the next part i.e. the Servlet code.

3. Click on Finish. This will generate the project and also create a sample Hello World Servlet for you. But we will be writing our own Servlet.

Adding Twitter4J Jar to the Classpath and WEB-INFlib folder

Since we will be using the excellent Twitter4J library for posting to Twitter, you need to download the latest version of the JAR file. Download it from here. I have used twitter4j-2.0.10.jar. You need to copy this file to the WEB-INFlib folder of the project. Also add the JAR file to the Build Classpath of the Eclipse project.

index.jsp

The first page is the index.jsp page. This is a simple page that will have a form that accepts the Twitter UserId/Password along with the file to be uploaded. The code is shown below. Just a quick note that all the JSP files will go into the war folder of the project.

[code language="java"]

<%@ page import="com.google.appengine.api.blobstore.BlobstoreServiceFactory" %>
<%@ page import="com.google.appengine.api.blobstore.BlobstoreService" %>

<%
BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
%>
<html>
<head>
<title>Tweet My Picture</title>
</head>
<body>
<img src="tweet.png"/>
<h1>Tweet My Picture</h1>
<hr/>
<h2>Upload a picture and send an automatic Tweet</h2>
<form action="<%= blobstoreService.createUploadUrl("/upload") %>" method="post" enctype="multipart/form-data">
Twitter User Id : <input type="text" name="twitter_userid"/><br/>
Twitter Password : <input type="p" name="twitter_password"/><br/>
File :
<input type="text" name="filename"/>
<input type="file" name="myTweetPic"/>
<input type="submit" value="Upload Picture"/>
</form>
</html>

[/code]

 

Let us look at the main points in the code above:

1. It has a HTML <form/> that accepts the twitter userid and password. We also have an  <input/> element of type file, that allows the user to select the file from his machine to be uploaded.

2. The action attribute of the FORM is where you should pay attention. The Form action as you know submits the request parameters to a particular server side resource like a Servlet/JSP, etc. The action here is created via a helper function provided by the Blobstore API itself. Notice that first we are getting an instance of the Blobstore service via the BlobstoreServiceFactory as shown below:

BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();

Then we are using a helper function called blobstoreService.createUploadURL(“SUCCESS_PATH”) for the action. Let us spend some time on that since it is important and will help us understand what the BlobstoreService does behind the scenes.

If you launch your local development server and navigate to the index.jsp page and do a view source on the HTML, you will find that the action that is generated by the createUploadURL(…) method looks something like this:

action=”/_ah/upload/agp0d2VldG15cGljchsLEhVfX0Jsb2JVcGxvYWRTZXNzaW9uX18YEgw”. You may wonder where did our SUCCESS_PATH i.e. /upload go away. Let me explain:

When you do an upload, the request hits an internal URL in the App Engine infrastructure that will do the following:

1. The BlobstoreService implementation inside of AppEngine will extract out the Blob and save it for you automatically in the datastore. You do not have to do anything special. It does this for the all the Blobs that are present in the HTTP Request stream.

2. On successfully saving these blobs into the stores, what it has with itself now are one or more name value pairs i.e. a Map. The name is the request parameter name for your blob. For e.g. in our index.jsp, the file that we uploaded had the name of myTweetPic i.e. the form element. And the value will be the Blob Key that you can use to retrieve the Blob. It will put this map into the Request stream i.e. it will augment the Request stream with this map and then invoke your SUCCESS_PATH url. The SUCCESS_PATH url is the end point or in our case the Servlet.

This Servlet can then extract out each of the Blobs that were saved by inspecting the Map and do further action. In our case, we will be posting a link to Twitter. This ervlet is what we shall see next.

Upload Servlet

The Upload Servlet code is listed below.

 

package com.gaejexperiments.twitter;

import java.io.IOException;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.mail.Session;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import twitter4j.Status;
import twitter4j.Twitter;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;

@SuppressWarnings("serial")
public class Upload extends HttpServlet {
private final static Logger _logger = Logger.getLogger(Upload.class.getName());
private BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();

public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {

Map<String, BlobKey> blobs = blobstoreService.getUploadedBlobs(req);
BlobKey blobKey = blobs.get("myTweetPic");

if (blobKey == null) {
res.sendRedirect("/");
}
else {
String strStatus = "";
try {
String twitter_userid   = (String)req.getParameter("twitter_userid");
String twitter_password = (String)req.getParameter("twitter_password");
_logger.info("UserId:"+twitter_userid + " , Password:"+twitter_password);
Twitter twitter = new Twitter(twitter_userid,twitter_password);
String strTweet = "Check out my picture at : " + "<a href="http://localhost:8888/viewpic.jsp?blob-key=&quot;+blobKey.getKeyString">http://localhost:8888/viewpic.jsp?blob-key="+blobKey.getKeyString</a>();
Status status = twitter.updateStatus(strTweet);
strStatus = "Successfully updated the status to [" + status.getText() + "].";
_logger.info(strStatus);
}
catch (Exception ex) {
strStatus = "Could not update Twitter Status : " + ex.getMessage();
_logger.log(Level.WARNING,strStatus);
}
finally {
res.sendRedirect("/submitpic.jsp?blob-key="+blobKey.getKeyString() + "&status="+strStatus);
}
}
}
}

 

 

Let us go through the main points in the Upload Servlet code:

1. The Servlet implementation is the doPost() method. This method was invoked by the Blobstore Service after the blobs were successfully saved in the datastore.

2. As mentioned, the Blobstore Service augments the Request stream with a Map of successfully saved Blobs. It also provides a helper method that we can use to extract out this Map instance from the request stream. The code is shown below:

Map<String, BlobKey> blobs = blobstoreService.getUploadedBlobs(req);

3. The next thing to do is to get the Blob Key for our saved Blob. The Blob Key is unique and will be used to retrieve the Blob as we shall see later on in the viewpic.jsp code. The Key that we will use is the same as the input parameter name for our file blob that we provided in the FORM i.e. index.jsp.

BlobKey blobKey = blobs.get(“myTweetPic”);

4. We do a simple check to verify if the blobKey instance is not null. If not, we create a status update for twitter giving a url that points to the viewpic.jsp page which is passed the blob-key request parameter as shown below:

String strTweet = “Check out my picture at : ” + “http://localhost:8888/viewpic.jsp?blob-key=”+blobKey.getKeyString();

5. Finally, we using Twitter4J to post out tweet using the twitter id and password that we provided.

The status of our upload and tweet is then displayed by navigating to the submitpic.jsp page that simply shows the status and the picture that we uploaded.

submitpic.jsp

This JSP file is invoked by our Upload Servlet to display the status of our Tweet to twitter and the image that we uploaded as discussed in the previous section. You will notice an interesting thing over here and that is the <img/> element. The source attribute of the <img/> element is a servlet residing at an endpoint /serve. All it needs is the blob-key request parameter. We shall see this servlet in a moment.

 

<%@ page import="com.google.appengine.api.blobstore.BlobstoreServiceFactory" %>
<%@ page import="com.google.appengine.api.blobstore.BlobstoreService" %>

<html>
<head>
<title>Tweet My Picture - Submission</title>
</head>
<body>
<img src="tweet.png"/>
<h1>Tweet My Picture</h1>
<hr/>
<h3>Submission Result: <%=request.getParameter("status")%></h3>
<%
String blobKey = (String)request.getParameter("blob-key");
if (blobKey != null) {%>
You uploaded : <br/>
<img width="200" height="150" src="<%="<a href="http://localhost:8888/serve?blob-key=&quot;+blobKey">http://localhost:8888/serve?blob-key="+blobKey</a> %>">
<%}%>
</body>
</html>

 

 

Serve Servlet

This Servlet uses another helper function of the BlobStoreService. The method is called serve and it takes two parameters as shown in the listing below. The blob Key and the HTTP response stream. Note that we pass it the Blob Key that we got after saving our blob. What the serve method will do is that it will automatically retrieve the Blob from the Datastore using the Blob Key that was provided to it. Once it retrieves the Blob successfully, it will take the content of the Blob, set the correct MIME types and insert that into the HTTP Response stream. You can the use the Response stream to assign it as a source for the HTML <img/> element. We did exactly that in the submitpic.jsp file and we do the same in the viewpic.jsp file that will be invoked when someone clicks on the Twitter post.

 

package com.gaejexperiments.twitter;

import java.io.IOException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;

@SuppressWarnings("serial")
public class Serve extends HttpServlet {
private BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();

public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
BlobKey blobKey = new BlobKey(req.getParameter("blob-key"));
blobstoreService.serve(blobKey, resp);
}
}

 

 

viewpic.jsp

As discussed earlier, the Tweet that we post has the following format:

Check out my picture at http://localhost:8888/viewpic.jsp?blob-key=A_BLOB_KEY

This will invoke the viewpic.jsp file with a request parameter named blob-key which contains the Blob key value. So all we have to do i to create a <img/> element, whose source attribute will be served via the /serve endpoint as shown below. Simple isn’t it ?

 

<%@ page import="com.google.appengine.api.blobstore.BlobstoreServiceFactory" %>
<%@ page import="com.google.appengine.api.blobstore.BlobstoreService" %>

<%
BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
String blobKey = (String)request.getParameter("blob-key");
%>
<html>
<head>
<title>Tweet My Picture - View</title>
</head>
<body>
<img width="200" height="150" src="<%="<a href="http://localhost:8888/serve?blob-key=&quot;+blobKey">http://localhost:8888/serve?blob-key="+blobKey</a> %>">
</body>
</html>

 

Configuring the Servlets

We need to add both the Upload Servlet and the Serve Servlet with their appropriate entries for  <servlet/> and <servlet-mapping/> in the web.xml file. This file is present in the WEB-INF folder of the project. The necessary fragment to be added to your web.xml file are shown below.

 

<servlet>
<servlet-name>Upload</servlet-name>
<servlet-class>com.gaejexperiments.twitter.Upload</servlet-class>
</servlet>
<servlet>
<servlet-name>Serve</servlet-name>
<servlet-class>com.gaejexperiments.twitter.Serve</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Upload</servlet-name>
<url-pattern>/upload</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Serve</servlet-name>
<url-pattern>/serve</url-pattern>
</servlet-mapping>

 

 

Deploying the application

Since we are going to be running this application locally itself, we simply need to validate it against the local development server. Assuming that the development is complete, simply run the Web Application from your Eclipse IDE and using a browser, navigate to http://localhost:8888/index.jsp. In my case, the port is 8888, but it could be different for you.

Please make sure that you are connected to the Internet and do have a valid Twitter userid and password.

Refer to the section above on Tweet My Picture in action for the flow. I will not repeat it here again.

An interesting thing that you could look at is to navigate to http://localhost:8888/_ah/admin console. Go to the DataStore viewer. If you have uploaded several images successfully, you will find instances of the Entity : __BlobInfo__ getting created. Click on the List Entities button to see the entities that you have uploaded so far. The ID/Name column is the Blob Key for your Blob. A sample screenshot from my development server instance is shown below:

Conclusion

This brings the episode to an end. In this episode we saw the Blobstore API service that has been introduced in version 1.3.0 of the App Engine SDK. The Blobstore API service is experimental at this point in time and could change but it gives us enough today to see how to create application that require blobs of large sizes like video and images. This episode showed a simple implementation called Tweet My Picture but the possibilities are endless. Do keep in mind that if you have to use Blobstore Service in a live application hosted at the appspot.com domain, then you will need to enable “billing” for your application.

Till the next episode, have fun with the Blobstore Service!

P.S: Just in case you were wondering… the picture of the lion was taken at a distance of 2 feet away from it in the National Park, Mumbai.

Welcome to Episode 12 of the series. This episode is an extension of the earlier episode where we saw how to write Simple Google Wave Robots using the WadRobotFramework. I strongly recommend that  you have completed the earlier episode and have got comfortable with the WadRobotFramework since this episode builds on the earlier one.

To recap, the WadRobotFramework distinguishes between 2 kinds of Robots and I summarize it again over here.

Simple Robots : These robots we covered in the earlier episode and saw how you can write a simple robot to react to a blip by appending a new blip (BlipAppenderRobot) or even modify the Blip Text (BlipModifierRobot).

Advanced Robots : These are of main focus in this article and I reproduce from the earlier episode the text so that you understand what Advanced Robots are first. The definition of Advanced Robots is per the WadRobotFramework and it is not meant to indicate this is the final definition of it.

Advanced Robots are those that can react to instructions ( or commands) in the Blips. Here are some samples of Advanced Robots and how they would react to commands from Blips:

1. A character Counting Advanced Robot:

Your submitted Blip Text contains : “Here is some text in the blip. Please count the length of this message for me.” {mycountingrobot:count}.

You can write an advanced Robot (mycountingrobot) that knows how to count the length of the message. So it gets notified when it there is a instruction (count) in the Blip. Your Advanced Robot can then count out the characters and then either append or modify a blip as needed.

2. A Tweeting Robot:

Your submitted Blip Text contains the following text : “{mytweetingrobot:tweet} Some text to tweet”

You can write an advanced Robot (mytweetingrobot) that knows how to tweet the message to Twitter. So it gets notified when it there is a instruction (tweet) in the Blip. Your Advanced Robot can then append a blip or modify a blip saying that the message has been tweeted.

The best part of it all is that you could combine all of this into a single Robot that can respond to one or more commands. For example, take a look at Today’s Special Robot (see http://ppandhi.wordpress.com/2009/11/08/todays-special-robot/) that can respond to more than one command. It can give you the quotes, day in history, word of the day, cricket score, your daily horoscope by simply responding to the command that you type in.

So for example, you could write a robot and give it commands like this:

1. {myrobot:doCommand1}

2. {myrobot:doCommand2}

3. {myrobot:doCommandN} and so on.

In this article, we are going to see exactly how to achieve the above command Robot that will delegate its work to different workers who are responsible for executing the command i.e. doing the work.

Let us get a few definitions in place first:

1. The Command Robot: This is the main class of your Robot and you need to extend the org.wadael.waverobotfrmwrk.advanced.WithWorkersRobot. You need to have an identifier for your robot, which is a unique ID for your Robot. Let us call it GAEJRobot.

2. Each Command Robot is capable of following instructions or commands. These instructions are executed by the Workers.

3. A Worker is a Command implementation that performs a certain logic. For e.g. fetching a stock quote, getting a word of a day, sending a Tweet, sending an email, etc. Each worker will indicate what instruction or command it obeys.

As an example, say you want to write an Advanced Robot class (WithWorkersRobot) whose identifier is named GAEJRobot that can responds to the following two commands:

a. SendTweet
b. GiveWordOfTheDay

So, you will implement two Workers and register (add) them to the GAEJRobot class. The two worker classes will be :

  • SendTweetWorker which says that listens to an instruction named tweet and it will implement its core logic in the doWork() method.
  • GiveWordOfTheDayWorker which says that it listens to an instruction named wotd and it will implement its core logic in the doWork() method.

Now, in your Wave Conversation, you can give the following text in the Blip (of course after adding the Robot as a participant).

1. {GAEJRobot:tweet}

2. {GAEJRobot:wotd}

Voila! The WadRobotFramework will then do the heavy lifting for you. It roughly works as follows:

  • When you submit a Blip, it will scan the Blip Text for the identifier and see if it matches itself.
  • If yes, it will scan out the instructions and invoke the doWork() method of the Worker Robot that implements the instruction.

This is not all. The WadRobotFramework has thought about parameters that you may need to pass to your Robot. For e.g. consider the following fictitious instruction that you need to give to a Stock Quote Robot.

{StockQuoteRobot:getquote GOOG} or {StockQuoteRobot:getquote GOOG,MSFT,ADBE,ORCL,IBM}

In short the format is as follows:

{RobotIdentifier:Instruction<space>[parameters]}

So in the above two examples the parameter GOOG and the parameter string “GOOG,MSFT,ADBE,ORCL,IBM” will be passed to the doWork() method of your RobotWorker that has registered with the Advanced Robot and who implements the getquote instruction. Please read this last statement clearly with the highlighted words as the key pieces required to build out an Advanced Robot.

Simple yet powerful and it opens up a wide range of Robots that you can start writing today. So let me get started and demonstrate the basic flow to get an Advanced Robot up and running which can accept 1 or more instructions. The Robot does not do anything specific except for simply demonstrating the flow. Readers are expected to extend it with their ideas brimming inside their minds.

To understand what we will build, it helps to take a look at the running robot as shown below:

You will notice that I have added my robot called MyAdvancedRobot. The identifier for the Robot is GAEJRobot and the Robot has two workers (Worker1 and Worker2)  registered with it, which implement the instructions  command1 and command2 respectively.

Now when I submit the text {GAEJRobot:command1} , the doWork() method of the Worker1 is invoked. It simply accepts the command and prints out that it received the command with no parameters passed to it.

Similarly, look at the wave conversation below:

Here I give the command2 to the GAEJRobot and I am also passing a string of parameters. When I click the Done button, the doWork() method of the Worker2 is invoked. It simply accepts the command and prints out that it received the command with the parameter string. Now, it could have processed the parameters and performed its logic accordingly. This demonstrates how the wiring is done by WadRobotFramework to make your life easier in writing Advanced Google Wave Robots.

Let us start writing code now. I will assume that you are fairly conversant now with the typical project structure and requirements of writing a Google Wave Robot. If not, I suggest that you first go through these tutorials in the following order:

  • Episode 7 : Writing your First Google Wave Robot
  • Episode 11: Develop Simple Google Wave Robots using the WadRobotFramework

Project Setup

Create a New Project or use the same project MyGoogleWaveRobot that we used in the earlier Episode 11.

If you are creating a New project, then please make sure that you download all the JARs from the following two locations:

http://code.google.com/p/wave-robot-java-client/downloads/list

The web page when you navigate to the above URL is shown below:

ep7-6

Download all the above files to your machine.

The WadRobotFramework JAR file is available at the following location :

http://code.google.com/p/wadrobotframework/downloads/list

The web page when you navigate to the above URL is shown below:

Download the JAR file to your machine. Once you have downloaded the 5 JAR files, make sure that they are copied to the WEB-INFlib folder of your project and that the Project Build Path is also setup with the JAR files as shown below:

Writing the Advanced Robot : MyAdvancedRobot.java

The first step is to create a Java class. Call it MyAdvancedRobot.java. The source code is listed below:

 

package com.gaejexperiments.waverobot;

import org.wadael.waverobotfrmwrk.advanced.WithWorkersRobot;

public class MyAdvancedRobot extends WithWorkersRobot {

public MyAdvancedRobot() {
super();
//This will process 'command1'
addRobotWorker(new Worker1());

//This will process 'command2'
addRobotWorker(new Worker2());
}

@Override
public String getRobotIdentifier() {
return "GAEJRobot";
}

@Override
protected String getUsage() {
return "Advanced Robot commands : command1 and command2";
}

@Override
protected String getRobotSelfIntroduction() {
return "I am an Advanced Robot";
}

}

 

Let us dissect the code now:

1. Our Advanced Robot class MyAdvancedRobot extends the WithWorkersRobot class, which is required for creating the Advanced Robots with the WadRobotFramework.

2. The WithWorkersRobot class constructor uses a method called addRobotWorker(…) to add one or more workers to it. Remember each worker will execute or do its job as per the instruction that it obeys. So we are going to have two workers : Worker1 and Worker2 which we are adding to our AdvancedRobot. We will get to the listing of the Worker1 and Worker2 later but it is sufficient to keep in mind, that Worker1 class will perform the work required with command1 instruction is given in the Blip and Worker2 class will perform the work required when command2 instruction is given in the Blip. To recap, as you add more workers, you will need to add them here in the constructor using the addRobotWorker method.

3. The getRobotIdentifier() method is used to return the string that is the unique identifier of your Robot. This is used to distinguish Robots in the same Wave. The identifier if you recollect is going to be used by the other participants in the wave to give instructions to your Robot. As mentioned, the format in the Blip to give an instruction to your robot will be like this:

{RobotIdentifier:Instruction<space>[parameters]}

Each Instruction is implemented by a Worker. For e.g. command1 will be implemented by Worker1 and command2 will be implemented by Worker2.

So to invoke Worker1, we have to say {GAEJRobot:command1} in the Blip and submit it. Hence we return GAEJRobot in the getRobotIdentifier() method and this will be the unique way in which we identify this Robot

4. The getUsage() method is used to return a string that will be displayed to the participant when they type /help in the start of the Blip. This is useful to give a list of possible instructions that your Advanced Robot implements. In our case here, we are implementing two instructions and hence we have returned some string. But you can give an elaborate help string stating detailed command text, sample parameters, etc.

5. Finally, we have the getRobotSelfIntroduction() method. This is not mandatory but it is nice to announce to the particpants when you (Robot) gets added to the wave as a participant. Simply return a String that you would like to announce to the other (existing) participants in the Wave.

Implementing the Workers

We are now going to implement the Workers i.e. Worker1 and Worker2. The code is identical for both of them and it is listed below:

Worker1.java

 

package com.gaejexperiments.waverobot;

import org.wadael.waverobotfrmwrk.advanced.RobotWorker;

import com.google.wave.api.Blip;
import com.google.wave.api.Event;
import com.google.wave.api.RobotMessageBundle;

public class Worker1 implements RobotWorker {

public String getInstruction() {

return "command1";
}

public boolean doWork(RobotMessageBundle bundle, Blip blip, Event evt, String params) {
blip.getDocument().append("Robot Worker 1 got the command with parameter string : " + params);
return true;
}

public String getDescription() {
return "Robot Worker 1";
}
}

 

Worker2.java

 

package com.gaejexperiments.waverobot;

import org.wadael.waverobotfrmwrk.advanced.RobotWorker;

import com.google.wave.api.Blip;
import com.google.wave.api.Event;
import com.google.wave.api.RobotMessageBundle;

public class Worker2 implements RobotWorker {

public boolean doWork(RobotMessageBundle bundle, Blip blip, Event evt, String params) {
blip.getDocument().append("Robot Worker 2 got the command with parameter string : " + params);
return true;
}

public String getDescription() {
return "Robot Worker 2";
}

public String getInstruction() {

return "command2";
}

}

 

Let us go through the code of one of them and you will be able to understand it:

1. To recap, a Worker implements an instruction or a single command. Each Worker class needs to implement the RobotWorker interface in the WadRobotFramework.

2. It needs to implement the getInstruction() method which returns a String. This is the instruction that the Worker will obey or perform. In our case, the command1 is being done by Worker1 class and the command2 is being done by Worker2 class respectively.  So when someone submits {GAEJRobot:command1} in the Blip, the doWork() implementation of the Worker1 class will be invoked and if they submit {GAEJRobot:command2} in the Blip, the doWork() implementation of the Worker2 class will be invoked.

3. It needs to implement the doWork() method. This method is the heart or the main implementation of the Worker. Here you will place all your processing logic. Notice that since this is an Advanced Robot, it is assumed that you would even like to make use of the Google Wave API classes directly. So you are passed in instances of RobotMessageBundle, Blip and Event classes. The last parameter passed is params and it represents any parameters passed to the robot.

You will notice in the implementation that we have done for the Worker, that we simply Append to the Blip Text saying that the Worker got its command and notice that we also print out the Parameter String. So if you logic depends on the values of the parameters passed, you can parse out the parameters here itself and perform your logic.

That is all we need to do as far as writing Java code is concerned. Of course we have the other mandatory files that we need to create, which we will cover quickly now:

Configuring the MyAppenderRobot in web.xml

We need to add the MyAdvancedRobot in the <servlet/> and <servlet-mapping/> entry to the web.xml file. This file is present in the WEB-INF folder of the project. The necessary fragment to be added to your web.xml file are shown below.

 

<servlet>
<servlet-name>MyAdvancedRobot</servlet-name>
<servlet-class>com.gaejexperiments.waverobot.MyAdvancedRobot</servlet-class>
</servlet>
<servlet>
<servlet-name>MyRobotProfileServlet</servlet-name>
<servlet-class>com.gaejexperiments.waverobot.MyRobotProfileServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyAdvancedRobot</servlet-name>
<url-pattern>/_wave/robot/jsonrpc</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>MyRobotProfileServlet</servlet-name>
<url-pattern>/_wave/robot/profile</url-pattern>
</servlet-mapping>

 

Notice that we also have the ProfileServlet configured here, which is a good and recommended thing to have for your Robot. The ProfileServlet class is covered in the next section.

ProfileServlet.java

The implementation is straightforward and contains the ApplicationId that I have used for my AdvancedRobot. You can replace it with your id.

 

package com.gaejexperiments.waverobot;

import com.google.wave.api.ProfileServlet;

public class MyRobotProfileServlet extends ProfileServlet {

@Override
public String getRobotAvatarUrl() {
return "http://myinfoagent.appspot.com/_wave/myimage.jpg";
}

@Override
public String getRobotName() {
return "MyAdvancedRobot";
}

@Override
public String getRobotProfilePageUrl() {
return "http://myinfoagent.appspot.com";
}

}

 

Creating the MyAdvancedRobot capabilities.xml files

We need an additional file to describe the capabilities of the Robot that we have written. This file is called the capabilities.xml and it needs to reside in a certain location. You need to create a _wave directory inside of the war directory of your project. The location of this file is  war/_wave directory.

You will need to create the _wave directory and create the capabilities.xml file over there. The capabilities file shown below is pretty straightforward and is shown below:

 

<?xml version="1.0" encoding="utf-8"?>
<w:robot xmlns:w="http://wave.google.com/extensions/robots/1.0">
<w:capabilities>
<w:capability name="BLIP_SUBMITTED" content="true" />
</w:capabilities>
<w:version>1</w:version>
</w:robot>

 

Deploying the Application

To deploy the application, you will need to first create your Application ID. The Application Identifier can be created by logging in at http://appengine.google.com with your Google Account. You will see a list of application identifiers already registered under your account (or none if you are just getting started). To create a new Application, click on the Create Application button and provide the Application Identifier as requested. Please note this name down since you will be using it for deployment.

For e.g. I have registered an application identifier named myinfoagent.

To deploy the application, follow these steps (they should be familiar to you now):

  1. Click on the Deploy Icon in the Toolbar.
  2. In the Deploy dialog, provide your Email and Password. Do not click on Deploy button yet.
  3. Click on the App Engine Project settings link. This will lead you to a dialog, where you need to enter your Application ID [For e.g. my Application Identifier myinfoagent]
  4. Click on OK. You will be lead back to the previous screen, where you can click on the Deploy button. This will start deploying your application to the GAEJ cloud. You should see several messages in the Console window as the application is being deployed.
  5. Finally, you should see the message “Deployment completed successfully”.

MyAdvancedRobot in Action

Your application is going to be available at the http://yourapplicationid.appspot.com. In my case, the application is available at http://myinfoagent.appspot.com.

You can test for the presence of your robot capabilities file by simply typing in the following:

http://yourapplicationid.appspot.com/_wave/capabilities.xml [Replace yourapplicationid with the Application ID that you have] and you should see the capabilities.xml file being served up.

To test out the Robot, you need to launch the Google Wave client and login in with your account by going to http://wave.google.com. On successful login, you will be inside the Wave client from where you can create a new wave by clicking on the New Wave link. I then add the myinfoagent@appspot.com, which is our AdvancedRobot to the Wave as a participant as shown below:

 

On addition, the AdvancedRobot will announce itself to the Participants. This is the method getRobotSelfIntroduction() that we implemented in the MyAdvancedRobot.java class. The output is shown below:

Now, we type in the message /help in the beginning of the Blip and submit it as shown below:

When submitted, the method getUsage() of the MyAdvancedRobot class is invoked and it displays the commands and any help instruction that you provided in there.

The next thing we do is give a command to the first Worker as shown below and click on the Done button.

This will invoke the doWork() method on the Worker1 class since this class has indicated that it implements command1 as mentioned in the getInstruction() method of the class. The response of the Worker is shown below:

As you can see we did not pass any parameters to it, so it printed out null.

Now, let us invoke the second Worker as shown below and notice that we are passing parameters to it now:

When we click on Done, the doWork() method on the Worker2 class since this class has indicated that it implements command2 as mentioned in the getInstruction() method of the class. The response of the Worker is shown below:

Notice that the parameter string was now passed and successfully displayed. You can easily parse out the String and perform your logic.

Conclusion

This concludes the 2-part tutorial on creating Simple and Advanced Robots using the WadRobotFramework. The framework takes away much of the API that you need to know to get going with the Wave API and instead lets you focus on the core of our Robot logic. The framework is still is its early stages and would do with some feedback from you.

Till the next episode, just Smile and Wave!

 

© 2012 iRomin Suffusion theme by Sayontan Sinha