Monday, July 20, 2009

Flex 4: Threaded Text Using TextFlow


Introduction

Ever wanted to build threaded text like you see above? This is trivial to do in a DTP package like Adobe InDesign or even a graphics tool like Adobe Illustrator, but has been next to impossible to do on the web. That is, until now.

For those of you unfamiliar with the term “threaded,” it’s when you have text flow from one frame to another as a chain. E.g., try to select the text in the left frame above and continue to the right frame: your selection will flow from one frame to the other.

How’s this possible? With the new Flex 4 (“Gumbo”) TextFlow control. TextFlow builds on the powerful Text Layout Framework (TLF) that runs on the new text engine introduced in Flash Player 10. Among its many capabilities: threading.

Playtime

Before we see how to code this, check out a few nifty features of TextFlow in the example above.

  1. Selection You can select across frames by simply dragging the mouse
  2. Edit You can insert/delete/update text
  3. Undo You can undo changes to text: make any edit you like and try Ctrl+Z
  4. Copy/Paste You can copy/paste from/to the frames
  5. Embedded Fonts You can use any font without worrying if the user has it

Excited? Read on for specifics on how you can do it too.

Application MXML

Flex uses a combination of MXML and ActionScript to define applications. TextFlow is new to Flex 4, so you will need either the Flex 4 SDK or the Flash Builder 4 IDE (new name for Flex Builder) to build. Both are currently in beta. Be sure to use the Beta 1 SDK (build 7219) that’s included in the Flash Builder download by default.

Here’s the code:

  1: <?xml version="1.0" encoding="utf-8"?>
  2: <mx:Application initialize="init();" layout="absolute" backgroundAlpha="0" xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/halo" minWidth="1024" minHeight="768" viewSourceURL="srcview/index.html">
  3:   <fx:Declarations>
  4:     <fx:String id="textXML" source="Snake.xml"/>
  5:   </fx:Declarations>
  6:
  7:   <fx:Style>
  8:     @namespace mx "library://ns.adobe.com/flex/halo";
  9:     @namespace s "library://ns.adobe.com/flex/spark";
 10:  
 11:     @font-face {
 12:       src: url("C:/Windows/Fonts/Windsong.ttf");
 13:       fontFamily: Windsong;
 14:       advandedAntiAliasing: true;
 15:       unicodeRange: U+0020-U+007E;
 16:       cff: true;
 17:     }
 18:   </fx:Style>
 19:
 20:   <fx:Script>
 21:     <![CDATA[
 22:       import flashx.textLayout.conversion.TextFilter;
 23:       import flash.text.engine.FontLookup;
 24:       import flashx.undo.UndoManager;
 25:       import flashx.textLayout.edit.EditManager;
 26:       import mx.core.UIComponent;
 27:       import flashx.textLayout.container.ContainerController;
 28:       import flashx.textLayout.elements.TextFlow;
 29:    
 30:       private var textFlow:TextFlow;
 31:       private var sections:XML =
 32:         <sections>
 33:           <section x="10" y="20" w="160" h="360"/>
 34:           <section x="270" y="80" w="160" h="390"/>
 35:         </sections>
 36:       private var textHolder:UIComponent = new UIComponent();
 37:    
 38:       private function init():void {
 39:         XML.ignoreWhitespace = false;
 40:         textFlow = TextFilter.importToFlow(textXML, TextFilter.TEXT_LAYOUT_FORMAT);
 41:         textFlow.fontLookup = FontLookup.EMBEDDED_CFF;
 42:      
 43:         for each (var section:XML in sections.*) {
 44:           var sprite:Sprite = new Sprite();
 45:        
 46:           sprite.x = section.@x;
 47:           sprite.y = section.@y;
 48:           textHolder.addChild(sprite);
 49:           textFlow.flowComposer.addController(new ContainerController(sprite, section.@w, section.@h));
 50:         }
 51:      
 52:         addElement(textHolder);
 53:         textFlow.interactionManager = new EditManager(new UndoManager());
 54:         textFlow.flowComposer.updateAllControllers();
 55:       }
 56:     ]]>
 57:   </fx:Script>
 58:
 59:   <s:Group>
 60:     <s:VideoElement id="snake" source="Snake.flv" complete="snake.play();"/>
 61:   </s:Group>
 62: </mx:Application>

If you know Flex, this should be self-explanatory. But you’ll want to make special note of a few things:

  1. The basic process is: define a TextFlow, create frames with “controllers,” attach the controllers to the TextFlow, and add a manager to handle user interactions
  2. Flex 4 TLF uses CFF, so see how we need to specify CFF (lines 16, 41) for TextFlow – this is in flux though, and Adobe has changed “cff: true” to “embedAsCFF: true” in subsequent builds

TextFlow XML

The text for the TextFlow is not dissimilar to HTML, but is enhanced to support some very powerful functionality. Our example is quite simple however. It is adapted from the Wikipedia entry on Snake.

  1: <TextFlow xmlns="http://ns.adobe.com/textLayout/2008" fontFamily="Windsong" fontSize="30" color="0x000000">
  2:   <linkNormalFormat color="0x763524" textDecoration="underline"/>
  3:
  4:   <p>Snakes are elongate legless <a href="http://en.wikipedia.org/wiki/Carnivore">carnivorous</a> <a href="http://en.wikipedia.org/wiki/Reptile">reptiles</a> of the suborder Serpentes that can be distinguished from <a href="http://en.wikipedia.org/wiki/Legless_lizard">legless lizards</a> by their lack of eyelids and external ears. Like all <a href="http://en.wikipedia.org/wiki/Squamates">squamates</a>, snakes are ectothermic amniote vertebrates covered in overlapping <a href="http://en.wikipedia.org/wiki/Scale_(zoology)">scales</a>. Like <a href="http://en.wikipedia.org/wiki/Lizards">lizards</a>, from which they evolved, they have loosely articulated <a href="http://en.wikipedia.org/wiki/Skulls">skulls</a> and most can swallow prey much larger than their own head. In order to accommodate their narrow bodies, snakes<span>'</span> paired organs (such as kidneys) appear one in front of the other instead of side by side, and most have only one functional <a href="http://en.wikipedia.org/wiki/Lung">lung</a>. Some species retain a <a href="http://en.wikipedia.org/wiki/Pelvic_girdle">pelvic girdle</a> with a pair of vestigial claws on either side of the cloaca.</p>
  5:   <p>Living snakes are found on every continent except Antarctica. Fifteen <a href="http://en.wikipedia.org/wiki/Family_(biology)">families</a> are currently recognized comprising 456 <a href="http://en.wikipedia.org/wiki/Genus">genera</a> and over 2,900 species.[1][2] They range in size from the tiny, 10 cm long <a href="http://en.wikipedia.org/wiki/Leptotyphlops_carlae">thread snake</a> to pythons and anacondas of up to 7.6 m (25 ft) in length. The recently discovered fossil <a href="http://en.wikipedia.org/wiki/Titanoboa">Titanoboa</a> was 13 m or 43 ft long. Snakes are thought to have evolved from either burrowing or aquatic lizards during the <a href="http://en.wikipedia.org/wiki/Cretaceous">Cretaceous</a> period (c 150 Ma). The diversity of modern snakes appeared during the Paleocene period (c 66 to 56 Ma).</p>

6: </TextFlow>

Conclusion

If you haven’t already checked out Flex 4, hope I’ve convinced you to try it. Download Flex 4 SDK or Flash Builder 4 today – if you’re new to Flex, I’d strongly recommend Flash Builder.

If you want to build the snake animation, that’s a different story. I did this using NewTek LightWave 3D. If you want to know how, I can prepare a tutorial. Meanwhile, you might be interested in my other tutorial, LightWave: Animation Along a Predefined Path.

Friday, June 19, 2009

LightWave: Animation Along a Predefined Path


Introduction

A common task in 3D animation is to move an object along a path: snake curling up a tree and paper rolling on a conveyor belt, to name a couple. If the object were stiff, in NewTek LightWave you could create the path as a spline in Modeler and use Curve Constraint modifier in Layout. But that won't work with the snake or the paper. For those, you could mess with the likes of Curve Conform displacement, but they're hard to control. So, what do we do?

An old (but ingenious) trick does this with bones and morph. It works like this:
  1. Create a morph for the object to move it along a long straight line
  2. Rig the object with a chain of bones that follows this line, with the object at one end
  3. Select Use Morphed Positions for all the bones
  4. Reshape the bone chain as necessary
  5. Move the object using Morph Mixer
As the object moves along the chain, the reshaped bones will deform the object to follow the new path rather than the original straight line. The magic here is coming from "Use Morphed Positions."

Case closed, right? Not quite. What if you want the object to follow a detailed predefined path? Like the conveyor belt. It would be tedious, if not impossible, to reproduce the path accurately by directly manipulating the bone chain in Layout.

We need to adapt the process, and that's what this tutorial is about. You'll be needing LightWave, though you can probably use the technique also in other apps (like Maya or Max). I used it in a little story of a fish in the sea; below is a simplified version of the shot in which the fish swims in a helical path.


For pictures showing the different steps, click on the Picasa Web Albums icon above.

Make the Path

First, we create the path that the object will follow. This will be a skelegon tree that we can convert to a bone chain in Layout.
  1. Create a helix curve. I made it like this: create a point at X=3', clone 72 copies of it (shortcut c) by stepping the heading by 30° and Y by 2". That gave me a helix with 6' dia, 2' pitch, and 12' length.
  2. Convert to skelegons. This is easy: just run Convert Skelegons.
  3. Optionally, model a tube from the curve.
Set Up the Object

Next, we align the object with the path.
  1. Orient the object in the +Z direction.
  2. If necessary, resize it to fit within the path (and tube).
  3. In the XY pane, align the object center with the start of the skelegon tree.
  4. Create a morph (call it "Move") that moves it a long way along +Z (imagine the skelegons stretched out). The exact length isn't critical because we're going to adjust this in Layout.
Animate

Finally, we put it all together in Layout:
  1. For the layer with the object and the skelegon tree, generate bones with Convert Skelegons into Bones. Let's call this object the "original."
  2. Make a clone of this object and disable/hide it. This will be the "clone."
  3. With Parent in Place set to On, parent the tube (if you have one) to the first bone in the original.
  4. In the original, select all bones and (a) set HPB to 0 (b) mark as rest position (shortcut r) (c) select Use Morphed Positions (in property panel).
  5. Open Scene Editor and copy the properties of all the bones except the top in the clone (click on the cells in Dope Sheet frame 1 and Right Click->Copy). Paste them to corresponding bones in the original (Right Click->Paste over).
  6. For the original, add a Morph Mixer displacement and keyframe the "Move" morph so the object just enters and exits the path over the duration you want.
  7. Unparent the tube, parent the original to the tube, and set the tube HPB to 0. This will get you back to the original orientation.
That's it, you're set. Play the timeline, and you'll see the object move precisely through the path (and the tube, if you have it).

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.