Chapter 8. Practice with SIP

In this chapter, we start putting into practice some of the SIP concepts learned so far. We will show some very simple programming examples that illustrate how SIP works. As was stated in previous chapters, the purpose of this book is not to teach the reader how to program commercial SIP applications. This book’s goal is to enable the reader to understand SIP-based multimedia communication. We believe that letting readers “play” with practical programming examples using a protocol-level API may help them to consolidate the learning about SIP. In order to understand the next examples, only a basic understanding of the Java language is required. Actually, we believe that the complexity of managing low-level SIP APIs such as JAIN SIP or SIP servlets does not stem from the complexity of the Java aspects, but rather, from the complexity of the underlying protocol. During the past few chapters, the reader has, we hope, obtained a sound understanding of what SIP is and how it works—so this chapter should be easy to follow, even for people with little programming experience. We invite even those readers without programming experience to have a look at the code snippets and identify the SIP concepts interleaved between the Java artifacts.

The chapter begins by reviewing the concepts around the Java Event model. Then we describe the general architecture of the JAIN SIP API and highlight its main interfaces and classes. We will then construct very simple programming examples to illustrate basic SIP concepts. First, we will show you how to set up the JAIN SIP environment; then we will learn how to build SIP messages. After that, we will learn how to use the SIP transport sublayer in order to send and receive messages statelessly. The next step will be to use the transaction sublayer, and last we will practice with the SIP dialog concept.

After this chapter has been completed, the reader should have the necessary tools to start thinking of building more-complex applications.

What Is JAIN SIP?

JAIN SIP is a Java API specification for the Session Initiation Protocol. It is developed for the J2SE environment, and provides application developers with a standardized interface for SIP services that are functionally compatible with the RFC 3261 specification. More specifically, JAIN SIP API mainly provides the application developer with an interface to:

  • Build and parse SIP messages.

  • Use the transaction sublayer (i.e., send/receive messages statefully).

  • Use the transport sublayer (i.e., send/receive messages statelessly).

In addition to that, the interface also provides access to SIP dialog functionality. The dialog functionality eases the task of writing applications that need to handle SIP dialogs. For example, SIP User Agents or SIP Back-to-Back User Agents are classical applications that require extensive management of SIP dialogs.

The JAIN SIP API can be seen as an interface to an implementation of SIP. In the industry jargon, a protocol implementation is commonly referred to as a SIP stack.[1] A SIP stack may consist just of Java code, or it may be written in other languages—in which case there is still the need for some Java wrapper classes that implement the JAIN SIP interfaces on one hand, and connect to the non-Java code on the other.[2] The application programmer does not need to bother with how the underlying protocol implementation is done. He or she will just “see” the Java interface in any case. Moreover if he or she is dissatisfied with the vendor that provides the protocol implementation, the application programmer could even replace it without the need to rewrite his or her application.

JAIN SIP is a protocol API—that is to say, it is a low-level API. An interesting aspect about a protocol API is that there are no limitations as to the type of SIP applications that may be developed using the interface. For instance, we could develop a SIP UA, a proxy, a B2BUA, and so forth. On the other hand, when using a protocol-level API, the programmer has to basically implement all the SIP core sublayer logic by himself or herself. Therefore, the JAIN SIP API is very good for helping us to better understand how the SIP protocol works, but we would recommend that readers use a higher-level interface if they want to build a quick-to-market and complex application.

Figure 8.1 shows the layering model for the SIP protocol that we learned in Chapter 7, and highlights how it maps to the functionality provided by JAIN SIP. The JAIN SIP API offers access to the functionality in the transport, transaction, and dialog sublayer, plus only partial access to the SIP core sublayer. Most of the functionality in the SIP core sublayer has to be implemented by programmers themselves in applications sitting on top of JAIN SIP.

Figure 8.1. 

JAIN SIP Versions

To date, there have been three versions of the JAIN SIP API. The first one (1.0) was based on SIP spec RFC 2543. As we already know, that SIP specification was replaced by RFC 3261. So, a newer version (1.1) of the JAIN SIP API, which had compliancy for RFC 3261, was developed. The latest JAIN SIP version is 1.2. It incorporates some enhancements to the 1.1 specification, and it is the one that we will use in this book. Table 8.1 shows some of the main differences between the three JAIN SIP specifications.

Table 8.1. 

JAIN SIP 1.0

JAIN SIP 1.1

JAIN SIP 1.2

Supports RFC 2543

Supports RFC 3261 and some SIP extensions

Supports RFC 3261 plus additional extensions

No transaction interface

Transaction interface defined

Transaction interface enhanced

No explicit dialog support

Dialog object is added

Dialog support is enhanced

No possibility to configure the protocol implementation

Added SipStack properties to configure the protocol implementation

SipStack properties are extended

Version 1.2 of the JAIN SIP specification complies with the base SIP specification defined in RFC 3261 and with the following SIP extensions:

We have not yet looked at SIP extensions. These and other SIP extensions will be explained in the third part of this book. In this chapter, we will use only the basic SIP functionality that JAIN SIP provides.

JAIN SIP Architecture

The JAIN SIP architecture is based on a number of patterns:

  • Peer-provider pattern

  • Factory pattern

  • Event-listener pattern

The main interfaces and classes in the JAIN SIP API are shown in the following UML class diagram in Figure 8.2.

Figure 8.2. 

The Peer-Provider Pattern

A peer is a platform-specific implementation of a Java API. In the case of JAIN SIP, the peer corresponds to a particular vendor’s SIP protocol stack—that is, the actual software that implements SIP. We have represented it with some pieces of machinery to illustrate this fact.

A provider provides functions using the platform-specific capabilities of the peer with which it is associated. In the case of JAIN SIP, the provider allows applications to send and receive SIP messages. It is represented by a Java interface called SipProvider.

In addition to SipProvider, there are other two interfaces that also provide access to some aspects of the underlying SIP implementation:

  • the SipStack interface

  • the ListeningPoint interface

The SipStack interface allows programmers to manage and configure the underlying SIP stack. For instance, it includes methods to start or stop the SIP stack. The SIP stack configuration is specified through a set of SipStack Java properties that define aspects such as stack name, outbound proxy, automatic dialog support, and so on. The SipStack interface also contains operations to create instances of SipProvider and ListeningPoint.

The other interface to the SIP stack functionality is the ListeningPoint. A ListeningPoint is a Java representation of the socket that a SipProvider messaging entity uses to send and receive messages. The underlying SIP stack may use several sockets for communicating with the network. The ListeningPoint interface allows the application to set and get the transport parameters (IP, port, transport) corresponding to a particular ListeningPoint instance (i.e., socket).

Therefore, the main three interfaces toward a SIP stack are the SipProvider, the SipStack, and the ListeningPoint, each of them exposing different aspects of the underlying implementation. This is shown in Figure 8.3.

Figure 8.3. 

The Factory Pattern

The application developer that wants to build a SIP application needs to gain access to the peer—that is, to the underlying SIP software machinery. In other words, the application needs to create SIP objects that encapsulate the SIP functionality. The application does this via a factory. A factory is an intermediary that encapsulates the method for accessing the SIP peer, and allows the application to obtain instances of the peer implementation classes. This is a common Object-Orientated design pattern that, in this case, maximizes the decoupling between the application and the peer implementation. For instance, our application is shielded from changes in the names of the implementation classes because it will always obtain the instances from the factory in the same way. One advantage of this decoupling is that the application will work transparently with different peer implementations. This is shown in Figure 8.4.

Figure 8.4. 

The factory class in JAIN SIP is called SipFactory. The factory is a singleton class—that is, there is only a single instance of it in the entire system. The application will get the instance of the factory and then invoke its methods, which create instances of the implementation classes.

Not all the JAIN SIP peer objects are obtained through the SipFactory. There are other objects that are obtained through the following factories also provided by JAIN SIP:

  • the MessageFactory

  • the AddressFactory

  • the HeaderFactory

These factories allow the developer to create Java objects that represent SIP messages, SIP addresses, and SIP headers, respectively.

The programmer can get an instance of these factories by invoking factory-creation methods on the SipFactory.

The Event-Listener Pattern

This pattern represents occurrences of interest as events. Each occurrence is represented by a Java object. The mechanism for processing events is defined by an event-listener interface that includes a “processEvent” operation for each type of event. When the SIP stack receives SIP messages (requests and responses) from the network, the SipProvider passes them as events on to the event listener, which is called SipListener in the JAIN SIP case. This situation is depicted in Figure 8.5.

Figure 8.5. 

The application developer is responsible for implementing the methods in the SipListener interface. These methods contain the code that processes the events fired by the SipProvider. The application on top of the JAIN SIP API will include an object that implements the SipListener interface. A SipListener has to register with the SipProvider for the reception of SIP events. Once registered, as soon as SIP messages arrive at the SIP stack, the SIP provider will invoke the corresponding “processEvent” method in SipListener.

The SIP events represent incoming messages from the network to the SIP stack. There are two types of events (Figure 8.6):

  • RequestEvent: Represents a SIP request received from the network.

  • ResponseEvent: Represents a SIP response received from the network.

Figure 8.6. 

The SipStack, SipProvider and ListeningPoint

Let us now look a bit more in detail at the way these classes and interfaces are used. As we said, first of all, the application needs to get a reference to an instance of the SipFactory. We will use the static getInstance() method for that. Then we need to get an instance of SipStack, by invoking the createSipStack() method on the SipFactory. This method takes a Properties object as an argument that contains the configuration for the SIP implementation (e.g, stack name, retransmission behavior, etc.). Table 8.2 shows the methods in SipFactory.

Table 8.2. 

 

SipFactory Methods

Description

AddressFactory

createAddressFactory()

Creates an instance of the AddressFactory implementation.

HeaderFactory

createHeaderFactory()

Creates an instance of the HeaderFactory implementation.

MessageFactory

createMessageFactory()

Creates an instance of the MessageFactory implementation.

SipStack

createSipStack (Properties properties)

Creates an instance of a SipStack implementation based on the configuration properties object passed to this method.

SipFactory

getInstance()

Returns an instance of a SipFactory.

String

getPathName()

Returns the current pathname of the SipFactory.

Void

resetFactory()

Resets the SipFactory’s references to the objects it has created.

Void

setPathName (String pathname)

Sets the pathname that identifies the location of a particular vendor’s implementation of this specification.

The next step is to create a ListeningPoint by invoking createListeningpoint() on the SipStack instance. The ListeningPoint represents the IP address, port, and transport that a SipProvider messaging entity will use to send and receive SIP messages, so in the creation method, the application will need to pass the “port” and “transport” parameters. Once the ListeningPoint is created, the application will create a SipProvider messaging entity by calling createSipProvider() on the SipStack instance. It uses the ListeningPoint previously created as an argument for the creation method. Table 8.3 shows the main methods in the SipStack interface, and Figure 8.7 is a sequence diagram that shows the creation of the relevant objects.

Table 8.3. 

 

SipStack Methods

Description

ListeningPoint

createListeningPoint (String ipAddress, int port, String transport)

Creates a ListeningPoint at a given IP address, port, and transport.

SipProvider

createSipProvider (ListeningPoint listeningPoint)

Creates a new peer SipProvider on the specified ListeningPoint.

Void

start()

Initiates the stack processing.

Void

stop()

Initiates the stack shutdown.

Figure 8.7. 

As we have said before, the SipProvider interface is absolutely key in JAIN SIP because it provides the application with a window to the operation of SIP. This interface hides the implementation of all the messaging functionality in SIP, including the transport and transaction sublayer. So this is the interface that we will use to send messages statelessly and to create transactions over which the application can send messages statefully. In addition to that, the SipProvider is also responsible for delivering the events to the SipListener as soon as they occur (a SIP message is received from the network or a timer fires). Furthermore, the SIP provider also implements the SIP dialog functionality, and allows the application to obtain access to SIP dialogs. This fact particularly contributes to easing the development of SIP UAs or applications on top of them, because this type of application relies heavily on the dialog concept. Table 8.4 summarizes the main methods in the SipProvider interface.

Table 8.4. 

 

SipProvider Methods

Description

Void

addSipListener (SipListener sipListener)

Registers the SipListener object to the SipProvider.

ClientTransaction

getNewClientTransaction (Request request)

Creates a new ClientTransaction in order to send messages statefully.

ServerTransaction

getNewServerTransaction (Response response)

Creates a new ServerTransaction.

Void

sendRequest (Request request)

Sends a SIP request statelessly.

Void

sendResponse (Response response)

Sends a SIP response statelessly.

The SipListener

So far, we have created the SipProvider so we would have the capability to send messages for transmission into the network. Now we need to enable my application to receive events as well. For that, we need to create a class that implements the SipListener interface and then registers the listener with the provider. For that, it will use a method called addSiplistener() invoked over the SipProvider (Figure 8.8). The methods in the SipListener interface are shown in Table 8.5. The meaning of these methods is quite straightforward. When a SIP request is received from the network, the SipProvider invokes the processRequest() method on the SipListener and passes the received request as an argument. The same applies for the processResponse() method relative to SIP responses. The remaining methods inform the SipListener about events occurring in the SipStack, such as time-outs and so on. All events that are used as arguments for these methods (RequestEvent, ResponseEvent, and so on.) derive from the Java EventObject class, whereas the SipListener interface derives from the EventListener interface.

Figure 8.8. 

Table 8.5. 

 

SipListener Methods

Void

processRequest(RequestEvent requestEvent)

Void

processResponse(ResponseEvent responseEvent)

Void

processTimeOut(TimeoutEvent timeoutEvent)

Void

processTransactionTerminated (TransactionTerminatedEvent transactionTerminatedEvent)

Void

processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent)

Void

processIOException(IOExceptionEvent ioexceptionEvent)

It is important to highlight that, in JAIN SIP terminology, SIP messages that are sent by the application are simply called messages, whereas SIP messages received by the application are referred to as events.[3] This is shown in Figure 8.9, which also shows the different levels (transport, transaction, dialog) that an application can use to send messages to or receive events from the SipProvider.

Figure 8.9. 

Other Factories: MessageFactory, HeaderFactory, AddressFactory

In addition to SipFactory, the JAIN SIP API also defines three other factories: MessageFactory, HeaderFactory and AddressFactory (Figure 8.10). The application can invoke methods on these factories in order to create Java objects that implement useful functionality for the manipulation of SIP messages, headers, and addresses.

Figure 8.10. 

MessageFactory

The MessageFactory allows the application to create Request and Response objects that represent SIP request and response messages. In Table 8.6, we can see the two main methods in MessageFactory: createRequest() and createResponse(). Input arguments to the methods are not shown here; they are used to specify the different elements of a SIP message (start line, headers, and so on).

Table 8.6. 

 

MessageFactory Methods

Description

Request

createRequest()

Creates a Request object.

Response

createResponse()

Creates a Response object.

The Request and Response interfaces provide access to SIP requests and responses. These interfaces extend the Message interface, as is shown in Figure 8.11.

Figure 8.11. 

The Message interface provides mainly:

  • Generic accessor functions to headers

  • Convenience header accessor methods for the body content type, language, disposition, and length

  • Accessor methods to the body content itself

The methods in the Message interface that we will more frequently use during this book are shown in Table 8.7.

Table 8.7. 

 

Message Methods

Description

Void

addHeader (Header header)

Adds a new header to the message.

Void

setHeader (Header header)

Sets the new header to replace existing headers of that type in the message.

Void

removeHeader (String headerName)

Removes the header of the supplied name from the message.

Void

removeFirst(String headerName)

Removes the first header from a list of headers.

The Request interface adds some methods to manipulate the request line, whereas the Response interface adds some others to manipulate the response line. These are shown in Tables 8.8 and 8.9.

Table 8.8. 

 

Request Methods

Description

String

getMethod()

Gets the method of the SIP request.

URI

getRequestURI()

Gets the request-URI.

Void

setMethod(String method)

Sets the method of the SIP request.

Void

setRequestURI(URI requestURI)

Sets the request-URI.

Table 8.9. 

 

Response Methods

Description

String

getReasonPhrase()

Gets the reason phrase of the response.

Int

getStatusCode()

Gets the status code.

Void

setReasonPhrase (String reasonPhrase)

Sets the reason phrase of the response.

Void

setStatusCode (int code)

Sets the status code.

HeaderFactory

The HeaderFactory allows the application to create instances of peer classes that implement interfaces for the manipulation of SIP headers. All of these interfaces are derived from the Header interface (Figure 8.12). Table 8.10 contains a list of the methods in HeaderFactory that we will most often use during this book.

Figure 8.12. 

Table 8.10. 

 

HeaderFactory Methods

ContactHeader

createContactHeader (Address address)

ViaHeader

createViaHeader (Address address)

ToHeader

createToHeader (Address address, String tag)

FromHeader

createFromHeader (Address address, String tag)

CallIDHeader

createCallIDHeader (String callID)

MaxForwardsHeader

createMaxForwardsHeader (int maxForwardsheader)

CSeqHeader

createCSeqHeader(int cseq, String method)

In addition to the header interfaces, there is another relevant interface called Parameters, which allows manipulation of the SIP parameters in the SIP headers. The interfaces for those headers that may have parameters extend the Parameters interface (in addition to the header interface).

AddressFactory

The AddressFactory interface allows the application to create Java objects that implement addresses of various kinds. More specifically, it allows us to create objects that implement the following interfaces:

  • Address

  • URI

  • SipURI

  • TelURL

Table 8.11 shows some of the creation methods in AddressFactory that we will use throughout the book.

Table 8.11. 

 

AddressFactory Methods

Address

createAddress (String address)

URI

createURI (String uri)

SipURI

createSipURI (String user, String host)

TelURL

createTelURL (String phoneNumber)

An Address object is used to represent a SIP user’s address. It comprises two elements: a display name and a URI. The display name of an address is optional, but, if included, can be displayed to an end user. The URI is the user’s address.

A URI object represents a generic Uniform Resource Identifier. It is the base interface for any type of URI. The URI interface has two subinterfaces: SipURI and TelURL.

A SipURI object represents a SIP URI, and the TelURL object represents a TEL URL. Figure 8.13 is a diagram that shows the relationships between the different interfaces. Both the SipURI and TelURL interfaces also extend the Parameters interface.

Figure 8.13. 

Programs and Practice

Structure of the Applications

For the practices in this chapter—except for Example 1, which is not a full application—we will use a simple architecture. The SIP application will consist of just two classes. The first one, called ExampleGUI, will contain the user interface. The second one, called ExampleListener, will be the core class where events will be processed.

ExampleListener will implement the SipListener interface in order to receive events from the underlying SIP implementation (3). These events will be passed to ExampleListener in processRequest() or processResponse() calls invoked by the SipProvider onto ExampleListener.

ExampleListener will also receive calls (2) from the ExampleGUI class representing events generated in the user interface (1). For example, when a user presses a button to initiate a call, ExampleGUI will invoke the custom userInput() method onto ExampleListener. The userInput() method may have an input parameter that indicates the type of GUI event that has been generated (e.g., user pressed “Yes” button or “No” button) so that ExampleListener can apply the appropriate service logic.

ExampleListener class will also generate SIP messages (4) toward the SipProvider, and it may also produce outputs to the user interface by invoking the display method (5) on ExampleGUI in order to display the received or sent SIP message (for tracing purposes).

Figure 8.14 illustrates this architecture.

Figure 8.14. 

In this architecture, we will not use Java events for the communication between ExampleGUI and ExampleListener but rather, direct method calls because it results in a simpler code. The ExampleListener class implements a simple state machine that receives events from the SipProvider and calls from the ExampleGUI, processes them and generates the appropriate output to the network or/and to the GUI.

We will focus on the ExampleListener class, which is the one that contains the actual SIP-related code. The reader may actually implement the ExampleGUI class as he or she prefers, using whatever graphical tool at hand to design the interface. GUI design is not the focus of this book.

Figure 8.15 shows the template that all our examples in this chapter will use in order to implement the ExampleListener class.

Figure 8.15. 

The program starts by importing the JAIN SIP packages plus other Java packages (e.g., java.net, java.util) that we may use in our class:

import javax.sip.*;
import javax.sip.message.*;
import javax.sip.header.*;
import javax.sip.address.*;
import java.net.*;
import java.util.*;

After the import section, there comes the name of the class. We can observe that ExampleListener implements the SipListener interface. Next comes the declaration section and the constructor. The constructor will be identical in all the examples in this chapter.

Next are the SipListener methods that process the events coming from the SipProvider. And, at the end of the class code, we will have the userInput() method that ExampleGUI will invoke on ExampleListener in order to communicate actions happening in the user interface. For example, if, when the user presses a button, there is a need to send a SIP message to the network, the code needed to send a SIP message would be included in the userInput() method.

All the examples shown in this chapter simply allow the reader practice with the API. Neither the clients nor the servers built here are full User Agent Clients (UAC) or User Agent Servers (UAS).

The full code, including GUI and listener, for all the examples can be downloaded from the book’s web page (see Appendix A).

JAIN SIP Initialization

When the application is started, the GUI is loaded. At that point, the user has to fill in some configuration parameters (listening port, and so on), and then press the “On” button. This GUI event causes ExampleGUI to create an instance of ExampleListener. The constructor method for ExampleListener contains the code needed for the JAIN SIP environment initialization. Once the system has been initialized, ExampleListener is ready to receive events from the SipProvider or from the user interface.

We will now see the code, contained in the constructor method, that is needed in order to set up the JAIN SIP environment and create instances of the fundamental classes.

We will consider two arguments in the constructor method. The first one is the port that we will be using. This will be introduced through the GUI, and communicated to the ExampleListener through the constructor method. The port will always refer to a UDP port because we will in all examples use UDP as the network transport. The second argument contains a reference to ExampleGUI so that ExampleListener can instruct the GUI to generate appropriate output when necessary by invoking the display() method on such a reference. So, the signature of the constructor methods would look like:

public ExampleListener (int port, ExampleGUI GUI) {

In the code of the constructor. we will create the main JAIN SIP objects. The first thing is to get an instance of SipFactory:

mySipFactory=SipFactory.getInstance();

The next step is to create the SipStack. Before invoking the creation method for SipStack, we need to set the pathname that identifies the location of a particular vendor’s implementation of the JAIN SIP implementation. The pathname must be the reverse domain name assigned to the vendor that provides the implementation. In our case, we will be using the open-source JAIN SIP implementation from NIST. Its pathname must commence with “gov.nist”:

mySipFactory.setPathName("gov.nist");

We will also need to get an instance of the remaining factories:

myMessageFactory=mySipFactory.createMessageFactory();
myHeaderFactory=mySipFactory.createHeaderFactory();
myAddressFactory=mySipFactory.createAddressFactory();

For the creation of the SipStack, we need first to create and configure the Properties object that we will pass as an argument to the creation method. The Properties class is imported from the java.util package. For the examples, we will use all the default values of the SipStack properties but one: the SipStack name.

Properties myProperties=new Properties();
myProperties.setProperty("javax.sip.STACK_NAME," "myStack");
mySipStack=MySipFactory.createSipStack(myProperties);

Then we create the ListeningPoint. In order to create the Listening Point, we need to pass as an argument the IP address, port, and transport. We already said that we will use UDP as the transport for our examples. On the other hand, it is the user who configures the port in the GUI, and then it is communicated to ExampleListener as an argument in the constructor method. So the only thing we don’t know is the IP address. There is a very simple way to programmatically determine the IP address of our computer. It consists of using the InetAddress class from the java.net package.

myIP=InetAddress.getLocalHost().getHostAddress();

So now we can create the ListeningPoint:

myListeningPoint=mySipStack.createListeningPoint(myIP,port,
"udp");

The next step is to create the SipProvider, passing the recently created ListeningPoint as an argument. We are telling the SIP implementation to create a SIP messaging entity (the SipProvider) that listens at the specified ListeningPoint:

mySipProvider=mySipStack.createSipProvider(myListeningPoint);

Last, we register the SipListener with the SipProvider:

mySipProvider.addSipListener(this);

A sequence diagram for the JAIN SIP initialization process is depicted in Figure 8.16. For simplicity, method arguments are not shown.

Figure 8.16. 

The main JAIN SIP entities are now created. Let us now look at the particular examples.

How to Test the Examples

We will show four different examples in this chapter. But for the first one, which is aimed at just showing how to build a SIP message, the rest of the examples imply an exchange of SIP messages between two communicating applications. Readers have two options in order to test those examples.

Option 1

If the reader has at his or her disposal two computers connected to a local area network or to the Internet, he/she can start one instance of our example applications in each machine. One application will initiate requests, and the other will respond to them. This approach has an interesting benefit: we can also run a network sniffer in either of the two machines, and we will be able to see all the SIP traffic generated and received by our application. The drawback comes from the fact that, for our examples, the application that initiates the requests needs to know the IP address of the other machine. Although this might be a practical way of running the examples if the reader has a couple of computers connected to a switch at home, it may be cumbersome to run it in an Internet environment, given that we would need to find an alternative way of communicating the IP addresses between the two machines before SIP communication can start. In Chapter 13, we will build some network infrastructure (a registrar) that will be used to locate the users so we will no longer need to know the IP address of the other party, but just his or her logical identity.

All in all, we recommend that readers follow the second option in order to run the examples.

Option 2

In this option, just one PC is needed (Figure 8.17). We will run two instances of our application on the same PC. As before, one application will act as client, and the other as server. The trick here is that we will use the local loop address of the TCP/IP stack (127.0.0.1) to avoid the need to have a physical network. Although the TCP/IP stack sees an IP packet destined to the local loop address, it knows the packet does not need to go out through the network interface, but rather, must be delivered to an application on the same machine. The only precaution we need to have in order for this scenario to work is that the two instances of our applications need to listen in two different ports.

Figure 8.17. 

A minor drawback of this option is that not all the sniffers in the market allow us to trace traffic on the local loop. In order to overcome such an issue, we will build a very simple sniffer functionality in our application so that whenever a SIP message is generated or received by it, the SipListener can pass it as a String to ExampleGUI using the display() method. ExampleGUI will display the message in a window of our application.

Example 1: Building SIP Messages

In this first example, we do not yet intend to send or receive SIP messages. We will just learn how to use the interfaces provided by the MessageFactory, HeaderFactory, and AddressFactory in order to build a SIP message. This first example is not a full Java application yet; it just intends to show the Java code needed for building a SIP message.

Let us imagine we want to build the following REGISTER message:

REGISTER sip:registrar.ocean.com SIP/2.0Via: SIP/2.0/UDP peterpc.ocean.com:5060;branch=z9hG4bKnashds7Max-Forwards: 70To: Bob <sip:[email protected]>From: Bob <sip:[email protected]>;tag=456248Call-ID: 843817637684230@998sdasdh09CSeq: 1826 REGISTERContact: <sip:[email protected]>Content-Length: 0

The first thing we will do is create the addresses that we will need for this example. There are three SIP URIs in different fields of this message:

▪ The registrar’s SIP URI:

sip:registrar.ocean.com

▪ Peter’s Address of Record:

sip:[email protected]

▪ Peter’s Contact Address:

sip:[email protected]

Address destAddress=myAddressFactory.createAddress("sip:
  registrar.ocean.com");
Address addressOfRecord=myAddressFactory.createAddress("sip:
  [email protected]");
Address contactAddress= myAddressFactory.createAddress("sip:
  [email protected]");

The destination address will have to be converted to a URI object before we can pass it to the createRequest() method, so:

javax.sip.address.URI myRequestURI=destAddress.getURI();

Next, we create the headers:

ArrayList viaHeaders=new ArrayList();
ViaHeader myViaHeader=myHeaderFactory.createViaHeader("peterpc.
  ocean.com"
,5060,udp, "z9hG4bKnashds7");
viaHeaders.add(myViaHeader);
MaxForwardsHeader myMaxForwardsHeader =
   myHeaderFactory.CreateMaxForwardsHeader(70);
ToHeader myToHeader= myHeaderFactory.createToHeader
 (addressOfRecord,null);
FromHeader myFromHeader=myHeaderFactory.createFromHeader(address
  OfRecord,
"456248");
CallIDHeader myCallIDHeader=
 myHeaderFactory.createCallIDHeader("843817637684230@
 998sdasdh09");
CseqHeader myCSeqHeader=
  myHeaderFactory.createCSeqHeader(1826,"REGISTER");

Then we create the message introducing the main headers as arguments to the createRequest() method in MessageFactory:

Request myRequest=myMessageFactory.createRequest(myRequestURI,
"REGISTER," myCallIDHeader myCSeqHeader, myFromHeader
,myToHeader, viaHeaders, myMaxForwardsHeader);

And last, we add to the message those headers that were not introduced as arguments to the createRequest() method:

ContactHeader myContactHeader=
HeaderFactory.createContactHeader(contactAddress);
myRequest.addHeader(myContactHeader);

The Request object (myRequest) now contains all the info in the SIP message that we wanted to build. The next line of code prints the message on the screen:

System.out.println(myRequest);

Example 2: Using the Transport Sublayer

In this example, we will build a simple application that is able to send and receive SIP messages statelessly. We have chosen to send a SIP REGISTER message. There will be two instances of the application. One instance will act as a client, sending the REGISTER, and the other one will act as a server, receiving the REGISTER.

User Interface

The program will present a simple GUI that includes the following elements:

  • A text field where the user can input the local port (where the application is bound). In the server instance, this port represents the listening port where incoming messages from the network are received. In the client instance, this port represents the value of the UDP Source Port field in outgoing packets.

  • A text field where the user introduces his or her own Address -of -Record. It is introduced only in the client instance.

  • A text field where the user introduces the SIP URI of the server instance. It is introduced only in the client instance.

  • An “Onbutton for initializing the SIP environment.

  • A “Send” button for sending the messages.

  • A text screen, where the user can visualize the sent and received messages.

Figure 8.18 shows a possible layout for the GUI.

Figure 8.18. 

Architecture

The application follows the general architecture for the practices that we described in previous sections. It consist of two classes: Example2GUI and Example2Listener. In addition to the SipListener methods, Example2Listener will implement a method called userInput() that will be invoked by Example2GUI when the user presses the “Send” button. The signature for the userInput() method is:

void userInput(String destination, String aor)

where “destination” is a String representing the SIP URI of the recipient of the request, and “aor” is a String representing the Address of Record of the user initiating the request. These two parameters are inputted in the GUI by the user, and conveyed as arguments in the userInput() method.

Likewise, Example2GUI will implement a method called “display.” This method will contain a String as argument that represents the SIP message sent or received so that it can be displayed in the GUI.

Figure 8.19 shows the method calls between the two classes.

Figure 8.19. 

Initialization

The way the application works is quite simple. As soon as the program is started and the frame is loaded, the user should configure the local (own) port. After doing so, the user will press the “On” button, causing Example2GUI to create an instance of Example2Listener. The constructor method of Example2Listener will set up the JAIN SIP environment on the port introduced by the user. If the initialization has been successful, we will see a message in the GUI that shows the own IP address and port to which our application is successfully bound (Figure 8.20). The port was introduced in the GUI, whereas the IP address is directly obtained by the program:

String myIP=InetAddress.getLocalHost().getHostAddress();

Figure 8.20. 

Creating and Sending the Request

Once both application instances (client and server) are initialized, the user will fill in his or her own Address of Record and the server address in the GUI for the client instance.

The server address will be of the form:

sip: <destination IP address>: <destination port>

If we are running the client and server instances of the application in the same machine, the destination IP address will be the local loop address: 127.0.0.1. Moreover, in that case, we need to make sure that the client own port and destination port are different!

It is also crucial that the destination port entered in the client GUI coincides with the local port configured in the server instance. Figure 8.21 shows the GUIs for the client and server instances.

Figure 8.21. 

The user will then press the “Send” button. At that point, Example2GUI will call the method userInput() in Example2Listener, including the parameters introduced by the user. The code in the userInput() method will first build the REGISTER message.

In order to create the request, we follow a similar procedure as in Example 1. Instead of inputting the CallID value into the header-creation method, we will ask the SIP implementation to generate a CallID value for us:

CallIdHeader myCallIdHeader=mySipProvider.getNewCallId();

Next, we have to send the created message statelessly. For that, we just need to invoke the sendRequest() method on the SipProvider. This will cause the SipProvider to pass the message to the transport sublayer, bypassing the transaction sublayer, and therefore sending the message statelessly:

mySipProvider.sendRequest(myRequest);

After this, we will then invoke the display() method on the Example2GUI object, whose reference we obtained as an argument in the constructor method. By invoking display(), the sent message can be presented to the user:

myGUI.display(">>> "+ myRequest.toString());

Figure 8.22 shows a sequence diagram that represents the actions needed for creating and sending the message.

Figure 8.22. 

Receiving the Request

When the server instance receives the message, its SipProvider will invoke the processRequest() method on Example2Listener. The code in the processRequest() method will first obtain the Request object from the RequestEvent:

Request myRequest=requestReceivedEvent.getRequest();

Then we just convert the Request object into a string and send it to the GUI to be displayed by invoking the display() method on Example2GUI so that we can see the actual received message:

Request myRequest=requestReceivedEvent.getRequest();
myGUI.display("<<< "+myRequest.toString());

Figure 8.23 shows a sequence diagram that represents the actions taken at reception of a message.

Figure 8.23. 

In Figure 8.24, we can see a snapshot of the running client and server. The tracing facility embedded in the application (i.e., the display method) allows us to see all the details of the transmitted and received messages.

Figure 8.24. 

Example 3: Using the Transaction Sublayer

We will now repeat the example in the previous section, but, in this case, sending the REGISTER message statefully instead of statelessly. That is to say, we will use the transaction sublayer. The program is then equivalent to the previous one, except for the following points:

  • The methods userInput() and processRequest() have been modified so as to send and receive the messages statefully.

  • When the server receives the request, it generates a 200 OK response. This will also allow us to understand how responses are constructed and managed.

Next is the code in class Example3Listener.

Let us try to understand the new code for handling messages statefully.

Creating the Request

In order to create the request, we follow the same procedure as in Example 2. The only difference in this case is that, rather than inputting the branch id value into the via header-creation method, we will ask the SIP implementation to generate a branch id value for us. In order to do so, we will set the branch id argument as null:

ViasHeader myViaHeader=HeaderFactory.createViaHeader(myIP,myP
  ort, "udp,"null);

Sending a Request

In order to send a request statefully, we first need to create a client transaction using SipProvider’s method getNewClientTransaction(). Then we can send the request using the sendRequest() method on the ClientTransaction interface.

After sending the message, the display method will be executed so as to visualize the sent message on the GUI:

ClientTransaction myClientTransaction=
 mySipProvider.getNewClientTransaction(myRequest);
myClientTransaction.sendRequest();
gui.display(">>> "+ myRequest.toString());

Figure 8.25 depicts a sequence diagram comprising the creation and sending of the request.

Figure 8.25. 

Receiving a Request

When a request is received, the SipProvider will invoke the processRequest() method. The application will also convert the request into a string, and display it in the GUI:

Request myRequest=requestReceivedEvent.getRequest();
myGUI.display("<<< "+myRequest.toString());

Sending a Response

In order to send a response statefully, the application has to obtain a new server transaction associated to the request just received. This is done by invoking the getNewServerTransaction() method on the SipProvider.

Then we need to create the response through the createResponse() method on the MessageFactory. Next, we just send it by invoking the sendResponse() method on the ServerTransaction:

ServerTransaction myServerTransaction=
  mySipProvider.getNewServerTransaction(myRequest);
Response myResponse=myMessageFactory.createResponse(200,
  myRequest);
myServerTransaction.sendResponse(myResponse);
myGUI.display(">>> "+myResponse.toString());

Figure 8.26 depicts a sequence diagram comprising the reception of the request and sending of the response.

Figure 8.26. 

Receiving a Response

When a response is received, the SipProvider will invoke the processResponse() method. We will obtain the Response object by invoking get-Response() on the ResponseEvent. Then we will just convert the Response object into a string and send it to the GUI to be displayed:

Response myResponse=responseReceivedEvent.getResponse();
myGUI.display("<<< "+myesponse.toString());

Figure 8.27 depicts a sequence diagram for the reception of the response at the client.

Figure 8.27. 

Example 4: Creating a Dialog

In this example, we will play with the Dialog interface. The dialog functionality that we learned in Chapter 7 is encapsulated in the JAIN SIP Dialog interface. The Dialog interface eases the task of writing applications that manage dialogs. In this example, we will also build a client and a server. The client will initiate the dialog by sending an INVITE.[4] As soon as the server receives the INVITE, it generates a 180 Ringing response. When the user accepts the invitation, the server will generate a 200 OK response.

The program structure will be similar to the one in the previous example. There will be some differences, though:

  • There will be two buttons in the GUI. One is used in the client to initiate the dialog; it is called “Initiate.” The other one is called “Accept,” and it is used in the server in order to accept the invitation.

  • Pressing either of the two buttons will cause Example4GUI to invoke the userInput() method on Example4Listener. In order to distinguish between the two buttons, we have added a new argument in userInput() called “type,” which takes value 0 when the “Initiate” button is pressed, and value 1 when the user presses the “Accept” button.

  • There is a new showStatus() method that Example4Listener will invoke on Example4GUI. This method allows us to present additional information on the GUI in a separate label. We will use it in order to convey dialog status information to be displayed.

  • In order to display dialog status information, we will need to obtain the Dialog object in different situations.

Figure 8.28 shows a possible layout for the GUI.

Figure 8.28. 

Creating the INVITE Request

The creation of the INVITE request is similar to the creation of the REGISTER in previous examples. The main difference is obviously the name of the method, and the fact that the To header contains the address of the recipient.

Sending the INVITE Request

This is done exactly in the same way as in Example 3. The dialog-creating request, in this case the INVITE, is not yet part of any dialog, so we will just create a new client transaction and send the message through it:

ClientTransaction myClientTransaction=
 mySipProvider.getNewClientTransaction(myRequest);
myClientTransaction.sendRequest();

Receiving the INVITE Request

This scenario is resolved in the same way as in Example 3. When a request is received, the SipProvider will invoke the processRequest() method:

Request myRequest=requestReceivedEvent.getRequest();

Sending a Provisional Response

Just after receiving the INVITE request, the application will send back a 180 Ringing provisional response. Before sending the response, we will include a tag in the To header, as well as add a contact header to the message. Once the message has been sent, we obtain the Dialog object and show the dialog’s status on the GUI:

ServerTransaction myServerTransaction=
  mySipProvider.getNewServerTransaction(myRequest);
Response myResponse=myMessageFactory.createResponse(180, myRequest);
ToHeader responseToHeader=(ToHeader) myResponse.getHeader("To");
ResponseToHeader.setTag("454326");
Address contactAddress =
  myAddressFactory.createAddress("sip:"+myIP+":"+myPort);
myContactHeader=myHeaderFactory.createContactHeader(contactAddress);
myResponse.addHeader(myContactHeader);
myServerTransaction.sendResponse(myResponse);
myDialog= myServerTransaction.getDialog();
myGUI.showStatus("Dialog status:"+myDialog.getState().toString());

Sending a 200 OK Response

When the called user presses the “Accept” button, the application needs to create a 200 OK response and send it back to the calling user. So, within the code for the userInput() method, for the case where type = 1 (ACCEPT), we will first need to invoke the createResponse() method on MessageFactory. For that, we pass as an argument the originally received request. In addition to that, we also obtain the Dialog object, which represents SIP dialog that has just been created:

Request originalRequest=myServerTransaction.getRequest();
Response myResponse =myMessageFactory.createResponse(200,
  originalRequest);
ToHeader responseToHeader=(ToHeader) myResponse.getHeader("To");
ResponseToHeader.setTag("454326");
myServerTransaction.sendResponse(myResponse);
myDialog= myServerTransaction.getDialog();
myGUI.showStatus("Dialog status: "+myDialog.getState().toString());

Receiving a 180 Provisional Response

When a response is received, the SipProvider will invoke the process Response() method, and we will get the Response object from the ResponseEvent. We also get the client transaction and the dialog corresponding to the request, and show the dialog state:

Response myResponse=responseReceivedEvent.getResponse();
myClientTransaction=responseReceivedEvent.getClientTransaction();
myDialog= myClientTransaction.getDialog();
myGUI.showStatus("Dialog status: "+myDialog.getState().toString());

Receiving a 200 OK Response

When a response is received, the SipProvider will invoke the processResponse() method, and we will get the Response object from the ResponseEvent. We also get the client transaction and the dialog corresponding to the request. It is crucial that we obtain the Dialog because we need it in order to generate the ACK in the next step:

Response myResponse=responseReceivedEvent.getResponse();
myClientTransaction=responseReceivedEvent.getClientTransaction();
myDialog= myClientTransaction.getDialog();
myGUI.showStatus("Dialog status: "+myDialog.getState().toString());

Sending the ACK

In order to send the ACK, we first need to build it. JAIN SIP offers a method called createAck() to build an ACK message from the Dialog that we obtained in the last step. We need to include as an argument to the method the sequence number of the original INVITE request that created the dialog. Once created, the Dialog interface again offers a method to send the ACK:

Request ackRequest=myDialog.createAck(1);
ackRequest.addHeader(myContactHeader);
myDialog.sendAck(ackRequest);

Receiving the ACK

When a request is received, the SipProvider will invoke the processRequest() method, and we will get the Request object from the RequestEvent:

Request myRequest=requestReceivedEvent.getRequest();

In Figure 8.29, we can see a snapshot of the running client and server.

Figure 8.29. 

Initiating a dialog without offering the user the capability of terminating it is not of much use to the user. In the next example (Example 5), we will extend Example 4 to incorporate also the possibility for terminating dialogs.

Example 5: Terminating a Dialog

  • We will now extend the previous example with the capability for the user to be able to terminate the dialog by sending a BYE message, which is a mid-dialog request.[5] This example will show how we can use the Dialog interface to construct and send any new requests within the dialog. So this is applicable to the BYE request, but also to any other mid-dialog requests (e.g., re-INVITE, and so on).

The new aspects as compared with Example 4 are:

  • From the GUI perspective, we will have an additional “Terminate” button for the user to press whenever he or she wants to terminate the dialog. Therefore, we will have an additional value for the “type” parameter in the userInput() method, and additional code in it to construct the BYE request. The new value of the “type” parameter will be 2.

  • We will need extra code in the processRequest() and processResponse() methods in order to cope with the reception of BYE requests or responses.

Figure 8.30 shows a possible layout for the GUI.

Figure 8.30. 

Sending a BYE Request

Sending a mid-dialog request is a very easy process by using the Dialog interface. A Dialog object stores all the state needed by a SIP dialog that will be used for constructing new requests.

First, we invoke the createRequest() method on the Dialog. The new mid-dialog request needs to be sent statefully, so we will also create a new client transaction that will be used to send the request. Finally, we send the request by invoking the sendRequest() method on the Dialog object; we pass the client transaction as an argument:

Request myRequest= myDialog.createRequest("BYE");
myClientTransaction= mySipProvider.getNewClientTransaction
  (myRequest);
myDialog.sendRequest(myClientTransaction);
myGUI.showStatus("Dialog status: "+myDialog.getState().toString());

Figure 8.31 shows a sequence diagram that includes the creation and sending of a mid-dialog request.

Figure 8.31. 

Receiving the BYE Request

When a request is received, the SipProvider will invoke the processRequest() method. The application then must obtain the server transaction. In order to obtain server transactions for requests outside of a dialog, we have so far used the getNewServerTransaction() method. However, for requests within a dialog, such as a BYE request, the server transaction is included in the received RequestEvent, so we can easily obtain it by:

Request myRequest=requestReceivedEvent.getRequest();
myServerTransaction=requestReceivedEvent.getServerTransaction();

Sending the 200 OK Response to BYE

After receiving the BYE request, the application will create a 200 OK response, and send it using the obtained server transaction for the BYE request. The response will be created based on the request message that created this transaction and that was previously stored:

Response myResponse=myMessageFactory.createResponse(200, myRequest);
myServerTransaction.sendResponse(myResponse);
myDialog=myServerTransaction.getDialog();
myGUI.showStatus("Dialog status: "+myDialog.getState().toString());

Figure 8.32 shows a sequence diagram that includes the reception of a mid-dialog request and the sending of a 200 OK response to it.

Figure 8.32. 

Receiving the 200 OK Response to BYE

When a response is received, the SipProvider will invoke the processResponse() method, and we will get the Response object from the ResponseEvent:

Response myResponse=responseReceivedEvent.getResponse();

In Figure 8.33, we can see a snapshot of the running client and server.

Figure 8.33. 

Summary

In this chapter, we have introduced the main concepts around the JAIN SIP interface, and we have built some very simple applications that allow us to experiment with the SIP signaling. But IP communications is not just signaling—there is media as well, and there is also the need to describe the media. In the next chapters, we will learn how to programmatically access the media layer so as to be able to code, decode, send, and receive media packets. We will also learn how to build applications that use the Real-time Transport Protocol, as well as learn to program the Session Description Protocol. Armed with these three ingredients (signaling, media, and session description), we will be able, in Chapter 12, to tackle the design and implementation of a simple SIP soft phone.



[1] On one hand, the industry uses the term SIP stack in order to refer to SIP implementation. On the other hand, the JAIN SIP specification defines an interface called SipStack, which actually represents a management interface to SIP implementation (or SIP stack). So the same term is used to refer to two different aspects. The context and the fact that the Java interface name is written without embedded blank spaces should permit the reader to distinguish when we are referring to one case or the other.

[2] In order to access non-Java functionality from a Java class, the so-called JNI (Java Native Interface) is used.

[3] This is because the upward path (network to application) is represented as an event stream received via the listener, and the downward path is triggered by “downcalls” (method calls) on the provider object. The pattern is different in each direction.

[4] Let us remember that the only method that is able to create a dialog in RFC 3261 is the INVITE method.

[5] Strictly speaking, the BYE method is used to terminate a session (not a dialog); however, the dialog created when establishing that session is also terminated. Given that in these academic exercises, we are not yet playing with the media plane (so there is no effective media session established), the practical effect of the BYE request is to terminate the dialog.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.134.29