Flex on Grails
Creating the detail view
Now that the master view has been created, it’s on to the detail view. The detail view will provide a mechanism for editing and updating your contacts. Although we could have allowed editing right in the DataGrid itself, it wouldn’t have made for a good example and doesn’t show off the power of data binding in Flex.
Main.mxml (c)
<mx:Form id="contactForm" width="100%"> #1
<mx:FormItem label="ID:" width="100%"> #2
<mx:Text text="{selectedContact.id}"/>
</mx:FormItem>
<mx:FormItem label="First Name:" width="100%">
<mx:TextInput id="firstName" text="{selectedContact.firstName}"/>
</mx:FormItem>
<mx:FormItem label="Last Name:" width="100%">
<mx:TextInput id="lastName" text="{selectedContact.lastName}"/>
</mx:FormItem>
<mx:FormItem label="Address:" width="100%">
<mx:TextInput id="address" text="{selectedContact.address}"/>
</mx:FormItem>
<mx:FormItem label="City:" width="100%">
<mx:TextInput id="city" text="{selectedContact.city}"/>
</mx:FormItem>
<mx:FormItem label="State:" width="100%">
<mx:TextInput id="state" text="{selectedContact.state}"/>
</mx:FormItem>
<mx:FormItem label="Zip Code:" width="100%">
<mx:TextInput id="zipCode" text="{selectedContact.zipCode}"/>
</mx:FormItem> #2
</mx:Form>
<mx:ControlBar> #3
<mx:Button label="New Contact"
enabled="true" click="selectedContact = new Contact()"/>
<mx:Button label="Save Contact"
click="updateContact(selectedContact)"/>
<mx:Button label="Reset" click="resetForm()"/>
</mx:ControlBar> #3
#1 The Form
#2 FormItems
#3 ControlBar
Here us the code for the detail view in which you leverage another container object, the Form XE "Form" component (#1). Unlike its HTML counterpart, the Form component in Flex is strictly a container. You don’t need to have your fields wrapped in a Form component to post data to the server side.
Inside the Form component you define a series of FormItem XE "FormItem" components (#2) that contain the GUI form components used for data entry. These should be fairly self-explanatory. Take note of the data binding syntax in the text attributes for these components. This indicates that you’ll be binding the text values of these components to a variable called selectedContact. As a last step, you add another ControlBar (#3) as you did for the master view to contain any buttons needed to control the application.
Adding the RemoteService
Flex has a few components that enable it to communicate with the server side, namely HTTPService, WebService, and RemoteService. In a nutshell WebService facilitates easy communication with SOAP-based web services. HTTPService allows you to consume and call other web services using a variety of protocols such as XML and JSON and is the best choice if you’re trying to interact with some sort of RESTful XE "RESTful" resource. RemoteService leverages Adobe’s binary AMF XE "AMF" ) protocol, which tends to give the best performance of the three.
Adding the RemoteService
<mx:RemoteObject id="contactService" destination="contactService">
<mx:method name="getContacts"
result="handleGetContacts(event.result)"
fault="showFault(event)"/>
<mx:method name="update" fault="showFault(event)"/>
<mx:method name="remove" fault="showFault(event)"/>
</mx:RemoteObject>
As stated previously you’re going to use the RemoteObject component to facilitate communication with the server side, and you’ve defined the methods that you’ll be calling so you can define the callback methods that you’ll be using to handle the results coming back from the server, as well as any faults.
Putting it all together
You’re almost done. You now put it all together and add methods that you’ll call to handle events from the UI.
Main.mxm (d)
<local:Contact id="selectedContact" #1
firstName="{firstName.text}"
lastName="{lastName.text}"
address="{address.text}"
city="{city.text}"
state="{state.text}"
zipCode="{zipCode.text}"/>
<mx:ArrayCollection id="contactsList"/>
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.controls.Alert;
private function doSelect(c:Contact):void {
selectedContact = c;
}
private function handleGetContacts(list:*):void { #2
contactsList.removeAll();
for each (var c:Contact in list) {
contactsList.addItem(c);
}
}
private function showFault(fault:*):void { #3
Alert.show(fault.message);
}
private function deleteContact(selectedContact:Contact):void { #4
contactService.remove(selectedContact);
contactService.getContacts();
}
private function updateContact(contact:Contact):void {
contactService.update(selectedContact);
contactService.getContacts();
}
private function resetForm():void {
var tmpObj:Contact = Contact(contactsGrid.selectedItem);
contactService.getContacts();
contactsGrid.selectedItem = tmpObj;
doSelect(Contact(contactsGrid.selectedItem)); #4
}
]]>
</mx:Script>
#1 Contact
#2 RemoteObject event handler
#3 RemoteObject fault handler
#4 Event handlers
Most of what is shown in the listing should make sense. Here you define a Contact object in MXML rather than ActionScript (#1). Notice that you’re also performing data binding back to the form components, creating a two-way binding between the selectedContact variable and the detail form. Next define the event handler (#2) and fault handler (#3) for the RemoteObject that you defined in listing 11.8. Last you have all the event handlers for the components in the master view and detail view (#4).
Now that you’ve completed this part of the application, you can start up your Grails application by executing grails run-app XE "Grails:running application" and navigating to the application. After your application is running, fire up your browser and navigate to http://localhost:8080/flex-contacts/main.mxml and you should see something resembling figure 11.1. You’ve got a functional Flex application running on Grails. Over the next few sections you’re going to modify this simple example to leverage JMS and ActiveMQ.
Install the Grails JMS and ActiveMQ plugins
XE "Grails:JMS plugin" XE "Grails:ActiveMQ plugin"
Next you’re going to demonstrate the ability of your Grails application to push data out to the Flex application using Flex components that integrate with JMS to produce and consume messages. This will allow you to remove the Refresh button and some of the plumbing involved to refresh the Flex DataGrid when a user adds, edits, or deletes a contact. This helps clean up the client by reducing the amount of view logic and complexity. To install the Grails JMS plugin from the root of the project directory use this snippet:
$ grails install-plugin jms
Then do the same for the ActiveMQ plugin:
$ grails install-plugin activemq
After these plugins are installed you’re ready for development. The beauty of Grails conventions is that they hide most of the complexity of plugging in new external frameworks such as JMS and ActiveMQ. It’s worth mentioning that the JMS and ActiveMQ plugins are still fairly new but seem to do the job for the most common situations. Now that you have the plugins for making the application JMS-enabled, you can move on to updating the Grails application code. Only a few things need to happen to provide a JMS service to the Flex client and these are described next.
Add the ActiveMQ Spring bean
XE "Spring bean:adding to Grails"
There’s one configuration detail you need to tend to. You need to configure the JMS plugin to leverage the ActiveMQ broker. You can do this either by adding a Spring bean to the resources.xml XE "resources.xml" or by adding the bean using the Groovy DSL approach. The snippet that follows demonstrates adding the bean by using the DSL approach of adding your connection factory as a bean in the resources.groovy configuration file located in the grails-app/conf/spring folder of the application.
// Place your Spring DSL code here
beans = {
connectionFactory(org.apache.activemq.ActiveMQConnectionFactory) {
brokerURL = "vm://localhost"
}
}
The code shown here is the Groovy way to configure Spring beans in Grails, and defines a connectionFactory bean of type ActiveMQConnectionFactory and initializes its brokerURL property to point at vm://localhost. Now that you got the configuration out of the way you can move on to tweaking the Contact domain class.
class Contact implements Serializable {
...
}
To store messages on a message queue the objects need to implement the Serializable XE "Serializable" interface. You need to update the Contact class to implement the Serializable interface as shown previously.
Subscribe the Flex client to the Grails JMS service
Now you’re ready to configure the Flex framework as a JMS consumer XE "Flex:configure framework as a JMS consumer" . First you’ll start with the BlazeDS configuration.
Update the services-config.xml
You need to configure Flex with a contactsTopic in the top-level BlazeDS configuration file. When the Flex plugin is installed it places the services-config.xml XE "services-config.xml" inside the /web-app/WEB-INF/flex directory. Let’s edit it by adding the Flex message service inside the services element. The full services-config.xml is included in the next listing for clarity and to show that the order of the bean definitions has significance. Inside the services element the Grails service comes first followed by the JMS configuration XE "JMS configuration" .
services-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<services>
<service id="grails-remoting-service"
class="flex.messaging.services.RemotingService">
<adapters>
<adapter-definition id="java-object"
class="flex.messaging.services.remoting.adapters.JavaAdapter"
default="true"/>
</adapters>
</service> <service id="grails-service"
class="org.codehaus.groovy.grails.plugins.flex.GrailsBootstrapService"/>
<service id="message-service" #1
class="flex.messaging.services.MessageService" #1
messageTypes="flex.messaging.messages.AsyncMessage"> #1
<adapters> #2
<adapter-definition id="jms" #2
class="flex.messaging.services.messaging.adapters.JMSAdapter" #2
default="true"/> #2
</adapters> #2
<destination id="contactsTopic"> #3
<properties> #3
<jms> #3
<destination-jndi-name>contacts</destination-jndi-name> #3
<message-type>javax.jms.ObjectMessage</message-type> #3
<connection-factory>ConnectionFactory</connection-factory> #3
<delivery-mode>NON_PERSISTENT</delivery-mode> #3
<message-priority>DEFAULT_PRIORITY</message-priority> #3
<acknowledge-mode>AUTO_ACKNOWLEDGE</acknowledge-mode> #3
<transacted-sessions>false</transacted-sessions> #3
<initial-context-environment> #3
<property> #3
<name>Context.PROVIDER_URL</name> #3
<value>vm://localhost</value> #3
</property> #3
<property> #3
<name>Context.INITIAL_CONTEXT_FACTORY</name> #3
<value>org.apache.activemq.jndi.ActiveMQInitialContextFactory</value> #3
</property> #3
<property> #3
<name>topic.contacts</name> #3
<value>contacts</value> #3
</property> #3
</initial-context-environment> #3
</jms> #3
</properties> #3
</destination> #3
</service>
<default-channels>
<channel ref="grails-amf"/>
</default-channels>
</services>
<channels>
<channel-definition id="grails-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf"
class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>
</channels>
</services-config>
#1 JMS service
#2 JMS adapter
#3 JMS configuration
The bulk of the services-config.xml file was generated when you installed the Flex plugin; however you need to add a new service section for the JMS service that you’ll be using (#1), and a section stating that you’d like the service to use the JMS adapter (#2). You also need to configure the JMS topic that you’ll be communicating with (#3). To see all the options you’ve defined, refer to the BlazeDS user documentation. Now let’s move on to modifying our ContactService to utilize your messaging.
Modifying the ContactService
XE "ContactService:modifying"
The existing ContactService class is relatively simple. Here is the updated ContactService class with the additions for publishing the updated contact list.
ContactService updated
class ContactService {
static expose = ['flex-remoting']
boolean transactional = true
def getContacts() {
return Contact.list()
}
def get(id) {
return Contact.get(id)
}
def update(Contact contact) {
contact.save()
publishContacts() #1
}
def remove(Contact contact) {
contact.delete(flush: true)
publishContacts() #1
}
def private void publishContacts() {
try {
sendPubSubJMSMessage("contacts", getContacts()); #2
} catch (Exception e) {
log.error("Failed to publish contacts.", e);
}
}
}
#1 publishContacts
#2 sendPubSubJMSMessage
You add the publishContacts()(#1) method to publish updates to the topic you configured in the services-config.xml file. The sendPubSubJMSMessage XE "sendPubSubJMSMessage" (#2) takes two arguments. The first argument is the JNDI destination name defined in your topic, and the other is the list of contacts. Next, you wire up the update and remove methods to publishContacts when they are invoked. Other methods can be called depending on your needs. Because a topic supports the publish/subscribe model, it is used for one-to-many messaging which works well with the Contacts application. For one-to-one or point-to-point messaging you would use a queue instead. To learn more about the JMS plugin, visit the Grails website.
Update the Main.mxml
The final thing to do before relaunching the contacts application is to update the Flex client main.mxml file. You will add the JMS service to the mix and make other minor changes. Let’s start with the Application element.
Main.mxml (e)
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:local="*"
layout="vertical"
viewSourceURL="srcview/index.html"
#1
creationComplete="contactService.getContacts();jmsConsumer.subscribe()">
<mx:Consumer id="jmsConsumer" #2
destination="contactsTopic"
message="handleGetContacts(event.message.body)"
fault="showFault(event)"/>
...
</mx:Application>
#1 creationComplete
#2 Consumer
The Flex Consumer object (#2) is used to connect to the contactsTopic. When the application initializes, you need to subscribe to the contactsTopic. To do so you configure the creationComplete event definition (#1) to call subscribe on the jmsConsumer XE "jmsConsumer" . The consumer listens for changes from the ActiveMQ broker and uses the handleGetContacts method to consume the data. This method updates the contact list when there are updates.
From here, the application will work fine as is. There are some things to clean up because we’re now making the application JMS aware and they are unnecessary overhead.
Main.mxml (f)
private function deleteContact(selectedContact:Contact):void {
contactService.remove(selectedContact);
}
private function updateContact(contact:Contact):void {
contactService.update(selectedContact);
}...
<mx:ControlBar>
<mx:Button label="Delete Contact"
enabled="{contactsGrid.selectedItem != null}"
click="deleteContact(selectedContact)"/>
</mx:ControlBar>
You remove the calls to the getContacts() method on your RemoteObject when deleteContact and updateContact are invoked. These calls were necessary, prior to enabling JMS, for the DataGrid to be refreshed whenever the contact list was updated. You can also remove the Refresh button. The Flex consumer will get updates every few seconds and will invoke the method that will update the DataGrid so there’s no more need for these extra calls to the server side. The consumer itself can be further configured if different timing or other options are needed.
To illustrate the push of information from the server to the client, start the Grails application by opening a command line and navigating to the project folder. Type the command grails run-app, which will start the embedded jetty container to run the application. Now open two browsers and point them both to the Flex application at http://localhost:8080/flex-contacts/main.mxml. Then as you make updates in the one window, you should see the contacts being updated in the other browser window—no more having to manually refresh the application to pick up other user’s changes.
Summary
In this article we showed you how to rapidly prototype data-enabled Flex applications using Groovy and Grails in combination with the Flex plugin for Grails. You started by defining the domain in Grails and exposing some services for your Flex application to use, and the Flex application itself. You then went one step further and enabled your application to use JMS and ActiveMQ for real-time updating of your UI.
| Attachment | Size |
|---|---|
| image003.gif | 13.43 KB |
| image002.jpg | 19.65 KB |
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)





Comments
Sebastien Arbogast replied on Tue, 2010/08/24 - 12:48pm
Too bad this article uses a technique that is at best very unlikely to be used in a real-world context, mainly for 3 reasons:
Those are the 3 reasons why I've been working on the Grails BlazeDS plugin (http://www.grails.org/plugin/blazeds) which works great with Grails up to version 1.2.x. Unfortunately I'm tackling some challenges with Grails 1.3 and even if I get things working, there are still some limitations with Flash Builder.
So from a Grails/Flex integration standpoint, we end up being stuck between an old solution that is working but incomplete and not integrated in tools, and a more modern solution that does not work anymore and forces us to use some workarounds.
All the community needs to make this work is a little coordinated help from Adobe and SpringSource so that we can have:
I've left a couple of messages on Grails mailing lists (http://bit.ly/d0cCDt and http://bit.ly/dD2Z4C), Adobe forums (http://bit.ly/a66tuE) and SpringSource forums (http://bit.ly/bkQtlH), but so far, all my calls for help have remained unanswered. So if anyone is willing to help or support this ongoing initiative, it'll be greatly appreciated.