A big problem in my current project is Concurrency. A client loads a Tree in his Flex application and starts making changes to this tree. If another client also makes changes in the Tree, then we have a Concurrency problem. We thought of two approaches of dealing with this problem. One of them is using Messaging with MSMQ. We did however had some issues developing this and are still trying to make it work.

We mainly worked with two examples provided by WebORB to figure things out. One of them is the Flex Messaging and MSMQ  example from the WebORBConsole --> Examples --> Real-time Messaging. The other one is the MSMQ to Flex Data Push example from the site of the midnight coders. In the first example Flex is the Producer and Consumer of Messages. In the second example the Server produces Messages and Flex consumes them.

What do you need to run these examples:

  1. MSMQ: If you look into Computer Management under Services and Applications you should see a folder Message Queuing. If not I suggest you google to find out how to install this onto your computer. For Vista you'll need to go to the Control Panel --> Programs and Features --> Turn windows features on or off. There you should find a checkbox for installing MSMQ.
  2. WebOrb for .NET  --> In your site you only need the weborb.dll, so this installation isn't really needed. But it comes with a lot of documentation and examples, so if you haven't installed it yet, go and do so now!
  3. The Messaging Server (WebOrb) should be online. If you're using the WebOrb30 installation to run your examples, then the Messaging Server is started by weborbee.exe. In your project website however, you shouldn't use this weborbee.exe. You only need to copy the global.asax. If you look into this global.asax, you'll see the weborbMessagingServer is started there. I'll show in the examples how to make sure the MessaginServer is online.

 When you open the Flex Consumer Resource from the second example you'll see in the properties of the Project --> Flex Compiler that a services-config.xml file is compiles with the application. In this file Channels and Destinations are declared. We left this out so we could choose at runtime which Channel and Destination to use.

Ok, let's start with the Flex Client Side. Here's the example somehow adjusted to our means. I'll go through it piece by piece. For now don't bother too much about the server side. I'll go deeper into that later.

Actionscript:
  1. public function onCreationComplete(event:FlexEvent):void {
  2. registerClassAlias("Messaging.Car", Car);
  3. pingWeborb();
  4. }
  5.  
  6. private function pingWeborb():void {
  7. var ro:RemoteObject = new RemoteObject("GenericDestination");
  8. ro.source = "Weborb.Management.ManagementService";
  9. ro.endpoint = "http://{server.name}/indiegroup-edumatic3-prod/weborb.aspx";
  10. ro.ping.addEventListener(ResultEvent.RESULT, onPingResult);
  11. ro.ping.addEventListener(FaultEvent.FAULT, onPingFault);
  12. ro.ping();
  13. }

We register an Alias for the Car class so it'll be mapped correctly to the C# Messaging.Car class.
Next we call the function pingWeborb() which will ensure us that the MessagingServer is started before we start to send Messages. By triggering the weborb gateway (weborb.aspx) in your site, you'll be starting the MessagingServer (in Global.asax). If you are not using the weborb30 site, make sure you haven't started the MessagingServer with the Weborbconsole in weborb30 site. If you want to be sure of this, go to Task Manager --> Processes and kill the w3wp.exe process.

The GenericDestination can be found in the remoting-config.xml in the WEB-INF\flex folder along the server side. More on this later. The source is the Class instance that will be created and the endpoint the url of the gateway. Make sure you use the RemoteObject class from the mx.rpc.remoting.mxml package, as this RemoteObject has an endpoint property. There's also a RemoteObject class in the mx.rpc.remoting package which has no endpoint property. Adding two EventListeners and executing the ping() function in the Weborb.Management.ManagementService class instance.

Actionscript:
  1. private function onPingResult(result:ResultEvent):void {
  2. connectAsConsumer("johleroqueue");
  3. connectAsProducer("johleroqueue");
  4. }
  5.  
  6. private function onPingFault(fault:FaultEvent):void {
  7. Alert.show( "Server reported an error - " + fault.fault.faultDetail);
  8. }

 If the Ping was successfull, then onPingResult should be triggered, in which we'll connect as a Consumer and as a Producer. If the Ping was unsuccessfull, onPingFault will be triggerd and an Alert Box will be shown with the error. If something goes wrong here, I would start a http sniffer like Charles to see if the Gateway is called. If it is, start by creating some breakpoints in Flex and certainly in the global.asax on the serverside. Make sure to keep on reading even if things don't work as I will talk about some other problems I've encountered. Maybe that one of these problems will be the issue that's causing your problem.

Actionscript:
  1. private function connectAsConsumer(subTopic:String):void{
  2. var cs:ChannelSet = new ChannelSet();
  3. var uri:String = "rtmp://{server.name}:2037/MessagingService";
  4. var channel:WeborbMessagingChannel = new WeborbMessagingChannel(subTopic, uri);
  5. channel.addEventListener(MessageEvent.MESSAGE, onMessage);
  6. cs.addChannel(channel);
  7.  
  8. consumer = new WeborbConsumer();
  9. consumer.channelSet = cs;
  10. consumer.destination = "ExampleDestination";
  11. consumer.subqueue = subTopic;
  12. consumer.addEventListener(MessageAckEvent.ACKNOWLEDGE, onMessageAck);
  13. consumer.addEventListener(MessageFaultEvent.FAULT, onMessageFault);
  14. consumer.subscribe();
  15. }

Ok, first we connect as a Consumer, because the connectAsProducer function will also be sending a message. First we create a ChannelSet containing a WeborbMessagingChannel. I haven't gone deep into this Channel thing, but one thing that was strange to me was that no site is given in the rtmp uri. Apparently the MessagingServer is listening to a port. The /MessagingService points to a folder in the Applications folder under the root of your site. In this MessagingService folder it's possible to place an app.config where you can change some configurations. The channel triggers a MessageEvent.MESSAGE event when a message is Consumed.

The destination can be found in the messaging-config.xml under WEB-INF\flex. In there you'll find an entry path with the name of the queue. This path together with the subqueue (in my case johleroqueue) will be the name of your queue. If the path is .\private$\WEBORB-[destinationName] and the destination name "ExampleDestination" then a queue weborb-exampledestination-johleroqueue will be created. The first time the queue is used it will automatically be created by weborb. However, if weborb creates the queue, you will not be able to access it with the Computer Management tool. Apparently access is denied. A workaround for this is to manually create this queue before running the example. If you manually create it, you'll have access rights. However, you'll need to give the Network Service rights to the queue, otherwise the messages will not arrive (you can check this!). Weborb will then use the queue you created if you use the same name off course. The ACKNOWLEDGE event listener will let the consumer know if subscription was successfull. The other listeners are clear enough I guess.

Actionscript:
  1. private function connectAsProducer(subTopic:String):void{
  2. var cs:ChannelSet = new ChannelSet();
  3. var uri:String = "rtmp://{server.name}:2037/MessagingService";
  4. var channel:WeborbMessagingChannel = new WeborbMessagingChannel(subTopic, uri);
  5. cs.addChannel(channel);
  6.  
  7. producer = new WeborbProducer();
  8. producer.channelSet = cs;
  9. producer.destination = "ExampleDestination";
  10. producer.subqueue = subTopic;
  11. producer.addEventListener(MessageAckEvent.ACKNOWLEDGE, onMessageAck);
  12. producer.addEventListener(MessageFaultEvent.FAULT, onMessageFault);
  13.  
  14. var car:Car = new Car();
  15. car.Make = "Zakke";
  16. car.Mileage = 10;
  17. car.Model = "Makke";
  18. producer.send(new AsyncMessage(car));
  19. }

Connecting as Producer is quite similar to the Consumer with the exception of the send function. Meanless to say that the Producer and Consumer need to connect to the same queue. Next the Producers sends a Car instance.

Actionscript:
  1. private function onMessage(event:MessageEvent):void {
  2. trace(event.message.body);
  3. //Put a breakpoint here
  4. //var body:* = event.message.body;
  5. }
  6.  
  7. private function onMessageAck(event:MessageAckEvent):void {
  8. //Put a breakpoint here
  9. }
  10.  
  11. private function onMessageFault(event:MessageFaultEvent):void {
  12. //Put a breakpoint here
  13. }

If the Consumer has successfully suscribed, then a onMessageAck should be triggered. If the Producer has successfully connected, then onMessageAck should be triggered a second time. The onMessageAck from the Producer does not mean that the message has successfully been send, it only means that the Producer has been able to connect to the MessagingService. There doesn't seem to be a clear way of knowing if the Message has successfully been send. However, if the Consumer gets the message, then other Consumers will have gotten the message too. After this onMessage should be triggered and a Car (if the server side is configured right) should be delivered.

 

Adios, Johlero!

Thx to the midnight coders!