<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type">
</head>
<body bgcolor="#ffffff" text="#000000">
Folks,<br>
<br>
7 months ago, I made some modifications to Asterisk to allow AGI
commands to be passed via the Manager API.&nbsp; The advantage over AGI and
FastAGI is the reduced number of sockets to monitor on the controlling
machine.&nbsp; If there are 200 channels open on 10 machines that are all
being managed from a single control machine, that's 2,000 open sockets
(a lot for a kernel to handle) and lots of threads listening to those
open sockets.&nbsp; By sending all the commands (AGI and Manager) through a
single socket, there are fewer open sockets and a single place to
manage all the interactions with Asterisk.<br>
<br>
I've had systems in production for the last 7 months running the MAGI
(Manager AGI) code.&nbsp; There hasn't been a single failure or problem.&nbsp;
I've got Java code running on my application server managing the
Asterisk instances.&nbsp; All works like a charm.<br>
<br>
I've had a couple of requests for the code.&nbsp; I figure I'll make it
available to everyone.&nbsp; Maybe if enough people use it, we'll be able to
get the mods into the main Asterisk distribution.<br>
<br>
You can download a tarball of Asterisk (it's the source from about a
week ago with the MAGI mods) plus Java code that allows you to control
Asterisk.&nbsp; In addition, I'm enclosing a sample Java class that answers
the call and plays "12345" to the caller.<br>
<br>
You can download the tarball from:<br>
<a class="moz-txt-link-freetext" href="http://www.projectsinmotion.com/ast_with_magi.tgz">http://www.projectsinmotion.com/ast_with_magi.tgz</a><br>
<br>
To access, MAGI, put the following in your dialplan:<br>
<tt><br>
exten =&gt; 4157380662,1,MAGI(DOSTUFF|${EXTEN})<br>
</tt><br>
The code that follows is example Java code.<br>
<br>
I hope folks out there find the code valuable.&nbsp; If you've got any
questions, please let me know.&nbsp; If you've got enhancements or
suggestions for improvement, I'd love to see them.<br>
<br>
Thanks,<br>
<br>
David<br>
<br>
<tt><b>Place this method someplace so that the connection to Asterisk
gets
initialized and the right factories vend the right instances.&nbsp; I have
this code executed as part of a class static {} in a class that's
loaded when my app server starts.</b><br>
<br>
<br>
&nbsp;private static void startAsterisk() {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // fire off the Asterisk manager
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AstManager.setChannelFactory(new AstManager.ICreateChannel() {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public AstChannel create(String chanNum, String chanId,
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AstConnection conn) {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new MyAstChannel(chanNum, chanId,
(MyAstConnection) conn);
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });
<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AstManager.setConnectionFactory(new
AstManager.ICreateConnection() {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public AstConnection create(String host, int port,
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AstConnection.IUserNamePassword pwd) throws
IOException {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new MyAstConnection(host, port, pwd);
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });
<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread t = new Thread(new Runnable() {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void run() {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AstManager man = new AstManager();
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; man.start(new AstManager.IConnected() {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void connectedToAsterisk(AstConnection conn)
{
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Dude... we're connected");
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void failedToConnect() {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; run();
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }, "localhost", 5038, new
AstConnection.IUserNamePassword() {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public String getUsername() {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "foo";
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public String getPassword() {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "bar";
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.start();
<br>
&nbsp;&nbsp; }
<br>
<br>
<b>MyAstChannel:
This class handles the current channel.&nbsp; The key thing is it subclasses
runNAExec which is called when the MAGI command is called in Asterisk
on the given channel.&nbsp; It answers the line, waits 2 seconds, and then
runs "SAY DIGITS" with 12345.</b><br>
<br>
/*
<br>
* Created on Aug 11, 2004
<br>
*
<br>
* (c) 2004-2005 David Pollak
<br>
*/
<br>
package net.maxvox.mvasterisk;
<br>
<br>
import java.net.URLDecoder;
<br>
import java.sql.SQLException;
<br>
import java.util.Date;
<br>
import java.util.HashMap;
<br>
<br>
<br>
import net.maxvox.asterisk.AstChannel;
<br>
import net.maxvox.asterisk.INameValuePair;
<br>
import net.maxvox.asterisk.IServerCommand;
<br>
import net.maxvox.asterisk.IVRUtils;
<br>
import net.maxvox.asterisk.NameValuePairImpl;
<br>
import net.maxvox.asterisk.ServerCommandImpl;
<br>
<br>
<br>
/**
<br>
* @author dpp
<br>
*
<br>
* Represents a channel in the Asterisk
<br>
*/
<br>
public class MyAstChannel extends AstChannel {
<br>
<br>
&nbsp;&nbsp; public MyAstChannel(String name, String uniqueId, MyAstConnection
conn) {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super(name, uniqueId, conn);
<br>
&nbsp;&nbsp; }
<br>
<br>
<br>
&nbsp;&nbsp; protected void runNAExec(HashMap&lt;String, String&gt; params,
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HashMap&lt;String, String&gt; results) {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.runNAExec(params, results);
<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.answer(new IServerCommand.ICommandStringCallback() {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void complete(int code, IServerCommand command,
String result) {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.sleep(2000);
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (InterruptedException ie) {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // ignore
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sayDigits("12345");
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });
<br>
&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; }
<br>
<br>
}
<br>
<br>
<b>MyAstConnection:
There's 1 instance of this class per Asterisk server connection.&nbsp; No
real reason to subclass it, but I did anyway... :-)</b><br>
<br>
/*
<br>
* Created on Aug 9, 2004
<br>
*
<br>
* (c) 2004-2005 David Pollak
<br>
*/
<br>
<br>
package net.maxvox.mvasterisk;
<br>
<br>
import java.io.BufferedReader;
<br>
import java.io.File;
<br>
import java.io.FileWriter;
<br>
import java.io.IOException;
<br>
import java.io.InputStream;
<br>
import java.io.InputStreamReader;
<br>
import java.io.OutputStream;
<br>
import java.io.PrintWriter;
<br>
import java.net.InetAddress;
<br>
import java.net.InetSocketAddress;
<br>
import java.net.Socket;
<br>
import java.nio.ByteBuffer;
<br>
import java.nio.CharBuffer;
<br>
import java.nio.channels.SelectableChannel;
<br>
import java.nio.channels.SocketChannel;
<br>
import java.nio.charset.Charset;
<br>
import java.sql.SQLException;
<br>
import java.util.ArrayList;
<br>
import java.util.HashMap;
<br>
import java.util.Iterator;
<br>
import java.util.LinkedList;
<br>
import java.util.Set;
<br>
<br>
import org.jendo.base.SmartBaseObject;
<br>
import org.jendo.base.SmartException;
<br>
import org.jendo.util.Diag;
<br>
<br>
import net.maxvox.asterisk.AstChannel;
<br>
import net.maxvox.asterisk.AstConnection;
<br>
import net.maxvox.asterisk.INameValuePair;
<br>
import net.maxvox.asterisk.IServerCommand;
<br>
import net.maxvox.asterisk.NameValuePairImpl;
<br>
import net.maxvox.asterisk.SelectChannelMonitor;
<br>
import net.maxvox.asterisk.ServerCommandImpl;
<br>
import net.maxvox.asterisk.AstConnection.IUserNamePassword;
<br>
<br>
/**
<br>
* @author dpp
<br>
*
<br>
* Connection to an Asterisk Server's Manager API
<br>
*/
<br>
public class MYAstConnection extends AstConnection {
<br>
&nbsp;&nbsp; public MyAstConnection(String host, int port, IUserNamePassword pwd)
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws IOException {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super(host, port, pwd);
<br>
&nbsp;&nbsp; }
<br>
&nbsp; &nbsp;&nbsp; public static MyAstConnection instance() {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AstConnection conn = AstConnection.instance();
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (conn != null &amp;&amp; conn instanceof MyAstConnection) {
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (MyAstConnection) conn;
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return null;
<br>
&nbsp;&nbsp; }
<br>
<br>
}
<br>
<br>
</tt>
<br>
</body>
</html>