In the previous recipe, we saw how to set up a project to use Flex and Spring BlazeDS integration. In this recipe, we go a step further and scaffold a complete Flex application that interacts with BlazeDS to perform CRUD operations on the JPA entities.
This recipe is an extension of the previous recipe, Getting started with Flex application development; therefore, perform the steps described in the previous recipe to set up the flightapp_flex
project to use the Flex and Spring BlazeDS integration.
Start the Roo shell from the C:
oo-cookbookch05-flex
directory—the directory in which the flightapp_flex
project was created when you went through the Getting started with Flex application development recipe.
To scaffold a flex application, follow the steps given here:
flex remoting all
command, as shown here:.. roo> flex remoting all --package ~.flex Created SRC_MAIN_JAVAsample ooflightappflex Created ..FlightDescriptionService.java Created ..FlightDescriptionService_Roo_Service.aj .. Created ROOTsrcmainflexsample ooflightappdomain Created ..FlightDescription.as Updated ROOTsrcmainflexflightapp_flex_scaffold.mxml Updated ROOTsrcmainflexflightapp_flex_scaffold-config.xml Created ROOTsrcmainflexsample ooflightapppresentation flightdescription Created ..FlightDescriptionEvent.as Created ..FlightDescriptionView.mxml Created ..FlightDescriptionForm.mxml
FlightDescription
JPA entity.mvn install
from the directory containing the flightapp_flex
project to build the flightapp_flex
project:C:
oo-cookbookch05-flex>mvn install
tomcat:run
goal (from the directory containing the flightapp_flex
Roo project) of the Tomcat Maven plugin to start the embedded Tomcat instance:C:
oo-cookbookch05-flex>mvn tomcat:run
flightapp_flex_scaffold.html
file, which acts as the HTML wrapper for our Flex application: http://localhost:8080/flightapp_flex/flightapp_flex_scaffold.html
. If Flash Player 10
or above is not already installed for your web browser, you'll be asked to install it. It is also recommended that you install Flash Debugger
for your web browser to view any exceptions raised while interacting with the Flex application. If you see the following Flex application user interface, then it means you have successfully deployed your Flex application on Tomcat:flightapp_flex
project contained Flight
and FlightDescription
entities, they are shown in the list.FlightDescription
and Flight
entity instances, double-click the JPA entity name in the list. The following screenshot shows the screen that is displayed when you double-click the FlightDescription item in the list:The flex
remoting
all
command is processed by the Flex add-on of Spring Roo.
Flex clients interact with server-side BlazeDS remoting destinations (which are Java objects) via BlazeDS RemotingService
. As we are using Spring BlazeDS integration, remoting destinations are configured as Spring service components, and the RemotingService
is configured with sensible defaults by the <message-broker>
element of Spring's flex
schema (refer SRC_MAIN_WEBAPPWEB-INFspringflex-config.xml
file). The package
argument of the flex
remoting
all
command specifies the package in which the remoting destinations are created.
It is important to note that Flex clients interact with messaging destinations (which could be a JMS queue or topic) using the MessageService
and with remoting destinations (Java objects) using the RemotingService
.
The following table describes the important directories and files that are created when the flex
remoting
all
command is executed:
Directory / file |
Description |
---|---|
|
Contains remoting destinations created by Roo corresponding to each JPA entity in the |
|
Contains Roo-generated ActionScript classes that map to JPA entities in the |
|
Contains MXML files and ActionScript classes for performing CRUD operations on the |
|
Contains MXML files and ActionScript classes for performing CRUD operations on the |
When the flex
remoting
all
command is executed, it creates a remoting destination (which is also Spring's service component) corresponding to each JPA entity in the application for which a remoting destination doesn't already exist. A remoting destination defines methods to perform CRUD operations on the corresponding JPA entity. The following code listing shows the remoting destination, FlightDescriptionService
class, created by Roo corresponding to the FlightDescription
JPA entity:
FlightDescriptionService.java package sample.roo.flightapp.flex; import org.springframework.flex.roo.addon.RooFlexScaffold; import sample.roo.flightapp.domain.FlightDescription; import org.springframework.flex.remoting.RemotingDestination; import org.springframework.stereotype.Service; @RooFlexScaffold(entity = FlightDescription.class) @RemotingDestination @Service public class FlightDescriptionService { }
In this code, @RooFlexScaffold
annotation instructs Roo to generate a corresponding AspectJ ITD file. This AspectJ ITD file introduces methods into the FlightDescriptionService
class for performing CRUD operations on the FlightDescription
entity. The entity
attribute of @RooFlexScaffold
annotation specifies the JPA entity managed by the FlightDescriptionService
class. The @RemotingDestination
annotation of Spring indicates that FlightDescriptionService
class is exported as a remoting destination.
Spring-managed MessageBroker
is responsible for routing messages received from the Flex clients to RemotingService
, which in turn invokes the method on the Spring-managed remoting destination. The @Service
annotation indicates that FlightDescriptionService
represents Spring's service component. The use of the @Service
annotation ensures that FlightDescriptionService
is auto-registered with Spring's web application context, using the classpath scanning feature of Spring (refer to the <component-scan>
element defined in the webmvc-config.xml
file).
The following code listing shows the AspectJ ITD file created corresponding to the @RooFlexScaffold
annotation in the FlightDescriptionService
class:
FlightDescriptionService_Roo_Service.aj package sample.roo.flightapp.flex; import java.lang.Long; import java.util.List; import sample.roo.flightapp.domain.FlightDescription; privileged aspect FlightDescriptionService_Roo_Service { public FlightDescription FlightDescriptionService.show(Long id) { ... return FlightDescription.findFlightDescription(id); } public List<FlightDescription> FlightDescriptionService.list() { return FlightDescription.findAllFlightDescriptions(); } ... }
This code shows that FlightDescriptionService_Roo_Service.aj
introduces CRUD operations for the FlightDescription
JPA entity in the FlightDescriptionService
class. Though not shown in the above code, pagination support is also introduced for reading the list of FlightDescription
JPA entities.
The name of the AspectJ ITD file corresponding to the @RooFlexScaffold
annotation has the following naming convention: <JPA-entity-name>Service_Roo_Service.aj
, where <JPA-entity-name>
is the name of the JPA entity specified by the entity
attribute of the @RooFlexScaffold
annotation.
Invoking Spring-managed remoting destination methods from the Flex client may require sending and receiving objects. For instance, the show
method of FlightDescriptionService
returns a FlightDescription
object and the create
method accepts a FlightDescription
object. Flex allows exchanging data between the Flex client and remoting destination method by auto-converting the ActionScript object to the Java object and vice versa. As the Flex client in the flightapp_flex
application exchanges flight description details with the FlightDescriptionService
remoting destination, Roo generates ActionScript classes corresponding to the JPA entity managed by FlightDescriptionService
. The following code shows the Roo-generated FlightDescription.as
ActionScript class corresponding to the FlightDescription
JPA entity:
FlightDescription.as package sample.roo.flightapp.domain{ [RemoteClass(alias="sample.roo.flightapp.domain.FlightDescription")] public class FlightDescription { public var destination:String; public var id:Number; public var origin:String; public var price:Number; public var version:Number; } }
This code shows that the FlightDescription.as
ActionScript class defines the same attributes as the corresponding FlightDescription
JPA entity. The [RemoteClass]
metadata tag specifies the remote Java object to which the ActionScript object maps. The alias
attribute specifies the fully-qualified class name of the remote Java object to which the ActionScript object maps.
Roo creates ActionScript and MXML files corresponding to each JPA entity so that CRUD operations can be performed on JPA entities from the scaffolded Flex user interface. The following table describes each of these Roo-generated files (located in ROOTsrcmainflexsample
ooflightapppresentationflightdescription
and ROOTsrcmainflexsample
ooflightapppresentationflight
directories):
The following code shows the FlightDescriptionEvent.as
ActionScript class createdby Roo:
package sample.roo.flightapp.presentation.flightdescription { import flash.events.Event; import sample.roo.flightapp.domain.FlightDescription; public class FlightDescriptionEvent extends Event { public static const CREATE:String = "flightDescriptionCreate"; public static const UPDATE:String = "flightDescriptionUpdate"; public static const DELETE:String = "flightDescriptionDelete"; public var flightDescription:FlightDescription; public function FlightDescriptionEvent(type:String, flightDescription:FlightDescription, bubbles:Boolean = true, cancelable:Boolean = false){ this.flightDescription = flightDescription; super(type, bubbles, cancelable); } } }
The FlightDescriptionEvent
class is a subclass of the flash.events.Event
class and defines three different types of events: flightDescriptionCreate
, flightDescriptionUpdate
, and flightDescriptionDelete
. The FlightDescription
ActionScript object (which corresponds to the FlightDescription
JPA entity on the server-side) represents the payload carried by the FlightDescriptionEvent
event type.
As mentioned earlier, in the flightapp_flex
application, flightapp_flex_scaffold.mxml
file defines the initial user interface of the application. When the flex
remoting
all
command was executed, we saw in the output that the flightapp_flex_scaffold.mxml
file was updated. The following code shows the modification that was made by Roo to the flightapp_flex_scaffold.mxml
file:
flightapp_flex_scaffold.mxml ... <fx:Declarations> <s:ArrayList id="entities"> <fx:String>FlightDescription</fx:String> <fx:String>Flight</fx:String> </s:ArrayList> ... </fx:Declarations> ... <s:Panel id="entityPanel" title="Entity List" height="100%"> <s:List id="entityList" dataProvider="{entities}" width="100%" height="100%" toolTip="Double-Click the selected Entity" doubleClickEnabled="true" doubleClick="entityList_doubleClickHandler(event)"/> </s:Panel> </s:Group>
If you compare this code with the code of the flightapp_flex_scaffold.mxml
file that we saw in the previous recipe, you'll notice that the only change that happened is the addition of the <fx:String>
elements to the <ArrayList>
. Roo creates an <fx:String>
element corresponding to each JPA entity in the application. By default, the value of the <fx:String>
element is the simple name of the corresponding JPA entity. As the <List>
component makes use of <ArrayList>
as its data provider, the <List>
component now displays Flight and FlightDescription list items in the user interface, as shown here:
This screenshot shows that Roo doesn't generate a list item corresponding to the finder method, findFlightDescriptionsByDestinationAndOrigin
, defined in FlightDescription
JPA entity.
When you double-click an item in the list shown above, it invokes the entityList_doubleClickHandler
ActionScript method defined in the flightapp_flex_scaffold.mxml
file, which displays the user interface generated either by FlightView.mxml
or FlightView.mxml
, depending upon the list item double-clicked. The following code shows the entityList_doubleClickHandler
method:
protected function entityList_doubleClickHandler(event:MouseEvent):void { .. var selectedEntity:String = entityList.selectedItem; var selectedEntityPackage:String = selectedEntity.toLowerCase(); var viewClass:Class = getDefinitionByName("sample.roo.flightapp.presentation." + selectedEntityPackage+"::"+selectedEntity+"View") as Class; if (viewClass != null) { var newView:UIComponent = UIComponent(new viewClass()); ... mainGroup.addElement(newView); } ... }
As MXML files are compiled into ActionScript classes, FlightDescriptionView.mxml
and FlightView.mxml
files are converted to FileDescriptionView
and FlightView
ActionScript classes, respectively. The entityList_doubleClickHandler
method obtains the selected item value from the list (which is either FlightDescription or Flight) and appends 'View' string to it—making the concatented value to FlightDescriptionView
or FlightView
. The entityList_doubleClickHandler
then creates an instance of FlightDescriptionView
or FlightView
and adds it to the main user interface.
The following sequence diagram summarizes the role played by the entity_doubleClickHandler
method:
It is important to note that the entityList_doubleClickHandler
method of the flightapp_flex_scaffold.mxml
file never directly references either the FlightView
or FlightDescriptionView
ActionScript class. In fact, FlightView
and FlightDescriptionView
classes are not referenced by any other MXML or ActionScript class in the flightapp_flex
project. The side-effect of this is that the Flex compiler doesn't include FlightDescriptionView
and FlightView
in the generated SWF file. To instruct Flex compiler to include FlightDescriptionView
and FlightView
ActionScript classes, Roo adds their fully-qualified name in the flightapp_flex_scaffold-config.xml
file, as shown here:
flightapp_flex_scaffold-config.xml <flex-config xmlns="http://www.adobe.com/2006/flex-config"> <includes append="true"> <symbol>sample.roo.flightapp.presentation.flightdescription.FlightDescriptionView </symbol> <symbol> sample.roo.flightapp.presentation.flight.FlightView </symbol> </includes> </flex-config>
The <symbol>
elements specify the ActionScript classes that should be included in the generated SWF file by the Flex compiler.
The FlightDescriptionView.mxml
shows a New FlightDescription button, and if clicked, it invokes the showForm
method. The showForm
method of FlightDescriptionView.mxml
shows the form (represented by FlightDescriptionForm.mxml
) for creating FlightDescription
entity instances, as shown here:
The following sequence diagram shows what happens behind the scenes when you click on the New FlightDescription button:
This sequence diagram shows that the showForm
method creates FlightDescriptionForm
and FlightDescription
objects. The FlightDescription
object (which corresponds to FlightDescription
JPA entity) acts as the form-backing object that we see in web applications. The showForm
methods sets the FlightDescription
object in the FlightDescriptionForm
instance. Also, showForm
adds an event listener for the FlightDescriptionEvent.CREATE
event to FlightDescriptionForm
.
The following code shows the showForm
method:
private function showForm
(flightDescription:FlightDescription = null):void {
var form:FlightDescriptionForm =
PopUpManager.createPopUp(this, FlightDescriptionForm, true)
as FlightDescriptionForm;
..
form.flightDescription = flightDescription != null ?
flightDescription : new FlightDescription();
form.addEventListener(FlightDescriptionEvent.CREATE,
flightDescriptionView_flightDescriptionCreateEventHandler);
}
This code shows that, the addEventListener
method accepts the type of event that FlightDescriptionForm
object listens to, which is FlightDescriptionEvent.CREATE
. The addEventListener
also accepts the name of the handler method that is invoked when the event is received by the FlightDescriptionForm
object. So, if the FlightDescriptionEvent.CREATE
event is received by the FlightDescriptionForm
object, it results in the invocation of the flightDescriptionView_flightDescriptionCreateEventHandler
method. We'll come back to the handler method, but first let's look at how the FlightDescriptionEvent.CREATE
event is generated.
The following sequence diagram shows that the FlightDescriptionEvent.CREATE
event is generated when the user presses the Save button to create a FlightDescription
JPA entity instance:
This sequence diagram shows that when the Save button is clicked, it results in the invocation of the processSave
method defined in FlightDescriptioForm.mxml
. The processSave
method validates the form data entered by the user using the mx.validators.Validator
. If the data validation succeeds, form data is set in the FlightDescription
ActionScript object. The processSave
method now creates a FlightDescriptionEvent
event of type FlightDescriptionEvent.CREATE
and passes the FlightDescription
ActionScript object as the payload of the event. Invoking the dispatchEvent
method results in dispatching the newly created event to listeners.
So, after receiving the FlightDescriptionEvent.CREATE
event, flightDescriptionView_flightDescriptionCreateEventHandler
is invoked, as explained earlier. The following code shows the flightDescriptionView_flightDescriptionCreateEventHandler
method, which invokes the FlightDescriptionService
's create
method to create an instance of the FlightDescription
JPA entity:
protected function
flightDescriptionView_flightDescriptionCreateEventHandler
(event:FlightDescriptionEvent):void {
flightDescriptionService.create(event.flightDescription);
}
...
The flightDescriptionService
object in the previous code represents a mx.rpc.remoting.RemoteObject
, which is used by Flex clients to access remoting destinations. RemoteObject
is defined in FlightDescriptionView.mxml
using the <RemotObject>
tag, as shown here:
<s:RemoteObject channelSet="{remotingChannels}" destination="flightDescriptionService" fault="flightDescriptionService_faultHandler(event)" id="flightDescriptionService"> ... </s:RemoteObject>
In this code, {remotingChannels}
identifies the ChannelSet
to use for communication with server-side Java objects. We saw in the previous recipe that remoting channels used by the flightapp_flex
application are defined in the flightapp_flex_scaffold.mxml
file using the <ChannelSet>
tag. The destination
attribute specifies the remoting destination that is accessed via RemoteObject
.
The Flex Add-on provides round-tripping support, that is, modifications to JPA entities are propagated to MXML and ActionScript files.
Flex Addon doesn't provide any support for controlling the methods that form a part of the Spring-managed remoting destinations. For instance, you can't control the methods that are part of the FlightDescriptionService_Roo_Service.aj
file using @RooFlexScaffold
annotation.
If you want that a method in the Spring-managed remoting destination is not accessible to Flex clients, then all you need to do is to either perform push-in refactoring or define the method in the corresponding Java class and add the @RemotingExclude
annotation of Spring to the method.
Spring Roo makes use of JSR 303 annotations specified in the JPA entity to add Flex validators in the MXML files. For instance, if a JPA entity field specifies @NotNull
JSR 303 annotation then Roo adds a Flex StringValidator
or Numbervalidator
that checks that the field on the form is not blank. Note that Flex addon support for JSR 303 annotations is limited.
3.135.247.181