143
Taking a SIP of Java RJ Auburn Voxeo Corporation Chief Technology Officer

JavaOne - A Sip Of Java - RJ Auburn

Embed Size (px)

DESCRIPTION

Slides from my JavaOne talk on June 4th 2009 about developing telephony applications using Java technologies. Includes sample applications in XML, Java and scripting languages.

Citation preview

Page 1: JavaOne - A Sip Of Java - RJ Auburn

Taking a SIP of Java

RJ AuburnVoxeo Corporation Chief Technology Officer

Page 2: JavaOne - A Sip Of Java - RJ Auburn

Telephony2

Page 3: JavaOne - A Sip Of Java - RJ Auburn

Sucks3

Page 4: JavaOne - A Sip Of Java - RJ Auburn

Expensive4

Page 5: JavaOne - A Sip Of Java - RJ Auburn

Complex5

Page 6: JavaOne - A Sip Of Java - RJ Auburn

Proprietary6

Page 7: JavaOne - A Sip Of Java - RJ Auburn

This is not how it should be...

7

Page 8: JavaOne - A Sip Of Java - RJ Auburn

Simple 8

Page 9: JavaOne - A Sip Of Java - RJ Auburn

Ubiquitous9

Page 10: JavaOne - A Sip Of Java - RJ Auburn

Open10

Page 11: JavaOne - A Sip Of Java - RJ Auburn

So what are the layers?

11

Page 12: JavaOne - A Sip Of Java - RJ Auburn

Application

Platform

API XML Tools

12

Page 13: JavaOne - A Sip Of Java - RJ Auburn

ApplicationApplication

Platform

API XML Tools

13

Page 14: JavaOne - A Sip Of Java - RJ Auburn

Application

Platform

API XML Tools

14

Page 15: JavaOne - A Sip Of Java - RJ Auburn

Application

Platform

API XML Tools

15

Page 16: JavaOne - A Sip Of Java - RJ Auburn

Application

Platform

API XML Tools

16

Page 17: JavaOne - A Sip Of Java - RJ Auburn

Some Basics:Signaling vs Media

17

Page 18: JavaOne - A Sip Of Java - RJ Auburn

Call Control vs Media

18

> 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

Page 19: JavaOne - A Sip Of Java - RJ Auburn

Media vs Call Control in the PSTN - SIP

Alice Bob

ProxyA

ProxyBSIP

SIP

RTP

SIP

19

Page 20: JavaOne - A Sip Of Java - RJ Auburn

Media vs Call Control in the PSTN - SS7

Alice Bob

SCPA

SCPBSS7

SS7

Switched TDM Media

SS7

20

Page 21: JavaOne - A Sip Of Java - RJ Auburn

Media vs Call Control in the PSTN - ISDN

Dev B

B Channels (23)Media

21

DevA

D Channel (q.931)Signaling

Page 22: JavaOne - A Sip Of Java - RJ Auburn

All this means is the APIʼs and standards for media vs

call control tend to be different.

22

Page 23: JavaOne - A Sip Of Java - RJ Auburn

So now for a bit about SIP(Call Control)

23

Page 24: JavaOne - A Sip Of Java - RJ Auburn

SIP

> Session Initiation Protocol (SIP)defines how to establish a communication session betweentwo 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 HTTP24

Page 25: JavaOne - A Sip Of Java - RJ Auburn

SIP Communication

Alice Bob

RTP (voice)

INVITE

180 RINGING200 OK

ACK

BYE

200 OK

25

Page 26: JavaOne - A Sip Of Java - RJ Auburn

Major SIP Methods

26

> INVITE> BYE> REFER > REGISTER > SUBSCRIBE > NOTIFY

oh ya...

> INFO (wait, we are not supposed to talk about this one)

Page 27: JavaOne - A Sip Of Java - RJ Auburn

Response Codes

> 1xx - Provisional > 2xx - All is good. Final > 3xx - Redirects > 4/5/6xx - Errors

27

Page 28: JavaOne - A Sip Of Java - RJ Auburn

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

Page 29: JavaOne - A Sip Of Java - RJ Auburn

So lets talk about...

29

Page 30: JavaOne - A Sip Of Java - RJ Auburn

Religion30

Page 31: JavaOne - A Sip Of Java - RJ Auburn

Religion31

Page 32: JavaOne - A Sip Of Java - RJ Auburn

XML32

Page 33: JavaOne - A Sip Of Java - RJ Auburn

33

VoiceXML and CCXML

Page 34: JavaOne - A Sip Of Java - RJ Auburn

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

Page 35: JavaOne - A Sip Of Java - RJ Auburn

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

Page 36: JavaOne - A Sip Of Java - RJ Auburn

36

VoiceXML and CCXML Architecture

CCXML VoiceXMLPhone

ASR

TTS

sip sipmrcp

rtp

Page 37: JavaOne - A Sip Of Java - RJ Auburn

Like Tribbles...37

Page 38: JavaOne - A Sip Of Java - RJ Auburn

It has taken over the Enterprise...38

Page 39: JavaOne - A Sip Of Java - RJ Auburn

How about some code?

39

Page 40: JavaOne - A Sip Of Java - RJ Auburn

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

Page 41: JavaOne - A Sip Of Java - RJ Auburn

isRJ()

start

play twitter status

call rj’s cell

prompt for dest

call dest

end

yes no

Page 42: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

42

Page 43: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

43

Page 44: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

44

<?xml version="1.0" encoding="UTF-8"?><ccxml version="1.0" xmlns="http://www.w3.org/2002/09/ccxml"></ccxml>

Page 45: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

45

Page 46: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

46

<var name="state" expr="'init'"/><var name="incomingcall"/><eventprocessor statevariable="state"></eventprocessor>

Page 47: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

47

Page 48: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

48

<transition event="connection.alerting" state="init"> <assign name="incomingcall" expr="event$.connectionid"/> <accept/></transition>

Page 49: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

49

Page 50: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

50

<transition event="connection.connected" state="init" cond="event$.connection.remote=='tel:+18315551234'"> <assign name="state" expr="'rjconnected'"/> <dialogstart src="'rj.vxml'"</transition>

Page 51: JavaOne - A Sip Of Java - RJ Auburn

<?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:[email protected]/statuses/update.xml'"/> <eventprocessor statevariable="state"> <transition event="connection.alerting" state="init"> <accept/> </transition> <transition event="connection.connected" state="init"> <assign name="state" expr="'calling'"/> <assign name="incomingcall" expr="event$.connectionid"/> <createcall dest="'tel:+18312392883'"/> </transition> <transition event="connection.connected" state="calling"> <assign name="state" expr="'connected'"/> <join id1="event$.connectionid" id2="incomingcall"/> <var name="status" expr="'RJ is on the phone'"/> <send targettype="'basichttp'" name="'update'" target="tURL" namelist="status"/> </transition> <transition event="connection.failed" state="calling"> <assign name="state" expr="'done'"/> <var name="status" expr="'RJ is not answering his phone'"/> <send targettype="'basichttp'" name="'update'" target="tURL" namelist="status"/> </transition> <transition event="connection.disconnected" state="connected"> <assign name="state" expr="'done'"/> <var name="status" expr="'RJ is off the phone'"/> <send targettype="'basichttp'" name="'update'" target="tURL" namelist="status"/> </transition> <transition event="send.successful" state="done"> <exit/> </transition> </eventprocessor></ccxml>

Call router + Twitter

51

<?xml version="1.0" encoding="UTF-8"?><vxml xmlns="http://www.w3.org/2001/vxml" version="2.1"> <form> <field name="dest" type="digits?length=10"> <prompt>Welcome RJ. Please enter the phone number you wish to reach.</prompt> <filled> <exit namelist="dest"/> </filled> </field> </form></vxml>

Page 52: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

52

Page 53: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

53

<transition event="connection.connected" state="init"> <assign name="state" expr="'callconnected'"/> <dialogstart src="'caller.vxml'"</transition>

Page 54: JavaOne - A Sip Of Java - RJ Auburn

<?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:[email protected]/statuses/update.xml'"/> <eventprocessor statevariable="state"> <transition event="connection.alerting" state="init"> <accept/> </transition> <transition event="connection.connected" state="init"> <assign name="state" expr="'calling'"/> <assign name="incomingcall" expr="event$.connectionid"/> <createcall dest="'tel:+18312392883'"/> </transition> <transition event="connection.connected" state="calling"> <assign name="state" expr="'connected'"/> <join id1="event$.connectionid" id2="incomingcall"/> <var name="status" expr="'RJ is on the phone'"/> <send targettype="'basichttp'" name="'update'" target="tURL" namelist="status"/> </transition> <transition event="connection.failed" state="calling"> <assign name="state" expr="'done'"/> <var name="status" expr="'RJ is not answering his phone'"/> <send targettype="'basichttp'" name="'update'" target="tURL" namelist="status"/> </transition> <transition event="connection.disconnected" state="connected"> <assign name="state" expr="'done'"/> <var name="status" expr="'RJ is off the phone'"/> <send targettype="'basichttp'" name="'update'" target="tURL" namelist="status"/> </transition> <transition event="send.successful" state="done"> <exit/> </transition> </eventprocessor></ccxml>

Call router + Twitter

54

<?xml version="1.0" encoding="UTF-8"?><vxml xmlns="http://www.w3.org/2001/vxml" version="2.1"> <form> <block> <data src="http://twitter.com/statuses/user_timeline.xml?screen_name=zscgeek" name="twitter" ecmaxmltype="e4x" /> <prompt>Thank you for calling RJ. We will connect you now. His twitter status is <value expr="twitter.statuses.status[0].text"/> </prompt> </block> </form></vxml>

Page 55: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

55

Page 56: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

56

<transition event="dialog.exit" state="callconnected"> <assign name="state" expr="'calling'"/> <createcall dest="'tel:+18315551234'"/></transition>

Page 57: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

57

Page 58: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

58

<transition event="dialog.exit" state="rjconnected"> <assign name="state" expr="'calling'"/> <createcall dest="event$.values.dest"/></transition>

Page 59: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

59

Page 60: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

60

<transition event="connection.connected" state="calling"> <assign name="state" expr="'connected'"/> <join id1="event$.connectionid" id2="incomingcall"/></transition>

Page 61: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

61

Page 62: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

62

<transition event="connection.failed" state="calling"> <exit/></transition><transition event="connection.disconnected" state="connected"> <exit/></transition>

Page 63: JavaOne - A Sip Of Java - RJ Auburn

<?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>

Call router + Twitter

63

Page 64: JavaOne - A Sip Of Java - RJ Auburn

So... How about APIʼs...

64

Page 65: JavaOne - A Sip Of Java - RJ Auburn

A Favorite of Carriers65

Page 66: JavaOne - A Sip Of Java - RJ Auburn

Java66

Page 67: JavaOne - A Sip Of Java - RJ Auburn

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

Page 68: JavaOne - A Sip Of Java - RJ Auburn

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

Page 69: JavaOne - A Sip Of Java - RJ Auburn

Response Methods

> doProvisionalResponse(SipServletResponse res);> doSuccessResponse(SipServletResponse res);> doRedirectResponse(SipServletResponse res);> doErrorResponse(SipServletResponse res);

69

Page 70: JavaOne - A Sip Of Java - RJ Auburn

Basic Request

70

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(); }}

Page 71: JavaOne - A Sip Of Java - RJ Auburn

Accessing SIP Headers

71

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"); }}

Page 72: JavaOne - A Sip Of Java - RJ Auburn

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

Page 73: JavaOne - A Sip Of Java - RJ Auburn

Sample Application Overview

> Lets try that application again in java...

73

Page 74: JavaOne - A Sip Of Java - RJ Auburn

isRJ()

start

play twitter status

call rj’s cell

prompt for dest

call dest

end

yes no

Page 75: JavaOne - A Sip Of Java - RJ Auburn

SIP Flow

Caller AppINVITE - doInvite()

200 OK

ACK doAck()

75

Dest

INVITE - transferDialog()

200 OK - doSuccessResponse()

ACK

Page 76: JavaOne - A Sip Of Java - RJ Auburn

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 }}

Call router + Twitter

76

Page 77: JavaOne - A Sip Of Java - RJ Auburn

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 }}

Call router + Twitter

77

Page 78: JavaOne - A Sip Of Java - RJ Auburn

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 }}

Call router + Twitter

78

public class TwitterSIPServlet extends SipServlet { SipFactory factory;

public void init() throws ServletException { super.init(); factory = (SipFactory)getServletContext() .getAttribute("javax.servlet.sip.SipFactory"); }}

Page 79: JavaOne - A Sip Of Java - RJ Auburn

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 }}

Call router + Twitter

79

Page 80: JavaOne - A Sip Of Java - RJ Auburn

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 }}

Call router + Twitter

80

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); } }

Page 81: JavaOne - A Sip Of Java - RJ Auburn

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 }}

Call router + Twitter

81

Page 82: JavaOne - A Sip Of Java - RJ Auburn

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 }}

Call router + Twitter

82

protected void doSuccessResponse(SipServletResponse resp) { resp.createAck().send(); }

Page 83: JavaOne - A Sip Of Java - RJ Auburn

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 }}

Call router + Twitter

83

Page 84: JavaOne - A Sip Of Java - RJ Auburn

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 }}

Call router + Twitter

84

protected void doAck(SipServletRequest req) { final TwitterSIPServletSession service = (TwitterSIPServletSession) req.getApplicationSession() .getAttribute("router-app"); service.startDialog(); }

Page 85: JavaOne - A Sip Of Java - RJ Auburn

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 }}

Call router + Twitter

85

Page 86: JavaOne - A Sip Of Java - RJ Auburn

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 }}

Call router + Twitter

86

protected void doBye(SipServletRequest req) { // Who the frack has cleanup code in // slide demo’s anyway? }

Page 87: JavaOne - A Sip Of Java - RJ Auburn

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 }}

Call router + Twitter

87

Page 88: JavaOne - A Sip Of Java - RJ Auburn

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 { ... }}

Call router + Twitter

88

Page 89: JavaOne - A Sip Of Java - RJ Auburn

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 { ... }}

Call router + Twitter

89

Page 90: JavaOne - A Sip Of Java - RJ Auburn

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 { ... }}

Call router + Twitter

90

public class TwitterSIPServletSession { final SipSession mySipSession; final SipFactory myFactory; MediaGroup myMediaGroup; boolean isRJ = false;

public TwitterSIPServletSession(SipSession aSipSession, SipFactory aFactory) { mySipSession = aSipSession; myFactory = aFactory; }}

Page 91: JavaOne - A Sip Of Java - RJ Auburn

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 { ... }}

Call router + Twitter

91

Page 92: JavaOne - A Sip Of Java - RJ Auburn

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 { ... }}

Call router + Twitter

92

public void init(final SipServletRequest req) throws ServletException { MediaSession myMediaSession = MediaSessionFactory.createMediaSession();

NetworkConnection myNetworkConnection = myMediaSession.createContainer(NetworkConnectionConfig.c_Basic);

myMediaGroup = myMediaSession.createContainer(MediaGroupConfig.c_PlayerSignalDetector);

MediaEventListener<NetworkConnectionEvent> myNetworkConnectionListener = new MediaEventListener<NetworkConnectionEvent>() { .... }; myNetworkConnection.addListener(myNetworkConnectionListener);

myNetworkConnection.modify("", new String(req.getRawContent()));}

Page 93: JavaOne - A Sip Of Java - RJ Auburn

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 { ... }}

Call router + Twitter

93

MediaEventListener<NetworkConnectionEvent> myNetworkConnectionListener = new MediaEventListener<NetworkConnectionEvent>() { public void onEvent(NetworkConnectionEvent anEvent) { if (NetworkConnectionConstants.ev_Modify.equals(anEvent.getEventID())) { if (req.getFrom().getURI().getUser().equals("8312392883")) { isRJ = true; myMediaGroup.getSignalDetector().addListener( new RJDialerSignalDetectorListener()); } else { myMediaGroup.getSignalDetector().addListener( new CallerPlayerListener()); } myMediaGroup.join(Direction.DUPLEX, myNetworkConnection); String sdpAnswer = myNetworkConnection.getRawLocalSessionDescription(); SipServletMessage msg = req.createResponse(200, "OK"); msg.setContent(sdpAnswer.getBytes(), "application/sdp"); msg.send(); } }};

Page 94: JavaOne - A Sip Of Java - RJ Auburn

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 { ... }}

Call router + Twitter

94

Page 95: JavaOne - A Sip Of Java - RJ Auburn

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 { ... }}

Call router + Twitter

95

public void startDialog() { if (isRJ) { Parameters collectOptions = mediaSessionFactory.createParameters(); URI prompt = URI.create("data:application/ssml+xml,"+ "<?xml version=\"1.0\"?>"+ "<speak>"+ "Welcome RJ. Please enter the phone number you wish to reach."+ "</speak>"); collectOptions.put(SignalDetectorConstants.p_Prompt, prompt); mg.getSignalDetector().receiveSignals(10, null, RTC.bargeIn, collectOptions); } else { net.unto.twitter.Api twitter_api = new net.unto.twitter.Api("zscgeek", "password"); URI prompt = URI.create("data:application/ssml+xml,"+ "<?xml version=\"1.0\"?>"+ "<speak>"+ "Thank you for calling RJ. We will connect you now."+ "His twitter status is:"+ twitter_api.getStatus()+ "</speak>"); myMediaGroup.getPlayer().play(prompt, null, null); }}

Page 96: JavaOne - A Sip Of Java - RJ Auburn

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 { ... }}

Call router + Twitter

96

Page 97: JavaOne - A Sip Of Java - RJ Auburn

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 { ... }}

Call router + Twitter

97

class CallerPlayerListener implements MediaEventListener<PlayerEvent> { public void onEvent(PlayerEvent anEvent) { log("Collected: "+anEvent.getSignalString()); MediaSession mediaSession = anEvent.getSource().getMediaSession().release(); transferDialog("sip:+18315551234@gateway:5060"); }}

Page 98: JavaOne - A Sip Of Java - RJ Auburn

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 { ... }}

Call router + Twitter

98

Page 99: JavaOne - A Sip Of Java - RJ Auburn

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 { ... }}

Call router + Twitter

99

class RJDialerSignalDetectorListener implements MediaEventListener<SignalDetectorEvent> { public void onEvent(SignalDetectorEvent anEvent) { log("Collected: "+anEvent.getSignalString()); MediaSession mediaSession = anEvent.getSource().getMediaSession().release(); transferDialog("sip:" + anEvent.getSignalString() + "@gateway:5060"); }}

Page 100: JavaOne - A Sip Of Java - RJ Auburn

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 { ... }}

Call router + Twitter

100

Page 101: JavaOne - A Sip Of Java - RJ Auburn

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 { ... }}

Call router + Twitter

101

void transferDialog(String uri) { SipServletRequest request = myFactory.createRequest(mySipSession.getApplicationSession(), "INVITE", "sip:addressbook@sip-as-uri:5060", uri); request.send();}

Page 102: JavaOne - A Sip Of Java - RJ Auburn

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 { ... }}

Call router + Twitter

102

Page 103: JavaOne - A Sip Of Java - RJ Auburn

Made it to the finish line!103

Page 104: JavaOne - A Sip Of Java - RJ Auburn

So. We Have Java...104

Page 105: JavaOne - A Sip Of Java - RJ Auburn

But is it Simple?105

Page 106: JavaOne - A Sip Of Java - RJ Auburn

Is it cool?106

Page 107: JavaOne - A Sip Of Java - RJ Auburn

Is It Web 2.0?107

Page 108: JavaOne - A Sip Of Java - RJ Auburn

Well Not Exactly...

108

Page 109: JavaOne - A Sip Of Java - RJ Auburn

So...

109

Page 110: JavaOne - A Sip Of Java - RJ Auburn

Tropo.com

110

Page 111: JavaOne - A Sip Of Java - RJ Auburn

answer();say("Hello, world!");

hangup();

Tropo is Simple111

Page 112: JavaOne - A Sip Of Java - RJ Auburn

Ruby

Telephony in YOUR Language(thanks to the magic of JSR223)

112

Page 113: JavaOne - A Sip Of Java - RJ Auburn

Simple to Learn

•! answer

•! redirect

•! reject

•! call

•! transfer

•! hangup

•! ask

•! say

•! record

•! log

•! wait

•! default

113

Page 114: JavaOne - A Sip Of Java - RJ Auburn

Simple to Deploy

•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

114

+ = GO

Page 115: JavaOne - A Sip Of Java - RJ Auburn

What are the Ingredients?

115

Page 116: JavaOne - A Sip Of Java - RJ Auburn

SIP Servlets(JSR289)

SIPMethod

Page 117: JavaOne - A Sip Of Java - RJ Auburn

SIP Servlets(JSR289)

Media Control(JSR309)

SIPMethod Prophecy

Page 118: JavaOne - A Sip Of Java - RJ Auburn

SIP Servlets(JSR289)

Media Control(JSR309)

SIPMethod Prophecy

Scripting(JSR223)Rhino, Jython, Jruby,Groovy, Quercus etc...

Page 119: JavaOne - A Sip Of Java - RJ Auburn

SIP Servlets(JSR289)

Media Control(JSR309)

SIPMethod Prophecy

Scripting(JSR223)Rhino, Jython, Jruby,Groovy, Quercus etc...

Java

Page 120: JavaOne - A Sip Of Java - RJ Auburn

SIP Servlets(JSR289)

Media Control(JSR309)

SIPMethod Prophecy

Scripting(JSR223)Rhino, Jython, Jruby,Groovy, Quercus etc...

Java

Page 121: JavaOne - A Sip Of Java - RJ Auburn

SIP Servlets(JSR289)

Media Control(JSR309)

SIPMethod Prophecy

Scripting(JSR223)Rhino, Jython, Jruby,Groovy, Quercus etc...

Java

Page 122: JavaOne - A Sip Of Java - RJ Auburn

SIP Servlets(JSR289)

Media Control(JSR309)

SIPMethod Prophecy

Scripting(JSR223)Rhino, Jython, Jruby,Groovy, Quercus etc...

Applications

Java

Page 123: JavaOne - A Sip Of Java - RJ Auburn

How about some code?123

Page 124: JavaOne - A Sip Of Java - RJ Auburn

T.1: Hello World

JavaScript and PHP

answer();

say("Hello, world!");

hangup();

Ruby

answer

say "Hello, world!”

hangup

Groovy

answer()

say 'Hello, world!'

hangup()

Python

answer()

say("Hello, world !")

hangup()

124

Page 125: JavaOne - A Sip Of Java - RJ Auburn

// -----------// 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();

Asking for Input - JavaScript

125

Page 126: JavaOne - A Sip Of Java - RJ Auburn

# 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()

Using ASR - Python

126

Page 127: JavaOne - A Sip Of Java - RJ Auburn

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"); }}

Using ASR and DTMF - JavaScript

127

Page 128: JavaOne - A Sip Of Java - RJ Auburn

How about a mashup?

128

Page 129: JavaOne - A Sip Of Java - RJ Auburn

Sample Application Overview

> Lets try the same application again in tropo...

129

Page 130: JavaOne - A Sip Of Java - RJ Auburn

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")}

Twitter Example - Groovy

130

Page 131: JavaOne - A Sip Of Java - RJ Auburn

131

Easy as Pie!

Page 132: JavaOne - A Sip Of Java - RJ Auburn

So...

132

Page 133: JavaOne - A Sip Of Java - RJ Auburn

Wrapping Up133

Page 134: JavaOne - A Sip Of Java - RJ Auburn

So why is this important?134

Page 135: JavaOne - A Sip Of Java - RJ Auburn

Quick Poll: Are you a phone developer? 135

Page 136: JavaOne - A Sip Of Java - RJ Auburn

Fact of Life

Phone Developers Web Developers

136

Page 137: JavaOne - A Sip Of Java - RJ Auburn

Web137

Page 138: JavaOne - A Sip Of Java - RJ Auburn

Innovation 138

Page 139: JavaOne - A Sip Of Java - RJ Auburn

Donʼt create phone applications...139

Page 140: JavaOne - A Sip Of Java - RJ Auburn

Instead create cool applications

that use the phone...

140

Page 141: JavaOne - A Sip Of Java - RJ Auburn

141

Page 142: JavaOne - A Sip Of Java - RJ Auburn

Love me? Hate me? Then say what you want about me...

142

Page 143: JavaOne - A Sip Of Java - RJ Auburn

143

Tropo.comJava SIP Application Server

Script Based Telephony

XML Telephony

RJ [email protected]

http://www.voxeo.com/free