JTapi Overview

The Java Telephony API (JTAPI) is a portable, object-oriented application programming interface for Java-based computer-telephony applications.

This article is an introduction to the Java Telephony API, presenting the most important elements of this and attempts to clarify some basic issues that will be the base for the following articles.

JTapi can be used to integrate CRM or other applications with a telephone system, create applications that handle incoming calls, create application that place outgoing call on behalf of a user, or in general provide an automated way for a user to handle his telephone set.

The purpose of JTAPI is to serve as an interface between a Java application and a telephone system. The point where this interface is located determines the degree of control an application has. In a first-party call control scenario the interface is located at a terminal. The application has the same degree of control a normal telephone user has. In a third-party call control scenario the interface is located inside the telephone system. Depending on the telephone system this internal access provides the application usually with more control capabilities than a first-party call control scenario.

First party call control

First party call control

Third party call control

Third party call control

Figure 1: First-party and third-party call control in a Private Telephone Network with a PBX

A design goal of JTAPI has been to cover both scenarios. As a consequence JTAPI provides a model of the telephone system and of telephone calls that corresponds to the more general third-party view, even when JTAPI is used for first-party call control. A third-party view of a call does not distinguish between the local end and the remote end of a call. Instead the two ends are symmetrical.

In this article the discussion is about the third-party call capabilities of the JTAPI and more important how to control or monitor the elements that the JTAPI offers. Next is an introduction of what are the basic JTAPI elements.

Basic JTapi elements

Telephony applications, involve in the control or monitor of objects a telephone system exposes. Such objects could be logical objects, for example an Address, a Call, a Connection etc, or physical objects as a Terminal. Following is a list with the most important JTAPI objects that will be covered throughout this article series. This is an attempt to give an introduction so for everyone to be on the same page.

JTapiPeer

The JtapiPeer interface represents a vendor’s particular implementation of the Java Telephony API.

JtapiPeer is the first object an application must instantiate. Depending on the vendor’s implementation of this interface, one or more different services can be obtained by the JtapiPeer object.

Provider

A Provider represents the telephony software-entity that interfaces with a telephony subsystem.

A Provider is created and returned by the JtapiPeer.getProvider() method which is given a string to describe the desired Provider. This method sets up any needed communication paths between the application and the Provider. The string given is one of the services listed in the JtapiPeer.getServices().

The rest of the JTAPI objects are derived from the provider and also the provider is responsible for the various actions the application is designed to make with the JTapi. For example, provider is will deliver Address events in case we monitor an Address or create a call between a local Address and a remote Address (example of an outgoing call).

Important to notice is the term Provider’s domain which refers to the collection of Address and Terminal objects which are local to the Provider, and typically, can be controlled by the Provider. For example,the domain of a Provider for a PBX may be the Addresses and Terminals in that PBX. The Provider implementation controls access to Addresses and Terminals by limiting the domain it presents to the application.

Address

An Address object represents what we commonly think of as a “telephone number”.

The purpose of the address could be something different than a telephone number if the underlying network is not a telephone network. As an example if the underlying network is an IP network; then the address might represent an IP address (e.g. 192.168.0.100).

When the Address object is created, a unique string name is assigned to it (e.g. 15121) and does not change throughout the lifetime of the object. The method Address.getName() returns the name of the Address object.

Address objects may be classified into two categories: local and remote. Local Address objects are those addresses which are part of the local telephone system domain, for example the extension numbers of a PBX. These Address objects are created by the implementation of the Provider object when it is first instantiated. All of the Provider’s local addresses are reported via the Provider.getAddresses() method. Remote Address objects are those outside of the Provider’s domain which the Provider learns about during its lifetime through various happenings (e.g. an incoming call from a currently unknown address). Remote Addresses are not reported via the Provider.getAddresses() method.

Note that applications never explicitly create new Address objects.

Terminal

A Terminal represents a physical hardware endpoint connected to the telephony domain. In other words, a Terminal is the telephone set of a PBX.

When the Terminal object is created, a unique string name is assigned to it and does not change throughout the lifetime of the object. The method Terminal.getName() returns the name of the Terminal. Important to notice here is that in contrary with the Address name, the name of the Terminal may not have any real-world interpretation since in order to interact with a Terminal (e.g. call a Terminal) we use the Address assigned to this Terminal.

Terminal objects may be classified into two categories: local and remote. Local Terminal objects are those terminals which are part of the local telephone system domain, for example the telephone sets of a PBX. These Terminal objects are created by the implementation of the Provider object when it is first instantiated. All of the Provider’s local terminals are reported via the Provider.getTerminals() method. Remote Terminal objects are those outside of the Provider’s domain which the Provider learns about during its lifetime through various happenings (e.g. an incoming call from a currently unknown address). Remote Terminal objects are not reported via the Provider.getTerminals() method.

Note that applications never explicitly create new Terminal objects.

Address and Terminal objects

Address and Terminal objects exist in a many-to-many relationship. An Address object may have zero or more Terminals associated with it. For each Terminal associated with an Address, that Terminal must also reflect its association with the Address. Since the implementation creates Address (and Terminal) objects, it is responsible for insuring the correctness of these relationships. The Terminals associated with an Address is given by the Address.getTerminals() method and the Addresses associated with a Terminal is given by the Terminal.getAddresses().

An association between an Address and Terminal object indicates that the Terminal contains the Address object as one of its telephone number addresses. In many instances, a telephone set (represented by a Terminal object) has only one telephone number (represented by an Address object) associated with it. In more complex configurations, telephone sets may have several telephone numbers associated with them. Likewise, a telephone number may appear on more than one telephone set.

Call

A Call object models a telephone call.

A Call can have zero or more Connections. A two-party call has two Connections, and a conference call has three or more Connections. Each Connection models the relationship between a Call and an Address, where an Address identifies a particular party or set of parties on a Call.

A Call maintain a list of the Connections on that Call. Applications obtain an array of Connections associated with the Call via the Call.getConnections() method. A Call retains a reference to a Connection only if it is not in the Connection.DISCONNECTED state. Therefore, if a Call has a reference to a Connection, then that Connection must not be in the Connection.DISCONNECTED state. When a Connection moves into the Connection.DISCONNECTED state (e.g. when a party hangs up), the Call loses its reference to that Connection which is no longer reported via the Call.getConnections() method.

The Provider maintains knowledge of the calls currently associated with it. Applications may obtain an array of these Calls via the Provider.getCalls() method. A Provider may have Calls associated with it which were created before it came into existence. It is the responsibility of the implementation of the Provider to model and report all existing telephone calls which were created prior to the Provider’s lifetime. The Provider maintains references to all calls until they move into the Call.INVALID state.

Applications may create new Calls using the Provider.createCall() method. A new Call is returned in the Call.IDLE state. Applications may then use this idle Call to place new telephone calls. Once created, this new Call object is returned via the Provider.getCalls() method.

Address and Call objects

Address objects represent the logical endpoints of a telephone call. A logical view of a telephone call views the call as originating from one Address endpoint and terminates at another Address endpoint.

Address objects are related to Call objects via the Connection object. The Connection object has a state which describes the current relationship between the Call and the Address. Each Address object may be part of more than one telephone call, and in each case, is represented by a separate Connection object. The Address.getConnections() method returns all Connection objects currently associated with the Call.

An Address is associated with a Call until the Connection moves into the Connection.DISCONNECTED state. At that time, the Connection is no longer reported via the Address.getConnections() method. Therefore, the Address.getConnections() method will never report a Connection in the Connection.DISCONNECTED state.

The Java Telephony API specification states that the implementation is responsible for reporting all existing telephone calls when a Provider is first created. This implies that an Address object must report information regarding existing telephone calls to that Address. In other words, Address objects must reports all Connection objects which represent existing telephone calls.

Terminal and Call objects

Terminal objects represent the physical endpoints of a telephone call. With respect to a single Address endpoint on a Call, multiple physical Terminal endpoints may exist. Terminal objects are related to Call objects via the TerminalConnection object. TerminalConnection objects are associated with Call indirectly via Connections. A Terminal may be associated with a Call only if one of its Addresses is associated with the Call. The TerminalConnection object has a state which describes the current relationship between the Connection and the Terminal. Each Terminal object may be part of more than one telephone call, and in each case, is represented by a separate TerminalConnection objet. The Terminal.getTerminalConnections() method returns all TerminalConnection object currently associated with the Terminal.

A Terminal object is associated with a Connection until the TerminalConnection moves into the TerminalConnection.DROPPED state. At that time, the TerminalConnection is no longer reported via the Terminal.getTerminalConnections() method. Therefore, the Terminal.getTerminalConnections() method never reports a TerminalConnection in the TerminalConnection.DROPPED state.

The Java Telephony API specification states that the implementation is responsible for reporting all existing telephone calls when a Provider is first created. This implies that an Terminal object must report information regarding existing telephone calls to that Terminal. In other words, Terminal objects must report all TerminalConnection objects which represent existing telephone calls.

Connection

A Connection represents a link (i.e. an association) between a Call object and an Address object.

The purpose of a Connection object is to describe the relationship between a Call object and an Address object. A Connection object exists if the Address is a part of the telephone call. Applications use the Connection.getCall() and Connection.getAddress() methods to obtain the Call and Address associated with this Connection, respectively.

From one perspective, an application may view a Call only in terms of the Address/Connection objects which are part of the Call. This is termed a logical view of the Call because it ignores the details provided by the Terminal and TerminalConnection objects which are also associated with a Call. In many instances, simple applications (such as an outcall program) may only need to concern itself with the logical view. In this logical view, a telephone call is views as two or more endpoint addresses in communication. The Connection object describes the state of each of these endpoint addresses with respect to the Call.

Connection objects are immutable in terms of their Call and Address references. In other words, the Call and Address object references do not change throughout the lifetime of the Connection object instance. The same Connection object may not be used in another telephone call. The existence of a Connection implies that its Address is associated with its Call in the manner described by the Connection’s state.

Although a Connection’s Address and Call references remain valid throughout the lifetime of the Connection object, the same is not true for the Call and Address object’s references to this Connection. Particularly, when a Connection moves into the Connection.DISCONNECTED state, it is no longer listed by the Call.getConnections() and Address.getConnections() methods. Typically, when a Connection moves into the Connection.DISCONNECTED state, the application loses its references to it to facilitate its garbage collection.

Connections objects are containers for zero or more TerminalConnection objects. Connection objects represent the relationship between the Call and the Address, whereas TerminalConnection objects represent the relationship between the Connection and the Terminal. The relationship between the Call and the Address may be viewed as a logical view of the Call.

The relationship between a Connection and a Terminal represents the physical view of the Call, i.e. at which Terminal the telephone calls terminates.


Resources:


[print_link]

Tags: , ,

26 comments

  1. hi,
    i need to create an interractif voice server and connect a KENEL PABX to a modem (PC) to communicate with the application who control this server, is that possible to do by using the JTAPI fonctionnalities? can i find any documentation about that?
    thanks for all

  2. Usually when you refer to modems you are talking about TAPI and not JTapi, but this again depends.
    As far as Kenel, do you mean Kannel SMS Gateway?
    Can you please elaborate on what you need to do?

    Thanks
    Gvag

  3. Hi,
    in ‘Terminal’ Section you write ‘The method Terminal.getTerminals() returns the name of the Terminal’. I think the method must be ‘Terminal.getName()’.

    Thanks for this article
    kads

  4. Kads,

    You are right, thanks for the remark. I made the appropriate change.

    Regards
    GvaG

  5. How do we get an external number that we dialed on the phone? For example, the following is a portion of my code when I’m making an outgoing call to an external number on my IP phone:

    —–

    if(evlist[i].getCall().getConnections().length==2)
    {
    Connection connection2 = evlist[i].getCall().getConnections()[1];
    Address addr2 = connection2.getAddress();
    if(addr2.getName()!=null)
    {
    outgoingNo = addr2.getName();
    }
    }

    —–

    In this case, the String outgoingNo will be something like “T13582#2″. How do we get the correct number that we dialed?

    Thanks.

    • Similar to your code, i am doing the following and it works fine for me:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      
      ...
      ...
      Call call = createCall.makeCall("1234", "123456789");
       
      		Connection[] conn = call.getConnections();
       
      		for (int i = 0; i < conn.length; i++) {
      			String add = conn[i].getAddress().getName();
      			System.out.println("For conn["+i+"] the address is: "+add);
      		}
      ...
      ...

      Maybe some restrictions on the user you use to access the JTapi services of the PBX? Or any PBX setting?

  6. hi,
    i use Cisco JTAPI to extension my IP fon. I must to read the address of fon number(remote address) and search the name of phon number in the SQL database. I can see the name of the local address on the display but i can’t see the name of the remote addresss although the remote address(external fon number) is saved on the my SQL-database. Have you experience oe any idea in CiscoJTAPI for external telefon number? Thank you very much.

  7. @gvag
    what is the makeCall method. Can you please eloborate. Thanks

    • Hary

      I created a class which has the makeCall method



      public Call createNewCall(Terminal origterm, Address origaddr, String dialedDigits) {

      try {
      Call call = providerService.getProvider().createCall();
      call.connect(origterm, origaddr, dialedDigits);

      return call;
      } catch (ResourceUnavailableException e) {
      e.printStackTrace();
      } catch (InvalidStateException e) {
      e.printStackTrace();
      } catch (PrivilegeViolationException e) {
      e.printStackTrace();
      } catch (MethodNotSupportedException e) {
      e.printStackTrace();
      } catch (InvalidPartyException e) {
      e.printStackTrace();
      } catch (InvalidArgumentException e) {
      e.printStackTrace();
      }


      So every time i want to create a call i don’t need to repeat all this clatter code. Furthermore i use the standard Jtapi way to create a new call, instantiate a new call object from the Provider and using the connect (passing the original terminal, address and the desired dialed digits) method of this object actually place the call.

  8. @gvag

    Thanks Gvag. I will try this out.

    I need some more information. I need to get a request from the phone when there is an incoming call and I need to check my DataBase and the send the response to the phone and display the personnel information on the phoneDiaplay. Do you have any Idea how to Do this. Your help will be greatly appreciated. Thanks in advance

  9. Hi, I’d like to know how can I make outgoing calls from the IP phone in JTapi.
    Any idea?
    Thanx.

  10. I was able to place call and evertthing was fine. but after 2 weekks of using the application I was not able to place the call. Our company MIS people looked at the logs and gave a trace of the log which is below.

    027395984| 2009/10/01 14:57:33.579| 001| LnkMsgRcv | | | | | | SignalName: CtiCallStateNotify RawDataSize: 6381, NodeId: 2, AppId: 100, TCPAddr: [OBI:37365]
    027395985| 2009/10/01 14:57:33.579| 001| AppInfo | | | CTIDeviceLineMgr(1,200,18,1) | | | [CTI-APP] [Line(11)::newCallNotify] (Signal=CtiNewCallNotify State=lineState_ready LineNumber=8564, LineHandle: CtiID=1:LineID=11, LineRequestTimeout=5)
    027395986| 2009/10/01 14:57:33.579| 001| AppInfo | | | CTIDeviceLineMgr(1,200,18,1) | | | [OpenedLine(530)::newCallNotify] [CTI-APP] (Signal=CtiNewCallNotify State=inService AppLH: ctiHandlerId=1|LineID=530
    027395987| 2009/10/01 14:57:33.579| 001| LnkMsgRcv | | | | | | SignalName: CtiDeviceStateChangedNotify RawDataSize: 199, NodeId: 2, AppId: 100, TCPAddr: [OBI:37365]
    027395988| 2009/10/01 14:57:33.579| 001| AppInfo | | | CTIDeviceLineMgr(1,200,18,1) | | | [OpenedLine(530)::inService_newCallNotify] [CTI-APP] (Signal=CtiNewCallNotify State=inService AppLH: ctiHandlerId=1|LineID=530
    027395989| 2009/10/01 14:57:33.579| 001| AppInfo | | | C

    | CTIDeviceLineMgr(1,200,18,1) | | | [OpenedLine(1374)::inService_newCallNotify] [CTI-APP] (Signal=CtiNewCallNotify State=inService AppLH: ctiHandlerId=1|LineID=1374
    027395991| 2009/10/01 14:57:33.579| 001| AppInfo | | | CTIDeviceLineMgr(1,200,18,1) | | | [OpenedLine(360)::newCallNotify] [CTI-APP] (Signal=CtiNewCallNotify State=inService AppLH: ctiHandlerId=1|LineID=360
    027395992| 2009/10/01 14:57:33.579| 001| AppInfo | | | CTIDeviceLineMgr(1,200,18,1) | | | [OpenedLine(360)::inService_newCallNotify] [CTI-APP] (Signal=CtiNewCallNotify State=inService AppLH: ctiHandlerId=1|LineID=360
    027395993| 2009/10/01 14:57:33.579| 001| AppInfo | | | CTIDeviceLineMgr(1,200,18,1) | | | [OpenedLine(352)::newCallNotify] [CTI-APP] (Signal=CtiNewCallNotify State=inService AppLH: ctiHandlerId=1|LineID=352

    The CTIManager is make multiple requests for the single connection. Can somebody help me out.

    Thank you

    • Haritha,

      Can you please provide source code for your application and details of the test, such as what are you trying to do and what is the outcome. Give as much details as you can.

      Regards

  11. here is the source code I am using.

    public MakeCall(String[] args) throws Exception
    {
    String hostname = args[0];
    String login = args[1];
    String passwd = args[2];
    String src = args[3];
    String dst = args[4];

    /* start up JTAPI */
    JtapiPeer peer = JtapiPeerFactory.getJtapiPeer(null);

    /* connect to the provider */
    String providerString = hostname;
    providerString += “;login=” + login;
    providerString += “;passwd=” + passwd;
    Provider provider = peer.getProvider(providerString);

    /* wait for it to come into service */
    final Condition inService = new Condition();
    provider.addObserver(new ProviderObserver() {
    public void providerChangedEvent (ProvEv [] eventList) {
    if (eventList == null) return;
    for (int i = 0; i < eventList.length; ++i) {
    if (eventList[i] instanceof ProvInServiceEv) {
    inService.set();
    }
    }
    }
    });

    inService.waitTrue();

    /*
    * get the array of addresses that are assigned to the devices that are administered in
    * the user control list in the directory
    * */
    Address srcAddr = provider.getAddress(src);

    /* add observer to the address */
    srcAddr.addObserver(new AddressObserver() {
    public void addressChangedEvent(AddrEv[] arg0) {
    // TODO Auto-generated method stub

    }
    });

    /* create a call object */
    Call call = provider.createCall();

    /* get the terminal associated to the address*/
    CiscoTerminal terminal = (CiscoTerminal) srcAddr.getTerminals()[0];
    /* add observer to the terminal */
    terminal.addObserver(new TerminalObserver(){
    public void terminalChangedEvent(TermEv[] arg0) {
    // TODO Auto-generated method stub

    }});

    /*System.out.println("test");
    try{
    terminal.sendData("Contact NameHello XYZ”);
    }catch(Exception e){
    System.out.println(“test////////////”);
    e.printStackTrace();
    }
    System.out.println(“test”);
    /* add callobserver to the address or the terminal which is originating the call.*/
    srcAddr.addCallObserver(new CallObserver(){
    public void callChangedEvent(CallEv[] arg0) {
    // TODO Auto-generated method stub

    }});

    /*Place the call*/
    call.connect(terminal, srcAddr, dst);

    }

    When I run the above the call is placing.

    But one of the MIS people looked at the logs and they told me that multiple requests are being sent and the ports are opening which is causing the memory issue.

    If you can help me out that would be great

  12. how to wait for in-Service for terminal and srcAddr?

  13. @haritha

    Can you please more specific?

  14. Thanx for you answer time ago.

    Hi, I’m making a call between two telephones, the source and the destination; my ask is how I can detect when in the call the destination has answered or it hasn´t.

    I hope you undestand me :D
    Thanx

    • Osvaldo, you need to place a CallObserver to the call you want to monitor.
      Once you start the call you will have an array with at least two Connection objects, one Connection to your station and one Connection to the Address you are dialing. Having the CallObserver in place, you will receive a ConnConnectedEv when the Connection moves to the the Connected state.

      Let me know if you need further help.
      Regards
      George

  15. I am attempting to write a program to record phone conversations within my office. We have phone system but I thought that the simplest way to do this would be from each individual modem. I thought jtapi would be the api to do this with, am i wrong? If jtapi is not the api to use for this, then what should I use? Please point me in right direction.
    Thanks
    Kyle

    • Kyle,

      JTapi is a telephony call control API so its not suitable for what you want to do. How you should do that depends on what telephony system you have in place there.

      To give you an example, Nice call recording for Digital extensions (proprietary protocol for each vendor) is working with two links, one JTapi link for monitoring events on the extensions and one or more E1 links for recording the calls. When the Nice recording software detects a new call on a monitored extension by receiving a JTapi event for that extension, it uses JTapi again to start a conference call between the given extension and one port of the E1 trunks that are connected on the Nice software, so the recording starts.

  16. Hi,
    I really need to dial an PSTN number from my modem came with laptop and take a call to the PSTN number. After the receiver takes the call I need redirect my laptop standard voice out put to it.
    Can I do this with this JTapi ?
    If possible pls be kind enough to help me for that in coding. (pls publish the simple coding or help)
    This is for my university final semester project and I am trying to do this in java comm api only with the at commands. That path is not success and difficult to me to do. Still I am doing testing and still didn’t see any green light yet.
    Thanks,