FAQ: Meeting Exchange 5.1 and Later (ACPI)

Last Release: 6.2 SP6 (Sept 2018)

Frequently Asked Questions

This document contains the Frequently Asked Questions abaout the use of the Avaya Conferencing Provider Interface (ACPI) in Meeting Exchange Release 5.1 and later releases.

General

The Avaya Conferencing Provider Interface (ACPI) is an object-oriented, programming interface, written in Java. ACPI can be used by any application that can make a Java call and supports Java version 5.0 or newer.

ACPI consists of two components:

  • Avaya Conferencing Provider Application Programming Interface, also known as ACP API.
  • Implementations of ACP API.

The first available implementation of ACP API is ACP-MODAPI-IMPL. MODAPI is a proprietary Avaya protocol used to control Meeting Exchange conferencing providers. It can be used to interface with the following supported conferencing providers:

  • Meeting Exchange 5.1 S700/780 Audio Conferencing Servers
  • Meeting Exchange 5.1 S6200 and S6800 Audio Conferencing Servers

The ACP API SDK includes the following JAR files and a detailed reference guide (javadocs):

acp-api-versionnumber.jar
acp-modapi-impl-versionnumber.jar
modapi-versionnumber.jar
acpl-versionnumber.jar

Note: versionnumber is the version number for each JAR file.

The above JAR files are required to be on the classpath for ACP API.

In addition, the programmer's guide 'Meeting Exchange 5.0 Bridge Control API (BCAPI) Guide' provides a detailed description of the Bridge Control API methods and how to use them to manage conferences. The ACPI SDK, BCAPI SDK and programmer's guide are available on the DevConnect portal.

ACP API has a minimum system requirement of Java version 5.0 or above.

Recommended reading and reference for developers includes the following documentation available on the DevConnect portal:

Programmer's Guides:

  • Avaya Conferencing Provider Interface (ACPI) User's Guide
  • Meeting Exchange? 5.0 Bridge Control API (BCAPI) Guide

Javadocs:

  • acp-examples-5.1.0.0.61.zip

Sample Applications:

  • Detailed technical description of ACP API interfaces (included in acp-modapi-impl-dist-5.1.0.0.61.zip)

ACP API is a java based interface which is extensible and minimizes compile time and run time dependencies. It is a superset of BCAPI and supports all BCAPI conference control functionality in addition to new operator functionality. It is the recommended Bridge Control API for new customer development as it has better performance and functionality than BCAPI.

ACP API exposes the ability to control Conferences, Callers and Queues. This can used to write applications like BridgeTalk, Avaya Audio Console, etc

Bridge Connections

An application uses the ConferencingProviders.getConnection () method to connect to the conference bridge. The ConferencingProviders is used to get the Connection via Java SPI mechanism and requires a set of bridge parameters.

How does an application connect to the MX Enterprise 5.1 conference bridge?

  • connectionUrl - The connection URI (java String)
  • username - The username to connect with (java String)
  • password - The password to connect with (java char [])

The ACP-MODAPI-IMPL supports two URI syntax schemes:

  • modapi: For connection to S700/S780 Audio Conferencing Servers or for connection to S6200/S6800 Audio Conferencing Servers if the non-SSL version of the modapi protocol has been enabled.
  • smodapi: For connection to S6200/S6800 Audio Conferencing Servers using the SSL encrypted version of the modapi protocol.

The syntax for the modapi protocol is:
modapi://ip-or-name[:cmdport][;nat=on[;tcp=port;udp=port]]

The syntax for the smodapi protocol is:
smodapi://ip-or-name[:cmdport][;nat=on[;tcp=port;udp=port]]

By default, the command port (cmdport) is 20002 for modapi and 20004 for smodapi. The default UDP port for nat connections is 20008 while the TCP port is 20008 for modapi and 20010 for smodapi.

Some valid URI examples are:

  • smodapi://bridge_name.company_name.com
  • modapi://10.1.1.56;nat=on

Please refer to ACP API javadocs and sample examples for further details.

There is a bridge limit on the number of operators / API connections. The maximum limit is 140. These can be split between BridgeTalk, ACPI and BCAPI, but the total is still 140. In addition, the bridge may have been licensed / restricted to a lower limit.

The bridge has a hard limit of 2000 conference rooms (or possibly the bridge port capacity divided by 2, whichever is smaller).

No, it will use the next available port. It starts at 5020 and works its way up to 5040, after which it gives up and tries random ports above 0xB000

Once the limit has been reached, no new conferences can be created. Attempts to create new conferences will fail.

The "modapi" and "smodapi" protocols communicate over three network connections:

A TCP connection from the client (from any unused port) to the conferencing provider (by default, to either port 20002 for modapi or 20004 for smodapi) used to send operations for execution to the conferencing provider.

A TCP connection from the conferencing provider (from any unused port) to the client (by default, to the first unused port in the range 5020 - 5040, otherwise any unused port) which is used to receive events from the conferencing provider.

A UDP connection from the conferencing provider (from 20008) to the client (by default, to the first unused port in the range 5020 - 5040, otherwise any unused port) which is used to receive active talker information from the conferencing provider.

Common ACPI Troubleshooting

This exception is thrown when the ACP API cannot find an implementation. There are two main causes for this exception:

  1. A typographic error in the connection URL where the protocol has been specified incorrectly. The solution is to correct the connection URL.
  2. A class-loading error preventing ACP API from getting any implementations. ACP-API uses the Java SPI mechanism (http://java.sun.com/j2se/1.5.0/docs/guide/jar/jar.html#Service%20Provider) to discover the implementations that are available.

By default, ACP API will use the class loader Thread.currentThread().getContextClassLoader() to search for ACPI implementations. Alternatively, the class-loader can be specified by using the alternate ConferencingProviders.getConnection() method which takes the class-loader to use as a parameter.

Most JavaSE applications do not modify the Thread.currentThread().getContextClassLoader(), for this class of application, the solution to this problem is to ensure that all the required JAR files for the ACPI implementation supporting the required protocol are on the class-path.

JavaEE applications run within a JavaEE container. JavaEE containers are designed to host multiple applications at the same time and often control the class-loaders of applications in order to isolate the applications running inside the container. For this class of application, it may be necessary to consult the JavaEE container's documentation to determine the deployment descriptor(s) and/or container configuration settings that should be applied in order to ensure that all the required JAR files for the ACPI implementation supporting the required protocol are on the class-path of the Thread.currentThread().getContextClassLoader().

This may occur due to the following reasons:

  • Check the firewall settings on the client to ensure that the TCP & UDP connections from the conferencing provider are not blocked.
  • If the conferencing provider and the client are separated by a NAT router, enable NAT traversal mode, whereby all connections are established by the client. To enable NAT traversal mode append ";nat=on" to the connection URL.

Migration from BCAPI to ACPI

ACPI is the new Avaya API for real time conference control replacing BCAPI. In order to allow customers to migrate pre-5.1 releases of Meeting Exchange applications developed using BCAPI, the ACPI comes with a BCAPI legacy adapter that converts BCAPI objects, events and command model into corresponding ACPI objects, event and operation model. Due to limitations in the BCAPI design, it is not possible to expose the new functionality provided in ACPI through BCAPI. Therefore customers wishing to access new APCI functionality need to rewrite the conference control code to use ACPI.

No, ACPI does not support connections to Meeting Exchange 4.1 or Meeting Exchange 5.0.

During an upgrade to Meeting Exchange 5.1, the following API related issues that must be addressed:

  1. The BCAPI legacy adapter can only be used with Meeting Exchange 5.1 because it depends on ACPI which requires Meeting Exchange5.1.
  2. BCAPI implementations cannot connect to a Meeting Exchange 5.1 bridge using the SSL encrypted protocol.

Mixed Meeting Exchange systems

Yes, in a scenario of mixed environment comprising of Meeting Exchange 4.1, Meeting Exchange 5.0 and Meeting Exchange 5.1 bridges, the following steps should be followed:

  1. The BCAPI implementation for Meeting Exchange 5.0 must be used.
  2. Disable the firewall rule on the Meeting Exchange 5.1 bridges that block the non-SSL version of the modapi protocol.
  3. Replace the Meeting Exchange 5.0 BCAPI with the 5.1 BCAPI legacy adapter once all the Meeting Exchange bridges have been upgraded to version 5.1.
  4. Enable the firewall rule blocking the non-SSL version of the 'modapi' protocol when all the Meeting Exchange 5.0 BCAPI implementations have been replaced by the BCAPI legacy adapter.

ACP API Operation fundamentals

Before an operation can be executed, one needs to answer three questions:

  1. What is the object to execute the operation on?
  2. Who is executing the operation?
  3. What is the operation to execute?

Once the above parameters are available, one can then execute the operation as
object.executeAs(executor,operation);

If the application needs to execute operations without reference to a specific user/executor authentication, then the operation can be executed using the available connection object as shown below:

object.executeAs(connection, operation) or object.execute(operation)

Conference participants and moderators can mute lines in several ways, but only the endpoint participant can unmute themselves. A line can be in a muted state as a result of participant actions or applications invoking ACPI methods.

The line is muted as a result of lecture-mode or when the application invokes operationFactory.newMute().on() using ACP API . The line stays muted until the line is disconnected or endPoint participant unMutes themselves.

  • Self Mute: Participants may mute themselves using DTMF keys or by calling endPoint.executeAs(endPoint.getAuthentication(), factory.newMute().off()) from an ACPI application. The endPoint is authenticated because only the endpoint participant can unmute themselves
  • AnnunciatorMute: When a line is being played a message, the line is muted while the message is being played.

Once there is an established Connection with the bridge, one can use it to get an operationFactory and invoke a Silence operation as shown below:

Connection connection = ...;
EndPoint endPoint = ...;
OperationFactory factory = connection.getOperationFactory();

// silence the end-point
Silence silenceOp = factory.newSilence().on();
endPoint.execute(silenceOp);

// un-silence the end-point
Silence silenceOp = factory.newSilence().off();
endPoint.execute(silenceOp);

One can also toggle between both the above states as shown below:

// toggle the silence state
Silence silenceOp = factory.newSilence().toggle();
endPoint.execute(silenceOp);

Once there is an established Connection with the bridge, one can use it to get an operationFactory and invoke a Hold operation as shown below:

Connection connection = ...;
EndPoint endPoint = ...;
OperationFactory factory = connection.getOperationFactory();

// put the end-point on hold
Hold holdOp = factory.newHold().on();
endPoint.execute(holdOp);

// take the end-point off hold
Hold holdOp = factory.newHold().off();
endPoint.execute(holdOp);

One can also toggle between both the above states as shown below:

// toggle the hold state
Hold holdOp = factory.newHold().toggle();
endPoint.execute(holdOp);

Once there is an established Connection with the bridge, one can use it to get an operationFactory and invoke a Hold operation as shown below:

Connection connection = ...;
EndPoint endPoint = ...;
OperationFactory factory = connection.getOperationFactory();

// ensure the participant is a moderator
Moderator roleOp = factory.newModerator().on();
endPoint.execute(roleOp);

// ensure the participant is a conferee
Moderator roleOp = factory.newModerator().off();
endPoint.execute(roleOp);

One can also toggle the participant between moderator and conferee as shown below:

// toggle the participant between moderator and conferee
Moderator roleOp = factory.newModerator().toggle();
endPoint.execute(roleOp);

Once there is an established connection with the bridge, one can invoke the method getRooms() on the connection object and it will return an array of type Room. This array can be queried or iterated to get the size which is basically the number of sub-conference rooms as shown below:

Connection connection = ...;
// get all the sub-conference rooms supported by the conferencing provider.
Room[] rooms = connection.getRooms();

if (rooms.length == 0) {
// subconferencing is either disabled or not supported
} else {
// sub-conferencing is both enabled and supported and the size of the
// Room array gives the total number of available sub-conferences
}

Once a connection to a conference bridge is established, invoke the connection.getOperationFactory() method to get an OperationFactory object which can be used to move endpoints between conferences. The code for moving endpoints between conferences is shown below:

Connection connection = ...;
Conference conference = ...;
EndPoint endPoint = ...;
OperationFactory factory = connection.getOperationFactory();

// move end-point into conference
PlaceConference op = factory.newPlaceInConference();
op.setDestinationConference(conference);
endPoint.execute(op);

It is similar to moving endpoints between conferences. Once a connection to the bridge is established, invoke the getOperationFactory() method to get an OperationFactory object. It is followed by operationFactory.newPlaceInSubconference() and invoke the PlaceInSubconference.setRoom() method to set the sub-conference to move into.

Connection connection = ...;
Room room = ...;
Room otherRoom = ...;
EndPoint endPoint = ...;
OperationFactory factory = connection.getOperationFactory();

// move end-point into subconference
PlaceInSubconference op = factory.newPlaceInSubconference();
op.setRoom(room);
endPoint.execute(op);

// get the subconference
Conference subConference = op.get();

// move the end-point to a different subconference
op = factory.newPlaceInSubconference();
op.setRoom(otherRoom);
endPoint.execute(op);

// move the end-point back to the main conference
endPoint.execute(factory.newPlaceInMainConference());

Because all objects in Avaya Conferencing Provider API (ACP-API/ACPI) are remote, rather than update the object directly the application will need to execute a ModifyDetails operation. This (as with all Avaya Conferencing Provider API (ACP-API/ACPI) operations) creates a Future which is executed upon a specific object. The operation is then dispatched to the bridge and the result retrieved later.

The first thing to note is that ModifyDetails is a masked operation – that means that it only operates on the specific values selected and thus you need only addproperties for elements that you wish to change; all other properties will remain unchanged. You will note from the javadocs that the addPropertyValue and setPropertyValues take String to object mappings. These map the property name (the String) to its value (the object).

If we look at EndPoint (as an example) we will see things like ACTIVE, FROM_DISPLAY, FROM_NUMBER, NAME, etc., which correspond to the properties of the EndPoint. We can then reference the appropriate get operation in order to see the defined type required for that property’s value. So, for example, the CUSTOM_FIELDS property has a corresponding getCustomFields function which returns a List<String> and, by association, we know the ModifyDetails operation must supply a List<String>. If we test this “get” function we will also note that it is a List of length three, corresponding to the three custom fields available.

The following snippet illustrates how to update the CUSTOM_FIELDS of an EndPoint:

                /*[ ... ]*/
                ModifyDetails modifyOperation = operationFactory.newModifyDetails() ;
                HashMap<String,Object> details = new HashMap<String,Object>() ;
                List< string > customFields = new Vector< string >() ;
                customFields.add( "Field 1" ) ;
                customFields.add( "Field 2" ) ;
                customFields.add( "Field 3" ) ;
                details.put( EndPoint.CUSTOM_FIELDS, customFields );
                modifyOperation.setPropertyValues( hashMap ) ;
                try {
                endPoint.execute(modifyDetails).get() ;
                } catch (InterruptedException e1) {
                e1.printStackTrace() ;
                } catch (ExecutionException e1) {
                e1.printStackTrace() ;
                }
                /*[ ... ]*/
                < /string >< /string >
            

NOTE: It's important to note that many Conferencing objects reflect status and, although it may seem intuitive to use the NAME property to store a “name”, you should use the CUSTOM_FIELDS to store application specific information.

NOTE: It is prudent to setValidating to true in order to prevent partial updates.

The behaviour is correct. Although the extended message suggests that the operation will be available in the future ...

Complete exception message from ModifyDetails operation:

com.avaya.conferencing.api.acp.control.exceptions.OperationNotEnabledException: \
                MODIFY_DETAILS operations on class \
                com.avaya.conferencing.api.acp.control.EndPoint \
                objects are allowed, howewever, they are not \
                currently allowed on \
                acp-control:op1483@smodapi://mx-server:20004/ep/c5682
            

... such a request will, in fact, be persistently rejected. This is because, although an operator’s EndPoint is modelled with the same object as a participant EndPoint, they are functionally quite different. The application should not attempt to update an operator’s EndPoint. Instead, where the application needs custom properties for operators in your application, you should model and manage them locally (i.e. within your application).

This is a common problem; fundamentally, you might think of the bridge objects as existing in many places at the same time – most significantly, they exist both on the bridge AND in conferences; or, to state it inversely, they remain on the bridge, even when they are in a conference.

The most common manifestation of this is reception of duplicate events when the application uses generic, top-level listeners, instead of using the specific event listener functions provided. The following snippet illustrates a common (but incorrect) means of listening for children on the Connection object.

Snippet illustrating the creation of a "generic" ChildListener on the Connection

                /* ... */
                public class
                GlobalChildListener
                extends ChildListener
                {
                @Override
                void childAdded( ChildEvent event )
                /* ... */
                @Override
                void childRemoved( ChildEvent event )
                /* ... */
                }
                /* ... */
                public class
                Application
                {
                Connection bridge ;
                GlobalChildListener bridgeSpy ;
                /* ... initialise ACP-API connection ... */
                bridge.addChildListener( bridgeSpy );
                /* ... */
                }
                /* ... */
            

When this application adds an EndPointListener to Conference objects (as in the snippet below) it will start to receive duplicate events. This is because the EndPoint and Conference objects are children of the Connection, whilst the EndPoint is also a child of the Conference.

Snippet illustrating adding the EndPointListener to the Conference object

                /* ... */
                public class
                GlobalChildListener
                extends ChildListener
                {
                @Override
                void childAdded( ChildEvent event )
                {
                if( event.getChild() instanceof Conference )
                {
                Conference c = (Conference)event.getChild() ;
                c.addChildListener( new GlobalChildListener() ) ;
                }
                /* ... */
                }
                /* ... */
                }
                /* ... */
            

This is further exasperated when we introduce the *PropertyChangeListeners which monitor property changes on child objects.

The solution is to manage where and how the application listens for changes; specifically, the application should use the add listener functions (e.g. addConferenceListener, addEndPointListener) wherever possible, rather creating generic child listeners. It is thus implied that listening for EndPoint objects on the Connection is discouraged (because there is no function for it) and by extension, the application should addConferenceListener, and then addEndPointListener to those Conference objects. The following illustrates the recommended implementation.

Snippet illustrating correct use of listeners for Conference and EndPoint objects

                /* ... */
                public class
                EndPointListener
                implements com.avaya.conferencing.api.acp.control.events.ConferenceEndPointListener
                {
                @Override
                public void
                childAdded( ChildEvent addEvent )
                {
                /* ... do something to endpoint upon arrival in conference ... */
                }
                /* ... */
                }
                /* ... */
                public class
                ConferenceListener
                implements com.avaya.conferencing.api.acp.control.events.ConferenceListener
                {
                @Override
                public void
                childAdded( ChildEvent addEvent )
                {
                addEvent.getChild()
                .addEndPointListener(
                new EndPointListener() ) ;
                }
                /* ... */
                }
                /* ... */
                public class
                Application
                {
                public static void
                main( String args[] )
                {
                Connection bridge ;
                /* ... connect to bridge ... */
                bridge.addConferenceListener(
                new ConferenceListener() ) ;
                /* ... */
                }
                }