Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Sending Messages from Grails to Flex/BlazeDS

DZone's Guide to

Sending Messages from Grails to Flex/BlazeDS

· Java Zone
Free Resource

The single app analytics solutions to take your web and mobile apps to the next level.  Try today!  Brought to you in partnership with CA Technologies

I started several weeks ago to see what we can do with Grails and its Enterprise side, and because where I'm working right now it's needed to do some messaging from the back-end to the clients (Flex clients); well, I did some modifications to the flex/grails plug-in (developed by Marcel Overdijk) in order to be able to send messages from grails to Flex (consumer).

Assuming that we already have our domain, controllers and maybe some views,  the only thing we need to do is install the flex plug-in, do some small modifications to the source (flex plug-in), create a helper class (to send the messages), create a service (this will defined our destination), add the messaging service tag to the flex configuration (services-config.xml) and test it on your Flex application. For these steps I'm using Grails IDE on Eclipse.

 

So here are the steps:

1) Install the Flex plug-in:
(Using the Grails IDE on eclipse, you can bring up the grails command prompt with Ctrl+Alt+G for Windows or Cmd+Alt+G for Mac).

grails > install-plugin flex

 2) Modify the Flex plug-in source code:
The files to modify will be GrailsBootstrapService.java and FlexUtils.java


GrailsBootstrapService.java, the bootstrap service will help to create dynamically the destination(s). 

 (This is the final Version). 

package org.codehaus.groovy.grails.plugins.flex;

import org.codehaus.groovy.grails.commons.GrailsApplication;
import org.codehaus.groovy.grails.commons.GrailsClass;
import org.codehaus.groovy.grails.commons.GrailsServiceClass;
import org.codehaus.groovy.grails.commons.ServiceArtefactHandler;
import org.codehaus.groovy.grails.web.util.WebUtils;

import flex.messaging.MessageBroker;
import flex.messaging.config.ConfigMap;
import flex.messaging.services.AbstractBootstrapService;

public class GrailsBootstrapService extends AbstractBootstrapService {

public void initialize(String id, ConfigMap properties) {

MessageBroker messageBroker = getMessageBroker();

// add spring factory if it's not yet registered
FlexUtils.addSpringFactory(messageBroker);

GrailsApplication application = WebUtils.lookupApplication(messageBroker.getInitServletContext());
GrailsClass[] grailsClasses = application.getArtefacts(ServiceArtefactHandler.TYPE);

for (int i = 0; i < grailsClasses.length; i++) {
GrailsServiceClass serviceClass = (GrailsServiceClass) grailsClasses[i];
if (FlexUtils.hasFlexRemotingConvention(serviceClass)) {
FlexUtils.createRemotingDestination(messageBroker, serviceClass);
}
if (FlexUtils.hasFlexMessagingConvention(serviceClass)){
FlexUtils.createMessagingDestination(messageBroker, serviceClass);
}
}
}

public void start() {}

public void stop() {}

}

As you can see, I added these lines to the original source:

if (FlexUtils.hasFlexMessagingConvention(serviceClass)){
FlexUtils.createMessagingDestination(messageBroker, serviceClass);
}

 

FlexUtils.java

Here, I added several lines, and I know there is a better way to implement this (maybe put a Factory Pattern) to create the correct service.

(This is the final Version).

package org.codehaus.groovy.grails.plugins.flex;

import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.groovy.grails.commons.GrailsClassUtils;
import org.codehaus.groovy.grails.commons.GrailsServiceClass;

import flex.messaging.MessageBroker;
import flex.messaging.services.RemotingService;
import flex.messaging.services.remoting.RemotingDestination;

import flex.messaging.services.MessageService;
import flex.messaging.MessageDestination;

public class FlexUtils {

private static final String SPRING_FACTORY_ID = "spring";
private static final String GRAILS_REMOTING_SERVICE_ID = "grails-remoting-service";
private static final String GRAILS_MESSAGING_SERVICE_ID = "grails-messaging-service";
private static final String EXPOSE_PROPERTY = "expose";
private static final String DESTINATION_PROPERTY = "destination";
private static final String FLEX_REMOTING = "flex-remoting";
private static final String FLEX_MESSAGING = "flex-messaging";

private static final Log LOG = LogFactory.getLog(FlexUtils.class);

public static void addSpringFactory(MessageBroker mb) {

// add spring factory if it's not yet registered
if (!mb.getFactories().containsKey(SPRING_FACTORY_ID)) {
mb.addFactory(SPRING_FACTORY_ID, new SpringFactory());
LOG.info("SpringFactory added to message broker");
}
}

public static RemotingService getGrailsRemotingService(MessageBroker mb) {
return (RemotingService) mb.getService(GRAILS_REMOTING_SERVICE_ID);
}

public static MessageService getGrailsMessagingService(MessageBroker mb) {
return (MessageService) mb.getService(GRAILS_MESSAGING_SERVICE_ID);
}

public static boolean hasFlexRemotingConvention(GrailsServiceClass serviceClass) {
List exposeList = (List) GrailsClassUtils.getStaticPropertyValue(serviceClass.getClazz(), EXPOSE_PROPERTY);
if (exposeList != null && exposeList.contains(FLEX_REMOTING)) {
return true;
}
return false;
}

public static boolean hasFlexMessagingConvention(GrailsServiceClass serviceClass) {
List exposeList = (List) GrailsClassUtils.getStaticPropertyValue(serviceClass.getClazz(), EXPOSE_PROPERTY);
if (exposeList != null && exposeList.contains(FLEX_MESSAGING)) {
return true;
}
return false;
}

public static void createRemotingDestination(MessageBroker mb, GrailsServiceClass serviceClass) {
RemotingService rs = getGrailsRemotingService(mb);
String destination = getDestination(serviceClass);
RemotingDestination rd = (RemotingDestination) rs.createDestination(destination);
rd.setFactory(SPRING_FACTORY_ID);
rd.setSource(serviceClass.getPropertyName());

// if the service is already started also start the destination
// this is needed for reloading and creating new Grails services
if (rs.isStarted()) {
rd.start();
}

LOG.info("Flex remoting destination created for " + serviceClass.getShortName());
}

public static void createMessagingDestination(MessageBroker mb, GrailsServiceClass serviceClass) {
MessageService ms = getGrailsMessagingService(mb);
String destination = getDestination(serviceClass);
MessageDestination md = (MessageDestination) ms.createDestination(destination);
md.setFactory(SPRING_FACTORY_ID);
md.setSource(serviceClass.getPropertyName());

// if the service is already started also start the destination
// this is needed for reloading and creating new Grails services
if (ms.isStarted()) {
md.start();
}

LOG.info("Flex messaging destination created for " + serviceClass.getShortName());
}

private static String getDestination(GrailsServiceClass serviceClass) {
String destination = (String) GrailsClassUtils.getStaticPropertyValue(serviceClass.getClazz(), DESTINATION_PROPERTY);
if (StringUtils.isBlank(destination)) {
destination = serviceClass.getPropertyName();
}
return destination;
}

}

 a) I added two constants:

private static final String GRAILS_MESSAGING_SERVICE_ID = "grails-messaging-service";
private static final String FLEX_MESSAGING = "flex-messaging";

 b) And these methods:

public static MessageService getGrailsMessagingService(MessageBroker mb) {
return (MessageService) mb.getService(GRAILS_MESSAGING_SERVICE_ID);
}

public static boolean hasFlexMessagingConvention(GrailsServiceClass serviceClass) {
List exposeList = (List) GrailsClassUtils.getStaticPropertyValue(serviceClass.getClazz(), EXPOSE_PROPERTY);
if (exposeList != null && exposeList.contains(FLEX_MESSAGING)) {
return true;
}
return false;
}

public static void createMessagingDestination(MessageBroker mb, GrailsServiceClass serviceClass) {
MessageService ms = getGrailsMessagingService(mb);
String destination = getDestination(serviceClass);
MessageDestination md = (MessageDestination) ms.createDestination(destination);
md.setFactory(SPRING_FACTORY_ID);
md.setSource(serviceClass.getPropertyName());

// if the service is already started also start the destination
// this is needed for reloading and creating new Grails services
if (ms.isStarted()) {
md.start();
}

LOG.info("Flex messaging destination created for " + serviceClass.getShortName());
}
 

 

3) Create a Helper Class:

This class will send any message to any destination (provided by the service created).
Just create a new Groovy class into the src/groovy folder.

 FlexMessageDispatcher.groovy

class FlexMessageDispatcher {

def sendMessageToClients(String messageBody,String destination){

AsyncMessage msg = new AsyncMessage()
msg.setClientId("Java-Based-Producer-For-Messaging")
msg.setTimestamp(new Date().getTime())

//Unique id
msg.setMessageId("Java-Based-Producer-For-Messaging-ID")

//destination to which the message is to be sent
msg.setDestination(destination)

//set message body
msg.setBody(messageBody != null?messageBody:"")

//set message header
msg.setHeader("sender", "From the server")

//send message to destination
MessageBroker.getMessageBroker(null).routeMessageToService(msg, null)

}


}

 

 

In this class, this method can be overloaded so we can pass more information, like Headers, IDs, etc. Take a look into the AsyncMessage.java (from BlazeDS API) for more information.


4) Create the Service:

Create the service using the grails command. And put the property identifier (static expose = ['flex-messaging'])

grails > create-service UpdateMessage

Final UpdateMessageService.groovy

class UpdateMessageService {

static expose = ['flex-messaging']

boolean transactional = true

}

The Bootstrap will create the updateMessageService destination

5) Add the Messaging service tag to the Flex configuration:
Its necessary to add new tags to the services-config.xml located on the web-app/WEB-INF/flex

This tag will be added to the existing services tag.

<service id="grails-messaging-service" class="flex.messaging.services.MessageService">
<adapters>
<adapter-definition id="actionscript"
class="flex.messaging.services.messaging.adapters.ActionScriptAdapter" default="true" />
</adapters>
</service>

 And this is the complete file: 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-messaging-service" class="flex.messaging.services.MessageService">
<adapters>
<adapter-definition id="actionscript" class="flex.messaging.services.messaging.adapters.ActionScriptAdapter" default="true" />
</adapters>
</service>
<!--
Grails bootstrap service. This service registers the SpringFactory and
creates destinations for the annotated Grails services.
-->
<service id="grails-service" class="org.codehaus.groovy.grails.plugins.flex.GrailsBootstrapService"></service>

<!--
Application level default channels. Application level default channels are
necessary when a dynamic destination is being used by a service component
and no ChannelSet has been defined for the service component. In that case,
application level default channels will be used to contact the destination.
-->
<default-channels>
<channel ref="grails-amf"/>
</default-channels>
</services>

<channels>
<channel-definition id="grails-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://localhost:8080/{context.root}/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>
</channels>

</services-config>

 

6) Test it in Flex
To Test the Flex client we need first to create a consumer tag and from any controller (from Grails) use the helper class to send a message to the updateMessageService destination.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="myConsumer.subscribe()">

<mx:Script>
<![CDATA[
import mx.messaging.messages.IMessage;
import mx.controls.Alert;

private function messageHandler(message:IMessage):void
{
Alert.show(message.body.toString());
}

]]>
</mx:Script>

<mx:Consumer id="myConsumer" destination="updateMessageService" message="messageHandler(event.message)" />

</mx:Application>

On Grails (in any controller or any other source where the message is necessary), just use the Helper class to send the message:

def flex = FlexMessageDispatcher()
flex.sendMessageToClients("any message", "updateMessageService")

 

To get the files you can go to my blog

 

 

 

CA App Experience Analytics, a whole new level of visibility. Learn more. Brought to you in partnership with CA Technologies.

Topics:

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}