When starting to use Flex together with a server you will propably allways have the problem that you have a server-side model and want to use that model in the Flex client. The naive approach would be to manually create ActionScript counterparts for every server class. This approach really sucks ... I think we all agree here.
Fortunately Flexmojos comes with a set of code generators that can be used to automatically create and maintain an ActionScript version of our server side model (I don't know if this is possible for non Java backends, but for Java it works nicely).
In order to have FM generate your model classes, you have to configure the "generate" execution. This will result in the default Generator being used (The default being the GraniteDS 2.2 Generator).
If you don't tell FM which Classes to generate, it will generate all Clases it finds in the modules classpath. This would generate quite a lot of junk. In order to explicitly tell FM which classes to generate Code for, the "includeJavaClasses" config option is used.
Here comes an example config:
<plugin> <groupId>org.sonatype.flexmojos</groupId> <artifactId>flexmojos-maven-plugin</artifactId> <extensions>true</extensions> <executions> <execution> <goals> <goal>generate</goal> </goals> <configuration> <includeJavaClasses> <include>de.cware.cweb.model.User</include> <include>de.cware.cweb.model.Group</include> </includeJavaClasses> </configuration> </execution> </executions> </plugin>
The above configuration would make FM generate two classes "User" and "Group". For every class FM actually generates up to 2 classes. It will generate a class de.cware.cweb.model.User in the src/main/flex source-tree. This is a dummy class that does nothing else than to extend a class named de.cware.cweb.model.UserBase. The de.cware.cweb.model.UserBase class is generated in target/generated-sources/flexmojos and contains the actual logic.
The reason for this is simple. The Base-class is re-generated with every build and represents the properties of the server-side class. If you want to extend this class with custom logic or properties these would be lost when rebuilding. Therefore you add your custom code to the de.cware.cweb.model.User class and have FM maintain the de.cware.cweb.model.UserBase class for you.
As long as your backend server uses GraniteDS as a Amf layer you're good to go ... if however you are using a differend backend, continue with the next chapter.
Generating classes for BlazeDS or other backends
Ok ... now as soon as you have generated your first classes you will notice that the Generator generates code that is highly dependent on a GraniteDS server. There classes will not work if you use a BlazeDS server on your backend.
In order to be able to generate code for BlazeDS (or other backends) you have to override the templates the generator uses.
Here comes the above configuration which uses custom templates:
<plugin> <groupId>org.sonatype.flexmojos</groupId> <artifactId>flexmojos-maven-plugin</artifactId> <extensions>true</extensions> <executions> <execution> <goals> <goal>generate</goal> </goals> <configuration> <includeJavaClasses> <include>de.cware.cweb.model.User</include> <include>de.cware.cweb.model.Group</include> </includeJavaClasses> <templates> <enum-template>src/main/templates/enum.gsp</enum-template> <interface-template>src/main/templates/interface.gsp</interface-template> <base-entity-template>src/main/templates/entityBase.gsp</base-entity-template> <entity-template>src/main/templates/entity.gsp</entity-template> <base-bean-template>src/main/templates/beanBase.gsp</base-bean-template> <bean-template>src/main/templates/bean.gsp</bean-template> <base-remote-template>src/main/templates/remoteBase.gsp</base-remote-template> <remote-template>src/main/templates/remote.gsp</remote-template> </templates> </configuration> </execution> </executions> </plugin>
I added all of the templates the GraniteDS generator supports.
- Java Enum classes will be generated by the enum-template (This doesn't have a base-template)
- Java Interfaces will be generated by the interface-template (This doesn't have a base-template)
- Classes annotated with the JPA annotation "@Entity" or "@MappedSuperclass" will be generated by entity-template and base-entity-template.
- Classes annotated with a custom GraniteDS annotation "@RemoteDestination" will be generated by remote-template and base-remote-template.
- All remaining classes will be generated by bean-template and base-bean-template
For a more detailed description, have a look at the documentataion of the GraniteDS Generator Ant-Task documentation: http://www.graniteds.org/confluence/display/DOC/3.+Gas3+Code+Generator
An Example of how to use Enums with Blazeds is documented on a separate Page of my wiki: Generating the ActionScript model
Optimizations in newer Flexmojos versions
When having multi-module projects with model classes generated by custom templates in several modules you will start to become anoyed of all the duplicate templates you have to place all over your project. In one of my projects I ended up with templates in about 20 different modules. Now if you want to change your templates you have to do this in 20 different files and if you miss to update one you will have serious trouble. If you don't specify any custom templates the generator uses default templates it loads from the classpath. As soon as you specify a template FM used to need a file in the filesystem.
As I found this rather anoying I submitted a patch to FM that allows the prefix "class:" to the template locations. This then loads a custom template from the classpath. This patch is part of the official FM4 distribution starting from FM 4.0.0-RC2.
This results in the following configuration:
<plugin> <groupId>org.sonatype.flexmojos</groupId> <artifactId>flexmojos-maven-plugin</artifactId> <extensions>true</extensions> <executions> <execution> <goals> <goal>generate</goal> </goals> <configuration> <includeJavaClasses> <include>de.cware.cweb.model.User</include> <include>de.cware.cweb.model.Group</include> </includeJavaClasses> <templates> <enum-template>class:de/cware/cweb/utils/granite/templates/enum.gsp</enum-template> <interface-template>class:de/cware/cweb/utils/granite/templates/interface.gsp</interface-template> <base-entity-template>class:de/cware/cweb/utils/granite/templates/entitsBase.gsp</base-entity-template> <entity-template>class:de/cware/cweb/utils/granite/templates/entity.gsp</entity-template> <base-bean-template>class:de/cware/cweb/utils/granite/templates/beanBase.gsp</base-bean-template> <bean-template>class:de/cware/cweb/utils/granite/templates/bean.gsp</bean-template> <base-remote-template>class:de/cware/cweb/utils/granite/templates/remoteBase.gsp</base-remote-template> <remote-template>class:de/cware/cweb/utils/granite/templates/remote.gsp</remote-template> </templates> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>de.cware.cweb.utils</groupId> <artifactId>granite-templates</artifactId> <version>1</version> </dependency> </dependencies> </plugin>
So in order to use the same templates throughout your entire project, you have to deploy the templates in a dedicated jar-module. This module can't be part of the current projects build, as it has to be available before starting to build. But as soon as you deploy it, you're free to use the same template throughout all of your modules.
Room for improvement
Currently generated code by FM gets rid of quite a lot of anoying maintenance work. Unfortunately there is still room for improvement.
One thing I find particularly anoying, is related to polymorphism over multiple modules.
In one of my projects I have a central event processing system that acts as a ESB that is extended to the Flex Client. So whenever a Server-Component or a Client fires one of these special events, these are automatically propagated and routed through the system. For this I created a base class called ApplicationEvent. FM nicely generates this class for me and it is part of the Flex lib-modules module that contains utility classes used by all modules. As I mentioned the event system takes care of the routing, but if a server-side component wants to send a custom event to the modules flex counterpart, it does this by extending ApplicationEvent. This is where the trouble starts.
It is no problem at all for FM to generate the custom ApplicationEvent in the corresponding module. Unfortunately the generated class does not extend ApplicationEvent as I didn't tell FM to generate ApplicationEvent too. I didn't want to generate ApplicationEvent as this is allready part of the lib-modues dependency and I didn't want to generate one class with the same name twice, as I didn't know what would happen in this case. I solved this problem by creating a custom template that manually outputs an extends ApplicationEvent and adds the propert imports, but this seems to be a big hack.
I know this is not a FM issue, but a GraniteDS generator issue.
What I would like to have would be to be able to specify a list of classes that Granite can rely on being available. So if I specify that ApplicationEvent is available, even if it is not generated right now, Granite wold treat it as if it was generated.
My Templates
The templates that I felt the need to modify were bean.gsp, beanBase.gsp and enum.gsp. In general I make do difference between simple POJOs and Entities so I usually configure the bean templates for beans and entities. In the bean Template I simply substitute the Map and the List counterparts. As BlazeDS uses an ArrayCollection for Lists and Objects for Maps. The Enum Template uses one of my custom Enum Classes that I described on a this page: Generating the ActionScript model
<templates> <enum-template>class:de/cware/cweb/utils/granite/templates/enum.gsp</enum-template> <base-entity-template>class:de/cware/cweb/utils/granite/templates/beanBase.gsp</base-entity-template> <entity-template>class:de/cware/cweb/utils/granite/templates/bean.gsp</entity-template> <base-bean-template>class:de/cware/cweb/utils/granite/templates/beanBase.gsp</base-bean-template> <bean-template>class:de/cware/cweb/utils/granite/templates/bean.gsp</bean-template> </templates>
Feel Free to use my templates:
Using them I had no problems at all to generate POJOs, Entities, Enums and Interfaces. I have to admit hat I haven't generated any Flex Clients to BlazeDS published Services yet. But I propably will in the next few weeks.