JavaOne - A Sip Of Java - RJ Auburn

Loading...

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

0 comments

Post a comment

    Post a comment
    Embed Video
    Edit your comment Cancel

    2 Favorites

    JavaOne - A Sip Of Java - RJ Auburn - Presentation Transcript

    1. Taking a SIP of Java RJ Auburn Voxeo Corporation Chief Technology Officer
    2. Telephony 2
    3. Sucks 3
    4. Expensive 4
    5. Complex 5
    6. Proprietary 6
    7. This is not how it should be... 7
    8. Simple 8
    9. Ubiquitous 9
    10. Open 10
    11. So what are the layers? 11
    12. Application API XML Tools Platform 12
    13. Application Application API XML Tools Platform 13
    14. Application API XML Tools Platform 14
    15. Application API XML Tools Platform 15
    16. Application API XML Tools Platform 16
    17. Some Basics: Signaling vs Media 17
    18. Call Control vs Media > Phone systems often are split into two components: Signaling and Media > Signaling handles the setup and tear-down of sessions and phone calls. > Media is responsible for the transport voice path 18
    19. Media vs Call Control in the PSTN - SIP Proxy Proxy SIP A B SIP SIP Alice RTP Bob 19
    20. Media vs Call Control in the PSTN - SS7 SCP SCP SS7 A B SS7 7 SS Alice Switched TDM Media Bob 20
    21. Media vs Call Control in the PSTN - ISDN D Channel (q.931) Signaling Dev Dev A B B Channels (23) Media 21
    22. All this means is the APIʼs and standards for media vs call control tend to be different. 22
    23. So now for a bit about SIP (Call Control) 23
    24. SIP > Session Initiation Protocol (SIP) defines how to establish a communication session between two endpoints > Primarily used for voice, but can for IM or virtually any other protocol > Almost always used in client/server configuration with \"SIP proxies\" in control of \"SIP endpoints\" • Work going on in P2PSIP - see www.p2psip.org > Text-based protocol, originally modeled on HTTP 24
    25. SIP Communication INVITE Alice 180 RINGING Bob 200 OK ACK RTP (voice) BYE 200 OK 25
    26. Major SIP Methods > INVITE > BYE > REFER > REGISTER > SUBSCRIBE > NOTIFY oh ya... > INFO (wait, we are not supposed to talk about this one) 26
    27. Response Codes > 1xx - Provisional > 2xx - All is good. Final > 3xx - Redirects > 4/5/6xx - Errors 27
    28. SIP Resources > Internet Engineering Task Force (IETF) • RFC 3261 • Hitchhikerʼs Guide to SIP > Open Source Info • VoIP Info Wiki: www.voip-info.org > Industry Sites • SIP Forum: www.sipforum.org • SIP Foundry: www.sipfoundry.org 28
    29. So lets talk about... 29
    30. Religion 30
    31. Religion 31
    32. XML 32
    33. VoiceXML and CCXML 33
    34. VoiceXML > Language created by the W3C to model computer human dialogs. > Supports speech and touchtone. > Built around a form filling model called the FIA. > Voice equivalent to HTML. > Focused on dialogs. Very limited call control. > http://www.w3.org/TR/voicexml/ 34
    35. CCXML > Call Control XML (CCXML) is the W3C standard for call control using XML > Sister standard to VoiceXML > Integrates with VoiceXML for dialog control > Provides a framework for issuing call control commands and handling call control events > http://www.w3.org/TR/ccxml/ 35
    36. VoiceXML and CCXML Architecture ASR Phone CCXML VoiceXML mrcp sip sip TTS rtp 36
    37. Like Tribbles... 37
    38. It has taken over the Enterprise... 38
    39. How about some code? 39
    40. Sample CCXML/VoiceXML application. > Caller dials in to the application > Caller is bridged to the subscriber > Results of the call attempt are posted to Twitter via their REST API 40
    41. start isRJ() yes no prompt for dest play twitter status call dest call rj’s cell end
    42. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"'tel:+18315551234'\"/> </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"event$.values.dest\"/> </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 42
    43. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"'tel:+18315551234'\"/> </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"event$.values.dest\"/> </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 43
    44. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <?xml version=\"1.0\" encoding=\"UTF-8\"?> <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" <ccxml version=\"1.0\" </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <assign xmlns=\"http://www.w3.org/2002/09/ccxml\"> name=\"state\" expr=\"'calling'\"/> <createcall dest=\"'tel:+18315551234'\"/> </ccxml> </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"event$.values.dest\"/> </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 44
    45. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"'tel:+18315551234'\"/> </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"event$.values.dest\"/> </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 45
    46. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" expr=\"'init'\"/> <var name=\"state\" </transition> <transition<var name=\"incomingcall\"/> event=\"dialog.exit\" state=\"callconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <eventprocessor statevariable=\"state\"> <createcall dest=\"'tel:+18315551234'\"/> </transition> <transition</eventprocessor> event=\"dialog.exit\" state=\"rjconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"event$.values.dest\"/> </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 46
    47. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"'tel:+18315551234'\"/> </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"event$.values.dest\"/> </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 47
    48. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'callconnected'\"/> <transition event=\"connection.alerting\" state=\"init\"> <dialogstart src=\"'caller.vxml'\" <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <accept/> dest=\"'tel:+18315551234'\"/> <assign name=\"state\" expr=\"'calling'\"/> <createcall </transition> </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"event$.values.dest\"/> </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 48
    49. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"'tel:+18315551234'\"/> </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"event$.values.dest\"/> </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 49
    50. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <transition event=\"connection.connected\" state=\"init\" <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" cond=\"event$.connection.remote=='tel:+18315551234'\"> </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <assign name=\"state\" expr=\"'rjconnected'\"/> <createcall dest=\"'tel:+18315551234'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> </transition> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"event$.values.dest\"/> </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 50
    51. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <var name=\"tURL\" expr=\"'http://zscgeek:password@twitter.com/statuses/update.xml'\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\"> <?xml version=\"1.0\" encoding=\"UTF-8\"?> <assign name=\"state\" expr=\"'calling'\"/> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <vxml xmlns=\"http://www.w3.org/2001/vxml\" version=\"2.1\"> <createcall dest=\"'tel:+18312392883'\"/> </transition> <form> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <field name=\"dest\" type=\"digits?length=10\"> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> <var name=\"status\" expr=\"'RJ is on the phone'\"/> <send <prompt>Welcome RJ. Please enter the phone targettype=\"'basichttp'\" name=\"'update'\" target=\"tURL\" namelist=\"status\"/> </transition> number you wish to reach.</prompt> <transition event=\"connection.failed\" state=\"calling\"> <filled> <assign name=\"state\" expr=\"'done'\"/> <var name=\"status\" expr=\"'RJ is not answering his phone'\"/> <send <exit namelist=\"dest\"/> targettype=\"'basichttp'\" name=\"'update'\" target=\"tURL\" namelist=\"status\"/> </filled> </transition> </field> <transition event=\"connection.disconnected\" state=\"connected\"> <assign name=\"state\" expr=\"'done'\"/> </form> <var name=\"status\" expr=\"'RJ is off the phone'\"/> <send targettype=\"'basichttp'\" name=\"'update'\" </vxml> target=\"tURL\" namelist=\"status\"/> </transition> <transition event=\"send.successful\" state=\"done\"> <exit/> </transition> </eventprocessor> </ccxml> 51
    52. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"'tel:+18315551234'\"/> </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"event$.values.dest\"/> </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 52
    53. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" <transition event=\"connection.connected\" state=\"init\"> </transition> <assign name=\"state\" expr=\"'callconnected'\"/> <transition name=\"state\" expr=\"'callconnected'\"/> <assign event=\"connection.connected\" state=\"init\"> <dialogstart src=\"'caller.vxml'\" <dialogstart src=\"'caller.vxml'\" </transition> </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"'tel:+18315551234'\"/> </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"event$.values.dest\"/> </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 53
    54. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <var name=\"tURL\" expr=\"'http://zscgeek:password@twitter.com/statuses/update.xml'\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <?xml version=\"1.0\" encoding=\"UTF-8\"?> <accept/> </transition> <vxml xmlns=\"http://www.w3.org/2001/vxml\" version=\"2.1\"> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'calling'\"/> <form> name=\"incomingcall\" expr=\"event$.connectionid\"/> <assign <createcall dest=\"'tel:+18312392883'\"/> <block> </transition> <transition event=\"connection.connected\" state=\"calling\"> <data src=\"http://twitter.com/statuses/ <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> user_timeline.xml?screen_name=zscgeek\" <var name=\"status\" expr=\"'RJ is on the phone'\"/> <send targettype=\"'basichttp'\" name=\"'update'\" name=\"twitter\" ecmaxmltype=\"e4x\" /> target=\"tURL\" namelist=\"status\"/> </transition> <prompt>Thank you for calling RJ. <transition event=\"connection.failed\" state=\"calling\"> <assign We will connect you now. name=\"state\" expr=\"'done'\"/> <var name=\"status\" expr=\"'RJ is not answering his phone'\"/> His twitter status is <send targettype=\"'basichttp'\" name=\"'update'\" target=\"tURL\" namelist=\"status\"/> <value expr=\"twitter.statuses.status[0].text\"/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> </prompt> <assign name=\"state\" expr=\"'done'\"/> <var name=\"status\" expr=\"'RJ is off the phone'\"/> </block> <send targettype=\"'basichttp'\" name=\"'update'\" target=\"tURL\" namelist=\"status\"/> </form> event=\"send.successful\" state=\"done\"> </transition> <transition </vxml> <exit/> </transition> </eventprocessor> </ccxml> 54
    55. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"'tel:+18315551234'\"/> </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"event$.values.dest\"/> </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 55
    56. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <transition event=\"dialog.exit\" state=\"callconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"'tel:+18315551234'\"/> <assign name=\"state\" expr=\"'calling'\"/> </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> <createcall dest=\"'tel:+18315551234'\"/> <assign name=\"state\" expr=\"'calling'\"/> </transition> dest=\"event$.values.dest\"/> <createcall </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 56
    57. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"'tel:+18315551234'\"/> </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"event$.values.dest\"/> </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 57
    58. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <transition name=\"state\" expr=\"'calling'\"/> <assign event=\"dialog.exit\" state=\"rjconnected\"> <createcall dest=\"'tel:+18315551234'\"/> <assign name=\"state\" expr=\"'calling'\"/> </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> <createcall dest=\"event$.values.dest\"/> <assign name=\"state\" expr=\"'calling'\"/> </transition> dest=\"event$.values.dest\"/> <createcall </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 58
    59. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"'tel:+18315551234'\"/> </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"event$.values.dest\"/> </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 59
    60. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" </transition> <transition event=\"connection.connected\" <transition event=\"dialog.exit\" state=\"callconnected\"> state=\"calling\"> expr=\"'calling'\"/> <assign name=\"state\" <createcall dest=\"'tel:+18315551234'\"/> </transition> <transition event=\"dialog.exit\" expr=\"'connected'\"/> <assign name=\"state\" state=\"rjconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> <createcall dest=\"event$.values.dest\"/> </transition> </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 60
    61. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"'tel:+18315551234'\"/> </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"event$.values.dest\"/> </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 61
    62. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" <transition event=\"connection.failed\" state=\"calling\"> </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <exit/> name=\"state\" expr=\"'calling'\"/> <assign </transition> dest=\"'tel:+18315551234'\"/> <createcall </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> <transition name=\"state\" expr=\"'calling'\"/> <assign event=\"connection.disconnected\" state=\"connected\"> <createcall dest=\"event$.values.dest\"/> <exit/> </transition> <transition event=\"connection.connected\" state=\"calling\"> </transition> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 62
    63. Call router + Twitter <?xml version=\"1.0\" encoding=\"UTF-8\"?> <ccxml xmlns=\"http://www.w3.org/2002/09/ccxml\" version=\"1.0\"> <var name=\"state\" expr=\"'init'\"/> <var name=\"incomingcall\"/> <eventprocessor statevariable=\"state\"> <transition event=\"connection.alerting\" state=\"init\"> <assign name=\"incomingcall\" expr=\"event$.connectionid\"/> <accept/> </transition> <transition event=\"connection.connected\" state=\"init\" cond=\"event$.connection.remote=='tel:+18315551234'\"> <assign name=\"state\" expr=\"'rjconnected'\"/> <dialogstart src=\"'rj.vxml'\" </transition> <transition event=\"connection.connected\" state=\"init\"> <assign name=\"state\" expr=\"'callconnected'\"/> <dialogstart src=\"'caller.vxml'\" </transition> <transition event=\"dialog.exit\" state=\"callconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"'tel:+18315551234'\"/> </transition> <transition event=\"dialog.exit\" state=\"rjconnected\"> <assign name=\"state\" expr=\"'calling'\"/> <createcall dest=\"event$.values.dest\"/> </transition> <transition event=\"connection.connected\" state=\"calling\"> <assign name=\"state\" expr=\"'connected'\"/> <join id1=\"event$.connectionid\" id2=\"incomingcall\"/> </transition> <transition event=\"connection.failed\" state=\"calling\"> <exit/> </transition> <transition event=\"connection.disconnected\" state=\"connected\"> <exit/> </transition> </eventprocessor> </ccxml> 63
    64. So... How about APIʼs... 64
    65. A Favorite of Carriers 65
    66. Java 66
    67. SIP Servlets > Standard Java based API for writing SIP applications. > 1.0 standardized as JSR-116. > 1.1 just released as JSR-289 > Extends the HTTP Servlet model to support SIP and telephony applications > http://www.sipservlet.com/ > Supported by a large number of application servers including Oracle (BEA), IBM, Sun, Voxeo. 67
    68. Request Methods > doInvite(SipServletRequest req); > doAck(SipServletRequest req); > doOptions(SipServletRequest req); > doBye(SipServletRequest req); > doCancel(SipServletRequest req); > doSubscribe(SipServletRequest req); > doNotify(SipServletRequest req); > doMessage(SipServletRequest req); > doInfo(SipServletRequest req); > doPrack(SipServletRequest req); 68
    69. Response Methods > doProvisionalResponse(SipServletResponse res); > doSuccessResponse(SipServletResponse res); > doRedirectResponse(SipServletResponse res); > doErrorResponse(SipServletResponse res); 69
    70. Basic Request public class BasicSIPServlet extends SipServlet { protected void doInfo(SipServletRequest req) throws ServletException, IOException { req.createResponse(SipServletResponse.SC_TRYING).send(); // do stuff req.createResponse(SipServletResponse.SC_OK).send(); } } 70
    71. Accessing SIP Headers protected void doInvite(SipServletRequest req) throws ServletException, IOException { String cpc ; if (req.getHeader(\"User-Agent\").equals(\"Sonus\")) { cpc = req.getHeader(\"CallParty\"); } else if (req.getHeader(\"User-Agent\").equals(\"ZTE\")) { cpc = req.getHeader(\"CPS\"); } } 71
    72. JSR-309 > Java Media Server API > Based on the CCXML media model > Still in draft stage > Provides dialog resources, conferencing, media routing to Java applications 72
    73. Sample Application Overview > Lets try that application again in java... 73
    74. start isRJ() yes no prompt for dest play twitter status call dest call rj’s cell end
    75. SIP Flow Caller App Dest INVITE - doInvite() 200 OK ACK doAck() INVITE - transferDialog() 200 OK - doSuccessResponse() ACK 75
    76. Call router + Twitter public class TwitterSIPServlet extends SipServlet { SipFactory factory; public void init() throws ServletException { super.init(); factory =(SipFactory)getServletContext().getAttribute(\"javax.servlet.sip.SipFactory\"); } protected void doInvite(SipServletRequest req) { if(req.isInitial()){ SipApplicationSession sipApp = factory.createApplicationSession(); TwitterSIPServletSession service = new TwitterSIPServletSession(req.getSession(), factory); sipApp.setAttribute(\"router-app\", service); service.init(req); } } protected void doSuccessResponse(SipServletResponse resp) { resp.createAck().send(); } protected void doAck(SipServletRequest req) { final TwitterSIPServletSession service = (TwitterSIPServletSession) req.getApplicationSession().getAttribute(\"router- app\"); service.startDialog(); } protected void doBye(SipServletRequest req) { // Need to send BYE request to the other legs } } 76
    77. Call router + Twitter public class TwitterSIPServlet extends SipServlet { SipFactory factory; public void init() throws ServletException { super.init(); factory =(SipFactory)getServletContext().getAttribute(\"javax.servlet.sip.SipFactory\"); } protected void doInvite(SipServletRequest req) { if(req.isInitial()){ SipApplicationSession sipApp = factory.createApplicationSession(); TwitterSIPServletSession service = new TwitterSIPServletSession(req.getSession(), factory); sipApp.setAttribute(\"router-app\", service); service.init(req); } } protected void doSuccessResponse(SipServletResponse resp) { resp.createAck().send(); } protected void doAck(SipServletRequest req) { final TwitterSIPServletSession service = (TwitterSIPServletSession) req.getApplicationSession().getAttribute(\"router- app\"); service.startDialog(); } protected void doBye(SipServletRequest req) { // Need to send BYE request to the other legs } } 77
    78. Call router + Twitter public class TwitterSIPServlet extends SipServlet { SipFactory factory; public void init() throws ServletException { public class TwitterSIPServlet extends SipServlet { super.init(); factory =(SipFactory)getServletContext().getAttribute(\"javax.servlet.sip.SipFactory\"); SipFactory factory; } protected void doInvite(SipServletRequest req) { if(req.isInitial()){ public void init() sipApp = factory.createApplicationSession(); SipApplicationSession throws ServletException { TwitterSIPServletSession service = new TwitterSIPServletSession(req.getSession(), super.init(); factory); sipApp.setAttribute(\"router-app\", service); } factory = (SipFactory)getServletContext() service.init(req); } .getAttribute(\"javax.servlet.sip.SipFactory\"); } protected void doSuccessResponse(SipServletResponse resp) { resp.createAck().send(); } } protected void doAck(SipServletRequest req) { final TwitterSIPServletSession service = (TwitterSIPServletSession) req.getApplicationSession().getAttribute(\"router- app\"); service.startDialog(); } protected void doBye(SipServletRequest req) { // Need to send BYE request to the other legs } } 78
    79. Call router + Twitter public class TwitterSIPServlet extends SipServlet { SipFactory factory; public void init() throws ServletException { super.init(); factory =(SipFactory)getServletContext().getAttribute(\"javax.servlet.sip.SipFactory\"); } protected void doInvite(SipServletRequest req) { if(req.isInitial()){ SipApplicationSession sipApp = factory.createApplicationSession(); TwitterSIPServletSession service = new TwitterSIPServletSession(req.getSession(), factory); sipApp.setAttribute(\"router-app\", service); service.init(req); } } protected void doSuccessResponse(SipServletResponse resp) { resp.createAck().send(); } protected void doAck(SipServletRequest req) { final TwitterSIPServletSession service = (TwitterSIPServletSession) req.getApplicationSession().getAttribute(\"router- app\"); service.startDialog(); } protected void doBye(SipServletRequest req) { // Need to send BYE request to the other legs } } 79
    80. Call router + Twitter public class TwitterSIPServlet extends SipServlet { SipFactory factory; public void init() throws ServletException { super.init(); factory =(SipFactory)getServletContext().getAttribute(\"javax.servlet.sip.SipFactory\"); } protected void doInvite(SipServletRequest req) { protected void doInvite(SipServletRequest req) { if(req.isInitial()){ SipApplicationSession sipApp = factory.createApplicationSession(); if(req.isInitial()){ TwitterSIPServletSession service = new TwitterSIPServletSession(req.getSession(), factory); sipApp.setAttribute(\"router-app\", service); SipApplicationSession sipApp service.init(req); } = factory.createApplicationSession(); } TwitterSIPServletSession service protected void doSuccessResponse(SipServletResponse resp) { } = new TwitterSIPServletSession(req.getSession(), resp.createAck().send(); factory); sipApp.setAttribute(\"router-app\", service); protected void doAck(SipServletRequest req) { service.init(req); final TwitterSIPServletSession service = (TwitterSIPServletSession) req.getApplicationSession().getAttribute(\"router- } app\"); service.startDialog(); }} protected void doBye(SipServletRequest req) { // Need to send BYE request to the other legs } } 80
    81. Call router + Twitter public class TwitterSIPServlet extends SipServlet { SipFactory factory; public void init() throws ServletException { super.init(); factory =(SipFactory)getServletContext().getAttribute(\"javax.servlet.sip.SipFactory\"); } protected void doInvite(SipServletRequest req) { if(req.isInitial()){ SipApplicationSession sipApp = factory.createApplicationSession(); TwitterSIPServletSession service = new TwitterSIPServletSession(req.getSession(), factory); sipApp.setAttribute(\"router-app\", service); service.init(req); } } protected void doSuccessResponse(SipServletResponse resp) { resp.createAck().send(); } protected void doAck(SipServletRequest req) { final TwitterSIPServletSession service = (TwitterSIPServletSession) req.getApplicationSession().getAttribute(\"router- app\"); service.startDialog(); } protected void doBye(SipServletRequest req) { // Need to send BYE request to the other legs } } 81
    82. Call router + Twitter public class TwitterSIPServlet extends SipServlet { SipFactory factory; public void init() throws ServletException { super.init(); factory =(SipFactory)getServletContext().getAttribute(\"javax.servlet.sip.SipFactory\"); } protected void doInvite(SipServletRequest req) { if(req.isInitial()){ SipApplicationSession sipApp = factory.createApplicationSession(); TwitterSIPServletSession service = new TwitterSIPServletSession(req.getSession(), factory); protected void doSuccessResponse(SipServletResponse resp) { sipApp.setAttribute(\"router-app\", service); resp.createAck().send(); } service.init(req); }} protected void doSuccessResponse(SipServletResponse resp) { resp.createAck().send(); } protected void doAck(SipServletRequest req) { final TwitterSIPServletSession service = (TwitterSIPServletSession) req.getApplicationSession().getAttribute(\"router- app\"); service.startDialog(); } protected void doBye(SipServletRequest req) { // Need to send BYE request to the other legs } } 82
    83. Call router + Twitter public class TwitterSIPServlet extends SipServlet { SipFactory factory; public void init() throws ServletException { super.init(); factory =(SipFactory)getServletContext().getAttribute(\"javax.servlet.sip.SipFactory\"); } protected void doInvite(SipServletRequest req) { if(req.isInitial()){ SipApplicationSession sipApp = factory.createApplicationSession(); TwitterSIPServletSession service = new TwitterSIPServletSession(req.getSession(), factory); sipApp.setAttribute(\"router-app\", service); service.init(req); } } protected void doSuccessResponse(SipServletResponse resp) { resp.createAck().send(); } protected void doAck(SipServletRequest req) { final TwitterSIPServletSession service = (TwitterSIPServletSession) req.getApplicationSession().getAttribute(\"router- app\"); service.startDialog(); } protected void doBye(SipServletRequest req) { // Need to send BYE request to the other legs } } 83
    84. Call router + Twitter public class TwitterSIPServlet extends SipServlet { SipFactory factory; public void init() throws ServletException { super.init(); factory =(SipFactory)getServletContext().getAttribute(\"javax.servlet.sip.SipFactory\"); } protected void doAck(SipServletRequest req) { protected void doInvite(SipServletRequest req) { if(req.isInitial()){ final TwitterSIPServletSession service SipApplicationSession sipApp = factory.createApplicationSession(); TwitterSIPServletSession service = new TwitterSIPServletSession(req.getSession(), factory); = (TwitterSIPServletSession) req.getApplicationSession() sipApp.setAttribute(\"router-app\", service); service.init(req); .getAttribute(\"router-app\"); } } service.startDialog(); } resp.createAck().send(); protected void doSuccessResponse(SipServletResponse resp) { } protected void doAck(SipServletRequest req) { final TwitterSIPServletSession service = (TwitterSIPServletSession) req.getApplicationSession().getAttribute(\"router- app\"); service.startDialog(); } protected void doBye(SipServletRequest req) { // Need to send BYE request to the other legs } } 84
    85. Call router + Twitter public class TwitterSIPServlet extends SipServlet { SipFactory factory; public void init() throws ServletException { super.init(); factory =(SipFactory)getServletContext().getAttribute(\"javax.servlet.sip.SipFactory\"); } protected void doInvite(SipServletRequest req) { if(req.isInitial()){ SipApplicationSession sipApp = factory.createApplicationSession(); TwitterSIPServletSession service = new TwitterSIPServletSession(req.getSession(), factory); sipApp.setAttribute(\"router-app\", service); service.init(req); } } protected void doSuccessResponse(SipServletResponse resp) { resp.createAck().send(); } protected void doAck(SipServletRequest req) { final TwitterSIPServletSession service = (TwitterSIPServletSession) req.getApplicationSession().getAttribute(\"router- app\"); service.startDialog(); } protected void doBye(SipServletRequest req) { // Need to send BYE request to the other legs } } 85
    86. Call router + Twitter public class TwitterSIPServlet extends SipServlet { SipFactory factory; public void init() throws ServletException { super.init(); factory =(SipFactory)getServletContext().getAttribute(\"javax.servlet.sip.SipFactory\"); } protected void doInvite(SipServletRequest req) { if(req.isInitial()){ SipApplicationSession sipApp = factory.createApplicationSession(); TwitterSIPServletSession service = new TwitterSIPServletSession(req.getSession(), factory); protected void doBye(SipServletRequest req) { sipApp.setAttribute(\"router-app\", service); service.init(req);the frack has cleanup code in // Who } } // slide demo’s anyway? protected }void doSuccessResponse(SipServletResponse resp) { resp.createAck().send(); } protected void doAck(SipServletRequest req) { final TwitterSIPServletSession service = (TwitterSIPServletSession) req.getApplicationSession().getAttribute(\"router- app\"); service.startDialog(); } protected void doBye(SipServletRequest req) { // Need to send BYE request to the other legs } } 86
    87. Call router + Twitter public class TwitterSIPServlet extends SipServlet { SipFactory factory; public void init() throws ServletException { super.init(); factory =(SipFactory)getServletContext().getAttribute(\"javax.servlet.sip.SipFactory\"); } protected void doInvite(SipServletRequest req) { if(req.isInitial()){ SipApplicationSession sipApp = factory.createApplicationSession(); TwitterSIPServletSession service = new TwitterSIPServletSession(req.getSession(), factory); sipApp.setAttribute(\"router-app\", service); service.init(req); } } protected void doSuccessResponse(SipServletResponse resp) { resp.createAck().send(); } protected void doAck(SipServletRequest req) { final TwitterSIPServletSession service = (TwitterSIPServletSession) req.getApplicationSession().getAttribute(\"router- app\"); service.startDialog(); } protected void doBye(SipServletRequest req) { // Need to send BYE request to the other legs } } 87
    88. Call router + Twitter public class TwitterSIPServletSession { final SipSession mySipSession; final SipFactory myFactory; MediaGroup myMediaGroup; boolean isRJ = false; public TwitterSIPServletSession(SipSession aSipSession, SipFactory aFactory) { ... } public void init(final SipServletRequest req) { ... } public void startDialog() { ... } class CallerPlayerListener implements MediaEventListener<PlayerEvent> { ... } class RJDialerSignalDetectorListener implements MediaEventListener<SignalDetectorEvent> { ... } void transferDialog(String uri) throws ServletParseException, IOException { ... } } 88
    89. Call router + Twitter public class TwitterSIPServletSession { final SipSession mySipSession; final SipFactory myFactory; MediaGroup myMediaGroup; boolean isRJ = false; public TwitterSIPServletSession(SipSession aSipSession, SipFactory aFactory) { ... } public void init(final SipServletRequest req) { ... } public void startDialog() { ... } class CallerPlayerListener implements MediaEventListener<PlayerEvent> { ... } class RJDialerSignalDetectorListener implements MediaEventListener<SignalDetectorEvent> { ... } void transferDialog(String uri) throws ServletParseException, IOException { ... } } 89
    90. Call router + Twitter public class TwitterSIPServletSession { final SipSession mySipSession; final SipFactory myFactory; MediaGroup myMediaGroup; boolean isRJ = false; public TwitterSIPServletSession(SipSession aSipSession, SipFactory aFactory) { ... } public void init(final SipServletRequest req) { public class TwitterSIPServletSession ... { } final SipSession mySipSession; public void startDialog() { final SipFactory myFactory; ... } MediaGroup myMediaGroup; boolean isRJ = false; class CallerPlayerListener implements MediaEventListener<PlayerEvent> { ... } public TwitterSIPServletSession(SipSession aSipSession, class RJDialerSignalDetectorListener implements MediaEventListener<SignalDetectorEvent> { ... SipFactory aFactory) { } mySipSession = aSipSession; ... myFactory = aFactory; void transferDialog(String uri) throws ServletParseException, IOException { } } } } 90
    91. Call router + Twitter public class TwitterSIPServletSession { final SipSession mySipSession; final SipFactory myFactory; MediaGroup myMediaGroup; boolean isRJ = false; public TwitterSIPServletSession(SipSession aSipSession, SipFactory aFactory) { ... } public void init(final SipServletRequest req) { ... } public void startDialog() { ... } class CallerPlayerListener implements MediaEventListener<PlayerEvent> { ... } class RJDialerSignalDetectorListener implements MediaEventListener<SignalDetectorEvent> { ... } void transferDialog(String uri) throws ServletParseException, IOException { ... } } 91
    92. Call router + Twitter public class TwitterSIPServletSession { final SipSession mySipSession; final SipFactory myFactory; MediaGroup myMediaGroup; boolean isRJ = false; public TwitterSIPServletSession(SipSession aSipSession, SipFactory aFactory) { ... } public void init(final SipServletRequest req) throws ServletException { MediaSession myMediaSession = MediaSessionFactory.createMediaSession(); public void init(final SipServletRequest req) { ... } NetworkConnection myNetworkConnection = public void startDialog() { myMediaSession.createContainer(NetworkConnectionConfig.c_Basic); ... } myMediaGroup = myMediaSession.createContainer(MediaGroupConfig.c_PlayerSignalDetector); class CallerPlayerListener implements MediaEventListener<PlayerEvent> { ... } MediaEventListener<NetworkConnectionEvent> myNetworkConnectionListener = new MediaEventListener<NetworkConnectionEvent>() { class RJDialerSignalDetectorListener implements MediaEventListener<SignalDetectorEvent> { ... .... } }; myNetworkConnection.addListener(myNetworkConnectionListener); void transferDialog(String uri) throws ServletParseException, IOException { ... } myNetworkConnection.modify(\"\", new String(req.getRawContent())); } } 92
    93. Call router + Twitter public class TwitterSIPServletSession { final SipSession mySipSession; final SipFactory myFactory; MediaGroup myMediaGroup; boolean isRJ = false; public TwitterSIPServletSession(SipSession aSipSession, SipFactory aFactory) { ... } MediaEventListener<NetworkConnectionEvent> myNetworkConnectionListener = new MediaEventListener<NetworkConnectionEvent>() { public void init(final SipServletRequest req) { ... public void onEvent(NetworkConnectionEvent anEvent) { } if (NetworkConnectionConstants.ev_Modify.equals(anEvent.getEventID())) { public void (req.getFrom().getURI().getUser().equals(\"8312392883\")) { startDialog() { ... if } isRJ = true; myMediaGroup.getSignalDetector().addListener( new RJDialerSignalDetectorListener()); class CallerPlayerListener implements MediaEventListener<PlayerEvent> { ... } else { } myMediaGroup.getSignalDetector().addListener( new CallerPlayerListener()); class RJDialerSignalDetectorListener implements MediaEventListener<SignalDetectorEvent> { ... } } myMediaGroup.join(Direction.DUPLEX, myNetworkConnection); String sdpAnswer = myNetworkConnection.getRawLocalSessionDescription(); void transferDialog(String uri) throws ServletParseException, IOException { ... SipServletMessage msg = req.createResponse(200, \"OK\"); } msg.setContent(sdpAnswer.getBytes(), \"application/sdp\"); } msg.send(); } } }; 93
    94. Call router + Twitter public class TwitterSIPServletSession { final SipSession mySipSession; final SipFactory myFactory; MediaGroup myMediaGroup; boolean isRJ = false; public TwitterSIPServletSession(SipSession aSipSession, SipFactory aFactory) { ... } public void init(final SipServletRequest req) { ... } public void startDialog() { ... } class CallerPlayerListener implements MediaEventListener<PlayerEvent> { ... } class RJDialerSignalDetectorListener implements MediaEventListener<SignalDetectorEvent> { ... } void transferDialog(String uri) throws ServletParseException, IOException { ... } } 94
    95. Call router + Twitter public class TwitterSIPServletSession { final SipSession mySipSession; final SipFactory myFactory; MediaGroup myMediaGroup; public void startDialog() { boolean isRJ = false; if (isRJ) { Parameters collectOptions = mediaSessionFactory.createParameters(); public TwitterSIPServletSession(SipSession aSipSession, SipFactory aFactory) { ... prompt = URI.create(\"data:application/ssml+xml,\"+ URI } \"<?xml version=\\\"1.0\\\"?>\"+ public void init(final SipServletRequest req) { ... \"<speak>\"+ } \"Welcome RJ. Please enter the phone number you wish to reach.\"+ \"</speak>\"); public void startDialog() { collectOptions.put(SignalDetectorConstants.p_Prompt, prompt); ... } mg.getSignalDetector().receiveSignals(10, null, RTC.bargeIn, collectOptions); } else { class CallerPlayerListener twitter_apiMediaEventListener<PlayerEvent> { net.unto.twitter.Api implements ... = new net.unto.twitter.Api(\"zscgeek\", \"password\"); } URI prompt = URI.create(\"data:application/ssml+xml,\"+ class RJDialerSignalDetectorListener \"<?xml version=\\\"1.0\\\"?>\"+ implements MediaEventListener<SignalDetectorEvent> { ... \"<speak>\"+ } \"Thank you for calling RJ. We will connect you now.\"+ \"His twitter status is:\"+ void transferDialog(String uri) throws ServletParseException, IOException { ... twitter_api.getStatus()+ } \"</speak>\"); } myMediaGroup.getPlayer().play(prompt, null, null); } } 95
    96. Call router + Twitter public class TwitterSIPServletSession { final SipSession mySipSession; final SipFactory myFactory; MediaGroup myMediaGroup; boolean isRJ = false; public TwitterSIPServletSession(SipSession aSipSession, SipFactory aFactory) { ... } public void init(final SipServletRequest req) { ... } public void startDialog() { ... } class CallerPlayerListener implements MediaEventListener<PlayerEvent> { ... } class RJDialerSignalDetectorListener implements MediaEventListener<SignalDetectorEvent> { ... } void transferDialog(String uri) throws ServletParseException, IOException { ... } } 96
    97. Call router + Twitter public class TwitterSIPServletSession { final SipSession mySipSession; final SipFactory myFactory; MediaGroup myMediaGroup; boolean isRJ = false; public TwitterSIPServletSession(SipSession aSipSession, SipFactory aFactory) { ... } public void init(final SipServletRequest req) { ... } public void startDialog() { ... class CallerPlayerListener implements MediaEventListener<PlayerEvent> { } public void onEvent(PlayerEvent anEvent) { class CallerPlayerListener\"+anEvent.getSignalString()); log(\"Collected: implements MediaEventListener<PlayerEvent> { ... } MediaSession mediaSession = anEvent.getSource().getMediaSession().release(); class RJDialerSignalDetectorListener transferDialog(\"sip:+18315551234@gateway:5060\"); implements MediaEventListener<SignalDetectorEvent> { ... } } } void transferDialog(String uri) throws ServletParseException, IOException { ... } } 97
    98. Call router + Twitter public class TwitterSIPServletSession { final SipSession mySipSession; final SipFactory myFactory; MediaGroup myMediaGroup; boolean isRJ = false; public TwitterSIPServletSession(SipSession aSipSession, SipFactory aFactory) { ... } public void init(final SipServletRequest req) { ... } public void startDialog() { ... } class CallerPlayerListener implements MediaEventListener<PlayerEvent> { ... } class RJDialerSignalDetectorListener implements MediaEventListener<SignalDetectorEvent> { ... } void transferDialog(String uri) throws ServletParseException, IOException { ... } } 98
    99. Call router + Twitter public class TwitterSIPServletSession { final SipSession mySipSession; final SipFactory myFactory; MediaGroup myMediaGroup; boolean isRJ = false; public TwitterSIPServletSession(SipSession aSipSession, SipFactory aFactory) { ... } public void init(final SipServletRequest req) { ... } class RJDialerSignalDetectorListener implements MediaEventListener<SignalDetectorEvent> { public void startDialog() { ... } public void onEvent(SignalDetectorEvent anEvent) { log(\"Collected: \"+anEvent.getSignalString()); class CallerPlayerListener implements = MediaSession mediaSession MediaEventListener<PlayerEvent> { ... } anEvent.getSource().getMediaSession().release(); transferDialog(\"sip:\" + anEvent.getSignalString() class RJDialerSignalDetectorListener + \"@gateway:5060\"); implements MediaEventListener<SignalDetectorEvent> { ... } } } void transferDialog(String uri) throws ServletParseException, IOException { ... } } 99
    100. Call router + Twitter public class TwitterSIPServletSession { final SipSession mySipSession; final SipFactory myFactory; MediaGroup myMediaGroup; boolean isRJ = false; public TwitterSIPServletSession(SipSession aSipSession, SipFactory aFactory) { ... } public void init(final SipServletRequest req) { ... } public void startDialog() { ... } class CallerPlayerListener implements MediaEventListener<PlayerEvent> { ... } class RJDialerSignalDetectorListener implements MediaEventListener<SignalDetectorEvent> { ... } void transferDialog(String uri) throws ServletParseException, IOException { ... } } 100
    101. Call router + Twitter public class TwitterSIPServletSession { final SipSession mySipSession; final SipFactory myFactory; MediaGroup myMediaGroup; boolean isRJ = false; public TwitterSIPServletSession(SipSession aSipSession, SipFactory aFactory) { ... } public void init(final SipServletRequest req) { ... } void transferDialog(String uri) { public void startDialog() request = SipServletRequest { ... } myFactory.createRequest(mySipSession.getApplicationSession(), \"INVITE\", \"sip:addressbook@sip-as-uri:5060\", class CallerPlayerListener implements MediaEventListener<PlayerEvent> { ... } uri); request.send(); class RJDialerSignalDetectorListener } implements MediaEventListener<SignalDetectorEvent> { ... } void transferDialog(String uri) throws ServletParseException, IOException { ... } } 101
    102. Call router + Twitter public class TwitterSIPServletSession { final SipSession mySipSession; final SipFactory myFactory; MediaGroup myMediaGroup; boolean isRJ = false; public TwitterSIPServletSession(SipSession aSipSession, SipFactory aFactory) { ... } public void init(final SipServletRequest req) { ... } public void startDialog() { ... } class CallerPlayerListener implements MediaEventListener<PlayerEvent> { ... } class RJDialerSignalDetectorListener implements MediaEventListener<SignalDetectorEvent> { ... } void transferDialog(String uri) throws ServletParseException, IOException { ... } } 102
    103. Made it to the finish line! 103
    104. So. We Have Java... 104
    105. But is it Simple? 105
    106. Is it cool? 106
    107. Is It Web 2.0? 107
    108. Well Not Exactly... 108
    109. So... 109
    110. Tropo.com 110
    111. answer(); say(\"Hello, world!\"); hangup(); Tropo is Simple 111
    112. Ruby Telephony in YOUR Language (thanks to the magic of JSR223) 112
    113. •! answer •! redirect •! ask •! reject •! say •! log •! record •! wait •! call •! default •! transfer •! hangup Simple to Learn 113
    114. •Hosted service •Accessible via Phone, SIP, Skype etc •Inbound and Outbound calling •Free for developers •No setup costs •Five minutes from sign-up to live deployment + = GO Simple to Deploy 114
    115. What are the Ingredients? 115
    116. SIP Servlets (JSR289) SIPMethod
    117. SIP Servlets Media Control (JSR289) (JSR309) SIPMethod Prophecy
    118. SIP Servlets Media Control Scripting (JSR289) (JSR309) (JSR223) Rhino, Jython, SIPMethod Prophecy Jruby,Groovy, Quercus etc...
    119. Java SIP Servlets Media Control Scripting (JSR289) (JSR309) (JSR223) Rhino, Jython, SIPMethod Prophecy Jruby,Groovy, Quercus etc...
    120. Java SIP Servlets Media Control Scripting (JSR289) (JSR309) (JSR223) Rhino, Jython, SIPMethod Prophecy Jruby,Groovy, Quercus etc...
    121. Java SIP Servlets Media Control Scripting (JSR289) (JSR309) (JSR223) Rhino, Jython, SIPMethod Prophecy Jruby,Groovy, Quercus etc...
    122. Applications Java SIP Servlets Media Control Scripting (JSR289) (JSR309) (JSR223) Rhino, Jython, SIPMethod Prophecy Jruby,Groovy, Quercus etc...
    123. How about some code? 123
    124. T.1: Hello World JavaScript and PHP Ruby answer(); answer say(\"Hello, world!\"); say \"Hello, world!” hangup(); hangup Python Groovy answer() answer() say(\"Hello, world !\") say 'Hello, world!' hangup() hangup() 124
    125. Asking for Input - JavaScript // ----------- // asking for input // ----------- answer(); result=ask( \"Hi. For sales, press 1. For support, press 2.\", {choices:\"1, 2\"} ); if (result.name=='choice') { if (result.value==\"1\") { say( \"sales is not available right now.\") } if (result.value==\"2\") { say( \"support is currently on the other line.\" ) } } hangup(); 125
    126. Using ASR - Python # Using speech input instead of touch-tone answer() result = ask(\"Hi. For sales, say sales. For support, say support\", {'choices':\"sales, support\", 'repeat':3}) if (result.name == 'choice'): if (result.value == \"sales\"): say(\"Sales is not available right now\") if (result.value == \"support\"): say(\"Support is currently on the other line.\") hangup() 126
    127. Using ASR and DTMF - JavaScript answer(); result=ask( \"For sales, just say sales or press 1. For support, say support or press 2.\", { choices:\"sales( 1, sales), support( 2, support)\", repeat:3, onBadChoice: function() { say(\"I'm sorry, I didn't understand what you said.\") } } ); if (result.name=='choice') { if (result.value==\"sales\") { say( \"Ok, let me transfer you to sales.\" ); transfer( \"14075551111\"); } if (result.value==\"support\") { say( \"Sure, let me get support. Please hold.\" ); transfer( \"14085552222\"); } } 127
    128. How about a mashup? 128
    129. Sample Application Overview > Lets try the same application again in tropo... 129
    130. Twitter Example - Groovy answer() if (currentCall.callerID == \"8315551234\") { def event = ask(\"Welcome RJ. Please enter the phone number you wish\", [choices:\"[10 DIGITS]\",timeout:10]) if (event.name==\"choice\") { transfer(event.value) } else { say (\"too slow bro.\") hangup(); } } else { say(\"Thank you for calling RJ. We will connect you now.\") say(\"His twitter status is\") String xml = \"http://twitter.com/statuses/user_timeline.xml?screen_name=zscgeek\".toURL().text def strXML = new XmlParser().parseText(xml) say(strXML.statuses.status[0].text.text()) transfer(\"tel:+18315551234\") } 130
    131. Easy as Pie! 131
    132. So... 132
    133. Wrapping Up 133
    134. So why is this important? 134
    135. Quick Poll: Are you a phone developer? 135
    136. Phone Developers Web Developers Fact of Life 136
    137. Web 137
    138. Innovation 138
    139. Donʼt create phone applications... 139
    140. Instead create cool applications that use the phone... 140
    141. 141
    142. Love me? Hate me? Then say what you want about me... 142
    143. RJ Auburn rj@voxeo.com http://www.voxeo.com/free Tropo.com Script Based Telephony Java SIP Application Server XML Telephony 143

    + Voxeo CorpVoxeo Corp, 5 months ago

    custom

    1267 views, 2 favs, 5 embeds more stats

    Slides from my JavaOne talk on June 4th 2009 about more

    More info about this document

    © All Rights Reserved

    Go to text version

    • Total Views 1267
      • 1204 on SlideShare
      • 63 from embeds
    • Comments 0
    • Favorites 2
    • Downloads 58
    Most viewed embeds
    • 59 views on http://blogs.voxeo.com
    • 1 views on http://zestinfotech.com
    • 1 views on http://www.zestinfotech.com
    • 1 views on http://dotopen.omatest.com
    • 1 views on http://www.fachak.com

    more

    All embeds
    • 59 views on http://blogs.voxeo.com
    • 1 views on http://zestinfotech.com
    • 1 views on http://www.zestinfotech.com
    • 1 views on http://dotopen.omatest.com
    • 1 views on http://www.fachak.com

    less

    Flagged as inappropriate Flag as inappropriate
    Flag as inappropriate

    Select your reason for flagging this presentation as inappropriate. If needed, use the feedback form to let us know more details.

    Cancel
    File a copyright complaint
    Having problems? Go to our helpdesk?

    Categories