Tuesday, September 29, 2009

Gdata library

Our university is using gmail for our students. Google provides database, service, maintainance and it is free.

My part of synchronize user's gmail password with their current password that they used to log in into our own system. We are using Spring framework for our application.

The hard part is make the decision on how to handle the token that google required to process all actions. We finally decided to have an application level servlet running with our application, this servlet will generate one UserService object and pass it to the ServletContext. The other java beans will implement ServletContextAware interface which is a Spring interface which call setServletContext method automatically, to pass the ServletContext as a bean variable. This approach is a good solution to our problem. We have more than 50,000 gmail account and about 14,372 users changed their password through our application to set the password for their gmail. No CAPTCHA errors was triggered.

Here is some sample code:

1. Web.xml

gmailUserService
servlet.gmail.GmailUserService


2. Application level servlet
try{
userService.setUserCredentials(adminEmail, adminPass); config.getServletContext().setAttribute("gmailUserService", serService);
} catch (CaptchaRequiredException e) {
//send a email to admin so administrator can unlock the CAPTCHER error SendEmailToAdmin sendMail = new SendEmailToAdmin(); sendMail.sendMail();
}

3. Java Beans to validate user and set the password

public class GmailClientPassword implements ServletContextAware {
private ServletContext servletContext;

//automatically be called by SpringFrameword to set the servletContext
public void setServletContext(ServletContext servletContext){ this.servletContext = servletContext;
log.info( "CSUNGmailClientPassword setServletContext() invoked" );
}

//validate user and set password
public void validateAndPassword(){

UserService userService = (UserService)servletContext.getAttribute("gmailUserService");
URL retrieveUrl = new URL(https://apps-apis.google.com/a/feeds/user/2.0/" + username);
userEntry = userService.getEntry(retrieveUrl , UserEntry.class);
String userName = userEntry.getLogin().getUserName();
userEntry.getLogin().setPassword("passwprd"); userService.update(updateUrl, userEntry);
}

}


Reference:
1. From Gmail Support team:

We actually have 2 common scenarios.

Scenario 1 - Service object is reused
In the case, the token renewal will be taken care by the code in the Java client lib. Reusing the UserService object should not generate any CAPCHA. We do have a quota limit on hitting our API server but I don't think your use case will cause any issue. For token renewal, it is actually already handled for you in the Java client library.

Scenario 2 - Service object is not reused For some customers, due to their code design or how their rest of their system works, the Service object somehow cannot be reused. In this case, the developer has to manage the token to avoid CAPTCHA as calling the setUserCredentials method in a new Service object will trigger a ClientLogin call and doing that often will generate CAPTCHAs. So rather than trying to login everytime when a new Service object is created, the developer should call the setUserToken method and apply a token they persist/manage every 24 hours.

CAPTCHA happens when you try to repeatedly doing ClientLogin or repeating create new UserService objects.

2. Resources
http://code.google.com/apis/gdata/faq.html#clientlogin
http://code.google.com/apis/gdata/clientlogin.html
http://code.google.com/apis/gdata/javadoc/
http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html
http://code.google.com/apis/apps/libraries_and_samples.html#provisioningv2
http://groups.google.com/group/google-apps-apis/msg/97e3e3754ee03561?pli=1