11.2. Generating AS3 Model Objects

In an enterprise Flex and Java application, the model objects can rapidly increase from a few in numbers to a substantial amount. When that happens, keeping tab of object counterparts on both sides of the wire gets cumbersome. On top of that, if you are tasked with hand-coding them, then you are in for some serious tedious effort.

In order to make things easy and enhance developer productivity, a few tools have emerged that make it possible to generate AS3 model objects based on the Java-side counterparts and populate the value objects at runtime based on the XML schema that defines their structure.

In this section, I will explain both these scenarios and show how to leverage some of the open source tools and framework libraries to auto-generate and auto-populate AS3 model objects, which are also referred to as value objects (VOs) or Data Transfer Objects (DTOs).

First, I consider auto-generation from Java to AS3 and use the GraniteDS Gas3 utility to achieve this goal.

11.2.1. Using Gas3

It's possible the mention of GraniteDS with BlazeDS is making you suspicious, wondering if it was a covert way of introducing the non-Adobe open source data service into the bundle. Well, if you do have that fear, then worry not, because there is no such intention! GraniteDS includes a number of utilities that can be used and leveraged even if you don't use GraniteDS as a whole, and that is exactly what I am doing here.

GraniteDS comes with a very useful utility called Gas3 that helps generate AS3 classes on the basis of its Java counterparts. Gas3 can be used as an ANT task or as an Eclipse plug-in, so it is a very handy tool for flexible code generation.

To understand Gas3, let's first set it up and use it to generate a sample AS3 class. I will use the Eclipse plug-in version first. Subsequently I will explore the Gas3 ANT tasks.

The Gas3 Eclipse plug-in is bundled as a part of the GraniteDS Eclipse plug-in distribution. Therefore, to get Gas3 you need to download the GraniteDS Eclipse plug-in. You can get the GraniteDS Eclipse plug-in from the GraniteDS project site on SourceForge, at http://sourceforge.net/projects/granite/files/. Download the org.granite.builder distribution, whose latest general availability release is org.granite.builder_2.0.0.GA.jar.

Installing the plug-in is simple. Just copy the downloaded plug-in to the plugins directory of your Eclipse installation.

Once installed, restart Eclipse. The GraniteDS plug-in is now available. Create a new Java project. I create a simple class Book.java in it. Next apply the GraniteDS nature to the project simply by right-clicking on project name and choosing Add GraniteDS Nature from the context menu. See Figure 11-24 to view the context menu choices.

Figure 11.24. Figure 11-24

On adding GraniteDS nature, you will be presented with a configuration wizard. The first screen of this wizard, as shown in Figure 11-25, asks you to select the Java source folder.

By clicking the Add Folder button, I get a dialog box that lets me choose the source folder that contains Book.java. Figures 11-26 and 11-27 show the dialog box and the screen after the selection is made.

In addition to specifying the source folder you can configure dependent projects and the classpath. See Figures 11-28 and 11-29.

Figure 11.25. Figure 11-25

Figure 11.26. Figure 11-26

Figure 11.27. Figure 11-27

Figure 11.28. Figure 11-28

Figure 11.29. Figure 11-29

Before the files are actually generated, you are also given a choice to configure the Groovy templates and the file generation options. The template configuration screen is in Figure 11-30. The file generation options screen is in Figure 11-31.

Figure 11.30. Figure 11-30

Figure 11.31. Figure 11-31

On clicking the Finish button, AS3 code is generated for the chosen Java files. In this case, the Java object is Book.java. The two generated AS3 classes are:

  • BookBase.as

  • Book.as

Book.as extends BookBase.as. BookBase is updated every time there are modifications to the underlying Java class or if the current generated class is too outdated. All custom behavior and extensions should be written in Book.as, not BookBase.as. Book.as is generated once and then not touched by Gas3.

The output messages displayed in the Eclipse console are shown in Figure 11-32.

Figure 11.32. Figure 11-32

Listing 11-1 shows Book.java and Listing 11-2 and 11-3 show BookBase.as and Book.as, respectively.

Example 11.1. Book.java
package problazeds.ch11;

public class Book {
      private int id;
      private String title;
      private String author;
      private double price;
      public int getId() {
           return id;
      }
      public void setId(int id) {
           this.id = id;
      }
      public String getTitle() {
           return title;
      }
      public void setTitle(String title) {
           this.title = title;
      }
      public String getAuthor() {
           return author;
      }

public void setAuthor(String author) {
           this.author = author;
      }
      public double getPrice() {
           return price;
      }
      public void setPrice(double price) {
           this.price = price;
      }



}

Example 11.2. BookBase.as
/**
 * Generated by Gas3 v2.0.0 (Granite Data Services).
 *
 * WARNING: DO NOT CHANGE THIS FILE. IT MAY BE OVERWRITTEN EACH TIME YOU USE
 * THE GENERATOR. INSTEAD, EDIT THE INHERITED CLASS (Book.as).
 */

package problazeds.ch11 {

    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import flash.utils.IExternalizable;

    [Bindable]
    public class BookBase implements IExternalizable {

        private var _author:String;
        private var _id:int;
        private var _price:Number;
        private var _title:String;

        public function set author(value:String):void {
            _author = value;
        }
        public function get author():String {
            return _author;
        }

        public function set id(value:int):void {
            _id = value;
        }

public function get id():int {
            return _id;
        }

        public function set price(value:Number):void {
            _price = value;
        }
        public function get price():Number {
            return _price;
        }

        public function set title(value:String):void {
            _title = value;
        }
        public function get title():String {
            return _title;
        }

        public function readExternal(input:IDataInput):void {
            _author = input.readObject() as String;
            _id = input.readObject() as int;
            _price = function(o:*):Number { return (o is Number ? o as Number :
   Number.NaN) } (input.readObject());
            _title = input.readObject() as String;
        }

        public function writeExternal(output:IDataOutput):void {
            output.writeObject(_author);
            output.writeObject(_id);
            output.writeObject(_price);
            output.writeObject(_title);
        }
    }
}

Example 11.3. Book.as
/**
 * Generated by Gas3 v2.0.0 (Granite Data Services).
 *
 * NOTE: this file is only generated if it does not exist. You may safely put
 * your custom code here.
 */

package problazeds.ch11 {

    [Bindable]
    [RemoteClass(alias="problazeds.ch11.Book")]
    public class Book extends BookBase {
    }
}

Gas3 can also be invoked from an ANT build script. The ANT task is available in the main GraniteDS distribution. A simple ANT task that could generate a set of AS3 classes based on Java beans defined in a particular folder is as follows:

<target name="generate.as3">
    <gas3 outputdir="as3">
        <classpath>
            <pathelement location="classes"/>
        </classpath>
        <fileset dir="classes">
            <include name="path/to/your/Java/class/files/**/*.class"/>
        </fileset>
    </gas3>
</target>

outputdir is where the generated AS3 classes reside. For each Java class, two AS3 classes are created.

Java classes in a particular package structure can be translated to corresponding AS3 classes that reside in a completely different package structure. Such translators can be defined as follows:

<translator
        java="path.to.my.java.class"
        as3="path.to.my.as3.class" />

Gas3 uses Groovy templates for code generation from Java to AS3. Although the built-in templates are fairly comprehensive, you can write newer templates if you desire. Newly created templates can be attached to a Gas3 instance by defining the fully qualified name of the template as the value of the entitytemplate Gas3 node attribute.

The built-in templates in Gas3 are as follows:

  • entitytemplate and entitybasetemplate — Used for EJBs and JPA objects

  • interfacetemplate and interfacebbaseTemplate — Used for Java interfaces

  • generictemplate and genericbasetemplate — For generic Java classes

  • enumtemplate — Template for Java.lang.Enum type

To provide custom translation and support for custom types, you can provide your own implementation for org.granite.generator.as3.Java2As3. org.granite.generator.as3. DefaultJava2As3 is the default implementation.

11.2.2. Auto-Populating AS3 Model Objects with XML Based on an XSD

In the last section, AS3 value objects were generated on the basis of their Java counterparts. In this section, AS3 model objects are populated on the basis of the underlying XML Schema (XSD). XSD-to-AS3 translation comes as a handy tool when using web services that publish their data types as XSD.

To get started, first create a sample XSD. A sample XSD is shown in Listing 11-4.

Example 11.4. book.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema id="schema"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns="http://www.shanky.org/problazeds"
    targetNamespace="http://www.shanky.org"
    elementFormDefault="qualified">

      <xs:complexType name="book">
          <xs:sequence>
              <xs:element name="id" type="xs:int"/>
              <xs:element name="title" type="xs:string"/>
              <xs:element name="author" type="xs:string"/>
              <xs:element name="price" type="xs:double"/>
            </xs:sequence>
      </xs:complexType>

</xs:schema>

An XML document in adherence to book.xsd is:

<?xml version="1.0" encoding="UTF-8"?>
<book
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.shanky.org/problazeds"
       xsi:schemaLocation="http://www.shanky.org book.xsd"
       xsi:noNamespaceSchemaLocation="http://www.shanky.org book.xsd" >

  <id>1</id>
  <title>pro blazeds</title>
  <author>shashank</author>
  <price>54.99</price>
</book>

A value object for the Book entity is:

package problazeds.ch11
{
    [Bindable]
    public class Book
    {
        public var id:int;
        public var title:String;
        public var author:String;
        public var price:Number;

        public function Book()
        {
        }

    }
}

Now that we have the XSD, a sample XML adhering to the XSD and an AS3 model object, I will illustrate how the AS3 model object can be populated with the data available as XML. The XSD and the value object are used to map the data between the XML and the object structure.

The mx.rpc.xml.SchemaLoader class can be used to load an XSD and then map it to an AS3 object type. First, you need to instantiate a SchemaLoader and then you need to set up an event listener for the SchemaLoadEvent.LOAD event, which is dispatched when a schema is loaded using the SchemaLoader instance. Once the listener is added, you actually load an XML schema. When the schema is loaded the SchemaLoadEvent.LOAD event is dispatched and the event listener handles this event.

The code for setting up the event listener and calling the load method is:

schemaLoader = new SchemaLoader();
schemaLoader.addEventListener(SchemaLoadEvent.LOAD, schemaLoader_loadHandler);
schemaLoader.load("book.xsd");

In the event handler, the schema is first accessed via the event object; then it is added to a SchemaManager instance. After that, the XSD is mapped to the AS3 model object. The mapping is done with the help of an instance of the mx.rpc.xml.SchemaTypeRegistry object, which registers an AS3 class with an XSD, identified by its target namespace URI.

The event handler itself could be a function as follows:

schemaManager.addSchema(event.schema);
var schemaTypeRegistry:SchemaTypeRegistry = SchemaTypeRegistry.getInstance();
schemaTypeRegistry.registerClass(new QName(schema.targetNamespace.uri, "book"), Book);

The example so far has suggested event handlers for the SchemaLoadEvent event, which is dispatched when a SchemaLoader has completed the task of loading the schema. As the schema is loaded, the SchemaLoader also dispatches the XMLLoadEvent.LOAD event. This means that if an XSD references other XSDs, the XMLLoadEvent is dispatched each time a schema is loaded. As opposed to this, the SchemaLoadEvent is dispatched only once per load.

Like any other loading functions, a schema load could end up in error, and you will benefit from defining a fault handler to take care of such situations.

Once the schema and the model object are mapped, auto-population of model objects with incoming XML data that complies with the XSD becomes possible. To populate a value object at runtime, you do the following:

  • Create an instance of mx.rpc.xml.XMLDecoder

  • Register the XMLDecoder instance with the SchemaManager instance that was created for the application

  • Instantiate a QName with two parameters: the schema target namespace URI and the root of the XML document

  • Invoke the decode function of the XMLDecoder instance passing in the XML and the QName instance as parameters

On calling the decode method of the XMLDecoder, the AS3 model object is returned, which can be accessed by type casting to the particular data type, in our example the Book AS3 class.

You could also carry out the reverse translation of object to XML using an XMLEncoder and calling its encode method with the object and a QName instance as parameters. The QName would again map the target namespace URI to the root of the XML document.

The XMLDecoder code could be as follows:

qName = new QName(schema.targetNamespace.uri, "book");

xmlDecoder = new XMLDecoder();
xmlDecoder.schemaManager = schemaManager;


decodedObject = xmlDecoder.decode(xml, qName)

book = decodedObject.book as Book;

The mx.rpc.xml package has many of these useful utilities, many of which are not even listed in the public live docs. It's worthwhile to explore these and use them for auto-population, which saves time and enhances developer productivity.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.224.226