Monday, August 01, 2011

Google App Engine Datastore Java BigDecimal and JDO

If you have tried to persist a BigDecimal field in the Google App Engine Datastore using the low level Java API, you may have noticed BigDecimal is not natively supported. However, through the JDO interface, it is possible to persist a BigDecimal field on your entity.

Define the entity with the proper JDO annotations.

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class MyEntity {
  @PrimaryKey
  @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
  private Long id;
 
  @Persistent
  private BigDecimal amount;
}

The JDO implementation for the app engine datastore will handle the persistence of the BigDecimal value even though BigDecimal itself is not persisted.

The important thing to understand is that there is a conversion happening so the BigDecimal precision is not entirely maintained. It seems to be the same as if you converted a BigDecimal object into float or double and back to BigDecimal. So, a BigDecimal with the value 1.99 might be actually stored as something like 1.9900000000000001101341... when it is converted back to BigDecimal.

Thursday, December 16, 2010

Google App Engine for Java and the SSL Session

If you are building an application on Google App Engine for Java and you use sessions, SSL, and a custom domain name, you may have ran into trouble transferring the session between the secure and non-secure portions of your site. This is because SSL on App Engine requires use of the appspot.com domain while the rest of your application is accessed by your own domain name. When a browser accesses one domain, the session is established by a cookie that is inaccessible to the other. This presents much difficulty if you are designing something like a login or registration form that must be secure while later redirecting to an insecure page that needs the same session to know if the user is authenticated.

A simple solution is to transfer the jsessionid cookie value to the insecure site after you manipulate the session on the secure side. The flow would look something like this in the example of a login process:
  • User requests secure page for login form.
  • Submits form by POST over SSL to servlet or other handler
  • In the servlet, update the session as you desire but also grab the jsessionid cookie
  • Redirect to an insecure servlet that accepts the jsessionid as a parameter
  • Write the received jsessionid cookie back to the response from the insecure side
  • Redirect or render appropriate view
The insecure session has now been replaced with the one established on the secure page. Login complete!

Since my application is built in Spring 3 MVC, I have some sample code readily available that would go something like this:

First, the controller method that handles the post of the login form has to grab the jsessionid from the current secure session. Remember, this post is done to the https appspot url.

@RequestMapping(method = RequestMethod.POST)
public String doSubmit(@ModelAttribute("loginForm") @Valid LoginForm loginForm
  , @CookieValue("JSESSIONID") String jsessionid) {

  //do the login logic
  Long userId = userService.doLogin(loginForm.getEmail(), loginForm.getPassword());

  if (userId != null ) {

    //....
    //set some kind of session variable here to indicate the user is authenticated
    //this variable will be accessible from any insecure page
    //....

    return "redirect:http://www.mydomain.com/session-transfer.html?jsessionid="+jsessionid+"&destination=%2F";
  }
}


Notice the jsession id being taken from the current session and passed as a url parameter to the url that will actually transfer the session to the insecure site. The handler for the session-transfer url would look like this:

@RequestMapping(method = RequestMethod.GET)
public String handleRequest(@RequestParam("jsessionid") String jsessionId
  , @RequestParam("destination") String destination
  , HttpServletResponse response) {

  //overwrite jsessionid from the one from the ssl site
  Cookie jsessionCookie = new Cookie("JSESSIONID", jsessionId);
  response.addCookie(jsessionCookie);
  return "redirect:" + destination;
}


Since this handler is happening in the context of the http site, the current jsessionid the browser is providing in its cookie is for the insecure session. Here, we overwrite that jsessionid with the one from the secure site where the session was updated to indicate the user is authenticated. Now, anywhere in the site that needs session variables from insecure pages can read those that were set during the secure session.

Friday, November 12, 2010

Java 6 on Mac OS X 10.5.8

It's not terribly obvious, but if you have Mac OS X 10.5.8, it is possible to get Java SE 6 without upgrading your OS.

Visit Apple's Developer Downloads

At the time of this writing, under the java category of downloads, this is the update that will give you Java SE 6 (1.6.0_22):
Java for Mac OS X 10.5 Update 8 Developer Package

Once installed, open the Java Preferences control panel and drag Java SE 6 to the top to use it by default.

Thursday, September 30, 2010

Upgrade Google App Engine SDK in Eclipse

Upgrading the Google App Engine SDK plugin for Eclipse is not terribly difficult but it's time I wrote down the steps I follow to get it done.

This works for Eclipse Galileo but should be similar in other versions.

  • Go to Help->Install New Software
  • Select the Google Plugin Site. Add if not there. http://dl.google.com/eclipse/plugin/3.5
  • Select the new version of the SDK. Complete the wizard.
Obvious so far. Now, go to your project and change it to use the new version you just installed.
  • Go to Properties of project
  • Go to Google -> App Engine
  • Select Use Specific SDK and pick the newest version
  • Click OK
This will make the proper changes to your project to use the new SDK. But, if you had to point to specific jars in the SDK for any reason like doing unit testing, your project will be partially pointing to another version of the SDK. To make the upgrade process much more smooth, make use of variables in the project's build path.
  • Go to the java build path -> libraries tab.
  • Add a variable. Call it something like GAE_SDK and point it to the root of the installed Google App Engine plugin: /eclipse-jee-galileo-win32/eclipse/plugins/com.google.appengine.eclipse.sdkbundle.1.3.7_1.3.7.v201008311405/appengine-java-sdk-1.3.7
Now update all your extra Google App Engine SDK jars to point through this variable. Next time you upgrade, repeat the same steps and update the variable.