This document gives a brief overview of the design of Asterisk-Java and the requirements that lead to this design.
When I first stumbled upon Asterisk there haven't been many options to integrate your own applications.
First of all you had the possibility to write your own Asterisk applications. That's the way many internal functions are implemented, for example Asterisk's Dial, Voicemail and Queue commands. This approach usually requires writing a small module in C that uses Asterisk's internal API. It is compiled into a shared object that is loaded when Asterisk starts. Your application is then tightly coupled to Asterisk but has access to any internal structure.
The second approach is to write AGI (Asterisk Gateway Interface) scripts. An AGI script is to Asterisk what a CGI script is to an HTTP server. You can think of AGI as a means to make your dialplan more dynamic by handing control to an external process. As with CGI in its early days Asterisk had to spawn a new short lived process for each AGI request. When using external application that consume a lot of resources at startup (like spawning a Java Virtual Machine when launching a Java application) this approach has major drawbacks. These shortcoming have been mitigated with the introduction of FastAGI that doesn't spawn a new process for each request but communicates with a long running process via TCP/IP.
The third approach is to use Asterisk's Manager API. The Manager API allows an external application to query and change Asterisk's state by sending actions and listening to responses and events. Communication occurs via TCP/IP. Compared to AGI the Manager API works without the need to operate on a concrete call (in Asterisk parlance a channel).
The last approach is to talk to Asterisk via IAX (Inter Asterisk eXchange protocol), a UDP based protocol used by Asterisk to communicate with other Asterisk servers and IAX enabled phones. In contrast to AGI and the Manager API IAX allows passing media streams, for example sending and receiving audio.
Regarding the first approach a JNI (Java Native Interface) based solution (JAsterisk) is available that spawns a Java Virtual Machine from within Asterisk and exposes Asterisk's internal API. Development of JAsterisk seems to be discontinued. Due to the involved tight coupling this approach is questionable anyway.
Regarding option two and three I realized there were two implementations of FastAGI for Java but none for the Manager API. So I decided to build a library to make those interfaces accessible from Java applications. This is what became Asterisk-Java.
Regarding IAX there is a C library available and a Java wrapper using Java Native Interface (JNI). Several people are working on a pure Java implementation though up to now no usable implementation is available to the public.
So what are the requirements for a library like Asterisk-Java that focuses on AGI and the Manager API?
In one sentence: Asterisk-Java must be flexible, robust and easy to use and maintain.
Flexible means you should be able to use it in a variety of different environments, like standalone Java GUI applications running on a user's desktop, servlets running in a J2EE servlet container, Java webstart applications, or maybe even applets if anybody is still using them. Being flexible also means you should have access to any feature exposed by the corresponding Asterisk interface and not be limited in any way due to using Asterisk-Java. And finally being flexible also means it should be easy to extend Asterisk-Java to support additional features. This extensibility is important when you choose to run a modified version of Asterisk that supports additional actions and events through the Manager API or additional commands via AGI.
Robust means you can focus on your own application and rest asured that Asterisk-Java "just works". This implies you don't have to worry about things like reconnecting after restarting Asterisk. Being robust also means that Asterisk-Java must still work as expected when put under heavy load.
Easy to use means there should only be a small number of well defined and easy to understand classes and interfaces that an application developer must be aware of. The programming model should follow the style Java developers are used to.
And finally easy to maintain means being well structured and allowing for automated testing. Of course being easy to maintain also requires extensibility as stated above as Asterisk itself is in active development and new features that Asterisk-Java must keep track of are added all the time.
Let's have a look at Asterisk-Java's overall design.
You might have noticed that Asterisk-Java is composed of two packages: one to support AGI and one to support the Manager API. Although these two packages are quite different (due to the conceptual differences in the Asterisk interfaces they support) they share several common design principles. This section will highlight them before we dive into the details of the two packages.
Asterisk-Java is based on interfaces. As you will see below starting to use Asterisk-Java usually implies getting an implementation of the interface you want to use (i.e. AGI-based or Manager API-based). This approach allows you to easily integrate Asterisk-Java into IoC (inversion of control) containers like the Spring Framework. It decouples your application from the internal implementation of Asterisk-Java and supports testability as you can easily substitue Asterisk-Java by some mock objects. You can see examples of what that means when looking at the source of Asterisk-Java's unit tests where I am using EasyMock for that purpose.
To provide extensibility the commands and replies (for
AGI) and actions, repsonses and events (for Manager API) are
designed as class hierarchies with one abstract base
class at the top of each one. Asterisk-Java does not need to know
the concrete classes upfront so you are free to extend these
hierarchies as needed. There is a little difference in how
the two packages implement this strategy, but the general
concept is the same.
Note that this looks like the GoF Command Pattern and in
fact both are fairly similar. The
difference is that Asterisk-Java's commands and
actions are not self-executable. Instead of an
execute method there is a uniform way to "render" them
to the format Asterisk understands (i.e. convert them to a
String that can be sent over the wire). When using AGI this
is done by the AGICommands themselves that provide
a buildCommand()
method, when using the
Manager API there is an ActionBuilder that renders
ManagerActions by using reflection.
Asterisk-Java depends on no external libraries at runtime. This decision is crucial when using Asterisk-Java in special environments where size matters. It also ensures that no conflicts arise from different components requiring different version of the same external library.
This section describes the design of Asterisk-Java's support
for FastAGI. The corresponding Java package is called
net.sf.asterisk.fastagi
.
As you can see on the diagram the AGI package of Asterisk-Java consists of three fundamental interfaces: AGIServer, AGIScript and MappingStrategy.
The responsibility of an AGIServer is to listen for new
incoming AGI requests that an Asterisk server directs to
that server. The AGIServer must then choose the right
processor for that request, invoke it and provide it with
a means to send commands to Asterisk and receive the
corresponding reply.
Asterisk-Java includes DefaultAGIServer that contains the
default implementation of this interface.
To choose the right processor for an AGIRequest, the
AGIServer uses a MappingStrategy that returns an executable
AGIScript based on the request.
Asterisk-Java includes a
simple implementation of a MappingStrategy that is
based on reading a resource bundle and matching the URL.
It is called ResourceBundleMappingStrategy. If you
have other requirements regarding the mapping you can provide
your own implementation of the MappingStrategy interface.
The third important interface in this package is the
AGIScript. An AGI Script is the piece of code that is
invoked to service the request. An AGIScript is to
Asterisk-Java what a servlet is to a servlet container.
So when you want to expose your own Java based services
to Asterisk you must implement this interface. The AGIScript
interface is really simple: It contains only one method called
service()
that is passed the AGIRequest and an
AGIChannel, that allows you to send AGICommands back to
Asterisk.
Asterisk-Java also includes an abstract base class that
implements the AGIScript interface and provides convenience
methods for sending commands. You can choose whatever
you like more: either implementing AGIScript or extending
AbstractAGIScript works fine.
This section describes the design of Asterisk-Java's support
for the Manager API. The corresponding Java package is called
net.sf.asterisk.manager
.
The important interfaces of Asterisk-Java's Manager API package are ManagerConnection, ManagerEventHandler and AsteriskManager.
In contrast to AGI communication between the Asterisk
server and your application is not initiated by Asterisk
when using the Manager API but has to be initiated by you.
Asterisk-Java provides the ManagerConnection interface that
is well suited for this purpose. Its responsibility is to
establish a TCP/IP connection to Asterisk (and reestablish
that connection in case of a restart of Asterisk or a network
failure), to enable its clients to send ManagerActions
and receive the corresponding ManagerResponses and to
dispatch events received from Asterisk.
Once you are connected to
Asterisk you can not only actively send ManagerActions but
you will also receive MangerEvents from Asterisk.
ManagerEvents usually inform your application about state
changes that occur within your Asterisk server, for
example due to a user dialing an extension or hanging
up the phone. To recieve these events you can implement
the ManagerEventHandler interface and register your
handler with the corresponding ManagerConnection.
Instances of the MangerConnection interface are usually obtained from the ManagerConnectionFactory unless you are using an IoC container. Asterisk-Java provides a default implementation of the ManagerConnection interface called DefaultManagerConnection.
Finally there is the AsteriskManager interface that
provides a higher level abstraction on top of the
ManagerConnection. It keeps track of Asterisk's state
by listening to all state changing events and provides
support for domain objects like Channel, Call and Queue
that make it easier for your application to interact
with Asterisk without diving into details.
Asterisk-Java provides a default implementation of the
AsteriskManager interface called DefaultAsteriskManager
that is initialized by passing a ManagerConnection.
You can use both interfaces at the same time thus using
the abstracted AsteriskManager where it is sufficient and
going back to ManagerConnection where needed. If you choose
to do so be sure to initialize the AsteriskManager before
you register any of your own ManagerEventHandlers.
Please note that the AsteriskManager interface is still in an early state and the functionality exposed through it is rather limited. This interface will certainly change in the future so be aware of that when using it.