JTapi hands-on, part II

This is the next article in the JTapi hands-on series that will present the Call and Connection interfaces from the Jtapi API. Using the Call and Connection interface, this article shows how to create and disconnect calls, and also inspect Connections of a call. This tutorial could be the base for a dialler application or a connector for a PBX with a CRM application.

Also please note that this article builds upon the previous articles JTapi Overview and JTapi hands-on, part I.

Prerequisites for the source code

In the Resources section of this articles you can find the source code for the examples. In order to compile and run the source code you need the JTapi library in your classpath and of course a PBX equipped with the appropriate JTAPI software services. For any help please leave a comment.

Use case

For the concepts and theories to be more easy to grasp, I will start by providing a specific use case to work with.

  • Given that we have a PBX with JTapi services in place, we need to develop an application that will provide users with a list of telephone numbers to dial automatically using their PC; that is no manual dialling using their telephone set.

For this we need to develop a library which will be the connector between the application and the JTapi enabled PBX. The purpose of this library will be to create calls on behalf of the users and deliver the calls to their telephone set. So for the rest of the article I will provide the necessary theory and examples with source code to develop such a library.

First will have some theory in order to get the basics of Call and Connection interface, since these two interfaces are the fundamentals in the application we want to develop.

The Call interface

The Call object represents a telephone call, the information flowing between the service provider and the call participants. A telephone call comprises a Call object and zero or more connections. In a two-party call scenario, a telephone call has one Call object and two connections. A conference call is three or more connections associated with one Call object.

In order to create a Call, an idle call object is instantiated using Provider.createCall(). Having the idle call object in place, the application must specify the originating Terminal (physical endpoint) and the originating Address (logical endpoint) of that Terminal (in the case that a Terminal has multiple telephone numbers on it). Next the application should provide the destination telephone number as a string. Two Connection objects are returned from the Call.connect() method, representing the originating and destination ends of the telephone call.

Call States

A Call, like other Jtapi objects, has a state which is obtained via the Call.getState() method. This state describes the current progress of a telephone call, where is it in its life cycle, and how many Connections exist on the Call. The Call state may be one of three values: Call.IDLE, Call.ACTIVE, or Call.INVALID. Here is a description of each state:

  • Call.IDLE: This is the initial state for all Calls. In this state, the Call has zero Connections, that is Call.getConnections() must return null.
  • Call.ACTIVE: A Call with some current ongoing activity is in this state. Calls with one or more associated Connections must be in this state. If a Call is in this state, the Call.getConnections() method must return an array of size at least one.
  • Call.INVALID: This is the final state for all Calls. Call objects which lose all of their Connections objects (via a transition of the Connection object into the Connection.DISCONNECTED state) moves into this state. Calls in this state have zero Connections and these Call objects may not be used for any future action. In this state, the Call.getConnections() must return null.

Note: The Connection states mentioned above will be discussed later in this article.

Call State Transitions

The possible Call state transitions are given in the diagram below:

Call State Transitions

Call State Transitions

Calls and Connections

A Call object 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 object retains a reference to a Connection only if it is not in the Connection.DISCONNECTED state. Therefore, if a Call object 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.

Connection interface

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.

Connection States

A connection can be in one of the states mentioned below and here is a description of each Connection state in real-world terms. These real-world descriptions have no bearing on the specifications of methods, they only serve to provide a more intuitive understanding of what is going on. Several methods in this specification state pre-conditions based upon the state of the Connection (for example see Connection.disconnect() method below)

  • Connection.IDLE: This state is the initial state for all new Connections. Connections which are in the Connection.IDLE state are not actively part of a telephone call, yet their references to the Call and Address objects are valid. Connections typically do not stay in the Connection.IDLE state for long, quickly transitioning to other states.
  • Connection.DISCONNECTED: This state implies it is no longer part of the telephone call, although its references to Call and Address still remain valid. A Connection in this state is interpreted as once previously belonging to this telephone call.
  • Connection.INPROGRESS: This state implies that the Connection, which represents the destination end of a telephone call, is in the process of contacting the destination side. Under certain circumstances, the Connection may not progress beyond this state.
  • Connection.ALERTING: This state implies that the Address is being notified of an incoming call.
  • Connection.CONNECTED: This state implies that a Connection and its Address is actively part of a telephone call. In common terms, two people talking to one another this call is represented by two Connections in the Connection.CONNECTED state.
  • Connection.UNKNOWN: This state implies that the implementation is unable to determine the current state of the Connection. Typically, methods are invalid on Connections which are in this state. Connections may move in and out of the Connection.UNKNOWN state at any time.
  • Connection.FAILED: This state indicates that a Connection to that end of the call has failed for some reason. One reason why a Connection would be in the Connection.FAILED state is because the party was busy.

Connection State Transition

With these loose, real-world meanings in the back of one’s mind, the Connection class defines a finite-state diagram which describes the allowable Connection state transitions. This finite-state diagram must be guaranteed by the implementation. Each method which causes a change in a Connection state must be consistent with this state diagram. This finite state diagram is below:

Connection State Transitions

Connection State Transitions

Note there is a general left-to-right progression of the state transitions. A Connection object may transition into and out of the Connection.UNKNOWN state at any time (hence, the asterisk qualifier next to its bidirectional transition arrow).

Connection.disconnect() method

In terms of controlling a call, an important method of the Connection interface is the Connection.disconnect() method. This method drops an entire Connection from a telephone call. The result of this method is to move the Connection object into the Connection.DISCONNECTED state.
This method drops a Connection from an active telephone call and the Connection’s Address is no longer associated with the telephone call. Important to notice is that this method does not necessarily drop the entire telephone call, only the particular Connection on the telephone call. The method provides the ability to disconnect a specific party from a telephone call, which is especially useful in telephone calls consisting of three or more parties. For example on a three party conference call, if one of the parties invoke the Connection.disconnect() method, then this party leaves the conference call, but the two parties left continue the call. Invoking this method may result in the entire telephone call being dropped though, which is a permitted outcome for this method. In that case, the appropriate events are delivered to the application, indicating that more than just a single Connection has been dropped from the telephone call.

This method returns successfully only after the Connection has been disconnected from the telephone call and has transitioned into the Connection.DISCONNECTED. Note that this method waits (i.e. the invocating thread blocks) until either the Connection is actually disconnected from the telephone call or an error is detected and an exception thrown.

Additional Connections may be dropped indirectly as a result of this method. For example, dropping the destination Connection of a two-party Call may result in the entire telephone call being dropped. It is up to the implementation to determine which Connections are dropped as a result of this method. Implementations should not, however, drop additional Connections if it does not reflect the natural response of the underlying telephone hardware.

Dropping additional Connections implies that their TerminalConnections are dropped as well. Also, if all of the Connections on a telephone call are dropped as a result of this method, the Call will move into the Call.INVALID state.

Be aware that the Connection object must be in one of several states in order for this method to be successfully invoked. These allowable states are: Connection.CONNECTED, Connection.ALERTING, Connection.INPROGRESS, or Connection.FAILED. If the Connection is not in one of these allowable states when this method is invoked, this method throws InvalidStateException. Having the Connection in one of the allowable states does not guarantee a successful invocation of this method.

Next will be discussed the structure of the project will be used for the library we need to implement the application in question.

Project Structure

The project has a bootstrap class, the ProviderService class, that provides the Jtapi Provider for the rest of the classes to use. This is convenient to have since several other classes can access the Provider (to retrieve resources, create calls etc.) without the need for each class to instantiate its own Provider object.
Next is the JTapiMakeCall class that is responsible to create and handle calls and side by side the CallTools class that provides useful information on the console about the progress of a call.

Even though this is a simple application, later can be used as a tool to create calls.

Bootstrap

As usual, the kick off of any JTapi project is the instantiation of JtapiPeer and Provider, and here is exactly that. So below is the ProviderService class that will make available the Provider to the rest of the project.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package org.devrealm.jtapitutorial2.bootstrap;
 
import javax.telephony.JtapiPeer;
import javax.telephony.JtapiPeerFactory;
import javax.telephony.JtapiPeerUnavailableException;
import javax.telephony.Provider;
 
public class ProviderService {
 
	private static ProviderService instance;
	private static Provider provider;
 
	private ProviderService() {
		bootStrap();
	}
 
	private void bootStrap(){
		try {
			JtapiPeer peer = JtapiPeerFactory.getJtapiPeer("");
			String[] myServices = peer.getServices();
                        String providerString=myServices[0]+
                                                ";login=user;passwd=passwd";
			provider = peer.getProvider(providerString);
		} catch (JtapiPeerUnavailableException e) {
			e.printStackTrace();
		}
	}
 
	public static Provider getProvider(){
		if (instance == null){
			instance = new ProviderService();
		}
		return provider;
	}
}

Having the Provider available for the rest of the project, we are ready to move on the rest of the classes.

JTapiMakeCall Class

The JTapiMakeCall class consists of 4 methods.

  • Call dial(String origAddrStr, String dialoutNumber): Accepts two strings, the original address and the dialout number to be called. The method will create the call object and will connect the original address with the number requested.
    Note: Be aware that the original address must be in the Provider’s domain but the same is not true for the dialout number. That is, the original address must be an extension of the PBX that is handled by the Provider while the dialout number could be any external number or an extension of the PBX also.
  • void disconnect(Call call): If you noticed before, the dial method returns a Call object so any other method can work on this object. So the disconnect method accepts a Call object and terminates all the connections of call, if they are eligible for disconnect. As said earlier, a connection should be in one of the allowed states in order to be disconnected.
  • Provider getProvider(): Will retrieve the Provider from the ProviderService class and make it available to the rest of the class.
  • void shutdownProvider(): Will shut down the Provider when needed

Following is the source code of the JTapiMakeCall class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package org.devrealm.jtapitutorial2.jtapimakecall;
 
import javax.telephony.Address;
import javax.telephony.Call;
import javax.telephony.Connection;
import javax.telephony.InvalidArgumentException;
import javax.telephony.InvalidPartyException;
import javax.telephony.InvalidStateException;
import javax.telephony.MethodNotSupportedException;
import javax.telephony.PrivilegeViolationException;
import javax.telephony.Provider;
import javax.telephony.ResourceUnavailableException;
import javax.telephony.Terminal;
 
import org.devrealm.jtapitutorial2.bootstrap.ProviderService;
 
public class JTapiMakeCall {
 
	private static Provider provider;
 
	public static Call dial(String origAddrStr, String dialoutNumber) {
 
		Provider provider = JTapiMakeCall.getProvider();
		Call call = null;
 
		try {
			Address myAddr = provider.getAddress(origAddrStr);
			Terminal myTerm = myAddr.getTerminals()[0];
			System.out.println("Calling from: "+origAddrStr
                                                      +" to: "+dialoutNumber);
			call = provider.createCall();
			call.connect(myTerm, myAddr, dialoutNumber);
		} catch (InvalidArgumentException e) {
			e.printStackTrace();
		} 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();
		}
 
		return call;
	}
 
	public static void disconnect(Call call) {
		Connection[] connections = call.getConnections();
 
		for (int i = 0; i < connections.length; i++) {
 
			try {
				if(checkConnectionEligibleForDisconnect
                                                           (connections[i])){
					connections[i].disconnect();
				}
			} catch (PrivilegeViolationException e) {
				e.printStackTrace();
			} catch (ResourceUnavailableException e) {
				e.printStackTrace();
			} catch (MethodNotSupportedException e) {
				e.printStackTrace();
			} catch (InvalidStateException e) {
				e.printStackTrace();
			}
 
		}
 
	}
 
 
	public static Provider getProvider() {
 
		provider = ProviderService.getProvider();
 
		return provider;
	}
 
	public static void shutdownProvider(){
		provider.shutdown();
	}
 
	private static boolean checkConnectionEligibleForDisconnect
                                                     (Connection connection) {
 
		boolean result;
 
		switch (connection.getState()) {
		case Connection.CONNECTED:
			result = true;
			break;
		case Connection.ALERTING:
			result = true;
			break;
		case Connection.INPROGRESS:
			result = true;
			break;
		case Connection.FAILED:
			result = true;
			break;
 
		default:
			result = false;
			break;
		}
 
		return result;
	}
 
}

CallTools class

This class provides some methods to get useful information about a call. The methods it provides are the following:

  • void inspect(Call call): Using this method we can retrieve information about the connectios, addresses, call state etc. of a call object
  • String connStateToString(int code): This method converts the integer code of a connection state to a string

Following is the source code of the CallTools class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package org.devrealm.jtapitutorial2.jtapicalltools;
 
import javax.telephony.Call;
import javax.telephony.Connection;
 
public class CallTools {
 
 
	public static void inspect(Call call) {
 
		Connection[] connections = call.getConnections();
 
		if (call.getConnections()!=null){
			System.out.println("Call Connections: "
                                           +call.getConnections().length);
			for (int i = 0; i < connections.length; i++) {
				System.out.println("***************");
				System.out.println("Connection["+i
                                                           +"] details: ");
				System.out.println("Connection State: "
                      + connStateToString(connections[i].getState()));
				System.out.println("Address: "
                               +connections[i].getAddress().getName());
				System.out.println("Call State: "
            +callStateToString(connections[i].getCall().getState()));
			}
		} else {
			System.out.println("Null Connections");
		}
	}
 
	private static String connStateToString(int code){
 
		String result = null;
 
		switch (code) {
		case Connection.ALERTING:
			result = "ALERTING";
			break;
		case Connection.CONNECTED:
			result = "CONNECTED";
			break;
		case Connection.DISCONNECTED:
			result = "DISCONNECTED";
			break;
		case Connection.FAILED:
			result = "FAILED";
			break;
		case Connection.IDLE:
			result = "IDLE";
			break;
		case Connection.INPROGRESS:
			result = "INPROGRESS";
			break;
		case Connection.UNKNOWN:
			result = "UNKNOWN";
			break;
		default: 
			result = "UNKOWN";
			break;
		}
		return result;
	}
 
	private static String callStateToString(int state) {
 
		String result = null;
 
		switch (state) {
		case Call.ACTIVE:
			result = "ACTIVE";
			break;
		case Call.IDLE:
			result = "IDLE";
			break;
		case Call.INVALID:
			result = "INVALID";
			break;
 
		default:
			result = "UNKNOWN";
			break;
		}
 
		return result;
	}
 
}

Last is the Dialout class which contains the main method that put together all the pieces.

Dialout class

The purpose of the Dialout class is to get the original address, the destination number and using the previous classes to make the call.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package org.devrealm.jtapitutorial2.dialout;
 
import javax.telephony.Call;
 
import org.devrealm.jtapitutorial2.jtapicalltools.CallTools;
import org.devrealm.jtapitutorial2.jtapimakecall.JTapiMakeCall;
 
public class Dialout {
 
	public static void main(String[] args) {
		String origAddrStr = "1234";
		String dialoutNumber = "00123456789";
 
// Optionally you can accept the original address 
// and dialout number from the command prompt
//		String origAddrStr = args[0];
//		String dialoutNumber = args[1];
 
		Call call = JTapiMakeCall.dial(origAddrStr, dialoutNumber);
		CallTools.inspect(call);
 
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("Disconnect Call");
		JTapiMakeCall.disconnect(call);
		System.out.println("...............");
		CallTools.inspect(call);
		JTapiMakeCall.shutdownProvider();
	}
}

Conclusion

The Call and Connection interface are straight forward and easy to work with and the documentation provided is well written. Using these two interfaces we can easily integrate an application with a PBX and give a production boost to a call centre.
Going back to the use case defined at the begining of the article, the connector for the auto-dial functionality and integration with the PBX is ready to use. Of course that kind of application needs a lot more to be complete but this library is one of the most, if not the most, important part of the application.
What is left to see is how we can answer, transfer, conference and observe calls so we can give a call centre with full set of automatic tools to handle both incoming and outgoing calls. Specially the observers that the JTapi API provides are useful to implement applications that observe addresses, terminals or calls for incoming traffic and captures details of the caller that can be used to popup the agent’s desktop with the caller details.

Finally please share any comment or idea derived from this article since that’s the driving power of thought and any effort made here.

[print_link]

Resources

Tags: , , ,

13 comments

  1. Do you have an example of how an incoming call can be alerting?

    i want listen to an terminal. when anybody call the terminal i want put a string into my console!

    thank you!!!

  2. Do you have an example of how an incoming call can be alerting?

    i want listen to an terminal. when anybody call the terminal i want put a string into my console!

    Im using the generic jtapi framework!

    thank you!!!

    • Hb,

      You should place an Address Observer in the address of the terminal that the incoming call will terminate. Then you should examine each CallEvent for useful information such as the ANI etc.
      Look at the JavaDoc of JTapi for the Address observer.

  3. hey gvag,

    it doesen’t work :(

    I have these two classes:

    ## PhoneObserver.java ##
    […]
    @Override
    public void addressChangedEvent(AddrEv[] eventList) {
    System.out.println(“CALL”);
    }
    […]

    ## Phone.java: ##
    […]
    peer = JtapiPeerFactory.getJtapiPeer(“net.sourceforge.gjtapi.GenericJtapiPeer”);
    prov = peer.getProvider(providerName);

    obsListener = new PhoneObserver();

    address = prov.getAddress(listeningAddr);
    address.addObserver(obsListener);
    […]

  4. Sorry,
    the PhoneObserver implements AddressObserver!

    many thanks for your help!

    • Good to know that you found out the solution. Would you be interesting to write a short description of the project you are working on? short of a JTapi case study.

  5. No, i don’t find the solution, it doesn’t work!

    the code i posted does not work :(
    and i can’t find the mistake!

    The Method addressChangedEvent is not called when a call comes in.
    I just want that when someone calls my number, this indicates, for
    Example in the Console.

  6. http://blog.nominet.org.uk/tech/2008/01/25/experiments-with-jtapi-part-1-making-a-call/ is a example for your solution. i take all classes of gvag and take extra code:
    Address srcAddr = provider.getAddress(src);
    srcAddr.addCallObserver(new CallObserver() {
    public void callChangedEvent (CallEv [] eventList) {

    }
    });

    it work fine.

    About the JTAPI case study i have an interesting document for you but it speak on german: Jtapi thomanek,jan (search with goole)

  7. @pink
    Danke Pink, ich nehme an dass du auch aus Deutschland kommst! Ich werde es damit mal versuchen und lasse es Dich wissen ob es funktioniert hat! Die Diplomarbeit ist super ich werde mir Sie mal durchlesen! Vielen Dank!

    Englisch:
    Thanks Pink, I assume that you also come from Germany! I will therefore
    try it and let you know whether it worked! The thesis
    is perfect, I will read it! Thank you very much!

    @gvag
    Thank you, that this site exists! It really helps me on!

  8. hi hb, ich komme aus Vietnam aber arbeite und studiere in Deutschland. Zur Zeit muss ich auch für CiscoJTAPI programmieren. Viel Spaß mit JTAPI

    English: hi hb, i come from viet nam but study and work in Germany. I have been working with CiscoJTAPI. Have fun with JTAPI

  9. hello.. could you please be more specific what’s really needed to get this work. i have jtapi.jar… whats needed more? error is -> javax.telephony.JtapiPeerUnavailableException: JtapiPeer: DefaultJtapiPeer could not be instantiated.

    i can use my alcatel through some other examples but not with java. whats this pbx? many thanks in advance for any help. best regards, toni

    • First make sure your PBX supports JTapi services (if your PBX supports TAPI doesn’t meen that can support JTapi), second make sure that the jtapi.jar library you have in hand supports the JTapi services you have. Third please provide the source code that causes the error. From the error you provided seems that you are trying to do something similar to “JtapiPeer peer = JtapiPeerFactory.getJtapiPeer(“”);” – that is passing no argument. Some time, a vendor specific implementation of JTapi requires you to ask for the JTapiPeer passing a specific argument. For example, for Avaya JTapi services, you need the following :
      JTapiPeer jtapiPeer = JtapiPeerFactory.getJtapiPeer(“com.avaya.jtapi.tsapi.TsapiPeer”).

  10. @gvag
    thank you for your response! i didn’t know which dependency are really necesseary… but
    i found out which *.jar and dll were needed. If someone else need this informations,
    in class path:
    – Tapi3.gtapi ->configuration-file
    – log4j.props ->configuration-file
    – GenericResources.props ->configuration-file

    in lib path:
    – gjtapi-1.9-rc1.jar
    – gjtapi-tapi3-1.9-rc1.jar
    – jtapi-1.3.1.jar
    – log4j-1.2.12.jar

    somewhere referenced – for ex C:windowssystem32
    – Tapi3Provider.dll

    of course you need your telefon-driver installed.