Faites de l’Asterisk avec du Java: Couplage Asterisk et Web Service REST Java

J’ai une affection particulière pour les projets liant ces 2 mondes:

  • télécom
  • informatique

J’utiliserai le terme TelcoWeb, découvert via un de mes 2 responsables de stages de fin d’études, il y a quelques années déjà (le temps passe vite :-)) pour désigner ce genre de projet à cheval sur ces univers.

Asterisk http://fr.wikipedia.org/wiki/Asterisk_%28logiciel%29

  • Serveur de téléphonie, Messagerie vocale, conférence téléphonique
  • Serveur Vocal Interactif
  • Centre d’appels
  • Passerelle VoIP vers d’autres types de réseaux…

Java: Si vous ne savez pas ce que sait que ce truc, arrêtez de lire cet article et allez vous prendre un café avant d’avoir mal à la tête🙂

But du projet: Développer un serveur vocal interactif pour héberger un service de votes par téléphone (Par exemple un duel d’artistes à la Radio: vous appelez, puis par les touches de votre téléphone vous choisissez l’un ou l’autre artiste: les résultats sont affichés en temps réels sur le site web de la radio…)

Commençons par le code du SVI dans le dialplan d’Asterisk:

;@author Zoumana TRAORE
;Here is ONLY interesting part of the file /etc/asterisk/extensions.conf 

[globals]
SVI_RETRY_COUNTER=3

[from-trunk]
;;Answer && Local variables init
;;NA means not answered (yet)
exten => s,1,Answer
exten => s,n,Set(SVI_ARTIST="NA")
;Handler doesn't work on all version of Asterisk: I've used it on 11.5.X  
exten => s,n,Set(CHANNEL(hangup_handler_push)=onHangup,s,1);
exten => s,n,Goto(svi-entry,s,1)

;--------------------------------------------------------
[svi-entry]
;--------------------------------------------------------
exten => s,1,Playback(svi/welcome)
exten => s,n,Goto(svi-dtmf,s,1)

[svi-dtmf]
exten => s,1,Set(SVI_RETRY_COUNTER=${SVI_RETRY_COUNTER}-1)
exten => s,n,Background(svi/artist-choice)
exten => s,n,WaitExten()
exten => 1,1,Set(SVI_ARTIST="1")
exten => 1,2,Goto(svi-dtmf-end,s,1)
exten => 2,1,Set(SVI_ARTIST="2")
exten => 2,2,Goto(svi-dtmf-end,s,1)

exten => i,1,GotoIf($[${SVI_RETRY_COUNTER}>=1] ? 2 : 4 )
exten => i,2,Playback(svi/invalid-retry)
exten => i,3,Goto(svi-dtmf,s,1)
exten => i,4,Goto(svi-dtmf-end,s,1)

exten => t,1,GotoIf($[${SVI_RETRY_COUNTER}>=1] ? 2 : 4 )
exten => t,2,Playback(svi/invalid-retry)
exten => t,3,Goto(svi-dtmf,s,1)
exten => t,4,Goto(svi-dtmf-end,s,1)

[svi-dtmf-end]
;;End message
exten => s,1,Playback(svi/goodbye)
exten => s,n,Hangup()

[onHangup]
exten => s,1,Agi(agi://localhost/vote-middleware.agi?SVI_ARTIST=${SVI_ARTIST})
same => n,Return()

[default]
include => from-trunk
include => onHangup
include => svi-entry
include => svi-dtmf
include => svi-dtmf-end

Vous l’aurez remarqué: le code est commenté en anglais! Non je ne parlais pas de ça. Vous avez remarqué qu’on limite le choix des touches à 1 ou 2, soyons un peu propre🙂 (avec 3 essais possibles).

Comment se fait le lien entre Java et Asterisk ?

La ligne la plus importante est celle ci:

exten => s,1,Agi(agi://localhost/vote-middleware.agi?SVI_ARTIST=${SVI_ARTIST})

Pour faire simple, quand l’appelant raccroche, son choix d’artiste, stocké au préalable dans une variable est envoyé à un programme entre Asterisk et l’application web via AGI (Asterisk Gateway Interface).

Le code du vote-middleware: Une classe Java utilisant l’API Asterisk-Java http://www.asterisk-java.org/development/

PS: Le code ci-dessous ne présente que les parties importantes, j’ai viré certaines méthodes (plus de type Helper qu’en lien avec Asterisk)

/**
 * @author Zoumana TRAORE
 * @since 20130701
 * @version 1.0
 * 
 * <p>
 *     This is the middle-ware class between the SVI Asterisk and the Web-service application
 *     in order to transmit the SVI Form data for persistence purpose on web application database.
 * </p>
 */
public class SVIMiddlewareAgi extends BaseAgiScript{

    /**
     *Application constants 
     */
    private static final String USER_AGENT = "GangstaTux/50.0";
    private static final String DEFAULT_SVI_URL = "http://apps.zoumanatraore.fr:8080/vote/";    
    private static final String API_KEY = "YHHD9002DKKD7199DSKD";
    private static final String ARTIST = "SVI_ARTIST";

    /**
     * Variables retrieved form Asterisk SVI Form
     */
    private String sviArtistChoice = "";

    /**
     * default service method for BaseAgiScript of the API
     */
    public void service(AgiRequest request, AgiChannel channel) throws AgiException {

        //Retrieving ASTERISK DIALPLAN parameters values
        sviArtistChoice = request.getParameter(ARTIST);

        //Debug
        System.out.println("sviArtistChoice: "+sviArtistChoice);

        //Build a simple REST URL parameters chain
        //Example of parameters chain: artist/1
        StringBuffer urlParameters = new StringBuffer();
        urlParameters.append("artist/"+sviArtistChoice);

        //Call the Web Service
        try {
            webserviceClient(urlParameters.toString(), getWebServiceCompleteURLFromConfig());
        } catch (IOException e) {
            writeToLocalFileForBatch(sviLanguage);
            e.printStackTrace();
        }

        }

    /**
     * Web Service client 
     * 
     * @param urlParameters
     * @throws IOException 
     */
    public static void webserviceClient(final String urlParameters, final String webserviceURL) throws IOException {

        String url = webserviceURL+"/apikey/"+API_KEY+"/"+urlParameters;

        URL urlObject = new URL(url);
        HttpURLConnection httpConnection = (HttpURLConnection) urlObject.openConnection();

        // optional default is GET
        httpConnection.setRequestMethod("POST");

        //add request header
        httpConnection.setRequestProperty("User-Agent", USER_AGENT);

        int responseCode = httpConnection.getResponseCode();

        BufferedReader in = new BufferedReader(
                new InputStreamReader(httpConnection.getInputStream()));
        String inputLine;
        StringBuffer response = new StringBuffer();

        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }
        in.close();

        //print result
        System.out.println(response.toString());
    }

}

Middleware: lien entre le fichier vote-middleware.agi dans Asterisk et cette classe SVIMiddlewareAgi.java ?

Un serveur AGI doit être lancé sur la même machine (ici dans mon exemple localhost) ou sur une autre (fournir dans ce cas le FQDN ou l’IP) qu’Asterisk, l’API Asterisk-Java fourni des implémentations de ce serveur. Comment ça fonctionne? Le serveur AGI utilise un fichier de mapping pour faire le lien entre .agi et .java: fastagi-mapping.properties

vote-middleware.agi = SVIMiddlewareAgi

Dans le même répertoire que ce fichier de mapping, vous devez déposer le .jar de la librairie (téléchargeable sur le lien fourni plus haut)

Voici un petit script shell que j’ai fait pour compiler et lancer Serveur AGI (ainsi que Asterisk et Tomcat). Vous pouvez l’ajouter à vos scripts de boot de votre serveur pour automatiser le process.

#! /bin/sh
#author Zoumana TRAORE

cd /home/zoumhussein/svi-build/
rm *.class

#compile the sources
javac -cp asterisk-java-0.3.1.jar SVIMiddlewareAgi.java

#stop an existing agi server: CAUTION IF OTHER JAVA PROCESSES
#sudo kill $(pgrep java)#launch new agi server
java -cp asterisk-java-0.3.1.jar: org.asteriskjava.fastagi.DefaultAgiServer#launch asterisk, tomcat, mysql servers
service asterisk start
service mysql-server start
service tomcat7 start

Et le webservice alors ?

A partir du moment ou les données sont extraites d’Asterisk et envoyées par REST à votre webservice, vous pouvez les traiter et en faire ce que vous demande votre application métier. Pour ma part, toujours fidèle au monde Java, mon web service est fait à base de Spring REST et l’application Web à coups de Spring, Hibernate et Struts2.

Je ferai un article sur un WebService REST avec Spring…dès que je pourrai.

Bilan?

Ce couplage simpliste est un petit exemple qui permet de montrer qu’il est possible de faire le lien entre le serveur Asterisk et une application web/métier assez facilement: récupérer des données dans le contexte d’un appel, ou en injecter: exploiter ces données pour les utiliser sur une autre application.

Je mettrai les sources de ce projet sur github bientôt (et un lien ici, n’hésitez pas à revenir sur l’article)

2 exemples dans la vraie vie🙂

  • AfricaSys (http://www.africasys.com/) a développé pour un client un serveur vocal interactif à usage basée sur du DTMF et de la reconnaissance vocale. L’appelant se balade dans un formulaire (médical pour le cout) et une interaction entre ses choix et une application web permet de le guider dans son parcours d’appel. En fin de processus, les informations de l’appel (durée de l’appel, fichier audios enregistrés sur commentaire libre ainsi que la durée associée, réponses aux questions du formulaire par DTMF/Voix…) sont envoyées par webservice à une application web Java qui elle les stockent en base de données. Une interface web permet l’exploitation des données, d’y appliquer des traitements statistiques, d’écouter les audios, d’exporter les résultats sous forme d’Excel et de ZIP). Ha j’oubliais: si le webservice tombait, le service téléphonique continuera de fonctionner et les réponses ne seront pas perdues car via un mode asynchrone, le webservice sera capable de récupérer les données et les digérer (en les insérant aux bons endroits niveau timing, comme si de rien n’était): Architecture sympa non?🙂
  •   Yaai (https://github.com/blak3r/yaai) est un projet pour réaliser un couplage entre Asterisk et SugarCRM. Cas d’usage: quand le client d’une entreprise appelle le service après vente, l’opérateur avant de décrocher voit s’afficher sur son ordinateur les informations relatives au client avec qui il s’apprête à discuter.

Je suis en train de développer en Java un projet semblable à YAAI dans le sens ou il intercepte un certain nombre d’évènements dans Asterisk (Dial, Answer, Hangup, Transfer…) mais pas en direction spécifiquement de SugarCRM. Plus globalement en les envoyant à n’importe quel WebService en face (autre CRM, ERP…)

Un autre article à vernir sur: Piloter Asterisk via AMI depuis une appli Java (Sympa pour faire des plugins web ClickToCall…)

3 réflexions sur “Faites de l’Asterisk avec du Java: Couplage Asterisk et Web Service REST Java

  1. Bonjour,

    Nous sommes une petite entreprise, nous avons un besoin ponctuelle pour un développement Java/Asterisk. Sauriez vous intéresser par un mission de quelques jours pour expliquer à nos développeurs votre exemple et l’adapter à notre besoin ?

    cordialement

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s