API de gestion des alertes

JCMS et ses modules proposent de nombreuses fonctions de notification et d'alerte : notification sur publication, validation dans les workflow, planification d’un événement, rappel sur une tâche, dépassement quotas, .... On dénombre ainsi une cinquantaine d’alertes différentes.

A partir de JCMS 8, toutes les alertes émises par la plateforme sont gérées de façon homogène et générique.

1. Principes

1.1 Type et niveau d’alerte

Les alertes sont caractérisées selon deux critères :

  1. Leur type
  2. Leur niveau

Le type de l’alerte est composé d’un domaine (p. ex. les alertes liées à l’administration, celles liées à la gestion des commentaires, …) et d’un nom (p. ex. alerte de dépassement de quota, nouveau commentaire, …)

Les alertes sont classées en trois niveaux avec un code couleur :

  • Information (en bleu) : l’utilisateur est informé d’une nouveauté ou d’un changement (p. ex. : nouveau commentaire, nouveau billet de blog, mise à jour d’un contenu suivi, …)
  • Action (en orange) : l’utilisateur est informé qu’une action de sa part est attendue (p. ex. : validation d’une étape de workflow, indiquer ses disponibilités pour une planification, …)
  • Alerte (en rouge) : l’utilisateur est alerté d’une situation d’urgence (p. ex. : dépassement de quota dans un espace, store ou base de données corrompu(e), …)

1.2 Canaux de diffusion des alertes

JCMS 8 propose la diffusion de ces alertes sur différents canaux : le mail, le site, la messagerie instantanée (XMPP), le smartphone (via le service Pushover), … Par défaut, les alertes sont envoyées dans les canaux E-Mail et Site.

Le système de diffusion est extensible et de nouveaux canaux peuvent aisément être développés et proposés aux utilisateurs.

Le canal Mail diffuse les alertes sur l’adresse mail de l’utilisateur. Les e-mails envoyés ont été homogénéisés et enrichis. Ils sont au format HTML. Ils ont un entête dans la couleur de leur type (bleu, orange, rouge). Si  l’alerte est émise par un autre utilisateur avec un message (p. ex. recommandation, commentaire, …), la photo de l’émetteur et le message sont clairement distingués du descriptif de l’alerte.

Nouveaux mails d'alerte.

Le canal Site est matérialisé par un compteur coloré qui apparaît dans la Topbar. Le compteur indique le nombre d’alertes non lues. Il est coloré selon le niveau d’alerte le plus élevé dans la liste des alertes non lues (si l’utilisateur a 2 alertes informations et 1 alerte action, il sera coloré en orange (type action)).

Liste des alertes reçues sur le canal site.

Le module XMPP fournit un canal qui diffuse les alertes sur la messagerie instantanée de l’utilisateur.

Le module Pushover fournit un canal qui diffuse les alertes sur smartphone en utilisant le service du même nom. Ce canal nécessite l’ouverture d’un compte application sur Pushover (gratuit et limité à 7500 envois par jours) et que les utilisateurs installent sur le smartphone l’application Pushover (payante).

Diffusion des alertes sur le canal Pushover.

1.3 Règles de distribution des alertes

Les utilisateurs peuvent définir des règles de diffusion des alertes selon leur niveau et leur type. Une même alerte peut être envoyée sur plusieurs canaux. P. ex. l’utilisateur peut décider de recevoir les informations par mail, les actions par mail et sur le site, les alertes sur tous les canaux disponibles.

Lorsque plusieurs règles sont applicables pour une alerte, c'est la règle la plus précise qui est utilisée. Si aucun canal de diffusion n’est indiqué pour un niveau ou un type d'alerte, ces alertes ne seront pas envoyées.

Les alertes sont affichées soit de façon détaillée, soit de façon synthétique selon le type de canal (p. ex. détaillé en mail ou sur le site et synthétique dans la messagerie instantanée).

Les alertes sont personnalisées pour chaque utilisateur. Elles sont composées dans la langue de son profil. Quel que soit son paramétrage, il ne reçoit que celles qui lui sont destinées. Par exemple, s’il a demandé à recevoir les alertes sur les dépassements de quotas disque, et qu’il n’est pas administrateur, alors il ne les recevra pas.

Interface de paramétrage des alertes.

1.4 Stockage et purge des alertes

Les alertes distribuées sur le canal Site sont enregistrées dans la base de données JcmsDB. Les alertes étant personnalisées, chaque alerte distribuée sur le canal Site d’un utilisateur est enregistrée.

La volumétrie engendrée pour le stockage des alertes peut donc être très importante. Pour une entreprise de 1000 personnes, en comptant une moyenne de 10 alertes site par jour par utilisateur, cela représenterait un volume 300 000 enregistrements / mois.

Pour faire face à cette volumétrie, JCMS dispose d’une stratégie de purge paramétrable.

La purge est déclenchée tous les jours à 5h00 (propriété alert.purge.schedule).

Lorsque la purge est déclenchée, les alertes suivantes sont supprimées :

  • Toutes les alertes lues, vieilles de plus de 2 jours (propriété alert.purge.read-retention)
  • Toutes les alertes INFO non lues plus vieilles de 15 jours (propriété alert.purge.not-read-info-retention)
  • Toutes les alertes (sans quelque critère que ce soit) plus vieilles de 30 jours (propriété alert.purge.not-read-other-retention)

2. Développement avec l’API des alertes

L’API des alertes vous permet d’intervenir dans tout le processus de traitement des alertes. Cet article aborde les points essentiels de cette API :

  • Envoi d’une alerte
  • Interception des alertes envoyées
  • Gestion des règles de distribution
  • Tests unitaires sur les alertes émises
  • Développement d’un nouveau canal de diffusion des alertes

3. Envoi d’une alerte

La classe AlertBuilder est la principale classe à utiliser pour la construction et l'envoi des alertes.

Elle peut envoyer une alerte à un ou plusieurs destinataires. C'est dans ce dernier cas qu'elle prend tout son intérêt. L'envoi est réalisé dans une thread séparée et chaque alerte peut être personnalisée selon le destinataire.

3.1 Propriété des messages d'alertes

L'utilisation d'un AlertBuilder nécessite de déclarer deux jeux de propriétés.

Les propriétés de description de l'alerte

Afin de permettre à l'utilisateur de choisir les règles de diffusion à appliquer sur une alerte, celle-ci doit être décrite avec 2 propriétés :

Propriété Rôle
alert.name.<domain> Nom du domaine
alert.name.<domain>.<alertName> Nom de l'alerte

 

Les propriétés contenant le message d'alerte

Propriété Content-type Rôle Canaux utilisateur
alert.msg.<domain>.<alertName>.title text/plain Titre du message d'alerte Mail
alert.msg.<domain>.<alertName>.short-description text/plain Description courte de l'alarme Pushover, XMPP, Twitter, ...
alert.msg.<domain>.<alertName>.description text/html Description longue de l'alerte en HTML Mail et Web

Si la propriété alert.msg.<domain>.<alertName>.description est absente, la propriété alert.msg.<domain>.<alertName>.short-description est utilisée à la place et le résultat est entouré d'un paragraphe HTML (<p>...</p>).

Ces propriétés peuvent contenir des paramètres. Cependant contrairement aux autres propriétés de JCMS, les paramètres ne sont pas identifiés par un numéro mais par des noms (p. ex. {author}, {data}).

La valeur de chaque paramètre est formatée selon le type de contenu du média (texte ou HTML). Par exemple, l'attribut {data} est formaté avec le lien HTML de la donnée pour la description mais avec juste le titre entre quotes pour le titre et la description courte.

Paramètres standard :

Paramètre Description text/plain text/html
{data} Nom de la donnée localisée pour le destinataire Nom entre quotes Lien
{author} Nom de l'auteur Nom complet Lien
{data.url} URL de la donnée localisée pour le destinataire URL URL

Pour chaque envoi il est possible d'ajouter de nouveaux paramètres qui pourront être utilisés dans les propriétés.

3.2 Envoi d'alerte simples

3.2.1 Envoi d'une alerte ni liée à une donnée, ni émise par un membre

Code :

AlertBuilder alertBuilder = new AlertBuilder(Alert.Level.ACTION, "myDomain", "simpleAlert");  
alertBuilder.sendAlert(memberSet);  

Propriétés de langue :

alert.name.myDomain:                              My domain
alert.name.myDomain.simpleAlert:                  A Simple alert
alert.msg.myDomain.simpleAlert.title:             Simple alert
alert.msg.myDomain.simpleAlert.short-description: A very simple alert.
alert.msg.myDomain.simpleAlert.description:       <p>A very <i>simple</i> alert.</p>

3.2.2 Envoi d'une alerte liée à une donnée mais non émise par un membre

Code :

AlertBuilder alertBuilder = new AlertBuilder(Alert.Level.ACTION, "myDomain", "myAlert", data);  
alertBuilder.sendAlert(memberSet);  

Propriétés de langue :

alert.name.myDomain:                              My domain
alert.name.myDomain.authorAlert:                  An alert about {data}
alert.msg.myDomain.authorAlert.title:             Simple alert about {data}
alert.msg.myDomain.authorAlert.short-description: An alert about {data}.
alert.msg.myDomain.authorAlert.description:       <p>An alert about {data}.</p>

3.2.3 Envoi d'une alerte liée à une donnée et émise par un membre

Code :

AlertBuilder alertBuilder = new AlertBuilder(Alert.Level.ACTION, "myDomain", "myAlert", data, author);  
alertBuilder.sendAlert(memberSet);  

Propriétés de langue :

alert.name.myDomain:                            My domain
alert.name.myDomain.dataAlert:                  An alert about {data}
alert.msg.myDomain.dataAlert.title:             Simple alert about {data}
alert.msg.myDomain.dataAlert.short-description: {author} has sent you an alert about {data}.

Note: La propriété alert.msg.myDomain.dataAlert.description n'est pas déclarée. Ce sera donc alert.msg.myDomain.dataAlert.short-description qui sera utilisée (placée dans une balise <p>).

3.3 Ajout de paramètres

L'ajout de paramètres pour les propriétés de formatage des messages se fait en surchargeant la méthode addParams(). Cette méthode reçoit en paramètre le markup utilisé pour le type de message.

La méthode getDataRepresentation() formate une donnée en fonction du format de balisage (markup) à utiliser et localisée dans la langue du destinataire.

Exemple :

Dans cet exemple, la description de l’alerte contient trois nouveaux paramètres {customTitle}, {abstract} et {related}.

Les propriétés de langue :

alert.msg.myDomain.myAlertName.title:             Changement de valeur sur {title}
alert.msg.myDomain.myAlertName.short-description: La publication {title} a la valeur {customTitle}
alert.msg.myDomain.myAlertName.description:       <p>La publication {title} a la valeur {customTitle}</p>{abstract}{related}

Code :

final Publication pub = getCustomPublication();  
final String customValue = getCustomValue(); 
final Set<Data> relatedDataSet = getSomeRelatedDataSet(); 
AlertBuilder alertBuilder = new AlertBuilder(Alert.Level.ACTION, "myDomain", "myAlert", data, author) { 
  @Override 
  protected void addParams(Member recipient, Map<String, String> paramMap, String markup) { 
    paramMap.put("title", getDataRepresentation(myPub, markup, recipient));       
    if (HTML_MARKUP.equals(markup)) { 
      paramMap.put("customValue", "<strong>" + customValue + "</strong>"); 
      paramMap.put("abstract", getHtmlAbstract(recipient)); 
      paramMap.put("related", AlertUtil.getHtmlDataList(relatedDataSet, recipient)); 
    } else { 
      paramMap.put("customValue",  customValue ); 
    }     
  } 
};
alertBuilder.sendAlert(memberSet);

 

3.4 Ajout d'un message

Certaines alertes peuvent être accompagnées d’un message de la part de l’auteur. Ce message peut être explicite (p. ex. dans le cas d’une recommandation) ou implicite (le contenu d’un commentaire lors de la publication d’un nouveau commentaire).

L'ajout d'un message se fait en surchargeant les méthodes :

  • getMessage() : retourne le texte du message
  • getMessageMarkup() : retourne le type de contenu du message (HTML_MARKUP, WIKI_MARKUP ou TEXT_MARKUP)

Exemple :

AlertBuilder alertBuilder = new AlertBuilder(Alert.Level.ACTION, "myDomain", "myAlert", data, author) {    
  @Override 
  public String getMessage(Member mbr) { 
    return (DBComment)data.getAbstract(); 
  } 
  @Override 
  public String getMessageMarkup() { 
    return WIKI_MARKUP; 
  } 
};   
alertBuilder.sendAlert(memberSet);    

3.5 Contrôle du destinataire

Dans certains cas, l’alerte ne doit pas être émise à certains utilisateurs. Pour cela, il faut soit filtrer l’ensemble des destinataires fournit dans la méthode sendAlert() soit surcharger la méthode isRecipient(Member).

Exemple :

On souhaite que l’alerte ne soit pas envoyée à son auteur.

AlertBuilder alertBuilder = new AlertBuilder(Alert.Level.ACTION, "myDomain", "myAlert", data, author) {  
  @Override 
  public boolean isRecipient(Member mbr) { 
     return !JcmsUtil.isSameId(mbr, author);
  } 
}; 
alertBuilder.sendAlert(memberSet);  

4. La classe Alert

La classe Alert représente une alerte. Elle sert aussi à persister une alerte pour le canal Site.

Elle possède les champs suivants :

Champ Rôle Exemple
level Un énuméré qui représente le niveau d'alerte Alert.Level.INFO
domain Le domaine de l'alerte jcmsplugin-dbcomment
name Le nom de l'alerte new-comment
data La donnée JCMS sur laquelle porte l'alerte un commentaire
author Le membre à l'origine de l'alerte l'auteur du commentaire
recipient Le membre JCMS qui est destinataire de l'alerte l'auteur de la publication commentée
message Un message de l'auteur de l'alerte Le texte du commentaire
messageMarkup Le format de balisage du message WIKI_MARKUP
read Un booléen qui indique si le destinataire a lu l'alerte false

Contraintes

  • domain
    • le nom de domaine de l'alerte ne doit pas contenir de "."
    • les modules doivent utiliser le préfixe jcmsplugin- (e.g. jcmsplugin-task)
  • name
    • le nom de l'alerte ne doit pas contenir de "."

5. Interception des envois d’alertes

5.1 Principes

L’interface AlertPolicyFilter (et son implémentation par défaut BasicAlertPolicyFilter) fournit des points de contrôle sur l'envoi des alertes :

Méthode Description
Set<AlertChannel> getAlertChannelSet(alert, alertChannelSet) Permet de modifier les canaux à utiliser pour cette alerte
boolean saveAlert(alert, saveAlert)

Permet d'empêcher ou de forcer l'enregistrement d'une alerte

Alert beforeSendAlert(alert, alertChannel) Permet de modifier l'alerte à envoyer dans le canal indiqué (Attention : faire un clone) (Non applicable pour le canal 'web')

 

5.2 Exemple

Dans cet exemple, on compte les alertes émises sur chaque canal.

package com.jalios.jcmsplugin.demo;
import com.jalios.jcms.alert.Alert;
import com.jalios.jcms.alert.AlertChannel;
import com.jalios.jcms.alert.BasicAlertPolicyFilter;
import com.jalios.util.ObjectIntTreeMap;

public class CountAlertPolicyFilter extends BasicAlertPolicyFilter {
private static final Logger logger=Logger.getLogger(CountAlertPolicyFilter.class);
  protected int order = Integer.MAX_VALUE; //To be the last in the policy filter call
  protected ObjectIntTreeMap countMap = new ObjectIntTreeMap();
  @Override
  public Alert beforeSendAlert(Alert alert, AlertChannel alertChannel) {
    countMap.inc(alertChannel.getName());
if (logger.isInfoEnabled()) { // only for the exemple.
logger.info("Count for '"+alertChannel.getName()+"' is : "+countMap.getInt(alertChannel.getName(), 0));
}
    return alert;
  }
}

6. Gestion des règles de distribution

La classe AlertRule représente une règle de distribution des alertes.

Une AlertRule est définie par les attributs suivants :

Attribut Obligatoire Description Comportement si absent

Domain

Si alertName défini Représente toutes les alertes d'un domaine Tout domaine
alertName Non Représente une alerte donnée Toute alerte du domaine
Level Non Niveau d'alerte Tout niveau
alertChannelSet Oui

Canaux de distribution pour cette règle

Canaux par défaut

La méthode Member.getAlertRuleSet() retourne l'ensemble des règles définies pour un membre. Si aucune règle n'a été définie pour un membre, il a alors la règle par défaut : toutes les alertes sont émises sur tous les canaux de distribution par défaut.

La liste des canaux de distribution par défaut est définie par la propriété suivante :

alert.default-channels: mail web

 

7. Personnalisation du canal mail

La classe MailAlertChannel représente le canal de diffusion par mail. Le corps du mail est composé uniquement en HTML.

L'origine du MailMessage est remplie avec alert.getFullName().

Le sujet du mail est formaté avec la propriété alert.channel.mail.subject.default :

alert.channel.mail.subject.default: [{0}] [{1}] {2}

avec :

  • {0} : le nom du site
  • {1} : le niveau de l'alerte
  • {2} : le titre de l'alerte

Le chemin de la JSP utilisée par défaut pour la génération du corps du mail est défini par la propriété alert.channel.mail.content.jsp.default.

Il est possible de définir une JSP particulière pour un domaine ou une alerte donnée en déclarant des propriétés de la forme :

alert.channel.mail.content.jsp.<domain>.<alertName>: <jspPath>

 Par exemple pour le domaine "jcmsplugin-collaborativespace" et l'alerte "signup-accepted" :

alert.channel.mail.content.jsp.jcmsplugin-collaborativespace.signup-accepted: /plugins/CollaborativeSpacePlugin/jsp/signupAcceptedMailAlert.jsp

(Attention à ne pas oublier le slash au début du path de la jsp)

La JSP utilisée par défaut est mailAlert.jsp. Elle ne fait qu’inclure des JSP fragments. Il est recommandé de respecter cette décomposition et de réutiliser ces fragments :

  • doMailInit.jspf : construit un objet Alert contenant l’alerte à afficher.
  • doMailHeader.jspf : produit l’entête du mail.
  • doMailBody.jspf : produit le contenu de l’alerte
  • doMailFooter.jspf : produit le pied de page du mail et inclus doMailTracker.jspf
  • doMailTracker.jspf : effectue une requête vers le site JCMS pour indiquer que l’alerte a été lue

La mise en forme actuelle du mail se base sur http://htmlemailboilerplate.com/ qui normalise la structure du mail en prenant en compte la plupart des clients mails Web ou desktop.

Si vous souhaitez personnaliser la mise en forme, il suffit donc de :

  1. Ecrire une nouvelle version de doMailHeader.jspf et/ou doMailBody.jspf.
  2. Ecrire une JSP qui reprend le principe d’inclusion de mailAlert.jsp.
  3. Déclarer cette JSP avec la propriété alert.channel.mail.content.jsp.default.

8. Tests unitaires

8.1 Principes

Pour tester que les alertes ont bien été émises suite à un traitement, le principe général consiste à tester que les alertes ont bien été créés pour les destinataires. Ceci est possible puisque les utilisateurs ont par défaut le canal site qui persiste les alertes.

Il suffit alors de tester le nombre et la nature des alertes.

Le code à tester génère des alertes qui sont enregistrées dans JcmsDB. Il doit donc être placé dans une transaction.

8.2 Nouvelles méthodes de test

Methode Rôle
deleteAllAlerts() Supprime toutes les alertes existantes. A appeler avant tout test sur les alertes.
assertAlert(expectedAlert, actualAlert) Compare le contenu de 2 alertes (level, domain, name, recipient, data et author).

 

8.3 Exemple

public void testAlert() {  
  deleteAllAlerts(); 
Member author = ws1Writer; 
  Member recipient = ws1Reader; 
  Data data = ...; 
  beginTransaction(); 
  { 
    myManager.myCustomCodeToSendNotifications(author, recipient, data); 
  } 
  commitTransaction(); 
  beginTransaction(); 
  { 
    List<Alert> alertList = recipient.getAlertList(); 
    assertEquals(1, alertList.size()); 
    Alert alert = alertList.get(0); 
    assertAlert(new Alert(Level.INFO, "myDomain", "myAlert", recipient, data, author), alert); 
    assertEquals("...", alert.getTitle()); 
    assertEquals("...", alert.getShortDescription()); 
    assertEquals("...", alert.getDescription()); 
  } 
  commitTransaction(); 
}  

 

9. Ajout d’un nouveau canal de distribution

9.1 Principes

Il est possible de développer de nouveaux canaux de distribution des alertes. Ceux-ci seront proposés aux utilisateurs dans la même interface de personnalisation que les canaux existants. Il est possible de conditionné l'affichage de ces canaux. Par exemple, un canal SMS de diffusion des SMS ne sera proposé qu'aux utilisateurs ayant renseigné leur numéro de mobile.

L’ajout d’un nouveau canal de distribution des alertes se fait en implémentant l’interface AlertChannel . Cette interface sert à la fois à la diffusion de l'alerte mais aussi la présentation du canal.

Elle impose les méthodes suivantes :

Méthode Rôle
getName() Le nom interne du canal (doit être unique)
getLabel(userLang) Le libellé du canal
getDescription(userLang) La description du canal
getIcon() L'icône du canal
isAvailable(Member) Doit retourner true si le canal est disponible pour le membre

send(Alert)

Diffusion d'une alerte dans le canal
saveAlert() Doit retourner true si l'alerte doit être enregistrée

 

Il est recommandé de dériver de la classe abstraite BasicAlertChannel qui fournit une implémentation par défaut des méthodes de AlertChannel et qui implémente PluginComponent.

Un module peut déclarer un nouvel AlertChannel (qui impémente PluginComponent) dans son fichier plugin.xml via la balise <alertchannel>. Attention ! ceci nécessite de déclarer la DTD 1.5 ou 1.6.

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE plugin PUBLIC "-//JALIOS//DTD JCMS-PLUGIN 1.5//EN" "http://support.jalios.com/dtd/jcms-plugin-1.6.dtd"> 
<plugin name="DemoPlugin"  ... > 
  ... 
  <plugincomponents> 
    <alertchannel class="com.jalios.jcmsplugin.myplugin.MyCustomAlertChannel" />     
  </plugincomponents> 
  ... 
</plugin>  

 

9.2 Exemple : envoi d’alerte par SMS

Dans cet exemple, nous allons développer un nouveau module proposant un canal d’alerte par SMS.

Il existe de nombreux services Web pour envoyer des SMS depuis un programme mais la plupart facturent chaque SMS envoyé. Une alternative consiste à envoyer des SMS via votre téléphone en le branchant sur votre ordinateur. Pour envoyer un SMS, il suffit alors d’appeler un programme déclenchant l’envoi. Par exemple :

Cette solution n'est pas forcement adaptée à un usage entreprise mais permet d'illustrer simplement notre exemple. Aussi, afin d’être le plus indépendant possible de la passerelle d’envoi des SMS, l’exemple introduit une interface, SMSGateway, représentant la passerelle utilisée. Pour diffuser le SMS via une autre passerelle, il suffit donc de développer une nouvelle classe implémentant SMSGateway.

Le module comporte 3 classes et une interface :

  • SMSAlertChannel : cette classe déclare dans JCMS le nouveau canal et reçoit les alertes à diffuser. Elle les aiguille au SMSManager.
  • SMSManager : ce singleton fournit l’interface pour envoyer un SMS vers une passerelle (gateway) particulière.
  • SMSGateway : cette interface représente une passerelle SMS.
  • CommandLineGateway : cette classe implémente l’envoi de SMS par l’exécution d’un programme externe (SMSSender ou gsmsendsms).

 

9.2.1 SMSAlertChannel

La classe SMSAlertChannel dérive de BasicAlertChannel. Elle retourne les informations sur le canal (icône, libellé, description), vérifie si l’utilisateur peut utiliser ce canal (i.e. si son numéro de téléphone mobile a été renseigné) et enfin envoie l’alerte à diffuser au SMSManager.

package com.jalios.jcmsplugin.sms;

import com.jalios.jcms.JcmsUtil;
import com.jalios.jcms.Member;
import com.jalios.jcms.alert.Alert;
import com.jalios.jcms.alert.BasicAlertChannel;
import com.jalios.jcmsplugin.sms.SMSManager;
import com.jalios.util.Util;

public class SMSAlertChannel extends BasicAlertChannel {

  @Override
  public String getName() {
    return "sms";
  }
 
  @Override
  public String getLabel(String userLang) {
    return JcmsUtil.glp(userLang, "jcmsplugin.sms.alert.channel.label");
  }
 
  @Override
  public String getDescription(String userLang) {
    return JcmsUtil.glp(userLang, "jcmsplugin.sms.alert.channel.description");
  }
 
  @Override
  public String getIcon() {
    return "plugins/SMSPlugin/images/sms.png";
  } 

  @Override
  public boolean isAvailable(Member member) {
    return member != null && Util.notEmpty(member.getMobile());
  }
 
  @Override
  public void send(Alert alert) {
    SMSManager.getInstance().sendAlert(alert);
  }
}

9.2.2 SMSManager

La classe SMSManager aiguille l’alerte reçue par SMSAlertChannel vers le gateway SMS qui a été configuré. 

package com.jalios.jcmsplugin.sms;

import com.jalios.jcms.Channel;
import com.jalios.jcms.alert.Alert;
import com.jalios.util.Util;

public class SMSManager {
 
  private static final SMSManager SINGLETON = new SMSManager();

  private Channel channel = Channel.getChannel();
  private SMSGateway gateway;
  private int maxLength;
 
  // --------------------------------------------------------
  // Singleton & init
  // --------------------------------------------------------
  private SMSManager() {
    init();
  }

  public static SMSManager getInstance() {
    return SINGLETON;
  }

  private void init() {
    maxLength = channel.getIntegerProperty("jcmsplugin.sms.msg.max-length", 160);
    gateway = new CommandLineGateway();
  }
 
  public void sendAlert(Alert alert) {
    String phoneNumber = cleanPhoneNumber(alert.getRecipient().getMobile());
    if (Util.isEmpty(phoneNumber)) {
      return;
    }

    String msg = alert.getShortDescription();
    msg = Util.truncate(msg, maxLength);
   
    gateway.sendSMS(phoneNumber, msg);
  }
 
  public String cleanPhoneNumber(String phoneNumber) {
    if (phoneNumber == null) {
      return null;
    }
    phoneNumber = phoneNumber.replaceAll("[^0-9+]", "");
   
    return phoneNumber;
  }

}

9.2.3 SMSGateway

L’interface SMSGateway représente une passerelle capable de diffuser un message SMS à un numéro de téléphone.

package com.jalios.jcmsplugin.sms;

public interface SMSGateway {

  public void sendSMS(String msg, String phoneNumber);
}

9.2.4 CommandLineGateway

La classe CommandLineGateway diffuse un SMS en appelant un programme externe configuré avec les propriétés jcmsplugin.sms.gateway.commandline.cmd et jcmsplugin.sms.gateway.commandline.cmd-opt.

package com.jalios.jcmsplugin.sms;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.jalios.jcms.Channel;
import com.jalios.jcms.tools.ProcessExecutor;
import com.jalios.jcms.tools.ProcessExecutor.ProcessExecutionResult;
import com.jalios.util.Util;

public class CommandLineGateway implements SMSGateway {

  private static final String PHONENUMBER_PARAM = "{phonenumber}";
  private static final String MSG_PARAM         = "{msg}";
 
  private Channel channel = Channel.getChannel();
 
  private boolean init = false;
  private String cmd;
  private String cmdOpt;
  private static Logger logger = Logger.getLogger(CommandLineGateway.class);

  private void init() {
    if (init) {
      return;
    }
   
    cmd = channel.getProperty("jcmsplugin.sms.gateway.commandline.cmd");
    cmdOpt = channel.getProperty("jcmsplugin.sms.gateway.commandline.cmd-opt");

    init = true;
  }
 
  @Override
  public void sendSMS(String phoneNumber, String msg) {
    init();

    String[] cmdArray = getCmdArray(phoneNumber, msg);
    ProcessExecutor executor = new ProcessExecutor(cmdArray);
    executor.setCaptureOutput(true);
   
    ProcessExecutionResult result = executor.execute();
    logger.debug("result output: " + result.getStdout());
  }
 
  private String[] getCmdArray(String phonenumber, String msg) {
    List<String> cmdList = new ArrayList<String>();
    cmdList.add(cmd);
    cmdList.addAll(Util.splitToList(cmdOpt, " "));
    String[] cmdArray = cmdList.toArray(new String[0]);
   
    for(int i = 1; i < cmdArray.length; i++) {
      String opt = cmdArray[i];
      if (opt.indexOf(PHONENUMBER_PARAM) >= 0) {
        opt = opt.replace(PHONENUMBER_PARAM, phonenumber);
      }
      if (opt.indexOf(MSG_PARAM) >= 0) {
        opt = opt.replace(MSG_PARAM, encodeMessage(msg));
      }     
     
      cmdArray[i] = opt;
    }
   
    logger.debug("Command line: [" + Util.join(cmdArray, "][") + "]");
    return cmdArray;
  }
 
  private String encodeMessage(String msg) {
    return msg.replaceAll("\"", "'");
  }
}

 

9.2.5 plugin.prop

Le fichier plugin.prop contient la commande à lancer ainsi que ses paramètres. Voici des exemples des propriétés pour les commandes SMSSender.exe (Windows) et gsmsendsms (Linux).

jcmsplugin.sms.msg.max-length: 160

# SMSSender (Windows): http://palkotools.wordpress.com/2011/06/01/smssender-en/
jcmsplugin.sms.gateway.commandline.cmd:     C:\\SMSSender.exe
jcmsplugin.sms.gateway.commandline.cmd-opt: -p:COM15 -t:int -d:{phonenumber} -msg:{msg}

# gsmsendsms (Linux): http://pwet.fr/man/linux/administration_systeme/gsmsendsms
#jcmsplugin.sms.gateway.commandline.cmd:     /usr/bin/gsmsendsms
#jcmsplugin.sms.gateway.commandline.cmd-opt: -d /dev/ttyACM0 {phonenumber} {msg}

 

9.2.6 plugin.xml

Le fichier plugin.xml déclare l’ensemble des ressources du module.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plugin PUBLIC "-//JALIOS//DTD JCMS-PLUGIN 1.6//EN" "http://support.jalios.com/dtd/jcms-plugin-1.6.dtd">

<plugin name="SMSPlugin"
        version="1.0-dev"
        author="Jalios"
        license="Jalios SA"
        initialize="true"
        jcms="([8-9]\.).*"
        order="0"
        url="http://support.jalios.com/plugin/sms"
        jsync="true">
       
  <label xml:lang="en">SMSPlugin</label>
  <label xml:lang="fr">Module SMS</label>
  <description xml:lang="en">This plugin provides a SMS alert channel.</description>
  <description xml:lang="fr">Ce module forunit un nouveau canal d'alerte par SMS.</description>

  <java-classes>
    <java package="com.jalios.jcmsplugin.sms" />
  </java-classes>

  <plugincomponents>
    <alertchannel class="com.jalios.jcmsplugin.sms.SMSAlertChannel" />
  </plugincomponents>

  <private-files>
    <directory path="properties" />
  </private-files>

  <public-files>
    <directory path="js" />
    <directory path="images" />
    <directory path="css" />
    <directory path="docs" />
    <directory path="jsp" />
  </public-files>

</plugin>

En résumé...

JCMS et ses modules proposent de nombreuses fonctions de notification et d'alerte : notification sur publication, validation dans les workflow, planification d’un événement, rappel sur une tâche, dépassement quotas, .... On dénombre ainsi une cinquantaine d’alertes différentes. A partir de JCMS 8, toutes les alertes émises par la plateforme sont gérées de façon homogène et générique. Cet article présente l'API de gestion des alertes.

Sujet
Produits
Publié

14/06/13

Rédacteur
  • Olivier Dedieu