Experiments with JTAPI - Part 1 - Making a call
We have a Cisco CallManager (CCM) VoIP telephone system here at Nominet and I’m currently investigating how this system might be adapted to support ENUM lookups for outbound calls so that calls can be made directly to other VoIP systems without going over the PSTN. There’s no built-in support for ENUM in CCM at all, so some sort of plugin would be required.
I’ve not been able to find any internal plugin APIs, but CCM does support most of Version 1.2 of the Java Telephony API. My hope is that this API will provide tight enough integration with the system to allow the necessary outbound call routeing.
I found that there are very few examples around of how to use JTAPI to actually do anything. Cisco does provide a sample application, but it’s unnecessarily complicated. The code below is therefore presented as a much simplified version of that application that does nothing except tell a CCM extension to dial a specified number.
To use this code you’ll need to obtain the jtapi.jar file from the CCM Administration software via Applications->Plugins->JTAPI. You’ll also need to create a new “Application User”, and add that user into the JTAPI Groups, and also grant that user access to some terminals.
With jtapi.jar in your CLASSPATH, this application can be run as:
% java MakeCall {host} {user} {password} {extension} {number}
As soon as the program has told CCM to place the call it will exit.
As this is only an example there’s only minimal input and error checking, but the application will generate an appropriate error message if you supply incorrect JTAPI login details, or if you try to trigger a call from an extension that your JTAPI user doesn’t have access rights to.
There are two specific parts of the code that deserve special mention:
- The com.cisco.cti.util.Condition class is not part of the JTAPI standard, but provides a useful semaphore object. In this case it’s used to make the main thread block until the ProvInServiceEv event is received by the anonymous ProviderObserver object.
- It appears that it’s always necessary to add a CallObserver to the Address object (even if that observer subsequently does nothing useful itself) since the JTAPI library throws an exception if you don’t. I couldn’t find this behaviour documented anywhere.
MakeCall.java
import javax.telephony.*; import javax.telephony.events.*; import com.cisco.cti.util.Condition; public class MakeCall { 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 an object for the calling terminal */ Address srcAddr = provider.getAddress(src); srcAddr.addCallObserver(new CallObserver() { public void callChangedEvent (CallEv [] eventList) { /* ignored */ } }); /* and make the call */ Call call = provider.createCall(); call.connect(srcAddr.getTerminals()[0], srcAddr, dst); } public static void main(String[] args) { try { new MakeCall(args); } catch (Exception e) { e.printStackTrace(); } finally { System.exit(0); } } }

(13 votes, average: 4.46 out of 5)
(2 votes, average: 4 out of 5)



