CCXML 1.0-W3C Development GuideHome  |  Frameset Home

  tutorial Basic Event Handling  |  TOC  |  tutorial Basic Error Handling  

Tutorial: Advanced Event Handling

This Lesson will delve deeper into the "state machine" nature of CCXML. If you have not mastered Lesson 2, it is highly recommend you do so before beginning this Lesson.

In this Lesson, we will:

Note: The code presented within this tutorial is intended for the "Prophecy - CCXML 1.0" application type only. Attempts to utilize this code on the "CCXML - Voxeo" application type will not function, as the latter platform is based on the W3C specification from 2002.  Prophecy CCXML 1.0 is compliant to the most recent specification from 2006.


Step 1: Creating our initial CCXML structure

From our previous tutorials, we are pretty familiar with creating our basic CCXML 1.0 container structure:


<?xml version="1.0" encoding="UTF-8"?>
<!-- NOTE THAT WE *MUST* DECLARE THE xmlns ATTRIBUTE -->
<ccxml version="1.0" xmlns:voxeo="http://community.voxeo.com/xmlns/ccxml">
</ccxml>



Step 2: State machines

One of the over-arching concepts in CCXML is the usage of state machines: a certain event changes the current state of an application. And when our state changes, we need to make sure that we can catch, and handle this state change with a predefined set of actions. Similar to what we covered in our previous tutorials, we again leverage the <transition> element, but introduce the usage of the 'state' attribute: This allows us to have multiple <transition> elements for the same events, but gives us a finer grain of control based on the state that the application is currently in. To illustrate in greater detail, consider the following code snippets, both of which are meant to handle a specific user-defined event that has been kicked off via the <send> element:


    <transition state="'niceGuy'" event="myEvent">
      <dialogstart src="'niceCaller.vxml'" type="'application/voicexml+xml'"/>
    </transition>

    <transition state="'meanGuy'" event="myEvent">
      <dialogstart src="'meanCaller.vxml'" type="'application/voicexml+xml'"/>
    </transition>


In this example, we are looking to handle 'myEvent', but depending on whether or not we have assigned the state variable to "nice" or "mean", we can shunt the caller off to a different dialog. As illustrated, a "state variable" is identical syntactically to a "common variable", but the usage characterizes its special nature. While an application can have multiple state variables, each <eventprocessor> can only be scoped to a single state variable. Let's take a look:


<?xml version="1.0" encoding="UTF-8"?>
<!-- NOTE THAT WE *MUST* DECLARE THE xmlns ATTRIBUTE -->
<ccxml version="1.0" xmlns:voxeo="http://community.voxeo.com/xmlns/ccxml">

  <var name="app_state" expr="'initial'"/>
  <eventprocessor statevariable="app_state">
  </eventprocessor>
</ccxml>


Each event that we capture by our <eventprocessor> will use "app_state" for the state variable. This does not mean that we must use state information as part of our specific event check; instead, it forces us to use the specified state variable if we choose to check for state, (which we do, in this case!).


Step 2: Answering a call using state

Now that we have a rock-solid foundation to our struktur, we will answer an incoming phone call, similar to the manner that we have in previous tutorials:


<?xml version="1.0" encoding="UTF-8"?>
<!-- NOTE THAT WE *MUST* DECLARE THE xmlns ATTRIBUTE -->
<ccxml version="1.0" xmlns:voxeo="http://community.voxeo.com/xmlns/ccxml">

  <var name="app_state" expr="'initial'"/>
  <var name="in_connID"/>

  <eventprocessor statevariable="app_state">

    <transition state="initial" event="connection.alerting">
      <assign name="app_state" expr="'accepting'"/>
      <accept/>
      <assign name="in_connID" expr="event$.connection.connectionid"/>
      <log expr="'*** connection.alerting app_state = ' + app_state + ' ***'"/>
      <log expr="'*** in_connID = ' + in_connID + ' ***'"/>     
    </transition> 

    <transition state="accepting" event="connection.connected">
      <assign name="app_state" expr="'incall_0'"/>
      <send name="'continue'" target="session.id" delay="'3000ms'"/>
    </transition> 
  </eventprocessor>
</ccxml>


We have a few added wrinkles to the actions we take when we handle the 'connection.alerting' and the 'connection.connection' events in our above code snippet. The first thing that you will undoubtedly notice is that we have added a new variable at the  <ccxml> scope, whose value will be available to us regardless of which <transition> we are at in the CCXML 1.0 document. This is an empty variable when we declare it, but you see that as soon as we hit the "connection.alerting" event <transition>, we assign this variable the value of the 'connection.connectionid' property: This variable assignation allows us to programmatically disconnect a specific call leg later on in our tutorial.

The next curve ball thrown is that we assign our "app_state"  variable a user-defined value of 'alerting'. This is also key, as our <transition> for the 'connection.connected' event will only execute when app_state='alerting'. This further illustrates that a <transition> tag handles more than one conditional requirement: for our first example, the state (as defined within the "app_state" variable) must be equal to "initial" as well as the raw event itself being "connection.alerting". If either of these is not true, then there is no match, and the <transition> handler is skipped. Case in point: we are changing the value (via the <assign> tag) to a new value -- should our state variable never be reset to "initial", then this <transition> will never be visited again.

Don't be thrown off by the idea that we just set the value of the state variable to "accepting" -- remember the wisdom taught by your tubby, C++ coding neighbor regarding asynchronous events. The inbound call is accepted, the state information is changed again, and now we <send> a user-defined event to this same application. With <send> we are able to post an event to any session we have the identifier of. In this example, we send one simple event to ourselves by specifying a value of "session.id" in the 'target' attribute. Additionally, we will delay the sending for 3000 milliseconds, allowing us to implement our "timer" functionality, or just pause our application for any given period of time.


Step 3: More events

Up until now, we have only tossed and caught CCXML defined events; however, our <send> element requires us to catch a user-defined event. How the heck do we do that? Easy, we just code a <transition> that is looking for the event name we just sent in the 'event' attribute, making sure that we are also looking for the correct application state in the 'state' attribute, as explained earlier:


<?xml version="1.0" encoding="UTF-8"?>
<!-- NOTE THAT WE *MUST* DECLARE THE xmlns ATTRIBUTE -->
<ccxml version="1.0" xmlns:voxeo="http://community.voxeo.com/xmlns/ccxml">

  <var name="app_state" expr="'initial'"/>
  <var name="in_connID"/>

  <eventprocessor statevariable="app_state">

    <transition state="initial" event="connection.alerting">
      <assign name="app_state" expr="'accepting'"/>
      <accept/>
      <assign name="in_connID" expr="event$.connection.connectionid"/>
      <log expr="'*** connection.alerting app_state = ' + app_state + ' ***'"/>
      <log expr="'*** in_connID = ' + in_connID + ' ***'"/>     
    </transition> 

    <transition state="accepting" event="connection.connected">
      <assign name="app_state" expr="'incall_0'"/>
      <send name="'continue'" target="session.id" delay="'3000ms'"/>
    </transition> 
 
    <transition state="incall_0" event="continue">
      <assign name="app_state" expr="'incall_1'"/>
      <log expr="'*** incall_0 app_state = ' + app_state + ' ***'"/>
      <send name="'continue'" target="session.id" delay="'1000ms'"/>
    </transition>

    <transition state="incall_1" event="continue">
      <disconnect connectionid="in_connID"/>
      <assign name="app_state" expr="'disconnecting'"/>
      <log expr="'*** incall_1 app_state = ' + app_state + ' ***'"/>
    </transition>
  </eventprocessor>


As promised, the most obvious difference between these transitions and our earlier examples is the 'event' attribute itself. Rather than looking for a system event, (such as "connection.connected"), we are looking for our humble user-event named "continue" in our <transition>. To further illustrate the usage of application state, we again reassign our "app_state" variable to a value of 'incall_1', and again send another "continue" event to ourselves, this time with a 1000ms delay.

We handle this somewhat redundant event in the same manner as the first: we code our <transition> to seek out a condition where the event is "continue", but this time, where the application state is now 'incall_1'.

Examining our second transition from above, we can see that after just four seconds of "activity", we choose to disconnect the call. Unless we specify a particular 'connectionid' the CCXML 1.0 interpreter will use the connectionid from the current state. Since we have already assigned our connectionid to the "in_connID" variable, we may as well use it, even though this too, is entirely redundant. That's right, our application is advanced enough to answer the phone, and do absolutely nothing in a substantially structured sense, before hanging up. 


Step 4: Handling multiple types of disconnects

Our last transition fired the <disconnect> event, but what if we want to detect an earlier event (say, for example, your impatient little sister called and the four seconds of silence overwhelmed her)? Clearly, you might want to know when this situation occurs. In order to do so, we employ the 'cond' attribute of the ever-popular <transition> element, which works just like an if/elseif/else conditional logic block. Basically, the events contained within this handler will only execute if:


In the below sample, we use the "||" operator as an "OR" operand, meaning that either of the two expressions denoted in the 'cond' attribute can be met in order for this event handler to be executed. In this case, we need to receive a situation where our caller hangs up prior to the app executing the 'disconnect' directive from our previous step. Should this happen, we kick off a notification to ourselves, (don't forget to fill in your own email address!), and <exit> gracefully.

In the event that the call executes normally, with a programmatic disconnect, we then handle that event, and invoke the <exit> tag to avoid any runaway call sessions, (we covered this concept in our previous tutorial). As a final line of defense, we include a <transition> handler for any "wildcard" error events that might occur via the "error.*" event value, so that we can be sure that no matter what happens, we can <exit> without bringing our IVR platform down with a zombie session.


<?xml version="1.0" encoding="UTF-8"?>
<ccxml version="1.0" xmlns:voxeo="http://community.voxeo.com/xmlns/ccxml">

  <var name="app_state" expr="'initial'"/>
  <var name="in_connID"/>

  <eventprocessor statevariable="app_state">

    <transition state="initial" event="connection.alerting">
      <assign name="app_state" expr="'accepting'"/>
      <accept/>
      <assign name="in_connID" expr="event$.connection.connectionid"/>
      <log expr="'*** connection.alerting app_state = ' + app_state + ' ***'"/>
      <log expr="'*** in_connID = ' + in_connID + ' ***'"/>     
    </transition> 

    <transition state="accepting" event="connection.connected">
      <assign name="app_state" expr="'incall_0'"/>
      <send name="'continue'" target="session.id" delay="'3000ms'"/>
    </transition> 
 
    <transition state="incall_0" event="continue">
      <assign name="app_state" expr="'incall_1'"/>
      <log expr="'*** incall_0 app_state = ' + app_state + ' ***'"/>
      <send name="'continue'" target="session.id" delay="'1000ms'"/>
    </transition>

    <transition state="incall_1" event="continue">
      <disconnect connectionid="in_connID"/>
      <assign name="app_state" expr="'disconnecting'"/>
      <log expr="'*** incall_1 app_state = ' + app_state + ' ***'"/>
    </transition>

    <transition event="connection.disconnected">
      <log expr="'*** call was disconnected ***'"/>
      <exit/>
    </transition>

    <transition event="connection.failed"
                cond="('incall_0' == app_state) || ('incall_1' == app_state)">
      <log expr="'*** premature disconnection detected in state [' + app_state + '] ***'"/>
        <voxeo:sendemail to="'yourEmail@there.com'"
                        from="'myApp@here.com'"
                        type="'debug'"
                        body=" 'premature disconnection detected!' "/>
      <exit/>
    </transition>

    <transition event="connection.failed">
      <log expr="'*** not premature...  wise, learned, mature disconnection ***'"/>
      <exit/>
    </transition>
 
    <transition event="error.*">
      <log expr="'*** unhandled error.* event detected ***'"/>
    <voxeo:sendemail to="'yourEmail@there.com'"
                        from="'myApp@here.com'"
                        type="'debug'"
                        body="'unhandled error event detected!'"/>
    </transition>

  </eventprocessor>
</ccxml>


First of all, it is entirely possible this tutorial will never end. Just wanted to get that out there. Secondly, we now see that in addition to specifying "state" and "event" criteria, a transition can also take explicit conditional statements. Our first transition can now spit out a debug line containing the state we were in when your little sister hung up the phone.

Our last transition uses the filter feature of CCXML 1.0, where whole event groups can be caught by one handler. In this case, we process every "call" message. As stated in our last tutorial, the order of the transitions is critical -- if we put this code piece at the top of the <eventprocessor>, nothing would work at all (we wouldn't get our four seconds of silence!).


  Download the CCXML source code!


What we covered:




  ANNOTATIONS: EXISTING POSTS
atacama
10/23/2008 2:29 PM (EDT)
Step 2 (well, in the second Step 2, not the first Step 2) says "...assign our "app_state"  variable a user-defined value of 'alerting'', but the value set in the code is 'accepting'. The next sentence also says 'alerting', but the next paragraph correctly says "accepting".
voxeojeff
10/23/2008 2:40 PM (EDT)
Hi there,

Good catch!  You're right, the "alerting" should be "accepting" in this case.  I'll go ahead and let our documentation gurus know that this should be fixed. :-)

Thanks,
Jeff

login
  tutorial Basic Event Handling  |  TOC  |  tutorial Basic Error Handling  

© 2008 Voxeo Corporation  |  Voxeo IVR  |  VoiceXML & CCXML IVR Developer Site