Tuesday, May 26, 2009

Cloud to RIA: Accessing Google App Engine (Java) Data Through Flex AMF - Part III

[Part I] [Part II] [Part III]

JPA Entity

Now that we have the client side set up, let's prepare the server side, starting with the JPA entity that will store the data. For this example, I'm going to use just two fields: "key" (primary key) and "value." For those of you relational folks new to GAE/JPA, there is no "table" per se: you declare a JPA "entity," which is a Java class annotated to be persistent. Then, to "insert" a "row," you create a class instance and "persist" it. GAE/J will associate all the class instances together as if they were rows in a table.

Create the below Java class DataItem.java in package com.acme.data (or change as necessary).

package com.acme.data;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity public class DataItem {
@Id String key;
String value;
public DataItem(String key, String value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

As you can see, this is ridiculously simple. All you had to do to make persistent was to add the @Entity annotation for the class and an @Id to mark "key" as the primary key. You do need to add getters/setters however: making the fields public and directly manipulating them will NOT work! Eclipse can automatically generate getters/setters for you - use Right Click->Source on the Java source.

EntityManagerFactory

Next, we need to create a POJO to do create/read/update/delete on the JPA entity we just declared. The basic steps are as follows:
  1. Get an EntityManagerFactory instance
  2. Create an EntityManager
  3. Get an EntityTransaction (if doing transactional work)
  4. Do your thing
The first step (get EntityManagerFactory) you want to do through a singleton because the operation is VERY expensive (10-20 seconds is pretty typical). The way AMF works is that it has servlets running on the server listening for requests from Flex RemoteObject (see web.xml in Part II). It is VERY important that you define the scope for these servlets as "application," meaning the servlets start once and will continue to run till the next update (see services-config.xml in Part II). If you set the scope as "session" (or worse, "request"), the servlets will keep restarting, creating EntityManagerFactory afresh, severely reducing the responsiveness.

Create a Java class EMF.java in package com.acme.data with the below code.

package com.acme.data;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class EMF {
private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("transactions-optional");
public static EntityManagerFactory get() {
return emf;
}
private EMF() {
}
}

POJO Interface

The final step is to create POJO that GraniteDS will use to access the JPA entities. It's the methods in this POJO that your RemoteObject uses (see createData, readValue, updateValue, and deleteData in the Data.mxml RemoteObject tag in Part II).

package com.acme.data;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;

public class Data {
public void createData(String key, String value) {
EntityManager em = EMF.get().createEntityManager();
EntityTransaction tx = em.getTransaction();
DataItem dataItem = new DataItem(key, value);
try {
tx.begin();
em.persist(dataItem);
tx.commit();
} finally {
if (tx.isActive()) {
tx.rollback();
}
em.close();
}
}
public String readValue(String key) {
EntityManager em = EMF.get().createEntityManager();
return em.find(DataItem.class, key).getValue();
}
public void updateValue(String key, String value) {
EntityManager em = EMF.get().createEntityManager();
EntityTransaction tx = em.getTransaction();
DataItem dataItem = em.find(DataItem.class, key);
dataItem.setValue(value);
try {
tx.begin();
em.merge(dataItem);
tx.commit();
} finally {
if (tx.isActive()) {
tx.rollback();
}
em.close();
}
}
public void deleteData(String key) {
EntityManager em = EMF.get().createEntityManager();
EntityTransaction tx = em.getTransaction();
DataItem dataItem = em.find(DataItem.class, key);
try {
tx.begin();
em.remove(dataItem);
tx.commit();
} finally {
if (tx.isActive()) {
tx.rollback();
}
em.close();
}
}
}

There's really nothing to this code, it's quite self-explanatory. I've simplified it to the extent possible, and as you get familiar with the process, you can explore other APIs (like em.createQuery).

BUILD/PUBLISH

The final step is to build and publish to GAE. To build the release version of the Flex app, select the project name in the Eclipse left pane and Right Click->Export->Flex Builder->Release Build. Point "Export to folder" to your war/ directory.

Next, create a GAE app (if you haven't already) and deploy your code by clicking the little GAE logo on the top left in Eclipse (or click on the project name in the left pane and Right Click->Google->Deploy to App Engine). If you don't have GAE Java access (see Part I), this operation will fail with a somewhat cryptic message.

That's it. Open your app with http://yourapp.appspot.com/Data.html and see the magic. If there's no magic, may be I missed something, so please post back here so I can fix it. If there IS magic, please do post comments/opinions. Happy coding!

Monday, May 18, 2009

Cloud to RIA: Accessing Google App Engine (Java) Data Through Flex AMF - Part II

[Part I] [Part II] [Part III]

Installation

OK, you should now have a GAE account and GAE (Java) access (if not, get those) and be ready to install the necessary software.
  1. Install Eclipse (3.4 "Ganymede" version). Downloads are available athttp://www.eclipse.org/downloads/, and I'd recommend getting Eclipse IDE for Java EE Developers to make sure you have everything you'd need. Installation is pretty straightforward.
  2. Add the Flex Builder plugin. Adobe offers a 60 day trial, if you're ready to buy.
  3. Once you have those going, get the Google Plugin. You install plugins in Eclipse by going to Help->Software Updates and specifying the install URL through "Add Site." The link for Google Plugin is http://dl.google.com/eclipse/plugin/3.4.
  4. Next, create a Google Project. Open File->New->Other (or Ctrl+N) and select Google->Web Application Project. Enter a Project name (e.g., "Data") and a Package name (e.g., "com.acme.data"). Uncheck "Use Google Web Toolkit" under "Google SDKs" because we're not going to use GWT.
  5. Since we're going to also build a Flex app in this project we need to give it a Flex Project Nature. Select the project name in the Eclipse left panel and Right Click->Flex Project Nature->Add Flex Project Nature. This will automatically create a Data.mxml file (which we will edit later) in the src/ folder.
  6. You will immediately see an error message "Cannot create HTML wrapper. Right-click here to recreate folder html-template." on the Problems tab in the bottom-right panel. Do that, and Flex Builder will create a template for generating Data.html.
  7. Install the GraniteDS library. Get the latest rev from the download site, unzip to graniteds/, and copy granite.jar (we don't need anything else for this project) from graniteds/build/ to your project's war/WEB-INF/lib/.
  8. Finally, get the Xalan-J version xalan-j_2_7_1 from the download site, unzip, and copy serializer.jar and xalan.jar from the root to your project's war/WEB-INF/lib/.
You now have all the software you'll need; next step is to configure it.

Configuration

For configuring, you need to mess with four files.

web.xml
This file is in war/WEB-INF/ and contains info about the servlets. Your GraniteDS installation has servlets that will run on GAE, and you need to add their info into web.xml. Copy/paste the below XML inside the <web-app> tag.

<!-- GraniteDS -->
<!-- Read services-config.xml file at web application startup -->
<listener>
<listener-class>org.granite.config.GraniteConfigListener</listener-class>
</listener>

<!-- Handle AMF requests ([de]serialization) -->
<filter>
<filter-name>AMFMessageFilter</filter-name>
<filter-class>org.granite.messaging.webapp.AMFMessageFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AMFMessageFilter</filter-name>
<url-pattern>/graniteamf/*</url-pattern>
</filter-mapping>

<!-- Handle AMF requests (execution) -->
<servlet>
<servlet-name>AMFMessageServlet</servlet-name>
<servlet-class>org.granite.messaging.webapp.AMFMessageServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>AMFMessageServlet</servlet-name>
<url-pattern>/graniteamf/*</url-pattern>
</servlet-mapping>

services-config.xml
Create a flex/ directory under war/WEB-INF/ and make a Flex-specific file "services-config.xml" with the below contents. This file tells Flex how to direct the RemoteObject calls. We're naming the service Data with the Java class Data (which we will create later) under the package com.acme (change this as necessary).

By default, the Flex compiler will not know of this file, so you'll need to add "-services ../war/WEB-INF/flex/services-config.xml" to the compiler flags. To access the flags, select the project name on the left panel and Right Click->Properties->Flex Compiler.

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<services>
<service
id="granite-service"
class="flex.messaging.services.RemotingService"
messageTypes="flex.messaging.messages.RemotingMessage">
<destination id="Data">
<channels>
<channel ref="my-graniteamf"/>
</channels>
<properties>
<scope>application</scope>
<source>com.acme.data.Data</source>
</properties>
</destination>
</service>
</services>

<channels>
<channel-definition id="my-graniteamf" class="mx.messaging.channels.AMFChannel">
<endpoint
uri="/graniteamf/amf"
class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>
</channels>
</services-config>

granite-config.xml
Next, create a granite/ directory in your war/WEB-INF/, and copy the GraniteDS file "granite-config.xml" from graniteds/examples/graniteds_pojo/resources/WEB-INF/granite/ to it.

persistence.xml
JPA loads its configuration info (like persistence unit "transactions-optional" that the EMF class in Part III uses) from a "persistence.xml" file, but GAE currently doesn't automatically create it. So, make a persistence.xml file in src/META-INF/ with the following contents.

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="transactions-optional">
<provider>org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider</provider>
<properties>
<property name="datanucleus.NontransactionalRead" value="true"/>
<property name="datanucleus.NontransactionalWrite" value="true"/>
<property name="datanucleus.ConnectionURL" value="appengine"/>
</properties>
</persistence-unit>
</persistence>

Flex Application

We're now set to create the Flex app. To illustrate the basic aspects of data access, I'm going to build a small app that does simple CRUD: create, read, update, and delete. Copy the below MXML to the Data.mxml file in your src/ folder. I've made this as simple as possible, and it should be self-explanatory. Note that Flex refers to the services-config.xml (see above) to figure out how to contact the service.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:RemoteObject id="data" destination="Data" fault="status.text=event.fault.toString();">
<mx:method name="createData" result="status.text='Created.';"/>
<mx:method name="readValue" result="value.text=event.result.toString();status.text='Read.'"/>
<mx:method name="updateValue" result="status.text='Updated.';"/>
<mx:method name="deleteData" result="status.text='Deleted.';"/>
</mx:RemoteObject>
<mx:Panel width="100%" height="100%">
<mx:Form>
<mx:FormItem label="Key"><mx:TextInput id="key"/></mx:FormItem>
<mx:FormItem label="Value"><mx:TextInput id="value"/></mx:FormItem>
</mx:Form>
<mx:HBox>
<mx:Button label="Create" click="data.createData(key.text,value.text);status.text=null;"/>
<mx:Button label="Read" click="data.readValue(key.text);status.text=null;"/>
<mx:Button label="Update" click="data.updateValue(key.text,value.text);status.text=null;"/>
<mx:Button label="Delete" click="data.deleteData(key.text);status.text=null;"/>
</mx:HBox>
<mx:TextArea id="status" width="100%" height="100%"/>
</mx:Panel>
</mx:Application>

This is all we need to do on the client side. Complete these, and we'll create the server side code and wrap up in Part III.

Tuesday, May 12, 2009

Cloud to RIA: Accessing Google App Engine (Java) Data Through Flex AMF - Part I

[Part I] [Part II] [Part III]

Introduction

Cloud
and RIA (Rich Internet Application) are probably the hottest buzzwords today in the web world. You can look at cloud as a virtual server that provides single-point access to a distributed (often across the globe) collection of hardware/software and coming in three shapes:
  1. IaaS: Infrastructure as a Service. A good example is Amazon EC2, where you basically get a virtual machine (Amazon Machine Image) on which you install what you want.
  2. PaaS: Platform as a Service. This adds infrastructure software to IaaS so you don't start from scratch. Examples are Google App Engine (GAE) and Salesforce Force.com.
  3. SaaS: Software as a Service. Here, you get the full package that you tweak/customize to your needs. Best example of course is Salesforce.com.
Clouds have many benefits, most importantly:
  1. You don't need to plan/invest in hardware/software upfront
  2. Your infrastructure will scale as your business/needs grow
  3. You system will be fault-tolerant and secure
  4. You don't need to maintain/manage the setup
In this example, I'm using GAE (Java) for the cloud. Google had required developers to use Python for GAE till recently, when they announced a Java version. All references to GAE in this write-up refer to GAE (Java). GAE is free to get started; you will need a Gmail account.

An RIA is an advanced web (or desktop) front-end that's more sophisticated than traditional HTML/CSS/JavaScript web apps. The dominant web RIA today is Adobe Flex, although Microsoft Silverlight is gaining rapidly. Sun (Oracle) recently announced JavaFX that's gaining some traction in the Java community. We will use Flex as the RIA in this example; you will need Flex Builder (free trial for 60 days) and Eclipse.

Technology Choices

Once we've decided to hook up a Flex RIA to a GAE cloud, we need to look at some specifics. First, how do you represent the data on GAE? GAE (Java) does not allow saving to files on the server, so our only option is to save on the Google BigTable. In fact, it's a better option anyhow. Luckily, the new GAE has a standards-based Java persistence API (rather than the Google-proprietary API with Python): JDO and JPA. I'm using JPA for this example.

Next, the interchange data format. Popular message formats with Flex are: XML (SOAP or REST), JSON, and the Adobe-proprietary AMF. XML is verbose and although accesss through REST performs reasonably well, more succint representations like JSON are better for larger chunks of data. But the most efficient (and strongly typed) representation is with AMF, which is what we will use for this example. To employ AMF for the data exchange, we use the Flex RemoteObject.

Adobe has made the AMF spec public, and there are multiple free implementations, probably the most important of which are: BlazeDS, WebORB, and GraniteDS. I've tried all the three with GAE. BlazeDS never worked for me, and as of today I'm not aware of any official fixes, though there are blog reports of unofficial workarounds. WebORB kind of worked with their fix in the sense I could move data through POJO interfaces to data, but not the persisted classes themselves. I also had performance issues. GraniteDS worked like a charm, and that's what we'll use in this example. Do keep checking Will it play in App Engine for the latest info.

Preparation

To get started, you need to do two things right away:
  1. Get a GAE account. This is easy and free: just sign up at Google App Engine.
  2. Get a GAE (Java) access. Sign up at GAE (Java) Sign-up.
Once you've done this, let's get going with the real thing... in Part II.