20 March 2011
Some experience creating ColdFusion applications and experience working with Flex and ActionScript 3 is recommended.
Intermediate
In this article, you will learn how to build a simple SMS chat application using Flex, ColdFusion, BlazeDS, and the Twilio SMS service. Twilio is a company that helps developers build applications that interact with phone calls and text messages. Twilio provides a simple API that emulates http Form POSTs and XML documents to simplify dialing and texting phone numbers. In the example application described in this article, the Twilio SMS service will communicate with a ColdFusion Server, which will then use the built-in BlazeDS service to push messages out to a Flex application. The sample application uses BlazeDS because it is important for the client to receive the messages as soon as it is received.
Note: The sample application works with ColdFusion 9, as well as with ColdFusion 8 Enterprise edition or ColdFusion 8 Developer edition.
The Twilio service enables you, as a developer, to interact with voice calls and SMS messages as if they were a part of your web service. Twilio makes a call to your web application, which responds with a specially formatted XML document telling Twilio what to do. You can also initiate phone calls or text messages by making calls out to the Twilio service.
Twilio is not free, but there is no charge to sign up as a developer. At the time of writing, when you sign up, they will give you a $30 credit, which is enough for thousands of SMS messages. If you are in the United States, you can also purchase phone numbers in your home area code for $1 per month.
To sign up for an account, follow these steps:
The dashboard displays some important information such as your account balance, your Account SID, your Auth Token, your sandbox phone number, and your PIN for the sandbox. All of the documentation for the Twilio service is available from the Documentation link near the top of the screen.
Follow these steps to configure Twilio to communicate with your ColdFusion server:
Set the API version to 2010-04-01.
You do not need to change the Voice URL for the purposes of this article.
ColdFusion 8 and ColdFusion 9 both have BlazeDS bundled in so if you have one of those installed you can continue without installing any additional software. If you installed ColdFusion 8, and opted not to have LiveCycle Data Services (the full-featured bigger brother of BlazeDS) installed, you can follow the instructions in Aaron West's post Integrating BlazeDS with ColdFusion 8 to install it.
When a message comes into your CFM from Twilio, it will tell ColdFusion to build a message and send it out to BlazeDS via an event gateway. Before that happens though, you'll need to configure this event gateway.
The first step is to create a new Destination on which the application will communicate. Think of the Destination as a dedicated conduit between your clients and server.
Start by navigating to your BlazeDS configuration directory. Go to the [coldfusion-install-directory]\wwwroot\WEB-INF\flex\ directory.
Note: This is not a directory that would be maintained under your web server's root if you were using a normal installation of ColdFusion.
<destination> entry within the <service> node:<destination id="sms">
<adapter ref="cfgateway"/>
<properties>
<gatewayid>sms</gatewayid>
</properties>
<channels>
<channel ref="my-cfamf"/>
</channels>
</destination>
Note the name of the <destination> that you are creating, as well as the name of the gatewayid. I have kept these names the same—in this case, sms— to avoid any confusion later on.
Next, you need to write the configuration for the event gateway.
Open sms.cfg and set the destination property as follows:
destination=sms
I suggest starting ColdFusion either from the command line or from ColdFusion Builder so you can see the console output.
Alternatively, you can check the server.log file located in the ColdFusion log directory for any error messages resulting from the configuration changes you just made.
Now that you have set up the BlazeDS configuration, you need to set up ColdFusion to use it.
I prefer to set the startup mode to Manual when working on my development server, but if you plan on deploying this configuration on a production server, you should set that option to Automatic.
Now that you have set up the back-end configuration, you are ready to write the ColdFusion code to handle the incoming messages and outgoing messages. You will end up with two ColdFusion files (a CFM and a CFC), one for each direction.
<cfscript>
msg = structNew();
msg.headers = structNew();
msg.headers.gatewayid = "sms";
msg.headers['TYPE'] = "SMS";
msg.body = structNew();
msg.body['fromNumber'] = form.From;
msg.body['toNumber'] = form.To;
msg.body['message'] = form.Body;
msg.body['serial'] = now();
msg.destination = "sms";
sendGatewayMessage("sms",msg);
</cfscript>
<cfcontent type="text/xml"><?xml version="1.0" encoding="UTF-8"?>
<Response>
</Response>
This is the file that you configured Twilio to call when an incoming SMS message is received. The purpose of this file is to package up the data Twilio passes in and send it over to the event gateway.
When Twilio calls the CFM file, it will pass what appears to the ColdFusion server to be a form submission with about two dozen fields, including the phone number of the person who sent the SMS (form.FROM), the message (form.BODY), the number they sent the message to (form.TO), and a few fields dealing with the location of the phone number's registered owner. For this request, you do need to reply to the Twilio service with what you want to do with the conversation/thread. In this case, however, you will tell Twilio that you don't want to do anything.
Sending the data to the event gateway involves creating a new structure that includes properties that the gateway will be expecting. You need to implement the BODY, HEADERS, HEADERS.GATEWAYID, and DESTINATION properties of this structure. Both the body and the headers are structures themselves, and can contain any data you wish to pass along to the end client. The code above sets the HEADERS.TYPE property to "SMS", so you can parse for that later on. It also sets the BODY.FROMNUMBER, BODY.TONUMBER, and BODY.MESSAGE properties to the proper fields coming in from Twilio. The CFM also appends a unique serial number to the message, as BlazeDS will not pass along messages that appear to be duplicates (seemingly duplicate messages are common with SMS messages). After assembling the structure, the code above sends it to the event gateway by invoking sendGatewayMessage().
Lastly, you will need to tell Twilio what to do with the message. Since Twilio won't need to do anything further with the message, the CFM responds with essentially an empty XML document. If you do not do this, subsequent calls from Twilio may fail.
When the Flex application publishes a message to BlazeDS, it will be picked up by the blank CFC that you created earlier. So, the next step is to add to the CFC a function that will pass on the message from BlazeDS to Twilio. Luckily, sending messages to Twilio is just as easy as receiving them. You simply have to do Form POST to the Twilio service with the proper fields filled out.
<cfcomponent output="false">
<cffunction name="onIncomingMessage" returntype="any">
<cfargument name="event" type="struct" required="true" />
<cfif arguments.event.data.headers.type EQ "SMS">
<cfhttp method="post" username="AC00000000000000000000000" password="350000000000000000000000" url="https://api.twilio.com/2010-04-01/Accounts/AC00000000000000000000000/SMS/Messages">
<cfhttpparam name="From" type="formfield" value="4150000000">
<cfhttpparam name="To" type="formfield" value="#arguments.event.data.body.toNumber#">
<cfhttpparam name="Body" type="formfield" value="#arguments.event.data.body.message#">
</cfhttp>
</cfif>
<cfreturn arguments.event.data>
</cffunction>
</cfcomponent>
username, password, and URL in the cfhttp tag to reflect the values you received when you signed up for a Twilio account.cfhttpparam named from with the Twilio phone number you were assigned.The CFC code starts with the function that is required for all event gateway CFCs: onIncomingMessage. This function is passed a single structure (named event) that contains the message that is published by the Flex client. To send the message out, the CFC uses the CFHTTP tag, which will make the REST based call to Twilio, and the CFHTTPPARAM tags to pass along the FROM, TO, and BODY form parameters. The URL portion of the CFHTTP will need to include the Twilio Account SID you were assigned earlier (after the /Accounts/). You also need to pass in your Account SID as the username for the request and your Auth Token as the password. Finally, be sure to set the FROM value to the Twilio phone number you were assigned.
With the entire back end set up and working, you can turn your attention to the client-side code. The Flex application simply displays the incoming messages in a List control, and enables the user to respond to messages typed in a TextInput control. The example below uses Spark components (Flex SDK 4.0+), but the concepts apply to Flex 2 or Flex 3 as well.
Note: A completed Flex application is included with the sample files for this article. If you prefer, you can import the SMSChatApp.fxp Flex project file into Flash Builder instead of building the application with the steps that follow.
Now it's time to create the Flex application's user interface.
Add a Label, a List, a TextArea, and an HGroup component aligned vertically from top to bottom. For each item, set the left and right boundaries to 0 (so they are pinned to the sides), and set their top and bottom properties appropriately.
Alternatively, you can insert the following MXML in Source View:
<s:Label y="10" horizontalCenter="0"/>
<s:List left="0" right="0" top="34" bottom="111"></s:List>
<s:TextArea left="0" right="0" bottom="39" height="64"/>
<s:HGroup left="0" right="0" bottom="0" height="32" verticalAlign="middle">
</s:HGroup>
Set the <s:Application> tag's width property to 350.
The user will type messages in the TextArea element, and received messages will be displayed in the List element.
<s:Label text="Send To:"/>
<s:TextInput/>
<mx:Spacer width="100%" height="10"/>
<s:Button label="Send"/>
The user will type the phone number (to send the SMS message to) in the TextInput control, and the Button control will act as the send button (see Figure 4).
Next, you will connect the application to the BlazeDS server within ColdFusion. This is done through two tags in the Flex SDK: Producer and Consumer. The Producer tag is used to send data to BlazeDS (and in this case the event gateway), and the Consumer tag is used to receive data.
<Consumer> and <Producer> tags to the <fx:Declarations> section of your code.<fx:Declarations>
<s:Producer id="sendToServer" destination="sms" />
<s:Consumer id="gotFromServer" destination="sms" />
</fx:Declarations>
initApp(), which will be called when the application starts:<fx:Script>
<![CDATA[
public function initApp():void
{
gotFromServer.subscribe();
}
]]>
</fx:Script>
This function calls the consumer's subscribe() function to connect to the BlazeDS server. The producer will automatically connect when the first message is publish.
Add applicationComplete="initApp()" to the Application root tag so that the initApp() function is called when the application starts.
Next, you need to adjust the top label's text property to reflect the application's actual connection status with the BlazeDS server.
text="Connected to Server : {gotFromServer.subscribed}"
Sending a message is straightforward. Simply assemble an asynchronous data packet containing the SMS message and send it off to the BlazeDS server.
import mx.messaging.messages.AsyncMessage;
protected function sendMessage():void
{
var msg:AsyncMessage = new AsyncMessage();
msg.headers.TYPE = "SMS";
msg.body.toNumber = toNumber.text;
msg.body.message = messageToSend.text;
msg.body.serial = new Date();
sendToServer.send(msg);
messageToSend.text = "";
}
click="sendMessage()" to the Send button:<s:Button label="Send" click="sendMessage()"/>
<s:TextArea left="0" right="0" bottom="39" height="64" id="messageToSend"/>
<s:TextInput id="toNumber"/>
The sendMessage() function builds the message packet in the structure that the ColdFusion code is expecting. This includes setting the headers.TYPE property to "SMS", as well as setting the toNumber and message properties of the body. The function sets the serial property so that BlazeDS doesn't ignore any subsequent similar messages that the user sends.
After the packet is built, sendMessage() passes the asynchronous data packet to the Producer's send() method. The Producer will connect to BlazeDS (if it is not already connected), and send the message to the destination. The code will then be processed by ColdFusion, and passed back to the application via the Consumer once ColdFusion processes it.
As a last step, sendMessage() clears the message text box so that the user does not inadvertently send the same message again.
Receiving messages from the BlazeDS server is also really easy. All that's needed is a new function that takes a single MessageEvent parameter. This function is set as the message event handler for the Consumer. When any message comes in from BlazeDS within the SMS destination, this function is invoked. The MessageEvent's message property contains the message just as it was processed by ColdFusion.
Follow these steps to set up message reception:
import mx.collections.ArrayCollection;
[Bindable] protected var messageList:ArrayCollection = new ArrayCollection();
<s:List left="0" right="0" top="34" bottom="111" dataProvider="{messageList}">
The default item renderer for the Spark List component is set up for strings, which makes it easy to display the SMS messages in the List control.
gotMessage() function, which takes the MessageEvent as a parameter:import mx.messaging.events.MessageEvent;
protected function gotMessage(e:MessageEvent):void
{
messageList.addItem('[' + e.message.body.fromNumber + '] ' + e.message.body.message);
toNumber.text = e.message.body.fromNumber;
}
This function creates a new string containing the message's BODY and fromNumber properties and adds it to the ArrayCollection.
It also sets the toNumber control's text property to the message sender's phone number to make it easy for the user to respond.
gotMessage() as the Consumer's event handler:<s:Consumer id="gotFromServer" destination="sms" message="gotMessage( event )" />
The application is now complete. To test it, follow these steps:
Verify that within a few seconds the "Connected to Server" label changes to "True".
Next, try sending a message to your cell phone.
Type a short message and then type your phone number in the Send To text input in the bottom of the screen and click Send.
It should not take more than a few seconds for your message to arrive on your cell phone.
Receiving messages should be just as simple.
If you are using the sandbox account with Twilio, you will need to prefix your sandbox PIN to each message you send back. Since Twilio uses a cloud infrastructure to communicate to your application, the address the service sends messages from may change. So if you are using a Developer edition of ColdFusion (not the 30-day trial or full version), you may need to restart ColdFusion on occasion to get around the two IP address limitation. If you have any difficulties, check your ColdFusion console, application logs, and the Twilio Debugger, which is available when you login to Twilio.
If you've followed the steps in this article, you have developed an infrastructure in ColdFusion and BlazeDS to allow a Flex based application to communicate with mobile telephones via the SMS protocol and the Twilio service. Now that you understand the basics of the Twilio service and how BlazeDS works, you can create your own applications enabled for real-time notifications, or build real-time and multiplayer games that use voice and texting services. To learn more about Twilio, see the Twilio developer documentation.
To find out more about building applications that use BlazeDS and its messaging service, watch Christophe Coenraets's Adobe TV video.
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. Permissions beyond the scope of this license, pertaining to the examples of code included within this work are available at Adobe.