The ITX Project
May 4, 1999
Telephony applications move sound across the telephone network and the internet. The ITX API (Application Programming Interface) allows telephony connections to be established between any combination of computers and telephones.
When sound is being transmitted over the Internet, it is generally transferred as UDP traffic. We use RTP (Real Time Protocol) packets to handle this traffic. This protocol helps with synchronizing send and receive buffers, timestamping and sequence numbering.
Sound on the telephone lines, however, is transmitted as sound samples (8-bit samples, Pulse Code Modulation), generally multiplexed into a TDMA (Time Division Multiple Access) flow and transferred at 64kbps.
Besides the data plane just described, telephony applications also involve a control plane. The control plane, or ``signaling'', is what sets up, monitors and tears down calls. DTMF (Dual-Toned Multiple Frequency) tones are also handled in the control plane.
To move sound between the PSTN (Public Switched Telephone Network) and the Internet, we use PC adapter cards that move voice data between the telephone network and the Internet. A networked computer that contains one or more of these cards is called a ``Gateway''. In the ITX system, the Gateway has been designed such that the programmer do not have to worry about whether the program is talking to a desktop PC or a Gateway.
If you have your own PBX (Public Branch Exchange), you can acquire a JTAPI (Java Telephony API) package for controlling it. In this case, the Gateway can send control requests to the PBX, and the PBX can be used to set up and tear down calls. In this way you can take advantage of any additional functionality that the PBX provides.
Finally, you need a way to look up telephone numbers and email addresses. The ITX system allows any backend database to be plugged in and used, in this release, we provide an implementation based on BIND. The application programmer uses the Directory Service API to look up addresses for users. Users register with the Directory Service, allowing them to be located by other users. The implementation of the backend database is transparent to the application programmer or user.
This overview has described the four main components of the ITX API:
The rest of this Guide explains how you, programming in Java, can use the cnrg.itx telephony package to create your own internet-telephony applications. Please refer to the JavaDoc for more detail; it can be found at http://www.cs.cornell.edu/cnrg/telephony/JavaDocs.
If you are a faculty, staff or student at Cornell:
Otherwise:
In telephony applications, ``data'' refers to sound, whether encoded as PCM samples and sent over wires, or stored as PCM samples in an audio file, or traveling over the internet as UDP packets that contain the samples.
ITX's data exchange package (cnrg.itx.datax) is used by the programmer to record, play and transfer sound. The basic object in this package is the Connection. A Connection consists of two channels, an input channel (this brings sound to the application) and the output channel (which carries sound from the application).
The channels contain devices. Each channel has a source device and zero or more destination device. (A common setup has a channel connected to a source and one destination.) The current ITX package contains implementations for the following devices:
Destination Devices
Source Devices
Example: Suppose you want to set up a connection that uses your computer's microphone as input, and a file as output. Of course, there are already tools for generating audio files, but here's how you can use ITX to record your own:
Channel c = new Channel(); c.setSource ( new MicrophoneSource(c)); FileOutputStream fos = new FileOutputStream ("mumble.raw"); c.addDestination ( new StreamDestination ( fos ) );
This application is inherently ``half-duplex'': there is only one uni-directional channel with the microphone as a source and the file as a destination. A ``full-duplex'' application would have two channels, each with a source and destination.
To conclude the example, the application sets up a half-duplex connection as follows:
AudioConnection ac = new AudioConnection ( c, null ); ac.open();
After a certain period of time, or after the application determines that the connection is no longer needed, it closes the connection:
ac.close();
The only kind of connection supported by ITX is the AudioConnection (appropriate enough for a telephony system).
For data transfer over the internet, a forward error correction is done at the network layer. Buffering is also done at the NetworkSource to remove jitter due to network delays.
Like all ITX components, Data Exchange can be used together or independently of the other components
Signaling provides the control layer for the ITX application. Signaling provides services to the programmers such as registrations, call setup, call termination, as well as keepalives. It is built as a distributed component without a central server.
All applications that wish to use signaling must either implement cnrg.itx.SignalingObserver, or extend cnrg.itx.AbstractSignalingObserver.
In addition, the application may also choose to implement cnrg.itx.SignalConnectionObserver in order to made non-block call setups.
The DesktopSignaling object is an application's primary control plane interface. This object is used to register the user with the Directory Service, to dial other applications, and to tell the application if it has an incoming call. We will discuss each in turn.
To obtain a signaling component, you instantiate a DesktogbpSignaling object, giving it parameters to authenticate your application with the database, and a handle to your application so that it can call your handler routines when certain signaling events occur. Here is the simplest DesktopSignaling constructor:
import cnrg.itx.signal.*; public class myFirstITXApp extends AbstractSignalingObserver { : DesktopSignaling signal = new DesktopSignaling ( this, "userid", "password" ) : }
Passing ``this'' as a handle tells our DesktopSignaling who its SignalObserver is. The functions in the SignalObserver are called by DesktopSignaling to notify it of events, such as call invitations and hangups. The next two arguments are Strings which are authenticated against the database of currently registered ITX users. In addition, the current location (IP address and port number) of this application is registered.
In the present version of ITX, DesktopSignaling locates the Directory Server (the only centralized server in the ITX system) by reading a file called resolv.conf, by default from your current working directory. The contents of this file says where the directory server is running, e.g.
1 nameserver 128.84.223.58 2 nameserver 128.84.223.59 3 ...
These are the ip addresses of the primary and secondary BIND servers.
If your resolv.conf file is in a different directory, you can use a variant of the DesktopSignaling constructor to say where it is:
// tell signaling where the directory server's config file is DesktopSignaling signal = new DesktopSignaling ( this, "id", "pass", "my custom telephony application ", "c:\\net\\reconfig.ini");
Note that this variant also lets you give a name to your application, which is entered as a descriptive tag in the directory's database.
Since the DesktopSignaling registers your current location in the database, you should remove this when finishing. Do this by invoking your DesktopSignaling's
logout()
method.
Suppose your application wants to call somebody on a telephone, or maybe it wants to
contact another application (Voice Mail, for example), or just wants to call a person,
contacting them wherever they may be located. This is done by your DesktopSignaling's Dial()
method. This method takes a String object that says who or what you want to call. If it is
a telephone, you might put in a string like 5551212, which would dial a telephone
number. If you want to dial an user in the system, you may also give the userid of the
user.
The Dial method sends a call invitation to the other party; if the invitation is accepted, Dial sets up a SignalConnection (which is a AudioConnection as described in the previous section with control information added to it). If the invitation is refused, an appropriate Exception is thrown.
By default, a full-duplex connection is set up. One channel goes from the microphone on your computer to the internet address of the entity you called (if your application is dialing a telephone, the internet address is that of a Gateway machine to place the call). The other channel comes in from the network (from whatever or whomever you called) and goes to the speakers on your computer.
If you don't like this setup for your input and output channels, then you can override them in the dial call.
Here are two examples (``signal'' is the DesktopSignaling object):
// Example 1: default is to use microphone and speakers SignalConnection flow1 = signal.Dial ( "user@itx.org" ); : // Example 2: over-ride the defaults for a half-duplex connection Channel cOut = new Channel(); cOut.setSource(new Microphone(cOut)); cOut.addDestination(new NetworkDestination()) SignalConnection flow2 = signal.Dial ("5551212", null, cOut);
The first example will send a dial invitation to user@itx.org trying each location in the list of places at which he is currently callable, until the call is picked up or the list is exhausted. His current locations are listed in the ITX Directory, and might include a roaming location.
The second example dials a telephone number and will send sound to the telephone but will not receive data.
If the application decides that it wants to hangup a call, it can call its DesktopSignaling's Hangup method:
// signal is our DesktopSignaling // con is the SignalConnection we were using signal.Hangup ( con );
Taking incoming calls starts with the application's SignalingObserver handler methods. These methods include:
There are a few other SignalingObserver methods that your application might wish to implement. These are
There are certain variations available to the ITX applications programmer concerning
the interaction between your control layer and the directory and the data layer. At
minimum, the application need never make direct calls on the directory or on the data
layer, because your DesktopSignaling will do this for you. Thus the most basic ITX
application is layered as follows:
ITX signaling protocol is based on the SIP call initiation protocol. Users are addressed by email-like addresses. The first step in a 3-way session setup is when the caller sends an INVITE signal to the callee. The invitation contains enough information to describe the caller and what media types and formats will be accepted in the call.
The callee then decides whether or not to ACCEPT. If so, then the packet that goes back to the caller contains the callee's part of a data connection (which media properties are supported, etc.). Finally, the caller completes the packet with its properties and sends a CONFIRM back to the callee.
Once the 3-way handshake is complete, both sides then open their data connection and the call proceeds.If at any time, the 3-way handshake times out, the call is torn down and the application is informed through the onAbortCall() event handler.
For example if the caller never receives an ACCEPT (which actually could also be a busy or reject as well as an accept), the Dial function completes and throws an InviteTimeOutException. Likewise, if the callee sends an ACCEPT packet back to the caller but never gets an ACK, it times out and tears down the half-way built connections.
A Directory Service client is encapsulated within each DesktopSignaling object. This client communicates with the directory server, to look up names and locations, to authenticate an user with the server, to add and delete entries, and so on. The actual directory server used by the ITX project is BIND from Berkeley; by overriding the DSComm class, one can interface with other databases
The directory service client is instantiated by your application's DesktopSignaling object. An application can get its DirectoryService object from its DesktopSignaling:
DesktopSignaling signal; // our DesktopSignaling DirectoryService ds; // our directory client : ds = signal.getDirectory();
Now the application can use the directory through any DirectoryService public methods. It should be pointed out that there are levels of privilege associated with the directory. Applications and users have the lowest level of access, and can manipulate only those database entries associated with their username. The access level for a user or application is determined when the entry is first added to the database by the database administrator.
When Directory Service is instantiated, the current location of the user and application is added to the directory so that other applications/users/telephones can find the user.
One simple ITX application might be a program that allows people to update their personal profile to specify new telephone numbers where she or he can be reached. This sample application would register on the user's behalf (collecting the userid and password via a dialogue box, say), and then call registerLocation() and unregisterLocation() as the user wishes.
One convenient method is ``dumpAllUsers'' which lists all the users registered with ITX.
Given a userid, you can look up their ITX extension number, at least for the users that are ``listed'', using the ``getExtension'' method:
// ds is my DirectoryService. How can I contact bob@cornell.edu? Digits extension = ds.getExtension(new UserID("bob@cornell.edu"));
(Digits is a class in the cnrg.itx.ds package.)
The application might want to look up its own information, registered in the database by your DesktopSignaling component when you constructed it. This information includes your extension or userid or whatever. In fact, if your application is invoking any method that requires a UserID object, one way of coming up with one is as follows:
// signal is my DesktopSignaling - who did I register as? UserID me = (signal.getDirectory()).getID();
See the DirectoryService JavaDoc page for other methods you can use. Some other objects in the cnrg.itx.ds package that programmers should be familiar with include these: UserID, Location, and Digits.
A Location contains the information about a participant in the ITX system. For example, SignalEvent.InvitePacket.getSenderLoc() returns the Location of the person who started a call. From the Location you can get its type (whether it is a telephone, ip address or user id), whether or not that Location can be directly dialed, and so on.
The UserID object encapsulates the name of an user. Location.getUID() returns the caller's name; DesktopSignaling.getDirectory().getID() returns the name that this application was logged in with.
ITX telephony applications that wish to include real telephones located in the telephone network use the cnrg.itx.gtwy package to relay sound data between the internet and the telephone network.
The way the gateway package is integrated with ITX is as follows: when launched, the Gateway Manager runs as a peer application with its own signaling, data, and directory clients, just like any other ITX application. The difference is that when it gets an invitation from another application, that invitation is asking for a real telephone number. The Gateway's implementation of the Dial method is to place a call from one of its telephone lines to some other telephone.
For example, suppose my application wants to call a telephone number. When directory services is asked to look up that number, it realizes it is a telephone because (1) it is not in the database, and (2) it is all digits. In this case, it returns to my application the list of all the locations (IP and port) of running gateways.
Similarly, using a real telephone, one might dial the gateway, and then punch in the extension of an application or user on the network that one wants to call. The gateway will then set up a datapath between the line you are calling on and a socket that leads to the indicated application, which could be some user's desktop telephone application.
Gateway code (unlike control, data, and directory) is not bound with ITX applications; it runs as a server and as a peer application. The name of the server is Gateway. Only one Gateway per machine is permitted, because a set of telephone lines can only be controlled by a single manager.
A gateway may or may not be associated with a PBX. If your group has control over a PBX, or part of a PBX (as we do in the ITX project), then you can have the gateway use the PBX to place and receive calls. In our case, the gateway passes all invitations to a telephone on to the PBX Server (PBXSignalingServer), which then dials the telephone and reports back whether the telephone line was picked up or not. In addition, it hangs up the call when asked to do so, and reports back to the gateway if the remote telephone hangs up.
The gateway also allows you to use a real telephone to send sound to an application running on another computer. The steps involved are:
All sound data between the Gateway and another application passes over the network, in UDP packets, using RTP. Accordingly any data sources or destinations that involve a telephone will use a NetworkSource or NetworkDestination device.
Sound data between the Gateway and the telephone is 8-bit sound samples multiplexed together and transmitted at 64 Kbps. The Gateway executes all the logic for converting between the one and the other.
None of the information in this section is needed by the ITX programmer, but it may be of interest anyway.
The gateway server, Gateway, is coded in Java with JNI calls to the low-level Dialogic system library, written in C. The Dialogic library handles such things as initializing the telephone lines and playing sound bytes to the telephone.
The Gateway instantiates a SignalInterface object, which in turn instantiates a standard ITX DesktopSignaling object (which in turn registers the gateway with directory service). The SignalInterface is the part of the gateway that implements the SignalingObserver interface.
The SignalInterface object also communicates with a PBX server.The PBX runs entirely under the control of a gateway and does not directly communicate with any other application.
The PBX server is coded entirely in Java, does not handle any data, and uses JTAPI (Java Telephony API) to control the PBX. Using JTAPI, the PBX can set up outgoing telephone calls to anywhere. (Incoming telephone calls go through the PBX machine, but not through the PBX server. The call goes directly to one of the lines on the gateway.)
Users of the cnrg.itx package will have to implement their own Gateway if they use a different telephony card. However, the DesktopSignaling component, programmed entirely in Java, should work as is. The PBX server should also work as is, because your PBX vendor should be able to supply you with a JTAPI implementation for their machine.
The ITX system extensively uses an event model. Events are passed to handlers, so that the handlers can take appropriate action. Exceptions are thrown in case of unexpected events. Properties are roughly media capabilities, and are part of every connection. Statistics can be generated for all data transfers.
Each ITX application is a SignalingObserver. Hence, events are used to convey information to the application's event handlers. For example, your onInvite handler is passed an InviteSignalEvent, which contains all the information about who is asking for you to pick up a call.
Most event objects have methods defined on them for extracting bits of information. For example, the AbortSignalEvent has the following methods:
If your application extends the AbstractSignalingObserver, all of the default event handlers might be acceptable. (None of these handlers do anything at all.) In this case, you do not need to put any of the handlers into your application. But if you want to do something special on onInvite, etc., then you must override the corresponding handler.
All of the SignalEvents (AbortSignalEvent, DTMFSignalEvent, HangupSignalEvent, and InviteSignalEvent) contain the InvitePacket that was used to initiate the call. getInvitePacket() can be used to retrieve the InvitePacket, and all of the information inside. There are many public methods that can be called on an InvitePacket.
If unexpected things happen during the course of ITX execution, then Exception objects are created and thrown. There are a wide variety of specific Exceptions, all of which inherit from Java's Exception class. You may choose to field individual Exceptions, or just Exception in general. In any case, the toString() method is defined on the Exception which gives more information about why the ITX Exception was thrown.
Once a call has been set up, each party to the call has a SignalConnection object, which contains basically all the information relating to a call. In particular you can use the getProperties() method to extract the PropertiesCollection from the SignalConnection. The Properties Collection is the set of device properties available to a call.
The NetworkProperty allows you to get the IP address and port of a network device's data port. Properties also include features of the encoded data stream, such as packet size and encoding method.
Does your application want to know how much sound data is flowing, or ineed whether any sound is flowing at all? The Stats object (in the cnrg.itx.datax package) does the trick.
Sources, Destinations, Channels, and Connections all implement the Statistics interface, meaning that they keep a count of bytes transmitted or whatever. The Statistics.getStatistics() method returns a printable Stats object, for example:
System.out.println ( myNetworkSource.getStatistics() );
With some effort, an application could also parse the object to extract, say, the number of bytes that have been sent. An application could also use the Statistics interface to collect its own statistics.
In the examples below, it is assumed that your CLASSPATH has the itx.jar archive somewhere in it, that you have a resolv.conf file in your working directory, that your PATH includes the directory containing DSComm.dll and jaudio.dll, and that the Gateway, PBX Server, and Directory Server are all up and running.
The minimal ITX application makes and takes no calls, sends and receives no data, but the location of its signaling component is registered in the directory for the duration of the run:
import cnrg.itx.signal.*; public class Minimal extends AbstractSignalingObserver { public static void main (String[] args) { Minimal me = new Minimal(); } public Minimal() { // register and then unregister try { DesktopSignaling sig = new DesktopSignaling (this, "guest", "guest"); sig.logout(); } catch (Exception e) { System.out.println("Couldn't get authenticated"); } } }
In a real application you'll probably want to have the unregister far apart from the constructor. This means you would store ``sig'' as a local variable, and have a logout method that invokes sig.logout().
The next most minimal ITX program runs entirely in the control plane, and does not send or receive any sound data. However, it does use signaling to register with the database, ring a telephone, and unregister with the database. Edit the preceding example to add the following code between constructing a DesktopSignaling and logging out:
: // Make 2545454 ring try { SignalConnection con = sig.Dial ("2545454",null,null); : sig.Hangup ( con ); } catch ( ConnectException e ) {} :
If you don't have a microphone on your computer, you can still use your telephone to record voice data. The following application will write incoming sound data into an output file:
import java.io.*; // for OutputStream import cnrg.itx.signal.*; // for DesktopSignaling import cnrg.itx.signal.SignalEvent.*; import cnrg.itx.datax.devices.*; // for AudioConnection, etc. import cnrg.itx.datax.*; // for Channel public class PhoneRecorder extends AbstractSignalingObserver { private DesktopSignaling myDS = null; private FileOutputStream ourFile = null; private NetworkSource source = null; private StreamDestination dest = null; // Default constructor gets a DesktopSignaling object public PhoneRecorder () { [ myDS = new DesktopSignaling ( this, "PhoneRecorder", ... ) ] } // Main entry point is here public static void main ( String[] args ) { PhoneRecorder me = new PhoneRecorder(); } // SignalingObserver method - invoked when someone calls this app public void onInvite (InviteSignalEvent ise) { Connection c = null; // our AudioConnection Channel in = new Channel(); // This will be telephone to file // Open a FileOutputStream, which is the file to which sound is written try { ourFile = new FileOutputStream ( getName() ); } catch ( Exception e ) { System.out.println ("Could not open an output file because" + e); System.exit( 0 ); } // Set up a half-duplex AudioConnection and open it try { source = new NetworkSource(in, Channel.SAMPLE_SIZE); dest = new StreamDestination(ourFile); // our in channel is NetworkSource -> StreamDestination in.setSource ( source ); in.addDestination ( dest ); // our connection has only an in channel (half-duplex) c = new AudioConnection ( in, null ); c.open(); } catch ( Exception e ) { System.out.println ("Could not open data connection because" + e); System.exit( 0 ); } // If we get to here, we have an open data connection ise.accept( c ); // set the accept flag in the invitation // Exit back to DesktopSignaling, which will return the // Accepted invitation back to the Gateway } // This method is invoked by DesktopSignaling when the caller hangs up. public void onHangup(HangupSignalEvent hse) { try { ourFile.flush(); ourFile.close(); } catch ( Exception e ) {} System.out.println ( source.getStatistics() ); System.out.println ( dest.getStatistics() ); myDS.logout(); } // +++++++++++ Private methods +++++++++++++++++= // Generate a file name to capture the data private String getName () { return "file1"; // Substitute your own here } }
The previous example illustrated a simple half-duplex application. A full duplex connection between two application is illustrated by the following example, which is the client side. The server side is left to the imagination.
package cnrg.itx.datax.devices; import cnrg.itx.datax.*; import java.io.*; import java.net.*; /** * This class tests audio streaming over the network. This sample routine * uses a NetworkSource * and SpeakerDestination to play audio originating * from a NetworkServer. Note that in creating the various destinations * and sources, the remote source/destination sample size MUST equal the * local source/destination sample size. */ public class NetworkClient { public static int PORT = 7777; public static void main(String []args) { try { // Input channel, source, and destination Channel inChannel = new Channel(); NetworkSource inSource = new NetworkSource(inChannel, PORT, Channel.SAMPLE_SIZE); SpeakerDestination inDest = new SpeakerDestination(); // Set input channel inChannel.setSource(inSource); inChannel.addDestination(inDest); // Start destination inChannel.open(); // Let the audio file play... System.out.println("Playing audio from the network..."); } catch (Exception e) { System.out.println(e.getMessage()); } } }
Here is a list of the classes that are useful to the ITX applications programmer, and (TBD) some of their methods.
Hierarchy For ITX Packages Class Hierarchy o class java.lang.Object o class cnrg.itx.signal.client.AbstractSignalingObserver (implements cnrg.itx.signal.client.SignalingObserver) o class cnrg.itx.datax.Channel (implements cnrg.itx.datax.Properties, cnrg.itx.datax.Statistics) o class cnrg.itx.signal.client.DesktopSignaling o class cnrg.itx.ds.client.Digits (implements java.io.Serializable) o class cnrg.itx.ds.client.DirectoryService o class cnrg.itx.ds.client.Location (implements java.io.Serializable) o class cnrg.itx.datax.devices.MicrophoneSource (implements java.lang.Runnable, cnrg.itx.datax.Source) o class cnrg.itx.datax.devices.NetworkDestination (implements cnrg.itx.datax.Destination) o class cnrg.itx.datax.devices.NetworkProperty (implements cnrg.itx.datax.Property) o class cnrg.itx.datax.devices.NetworkSource (implements java.lang.Runnable, cnrg.itx.datax.Source) o class java.util.Observable o class cnrg.itx.datax.Connection (implements cnrg.itx.datax.Properties, java.lang.Runnable, cnrg.itx.datax.Statistics) o class cnrg.itx.datax.AudioConnection o class cnrg.itx.datax.PropertiesCollection (implements java.io.Serializable) o class cnrg.itx.signal.client.SignalConnection o class cnrg.itx.signal.SignalEvent.SignalEvent (implements java.io.Serializable) o class cnrg.itx.signal.SignalEvent.AbortSignalEvent o class cnrg.itx.signal.SignalEvent.AliveSignalEvent o class cnrg.itx.signal.SignalEvent.DTMFSignalEvent o class cnrg.itx.signal.SignalEvent.HangupSignalEvent o class cnrg.itx.signal.SignalEvent.InviteSignalEvent o class cnrg.itx.signal.SigPacket (implements java.io.Serializable) o class cnrg.itx.signal.InvitePacket (implements java.io.Serializable) o class cnrg.itx.datax.devices.SpeakerDestination (implements cnrg.itx.datax.Destination) o class cnrg.itx.signal.SignalEvent.StartCallSignalEvent o class cnrg.itx.datax.Stats o class cnrg.itx.datax.devices.StreamDestination (implements cnrg.itx.datax.Destination) o class cnrg.itx.datax.devices.StreamSource (implements java.lang.Runnable, cnrg.itx.datax.Source) o class java.lang.Throwable (implements java.io.Serializable) o class java.lang.Exception o class cnrg.itx.datax.DataException o class cnrg.itx.datax.DuplicateDestinationException o class cnrg.itx.datax.jaudio.JAudioException o class cnrg.itx.ds.client.DirectoryServiceException o class cnrg.itx.ds.client.AccessDeniedException o class cnrg.itx.ds.client.AuthenticationException o class cnrg.itx.ds.client.RecordAlreadyExistsException o class cnrg.itx.ds.client.RecordNotFoundException o class cnrg.itx.gtwy.GatewayException o class cnrg.itx.ITXException o class cnrg.itx.signal.client.DesktopSignalingException o class cnrg.itx.signal.client.ConnectException o class cnrg.itx.signal.client.ConnectFailedToOpenSocketException o class cnrg.itx.signal.client.ConnectNullObjectSentException o class cnrg.itx.signal.client.ConnectNullSocketException o class cnrg.itx.signal.client.DesktopSignalingServerException o class cnrg.itx.signal.client.InviteException o class cnrg.itx.signal.client.InviteBusyException o class cnrg.itx.signal.client.InviteIncompatibleException o class cnrg.itx.signal.client.InviteInvalidException o class cnrg.itx.signal.client.InviteRejectException o class cnrg.itx.signal.client.InviteTimeoutException o class cnrg.itx.signal.pbx.DialingException o class cnrg.itx.signal.pbx.PBXSignalingException o class cnrg.itx.signal.client.SigPacketHandlerException o class cnrg.itx.ds.client.UserID (implements java.io.Serializable) o class cnrg.itx.ds.client.UserProperty Interface Hierarchy o interface cnrg.itx.datax.Properties o interface cnrg.itx.datax.Destination(also extends cnrg.itx.datax.Statistics) o interface cnrg.itx.datax.Source(also extends cnrg.itx.datax.Statistics) o interface java.io.Serializable o interface cnrg.itx.datax.Property o interface cnrg.itx.signal.SignalID o interface cnrg.itx.signal.client.SignalingObserver o interface cnrg.itx.datax.Statistics o interface cnrg.itx.datax.Destination(also extends cnrg.itx.datax.Properties) o interface cnrg.itx.datax.Source(also extends cnrg.itx.datax.Properties)
ITX Programmer's Guide
This document was generated using the LaTeX2HTML translator Version 98.1p1 release (March 2nd, 1998)
Copyright © 1993, 1994, 1995, 1996, 1997, Nikos Drakos, Computer Based Learning Unit, University of Leeds.
The command line arguments were:
latex2html -local_icons -split 2 Guide.
The translation was initiated by Donna Bergmark on 1999-04-15