by Emmanuel Proulx
10/17/2007
This article shows how to develop client-side applications using the Session Initiation Protocol (SIP) on Java SE. It presents the JAIN SIP API, a powerful "SIP stack." A simple IM application is shown and then dissected to explore this technology.
The Java APIs for Integrated Networks (JAIN) is a JCP work group managing telecommunication standards. Session Initiation Protocol (SIP) is a standard communication protocol, discussed in a previous article. Put Java and SIP together and you get the JAIN SIP API, a standard and powerful API for telecommunications. This idea started in 1999 with JSR 32. The reference implementation is open source, very stable, and very widely used. If you're writing a SIP application with Java, using this SIP stack makes perfect sense.
This API is typically used for client-side application development. Other container-based technologies, like SIP Servlet API (see BEA WebLogic SIP Server for example), are better suited for server-side development. A previous article focuses on the SIP Servlet API. Now we're going to turn to the client API stack.
This article requires a good understanding of Java. Also, I suggest you familiarize yourself with SIP, as using the JAIN SIP API requires a good understanding of this protocol. SIP signaling, especially messages and headers, is particularly important. See the References section at the end of the article for links to relevant information.
To get the JAIN SIP API libraries, go to the jain-sip project home page. Click the link to go to the download page. You'll need to get these files:
JainSipApi1.2.jar
: SIP interfaces and main classes JainSipRi1.2.jar
: SIP reference implementation log4j-1.2.8.jar
(Available inside the file jain-sip-1.2.jar
under the folder jain-sip/lib
): Logging service concurrent.jar
(Available inside the file jain-sip-1.2.jar
under the folder jain-sip/lib
): Concurrency utilities Other files are not needed in this example. Include these libraries in your project.
I suggest you have a look at the first section of the Appendix for a list of the various classes and interfaces provided in the JAIN SIP API. These classes and interfaces are standard and provided to you free of charge, and soon you will see how to use them as part of our SipLayer
example class.
As an example of a JAIN SIP API program, let's dissect an application that you may be familiar with if you read the article on the topic of SIP Servlets I wrote previously. The TextClient is an instant messaging application that can send and receive text messages over the SIP protocol. One instance of this application can send messages to another instance, but in theory, this client can be used to send messages to other kinds of SIP instant messaging clients, and even SIP server applications. Figure 1 shows a screen capture.
To run this application, first you need to download the provided source code. Second, you have to build it using the provided Ant script. This will produce a JAR file. Finally, you run the application, using this command:
java -jar textclient.jar <username> <port>
You can run multiple instances of this client (make sure they use different ports) and send each other messages. The rest of the article explores the code of this sample application.
A couple of classes and an interface make up the whole TextClient code. This table introduces them:
Class / Interface | Description |
---|---|
TextClient | Main class, Swing window that contains the widgets of the application. See Figure 1. |
SipLayer | Class that takes care of all the SIP communication. It is instantiated by the TextClient class, and calls it back through the MessageProcessor interface. |
MessageProcessor | Callback interface (observer pattern), which serves to decouple the SipLayer from its container. |
In the upcoming sections, I'll introduce the MessageProcessor
and then spend most of the time looking at SipLayer
. I won't talk about the TextClient
class at all because it simply contains user interface Swing code and is not relevant to the topic of this article. For more information, please refer to the source code provided with this article.
Before I jump into the SipLayer
class, I'll briefly cover the MessageProcessor
interface. To decouple the SIP layer from the GUI layer, you use a callback interface that allows sending information from the former without having to know the signature of the latter. The interface is shown below:
public interface MessageProcessor{public void processMessage(String sender, String message);public void processError(String errorMessage);public void processInfo(String infoMessage);}
The SipLayer
constructor will take an implementation of this interface (that is, the TextClient
object) as a parameter and will hold on to it. Later you'll be able to use this object to send information back to the GUI.
Let's start writing the SipLayer
class. TextClient
must be able to receive asynchronous messages coming from other SIP end points. The observer pattern is used for this: The class implements the SipListener
interface to process incoming messages:
public class SipLayerimplements SipListener {
The methods of this interface are:
void processRequest(RequestEvent evt);void processResponse(ResponseEvent evt);void processTimeout(TimeoutEvent evt);void processIOException(IOExceptionEvent evt);void processTransactionTerminated(TransactionTerminatedEvent evt);void processDialogTerminated(DialogTerminatedEvent evt);
In this example, the most important methods evidently are processRequest()
and processResponse()
for processing incoming messages. I'll look at those a bit later.
Next are two fields to store objects needed later. These are not directly related to the SIP API, but you'll need them for the example. The first is a MessageProcessor
object as discussed before. You also need to keep the username handy. These two fields have getters and setters which, for brevity, I'm not showing in this article.
private MessageProcessor messageProcessor;private String username;
Next is the constructor. A typical way to start a JAIN SIP API application—and TextClient follows this pattern—is to set up a bunch of objects that will be useful later on. I'm talking about a number of factories, and a single SIP stack instance, initialized.
private SipStack sipStack;private SipFactory sipFactory;private AddressFactory addressFactory;private HeaderFactory headerFactory;private MessageFactory messageFactory;private SipProvider sipProvider;public SipLayer(String username, String ip, int port) throwsPeerUnavailableException, TransportNotSupportedException,InvalidArgumentException, ObjectInUseException,TooManyListenersException {setUsername(username);sipFactory = SipFactory.getInstance();sipFactory.setPathName("gov.nist");Properties properties = new Properties();properties.setProperty("javax.sip.STACK_NAME","TextClient");properties.setProperty("javax.sip.IP_ADDRESS",ip);sipStack = sipFactory.createSipStack(properties);headerFactory = sipFactory.createHeaderFactory();addressFactory = sipFactory.createAddressFactory();messageFactory = sipFactory.createMessageFactory();...
The SIP factory is used to instantiate a SipStack
implementation, but since there could be more than one implementation, you must name the one you want via the setPathName()
method. The name " gov.nist
" denotes the SIP stack you've got.
The SipStack
object takes in a number of properties. At a minimum, you must set the stack name. All other properties are optional. Here I'm setting an IP address to use by the stack, for cases where a single computer has more than one IP address. Note that there are standard properties, which all SIP API implementations must support, and non-standard ones that are dependent on the implementation. See the References section for links to these properties.
The next step is to create a pair of ListeningPoint
and SipProvider
objects. These objects provide the communication functionality of sending and receiving messages. There's one set of these for TCP and one set for UDP. This is also where you select the SipLayer
(this) as a listener of incoming SIP messages:
...ListeningPoint tcp = sipStack.createListeningPoint(port, "tcp");ListeningPoint udp = sipStack.createListeningPoint(port, "udp");sipProvider = sipStack.createSipProvider(tcp);sipProvider.addSipListener(this);sipProvider = sipStack.createSipProvider(udp);sipProvider.addSipListener(this);}
And this is how the constructor ends. You've just used the JAIN SIP API to create a SipStack
instance, a bunch of factories, two ListeningPoint
s, and a SipProvider
. These objects will be needed in the upcoming methods to send and receive messages.
联系客服