Sommaire
Avant de tout, il est recommandé de lire cet article d’introduction aux concepts de Message, Scope, MessageCallback & Saga dans Constellation pour bien comprendre le principe des MessageCallbacks.
Exposer des méthodes
Pour exposer une méthode .NET dans Constellation, il suffit d’ajouter l’attribut “MessageCallback” sur une méthode :
1 2 3 4 5 |
[MessageCallback] void MyMethod() { PackageHost.WriteInfo("This is a MessageCallback !"); } |
Cela fonctionne que votre méthode soit privée ou public, d’instance ou statique.
Une méthode marquée MessageCallback peut accepter zéro, un ou plusieurs paramètres :
1 2 3 4 5 |
[MessageCallback] void MyMethodWithMultipleParameters(string input, int number, bool boolean) { PackageHost.WriteInfo("Input= {0} - Number: {1} - Boolean: {2}", input, number, boolean); } |
De plus chaque paramètre peut être de type simple ou complexe :
1 2 3 4 5 |
[MessageCallback] void MyMethodWithComplexParameter(UserInfo user) { PackageHost.WriteInfo("User: {0} & Password = {1}", user.User, user.Password); } |
Par défaut seules les méthodes marquées [MessageCallback] sur votre classe ”IPackage” sont enregistrées.
Si vous souhaitez également enregistrer les MessageCallbacks référencés de vos autres types, vous devez appeler la méthode “RegisterMessageCallbacks” en passant en paramètre l’instance ou le type à enregistrer :
1 |
PackageHost.RegisterMessageCallbacks(source); |
Testez vos MessageCallbacks depuis la Console Constellation
Si vous publiez votre package ou démarrer votre package depuis VS avec les trois méthodes ci-dessus, vous retrouverez ces trois MessageCallbacks dans l’explorateur :
Tout y est décrit, y compris les types complexes :
Vous pourrez alors directement tester vos MC depuis la Console :
Ce qui compte tenu du code donné en exemple ci-dessus affichera des messages dans les logs Constellation visible sur la Console Log :
Personnaliser le nom du MessageCallback
Vous pouvez également changer le nom du MessageCallback sans forcement changer le nom de votre méthode :
1 2 3 4 5 |
[MessageCallback(Key="Logon")] void MyMethodWithComplexParameter(UserInfo user) { PackageHost.WriteInfo("User: {0} & Password = {1}", user.User, user.Password); } |
Ici la méthode se nomme toujours “MyMethodWithComplexParameter” dans notre code C# mais est vue dans Constellation comme le MessageCallback nommé “Logon” :
MessageCallback caché
Vous pouvez aussi exposer des MessageCallbacks que vous ne souhaitez “rendre publique”. En d’autre mots, vous ne souhaitez pas que votre MC soit référencé dans la description de votre package.
Exemple : vous avez un package qui envoie des messages à un groupe pour prévenir de l’arrivée d’un événement (ex: le téléphone sonne). Vous souhaitez, dans le code de votre package, déclencher une méthode lorsque cet événement se produit. Autrement dit, vous créez une méthode marquée “MessageCallback“ qui porte le même nom que le MessageKey envoyé par l’autre package (le même nom de méthode ou via la propriété Key comme vu ci-dessus).
Ainsi, comme votre package fait partie du groupe cible, lorsque le téléphone sonne, l’autre package envoie un message au groupe, vous recevez donc le message et comme vous avez un MessageCallback du même nom, votre méthode est invoquée !
C’est parfait à l’exception que tout le monde “voit l’existence” de cette méthode et peuvent tous l’invoquer !
Deux solutions :
-
Cacher le MessageCallback (= ne pas le déclarer dans la description du package)
-
Vérifier l’émetteur
Pour cacher un MC, il suffit simple de définir la propriété “IsHidden” à vrai :
1 2 3 4 5 |
[MessageCallback(IsHidden = true)] void HiddenMethod() { PackageHost.WriteInfo("This is a MessageCallback !"); } |
Ainsi la méthode, n’apparaîtra nulle part, personne ne pourra savoir que votre package “écoute” des messages dont la clé est ici “HiddenMethod”.
MessageContext
Vous pouvez, dans le code d’un “MessageCallback”, accéder au contexte du message reçu via la propriété “MessageContext.Current”.
Le contexte du message contient notamment le MessageSender (identifié de l’émetteur du message) et le MessageScope (scope dans lequel le message a été envoyé ).
Par exemple on pourrait écrire :
1 2 3 4 5 6 7 8 9 10 11 12 |
[MessageCallback(IsHidden = true)] void HiddenMethod() { if(MessageContext.Current.Sender.FriendlyName == "SENTINEL-DEMO/MySafePackage") { PackageHost.WriteInfo("OK j’autorise le package MySafePackage de la sentinelle SENTINEL-DEMO"); } else { PackageHost.WriteWarn("Je refuse que {0} puisse invoquer cette méthode !", MessageContext.Current.Sender.FriendlyName); } } |
Répondre à une saga
Comme nous l’avons vu dans les concepts, il est possible de répondre à un message en renvoyant un message avec le même identifiant de saga. De ce fait, un MessageCallback peut renvoyer une réponse.
Dans la pratique, il suffit de regarder dans le contexte du message si c’est une saga (c’est à dire que le scope porte un identifiant de saga). Si c’est une saga, vous pouvez appeler la méthode d’extension “SendResponse” qui se chargera pour vous d’envoyer le contenu de votre message dans un scope portant le même identifiant de saga et à destination du package émetteur.
De plus, pour indiquer que le MessageCallback répond aux sagas, on indique quel est le type de l’objet de réponse dans l’attribut [MessageCallback] avec la propriété ResponseType.
Par exemple :
1 2 3 4 5 6 7 8 9 10 |
[MessageCallback(Key="Logon", ResponseType=typeof(bool))] void MyMethodWithComplexParameter(UserInfo user) { PackageHost.WriteInfo("User: {0} & Password = {1}", user.User, user.Password); if (MessageContext.Current.IsSaga) { MessageContext.Current.SendResponse(user.User == user.Password); } } |
Avec Constellation 1.8, la syntaxe est encore plus simple. Vous pouvez simplement écrire votre MessageCallback avec une fonction .NET, c’est à dire une méthode qui retourne (return) une valeur :
1 2 3 4 5 6 7 |
[MessageCallback(Key="Logon")] bool MyMethodWithComplexParameter(UserInfo user) { PackageHost.WriteInfo("User: {0} & Password = {1}", user.User, user.Password); return user.User == user.Password; } |
La propriété “ResponseType” du MessageCallback est automatiquement définie avec le type de retour de la méthode. C’est l’API .NET qui se chargera d’envoyer la réponse si le message est reçu est une saga. La réponse renvoyée étant l’objet retourné par votre méthode, dans l’exemple ci-dessus un booléen.
Ainsi en republiant le package avec le code ci-dessus, vous constaterez dans le MessageCallbacks Explorer que votre MessageCallback indique son type de réponse dans le cas d’une saga :
Vous pourrez alors tester votre MessageCallback :
Et visualisez le message de retour, ici un booléen :
Décrire ses MessageCallbacks
Pour finir il est vivement conseillé de décrire ses MessageCallbacks et les types utilisés en paramètre ou en réponse.
Vous avez deux possibilités :
- Utilisez la propriété “Description” sur l’attribut MessageCallback (mais limité aux MC et non aux types)
- Utilisez les commentaires de documentation XML
Pour gagner en productivité, je vous recommande l’extension Visual Studio GhostDoc de SubMain pour générer automatiquement des commentaires de documentation XML dans votre code .NET.
Ainsi ajoutons des commentaires de documentation XML sur nos MessageCallbacks et sur la classe UserInfo utilisée dans nos exemples comme paramètre de notre MC :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
/// <summary> /// Methode d'identification d'un utilisateur. /// </summary> /// <param name="user">L'utilisateur à identifier.</param> /// <returns><c>true</c> si identifié</returns> [MessageCallback(Key="Logon")] bool MyMethodWithComplexParameter(UserInfo user) { PackageHost.WriteInfo("User: {0} & Password = {1}", user.User, user.Password); return user.User == user.Password; } /// <summary> /// Classe decrivrant un utilisateur /// </summary> public class UserInfo { /// <summary> /// Le nom d'utilisateur /// </summary> public string User { get; set; } /// <summary> /// Le mot de passe de l'utilisateur /// </summary> public string Password { get; set; } } |
Si vous redéployez le package, vous observerez que l’ensemble des MessageCallbacks et des paramètres sont décrits par vos commentaires XML :
S’abonner et recevoir les messages d’un groupe
Comme vous le savez, il existe plusieurs type de scope pour recevoir un message. Par défaut votre package recevra les messages adressés au scope “All” et “Other” (si il n’est pas l’émetteur du message) mais aussi et surtout ceux destinés au scope de sa sentinelle et/ou de son package.
Il y a un dernier type de scope fort utile : “les groupes”. Pour recevoir des messages destinés à un groupe, il faut d’abord s’abonner au groupe.
Vous avez deux manières d’ajouter votre package dans un groupe :
- Depuis la configuration du serveur
- Depuis le code de votre package
La première méthode se réalise dans la configuration de votre package directement dans la configuration du serveur Constellation.
Il suffit d’ajouter un élément “group” en indiquant les noms des groupes à rejoindre.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<sentinel name="PO-SWARIN" credential="StandardAccess"> <packages> <package name="HWMonitor" enable="true"></package> <package name="ConstellationPackageConsole1" enable="true"></package> <package name="MonPremierPackage" enable="true"> <groups> <group name="MonGroupDemo" /> </groups> <settings> <setting key="Interval" value="2000" /> </settings> </package> </packages> </sentinel> |
Vous pouvez aussi utiliser la Console Constellation pour éditer les groupes de chaque package.
Autre manière, directement dans votre code en utilisant la méthode “PackageHost.SubscribeMessages” (ou PackageHost.UnsubscribeMessages).
Par exemple :
1 |
PackageHost.SubscribeMessages("MonGroupDemo"); |
Démarrez la discussion sur le forum Constellation