Leveraging a CCXML Wrapper for Post-call Cleanup

December 17th, 2008 by Dustin Hayre

The default CCXML wrapper that quietly powers many VoiceXML applications on Voxeo’s Prophecy platform is certainly sufficient for many simple applications. So why should I bother diving into yet another markup language to do re-invent the wheel? Because your application isn’t that simple and you’re a control freak. Or maybe I am. Who knows.

Typically, we can handle post-call cleanup in VoiceXML easily enough by using the <submit> or <data> elements to our server-side cleanup scripts.

  <catch event="connection.disconnect">
     <submit next="cleanup.php" method="POST"/>
  </catch>

While this is a valid way to handle this, VoiceXML requires that we either transfer control to another document(<submit>) or respond with valid XML(<data>). Instead, we can leverage the disconnect event and any other portion of the application that we’d like the call to end to send this data back to our CCXML handler and let it do all the work asynchronously. The bonus here is we can have unfettered control of our voice applications. Once we have this in place, it’s simple work to leverage CCXML to control call duration, pre/post-processing and more.

When using a custom CCXML wrapper to handle post-call cleanup, our VoiceXML disconnect handler will look something like this:

  <catch event="connection.disconnect">
     <exit namelist="my_var1 my_var2 my_var3"/>
  </catch>

Here we’re using the namelist attribute of <exit> to send back our values back to CCXML. We can now handle this inside our CCXML wrapper with a dialog.exit transition:

  <transition event="dialog.exit">
    <log expr="'**** DIALOG COMPLETE - SENDING POST CALL CLEANUP'"/>
    <assign name="dialog_active" expr="false"/>
      <log expr="'my_var1 = ' + event$.values.my_var1"/>
      <log expr="'my_var2 = ' + event$.values.my_var2"/>
      <log expr="'my_var3 = ' + event$.values.my_var3"/>
      <assign name="my_var" expr="event$.values.my_var1"/>
      <!-- sends a POST to our cleanup script -->
    <disconnect connectionid="conn_id"/>
      <send name="'user.call.cleanup'" target="'cleanup.php'" targettype="'basichttp'" namelist="my_var1"/>
  </transition>

The values of our VXML variables are stored in the object event$.values. We can then grab them from the event$.values object by referencing them as event$.values.variablename – in this case, event$.values.my_var1. Since our VoiceXML dialog is complete, we’re ready to end the call. We issue a disconnect to the caller’s connectionid and send our post call cleanup. CCXML’s <disconnect>, unlike VoiceXML’s, physically disconnects the call leg. So now the call leg is ended, we’re free to handle our post-processing without paying to keep that call leg up. Brilliant!

Now that we’ve shot off our post-processing request, we can handle the send.successful event and close the session out, right? Well, we could do that, but how do we know the request was received successfully? Note, this next portion is a Voxeo specific extension and is not part of the W3C spec and requires the Voxeo namespace - <ccxml version=”1.0″ xmlns:voxeo=”http://community.voxeo.com/xmlns/ccxml”>.

Instead of assuming everything went swimmingly with our post-processing, we can be sure by having our server-side send back an event to the CCXML browser if we format the body of the response like this:

  user.cleanup.successful
  my_var1=foo
  my_var2=bar

Note that we can inject not only an event here, but name/value pairs, though they are entirely optional. Now that my server side has responded, with an event, I’ll need to handle this in my CCXML:

  <transition event="user.cleanup.successful">
    <log expr="'**** POST CALL CLEANUP COMPLETED SUCCESSFULLY'"/>
    <log expr="'**** EXITING SESSION'"/>
     <exit/>
  </transition>

Last, but very definitely not least, we will want to ensure our session does not stay alive after the call leg has disconnected. Since we are doing a little post-processing, we don’t want to end the session immediately, as we’ll want to give that time to process. So, we’ll simply shoot off a delayed user event to kill the session 60 seconds after a disconnect and prevent the zombie apocalypse.

  <transition event="connection.disconnected">
     <!-- send to unconditionally end a runaway session -->
     <send name="'user.kill.unconditional'" target="session.id" delay="'60s'"/>
  </transition>

  <transition event="user.kill.unconditional">
     <log expr="'**** UNCONDITIONAL KILL - EXITING SESSION'"/>
       <exit/>
  </transition>

That’s it. Find the complete CCXML below.

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

      <meta name="author" content="Dustin Hayre"/>
      <meta name="maintainer" content="YOUR_EMAIL@HERE.COM"/>

<!-- how long to wait before assuming a session is a runaway and tearing it down -->
<var name="conn_id"/>
<var name="dialog_id"/>
<var name="my_var"/>

<eventprocessor>
  <transition event="connection.alerting">
    <assign name="conn_id" expr="event$.connectionid"/>
    <accept connectionid="conn_id"/>
  </transition>

  <transition event="connection.connected">
    <log expr="'**** STARTING DIALOG TO CONNECTION ID ' + conn_id"/>
     <!-- edit SRC attribute to point to VXML dialog -->
     <dialogstart src="'dialog.vxml'" connectionid="conn_id" dialogid="dialog_id"/>
  </transition>

  <transition event="dialog.exit">
    <log expr="'**** DIALOG COMPLETE - SENDING POST CALL CLEANUP'"/>
      <log expr="'event$.values.my_var1 = ' + event$.values.my_var1"/>
      <assign name="my_var" expr="event$.values.my_var1"/>
      <!-- sends a POST to our cleanup script -->
      <send name="'user.call.cleanup'" target="'cleanup.php'" targettype="'basichttp'" namelist="foo"/>
  </transition>

  <transition event="user.cleanup.successful">
    <log expr="'**** POST CALL CLEANUP COMPLETED SUCCESSFULLY'"/>
    <log expr="'**** EXITING SESSION'"/>
     <exit/>
  </transition>

  <transition event="error.*">
    <log expr="'**** ERROR - REASON: ' + event$.reason"/>
      <exit/>
  </transition>

  <transition event="connection.disconnected">
     <!-- send to unconditionally end a runaway session -->
     <send name="'user.kill.unconditional'" target="session.id" delay="'60s'"/>
  </transition>

  <transition event="user.kill.unconditional">
     <log expr="'**** UNCONDITIONAL KILL - EXITING SESSION'"/>
       <exit/>
  </transition>

  <transition event="connection.failed">
    <log expr="'**** CONNECTION FAILED - REASON: ' + event$.reason"/>
      <exit/>
  </transition>

</eventprocessor>
</ccxml>

Feel free to comment with any questions or suggestions.

-Dustin


Related posts:

  1. Never forget to make a call with scheduled conferencing
  2. Adding Call Control to Voxeo Designer Applications via CCXML
  3. Certified Tech Tip: Prophecy 8 CCXML 1.0 Call Recording
  4. CCXML and SIP, Part 1: Accessing SIP headers
  5. How to call your VoiceXML, CCXML or CallXML app directly from Skype

Tags: , ,


If you found this post interesting or helpful, please consider either subscribing via RSS, becoming a fan on Facebook, or following us on Twitter.


2 Responses to “Leveraging a CCXML Wrapper for Post-call Cleanup”

  1. Mark Says:

    Dustin – great post.

    You mention that the use of the <send> element with the targetype=”‘basichttp’” is a Voxeo extension. I think that used to be the case, but my understanding is that this functionality was incorporated into the official spec as of the latest version (the 1/19.2007 Working Draft). As such, I don’t think you need to declare the Voxeo namespace to use <send> in this way. Can you clarify?

    Also, in your code example above you use a comment that says “sends a POST to our cleanup script” — is the default behavior for <send> to use HTTP POST or GET (I thought it was GET). This could be confusing if someone was trying to access these submitted variables — for example, if the default behavior is to use GET, but they’re trying to access the submitted variables in PHP with $_POST.

    Can you verify that <send> uses GET or POST? Any way to explicitly set this using an attribute value?

  2. Dustin Hayre Says:

    Hey Mark,

    Thanks for the kind words.

    The send element itself is not Voxeo specific, however handling the return in the manner I did is(e.g. throwing an event based on the body of the response). The spec does not cover injecting an event in the manner that we are here, but does allow us some leeway in its wording. This is a very easy way to inject events and data back into your CCXML application. Another great way is using <fetch> with XML data and E4X, and I think that’s something I may explore further(both for CCXML and VoiceXML) in a future blog posting.

    By default, <send> uses POST. You can enable GET on Voxeo’s platform by using <send name="'http.get'" ../>, however this is also Voxeo specific and not part of the spec. While the spec is quite open about allowing implementation specific methods with the I/O processor, it’s not guaranteed that this functionality will always be present so I would avoid it unless you absolutely need to use GET.

    Cheers, Dustin

Leave a Reply

Please note: By submitting a comment you agree to comply with our Comment Policy. We welcome all comments, positive or negative, but do reserve the right to remove all or part of blog comments that do not comply with our policy.

Additionally, the first time you leave a comment on this blog, it will be held for moderation. After that first comment has been approved, future comments will be posted without delay.

Additional comments powered by BackType