graniteds.orgCommunity Documentation
One of the main interests of using AMF remoting is that is can maintain a strongly typed bindable JavaFX data model in the client application. However that implies that you have to write a specific JavaFX class for each Java class that will be serialized. Writing and maintaining these JavaFX beans is tedious and a source of many errors. In order to solve this problem and accelerate the development of JavaFX/Java EE applications, GraniteDS comes with an code generator that writes JavaFX beans for all Java beans.
Additionally this generator specifically supports the externalization mechanism of GraniteDS and is able to generate corresponding JavaFX classes for externalized Java beans (typically JPA/Hibernate entities) with specific templates.
Finally this generator is able to write typesafe client proxies for exposed remote services.
Compared to the RemoteService API, this can greatly help development by bringing auto-completion and improved type-safety when using remote services.
Gfx may also replicate validation annotations in order to use the client side validation framework (see Bean Validation (JSR-303)).
The generator (named GFX) is implemented as an Ant task. This Ant task is packaged as an Eclipse 3.2+ Ant plugin but may also be used outside of Eclipse for command line Ant calls. It can also be used with Maven by means of the Flexmojos Maven plugin.
A common problem with code generators is the potential loss of manual modifications made in generated files. A generated file must be either generated once and only once, allowing for safe manual modifications, but it will not be able to reflect the modifications made in its model (JavaBeans), or regenerated each time its model has been changed, thus preventing safe manual modifications.
Gfx uses the principle of "Base" and customizable inherited classes that let you add methods to generated classes without facing the risk of losing them when a new generation process is executed. For example, here are the two files generated for a given Java entity bean:
Welcome.java
package org.test;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Welcome implements Serializable {
private static final long serialVersionUID = 1L;
@Id @GeneratedValue
private Integer id;
@Basic
private String name;
public Welcome() {
}
public Welcome(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Welcome.java
/**
* Generated by Gas3 v2.3.0 (Granite Data Services).
*
* NOTE: this file is only generated if it does not exist. You may safely put
* your custom code here.
*/
package org.test.client;
@JavaFXObject
@RemoteClass("org.test.Welcome")
public class Welcome extends WelcomeBase {
}
WelcomeBase.java
/**
* Generated by Gas3 v2.3.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 (Welcome.as).
*/
package org.test.client;
@JavaFXObject
public class WelcomeBase implements IExternalizable {
...
}
The recommendations for manual editing are explicit in the header comments of each generated classes: while the "Base" class may be regenerated at any time, keeping it sync with its Java model class, the inherited one is only generated when it does not exist and you may safely add custom methods into it.
This two files generation principle is used for all generated classes except interface and enum: these classes are generated without any "Base" class and overwritten each time you have modified their Java counterparts.
Note: Do not modify manually generated client interface or enum classes !
Here are the details for (re)generation conditions:
Note that for Java classes, relevant timestamp is the last modified time of the .class file, not the .java file.
| Templates | Conditions for (re)generation |
|---|---|
| Dual templates (base + inherited) | The inherited JavaFX class is generated only once if it does not exist. The JavaFX base one is generated if it does not exist or if its timestamp (last modified time) is less than the Java class one |
| Single template (enums or interfaces) | Like the base condition above, the JavaFX class is (re)generated if it does not exist or if its timestamp is less than the Java class one |
Here is the summary of templates used by the generator depending on the kind of Java class it encounters:
| Type of Java Class | Template | Base Template |
|---|---|---|
| Standard Java beans | bean.gsp | beanBase.gsp |
JPA entities: all classes annotated with @Entity and @MappedSuperclass | entity.gsp | entityBase.gsp |
| Java enums | enum.gsp | (none) |
| Java interfaces | interface.gsp | (none) |
Java services: all classes annotated with @RemoteDestination | remote.gsp | remoteBase.gsp |
Java events (CDI): all classes annotated with @TideEvent | bean.gsp | beanBase.gsp |
Note that all these templates are bundled in the granite-generator.jar archive, in the org.granite.generator.javafx.template package and accessible as resources via the class loader.
TODO (see doc for Flex plugin).
JavaFX classes can also be generated with the Gas3 plugin for Flex. You just need to define a specific configuration to generate JavaFX classes :
Use the org.granite.generator.javafx.JavaFXGroovyTransformer transformer class
Use the org.granite.generator.javafx.DefaultJavaFXTypeFactory type factory class
Select the JavaFX templates in the org/granite/generator/javafx/template (for example class:org/granite/generator/template/entity.gsp)
Download org.granite.builder_***.jar, and drop it in your Eclipse plugins directory
(remove any older version and restart Eclipse). The Add GraniteDS Nature option should now be available
if you right-click on your Java project and the gas3 Ant task should be ready to use in your build.xml file under Eclipse.
Download org.granite.builder_***.jar and unzip it somewhere (create a new directory, this jar doesn't contain a root folder).
Move the lib directory somewhere else (say gas3libs at the root of you harddrive).
In your build.xml, you must declare the Gfx ant task as follows:
<taskdef name="gas3" classname="org.granite.generator.javafx.AntJavaFXTask"/>
To launch a build process with Gfx targets, you should go to your Java source root directory and type something like:
$ ant -lib /gfxlibs -f build.xml {target}
...
Just replace {target} with a valid target name and make sure Ant is correctly set up: set ANT_HOME variable
and put <ANT_HOME>/bin in your PATH environment variable.
After installation, you may use the Gas3 Ant task in any target of an Ant build file.
A working example of Gas3 usage is available in the examples/graniteds_ejb3 sample application.
For example:
<target name="generate.fx">
<gfx outputdir="java">
<classpath>
<pathelement location="classes"/>
</classpath>
<fileset dir="classes">
<include name="com/myapp/entity/**/*.class"/>
</fileset>
</gas3>
</target>
As you can notice, Gfx generates JavaFX beans from Java compiled classes.
You may use multiple Ant filesets in order to specify for which JPA classes you want to generate JavaFX beans.
The classpath node is used for fileset class loading, and you may reference extra jars or classes needed by your beans class loading.
The outputdir attribute lets you instruct Gfx in which directory JavaFX beans will be generated (e.g., ./java).
This path is relative to your current project directory and Gfx will create subdirectories for packages.
JavaFX beans will by default have the same package hierarchy as Java classes, with the same subdirectories as well.
This may not be very convenient, so it is recommended that you use a package translation definition (see below package translators).
For each JPA entity (say com.myapp.entity.MyEntity), Gfx will generate two JavaFX beans:
org.entity.MyEntityBase.java: This bean mainly contains fields, getters, setters, and extra methods.
This file is generated if it does not exist or if it is outdated.
org.entity.MyEntity.java: This bean inherits from the "Base" one and is only generated if it does not exist.
While you should not modify the "Base" file, since your modifications may be lost after another generation process, you may safely add your code to the inherited bean.
You can also use Ant zipfilesets if you want to generate JavaFX classes from an existing jar. Note that
the jar must be in the classpath:
<target name="generate.fx">
<gfx outputdir="java">
<classpath>
<pathelement location="lib/myclasses.jar"/>
</classpath>
<zipfileset src="lib/myclasses.jar">
<include name="com/myapp/entity/**/*.class"/>
</zipfileset>
</gas3>
</target>
You may tell Gfx to generate client classes with a different package and directory structure than the corresponding Java server classes. This is even highly recommended to avoid classpath conflicts or ambiguous autocompletions in the IDE.
<gfx ...>
<classpath .../>
<fileset .../>
<translator
java="path.to.my.java.class"
client="path.to.my.client.class" />
<translator
java="path.to.my.java.class.special"
client="otherpath.to.my.client.class.special" />
...
</gfx>
Gfx uses these translators with a "best match" principle; all Java classes within the path.to.my.java.class package,
and subpackages as well, will be translated to path.to.my.client.class, while path.to.my.java.class.special
will use a specific translation (otherpath.to.my.client.class.special).
Gfx generation relies on Groovy templates. You may plug your own templates in by using one of the advanced options attributes below.
For example, you could add a entitytemplate="/absolute/path/to/my/groovy/entityTemplate.gsp" attribute to the gfx node.
You can also specify paths to your custom templates relative to the current Ant project basedir directory.
If you want to see the Groovy code of the default templates, just unpack granite-generator.jar
in the lib directory of the plugin, and look for org/granite/generator/template/*[Base].gsp files.
Here is the complete list of Gfx node attributes:
outputdir and baseoutputdir: We have already seen the outputdir attribute
in basic usage. baseoutputdir lets you define a custom output directory for your "Base" generated files.
The default is to use the same directory as specified by the outputdir attribute.
uid: If you want your JavaFX to implement Identifiable, you must tell the generator
the name of the Java field that contains this UID. By default, Gfx will search for a field named uid.
You may change this by adding a uid="myUid" attribute to the gfx node.
If Gfx does not find this uid, it will be silently ignored.
tide: Should we use a Tide specific template instead of the standard base template used for entity beans
(true or false, defaut is false). Setting this attribute has no effect
if you use a custom entity base template. See below.
entitytemplate and entitybasetemplate: Templates used for classes annotated with
@Entity or @MappedSuperclass.
interfacetemplate: Template used for Java interfaces.
beantemplate and beanbasetemplate: Templates used for other Java classes including @Embeddable.
enumtemplate: Template used for java.lang.Enum types.
remotetemplate and remotebasetemplate: Templates used for server services (EJB3, Spring or Seam services).
clienttypefactory: You can plug your own org.granite.generator.as3.As3TypeFactory implementation
in order to add support for custom types. For example, if you have configured a custom Joda time converter,
you may extend Gfx accordingly for this custom type. Just extend the org.granite.generator.javafx.DefaultJavaFXTypeFactory class
and return for example com.myapp.custom.DATE when you encounter a Joda DateTime instance.
See Handling custom data types for a detailed example.
entityfactory: Class used to introspect specific entity properties or metadata (default is
org.granite.generator.as3.DefaultEntityFactory).
You may also use the built-in org.granite.generator.as3.BVEntityFactory in order to replicate bean validation annotations
into your AS3 model Bean Validation (JSR-303).
remotedestinationfactory: Class used to introspect specific service properties or metadata (default
is org.granite.generator.as3.DefaultRemoteDestinationFactory).
transformer: Class used to control the generation process (very advanced use). Default for JavaFX is org.granite.generator.javafx.JavaFXGroovyTransformer.
For example:
<target name="generate.fx">
<gfx
outputdir="java"
baseoutputdir="base_java"
uid="myUidFieldName"
entitytemplate="/myEntityTemplate.gsp"
entitybasetemplate="/myEntityBaseTemplate.gsp"
interfacetemplate="/myInterfaceTemplate.gsp"
beantemplate="/myBeanTemplate.gsp"
beanbasetemplate="/myBeanBaseTemplate.gsp"
enumtemplate="/myEnumTemplate.gsp"
remotetemplate="/myRemoteTemplate.gsp"
remotebasetemplate="/myRemoteBaseTemplate.gsp"
tide="true"
clienttypefactory="path.to.MyCustomTypeFactory"
entityfactory="path.to.MyEntityFactory"
remotedestinationfactory="path.to.MyRDFactory"
transformer="path.to.MyTransformer"
externalizelong="true"
externalizebiginteger="true"
externalizebigdecimal="true">
<classpath>
<pathelement location="classes"/>
</classpath>
<fileset dir="classes">
<include name="test/granite/ejb3/entity/**/*.class"/>
</fileset>
</gas3>
</target>
Note that when using a custom clienttypefactory, entityfactory, remotedestinationfactory
or transformer attribute, you must configure the classpath in order to make your custom classes available to the Gfx engine;
either use the classpath attribute in the taskdef declaration or in the gfx call.
The Gfx generator is used as the default code generation tool in the Flexmojos plugin. To use it, you need to add the following part to your maven POM :
<build>
...
<pluginManagement>
<plugins>
<plugin>
<groupId>org.sonatype.flexmojos</groupId>
<artifactId>flexmojos-maven-plugin</artifactId>
<version>${flexmojos.version}</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.sonatype.flexmojos</groupId>
<artifactId>flexmojos-maven-plugin</artifactId>
<version>${flexmojos.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<generatorToUse>graniteds23</generatorToUse>
<baseOutputDirectory>${project.build.directory}/generated-sources</baseOutputDirectory>
<outputDirectory>${basedir}/src/main/java</outputDirectory>
<translators>
<translator>com.myapp.server=com.myapp.client</translator>
</translators>
<extraOptions>
<tide>true</tide>
<uid>uid</uid>
<transformer>org.granite.generator.javafx.JavaFXGroovyTransformer</transformer>
<as3typefactory>org.granite.generator.javafx.DefaultJavaFXTypeFactory</as3typefactory>
<entityFactory>org.granite.generator.as3.BVEntityFactory</entityFactory>
<outputEnumToBaseOutputDirectory>false</outputEnumToBaseOutputDirectory>
</extraOptions>
<includeJavaClasses>
<include>${package}.entities.**</include>
<include>${package}.services.*Service</include>
</includeJavaClasses>
<templates>
<base-bean-template>classpath:org/granite/generator/javafx/template/tideBeanBase.gsp</base-bean-template>
<bean-template>classpath:org/granite/generator/javafx/template/bean.gsp</bean-template>
<base-entity-template>classpath:org/granite/generator/javafx/template/tideEntityBase.gsp</base-entity-template>
<entity-template>classpath:org/granite/generator/javafx/template/entity.gsp</entity-template>
<base-remote-template>classpath:org/granite/generator/javafx/template/tideRemoteBase.gsp</base-remote-template>
<remote-template>classpath:org/granite/generator/javafx/template/tideRemote.gsp</remote-template>
<enum-template>classpath:org/granite/generator/javafx/template/enum.gsp</enum-template>
</templates>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
<version>1.0.1.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<dependency>
<groupId>javax.jdo</groupId>
<artifactId>jdo2-api</artifactId>
<version>2.3-eb</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>1.6.0</version>
</dependency>
<dependency>
<groupId>antlr</groupId>
<artifactId>antlr</artifactId>
<version>2.7.7</version>
</dependency>
<dependency>
<groupId>asm</groupId>
<artifactId>asm</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.sonatype.flexmojos</groupId>
<artifactId>flexmojos-generator-graniteds-2.3.0</artifactId>
<version>${flexmojos.version}</version>
</dependency>
<dependency>
<groupId>org.graniteds</groupId>
<artifactId>granite-core</artifactId>
<version>${graniteds.version}</version>
</dependency>
<dependency>
<groupId>org.graniteds</groupId>
<artifactId>granite-generator-share</artifactId>
<version>${graniteds.version}</version>
</dependency>
<dependency>
<groupId>org.graniteds</groupId>
<artifactId>granite-generator</artifactId>
<version>${graniteds.version}</version>
</dependency>
</dependencies>
</plugin>
...
</plugins>
...
</build>