QueTwo's Blog

thouoghts on telecommunications, programming, education and technology

Creating runtime remoting destinations in BlazeDS and LiveCycle Data Services

Ok, lets dive right into a really geeky topic… destinations in BlazeDS and LiveCycle Data Services.

For one of my customers, I was given the requirement to create a runtime destination in BlazeDS.  BlazeDS, along with its bigger brother LiveCycle allow you to create destinations in one of two ways — via the configuration XML files (services-config.xml, and related files), or via Java calls.  I won’t talk about the XML file method because it is extremely well documented.  However, the biggest disadvantage of the XML file method of creating destinations is that you usually have to reload or reboot your server in order for the changes to take affect. 

Runtime Destinations are not permanent — they do not persist during restarts.  They are also not magically added to the XML documents either.  Their creation is also often blocked on shared hosting servers as well.  But besides that, they act and smell just like ones created by the XML documents.  Why would you want to use them?   There are quite a few reasons for wanting them — for example to help secure your server by only exposing your destinations on demand, or by providing private destinations to clients so there is no way that data can be shared (in that case you COULD use subtopics, but that is controlled by the client).  In my case I was asked to provide a solution where the client would not have to change any configuration files when they installed our application.  We were using ColdFusion 9 as our back-end server, but these techniques work for Java based solutions too (you would just have to change the Syntax a bit).

I’ll start off by saying that the documentation is extremely poor (I wish Adobe would make this stuff clearer one day!), but with the help of two people, Sven Ramuschkat and Marko Simic, I was able to get to the point where reading the source code made sense.

The first step in creating the dynamic destination was getting the instance of the Message Broker.  The Message Broker in BlazeDS/LCDS is the guy who does all the work for remoting, messaging, data services and proxying.  You can access it in ColdFusion with the following commands

this.blazeDSClass = createObject("java", "flex.messaging.MessageBroker");
this.blazeDS = this.blazeDSClass.getMessageBroker(javacast("null", ""));

Next, in my case I needed to check to see if the destination already existed.  I decided to use the getDestinations() function on the Remoting service.  There are four services that could hold the destination, but in my case I know it would only exist under the remoting-service.  What will be returned is a structure that holds all sorts of info about the available destinations (send the output to CFDUMP or your debugger to see what is available).  I do a structFindKey method on this to see if my destination exists.

findMyDestination = StructFindKey(this.BlazeDS.getService("remoting-service").getDestinations(), "myDestination");

Now, we will know if we have to actually create the destination, or if we can simply use the existing one by the length of the array that is returned.  Creating the destination involves a few steps.  Look at the code below, and then I will walk through each step :

myDestination = this.BlazeDS.getService("remoting-service").createDestination("myDestination"); 
myDestination.setSource("path.to.cfc");
myDestination.setScope("application"); 
myCFAdapter = myDestination.createAdapter("cf-object");
myAdapterConfig = createObject("java", "flex.messaging.config.ConfigMap").init(); 
myAccessConfig = createObject("java", "flex.messaging.config.ConfigMap").init();
myAccessConfig.addProperty("use-mappings",true); 
myAccessConfig.addProperty("method-access-level","remote"); 
myAdapterConfig.addProperty("access",myAccessConfig); 
myCFAdapter.initialize("cf-object",myAdapterConfig);
myDestination.setAdapter(myCFAdapter);
myDestination.addChannel("my-cfamf");
myDestination.start();

The first thing we do is call the createDestination() funciton on the remoting-service.  This will pass back a reference to the new destination that was created for us with the name we passed in.  Next, we set the source property.  Since we are working within ColdFusion, this could be the dot-path-name of the CFC we are working with, or * to allow our dynamic destination to talk to ANY CFC.  We then set the Scope of the destination (this can either be application or session, but of the two paragraphs that the official docs mention about this topic, they do state that this needs to be set to application). 

The next thing we do is set up the Adapter.  The adapter is what actually processes the requests from our connected Flex client and sends it over to ColdFusion to be worked on.  In our case, I am having a ColdFusion CFC do the processing, so I went with the “cf-object” adapter.  There are a variety of adapters available, and depending on your situation, you may want to choose from a list of adapters, or use the getDefaultAdapter() method to find out what is the default for the service you are working on.  Creating an instance of the adapter involves calling the createAdapter() method on the destination.  What is returned to you is a reference to the adapter that isn’t initialized yet.  When diving through the source code — this is what screwed me up the most, as I was under the impression that the createAdapter() was all I had to call to get things working.  If that is all you do, you will get NullPointerException errors when you try to pass data to the destination. 

In order to initialize the adapter, you need to pass in a property with the type of a ConfigMap.  The ConfigMap holds the configuration properties that the adapter is expecting.  There are lots of properties that could be set (and if you want to get a general idea of what they are, take a look at the XML config).  In theory, you should be able to pass in an empty ConfigMap to the initialize method and it will take all the defaults (which work in my case), but there is a bug in the ColdFusionAdapter that requires to you set the use-mappings and method-access-level properties to something or you can’t send any data to the destination.  These two properties are within a ConfigMap named “access” (again, take a look at the XML in the remoting-config.xml to see how this translates).  Finally, in order to initialize the adapter, we pass in the ConfigMap we just setup to the initialize() method and we are set.

Our final steps include binding our newly created adapter instance to the destination by using the setAdapter() method, and setting the channel that the destination will bind to.  If I wasn’t making the assumption that the channel “my-cfamf” already exists, I could call the getChannels() method on the Message Broker to verify that it is one of the ones available.  If the channel wasn’t available, I could create using a similar method as above.  And finally, we need to start the instance.  Call the start() method to start it.  If everything is set up right, you should be able to send data to your new destination!

Stopping the destination is as simple as calling the removeDestination() method on the Message Broker. 

Below is the final code of my CFC that creates the new destination.  It is not exactly like my samples above (it creates a more dynamic destination for my purposes).

component output="false"
{
 
 public void function init()
 {
  this.blazeDSClass = createObject("java", "flex.messaging.MessageBroker");
  this.blazeDS = this.blazeDSClass.getMessageBroker(javacast("null", ""));
  findMyDestination = StructFindKey(this.BlazeDS.getService("remoting-service").getDestinations(), 
                            application.applicationname);
 
  if(ArrayLen(findMyDestination) == 0)
  {
   myDestination = this.BlazeDS.getService("remoting-service").createDestination(application.applicationname);
   myDestination.setSource(getDotLocation());
   myDestination.setScope("application");
   myCFAdapter = myDestination.createAdapter("cf-object");
   myAdapterConfig = createObject("java", "flex.messaging.config.ConfigMap").init();   
   myAccessConfig = createObject("java", "flex.messaging.config.ConfigMap").init();
   myAccessConfig.addProperty("use-mappings",true);
   myAccessConfig.addProperty("method-access-level","remote");
   myAdapterConfig.addProperty("access",myAccessConfig);
   myCFAdapter.initialize("cf-object",myAdapterConfig);
   
   myDestination.setAdapter(myCFAdapter);
   myDestination.addChannel("my-cfamf");
   myDestination.start();
  }
 }
 
 public void function cleanup()
 {
  this.blazeDSClass = createObject("java", "flex.messaging.MessageBroker");
  this.blazeDS = this.blazeDSClass.getMessageBroker(javacast("null", ""));
  this.BlazeDS.getService("remoting-service").removeDestination(application.applicationname);
 }
 
 public string function getDotLocation()
 {
  myScriptName = cgi.SCRIPT_NAME;
  myScriptName = Replace(myScriptName, "/", ".", "all");
  myScriptName = Left(myScriptName, find(getFileFromPath(getBaseTemplatePath()), myScriptName));
  myScriptName = mid(myScriptName, 2, Len(myScriptName) - 2);
  myScriptName = myScriptName & getFileFromPath(getCurrentTemplatePath());
  myScriptName = left(myScriptName, Len(myScriptName) - 4);
  return myScriptName;
 }
}

Good Luck!

About these ads

4 responses to “Creating runtime remoting destinations in BlazeDS and LiveCycle Data Services

  1. Victor May 15, 2011 at 11:26 pm

    Hi great example, i just make a little changes and get this work with Java Server instead of a ColdFusion server. I will post a blog entry about this, could i use your explanations in my post? obviously all the credits go to you.

    Thanks

    • quetwo May 15, 2011 at 11:44 pm

      Please do! The more we can get people to figure this stuff out would mean less reverse engineering BlazeDS for me! Let me know if you need any help with it.

  2. Mike S May 19, 2011 at 5:07 pm

    Hi, Great post. I am thinking about this as an alternative to subtopics. I am not sure how the underlying mechanics are, but I feel like this approach would be much more efficient. Any thoughts?

    • quetwo May 19, 2011 at 5:46 pm

      I guess it depends. Each additional destination that you add in will add additional overhead to your server — but then again, each message with a sub-topic is still tracked too. I haven’t done any testing as to which is more efficient.

      For small numbers, I assume it would be, but I don’t know about scale. Maybe you can test it and report back?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 29 other followers

%d bloggers like this: