1 //!-- UTF8 2 /* 3 Oregano Multiuser Server - Version 1.2.0 - January 4th, 2005 4 5 Web: www.oregano-server.org 6 Mail: info@oregano-server.org 7 8 Copyright 2003 - 2004 - 2004 Jens Halm / Cologne, Germany 9 10 This library is free software; you can redistribute it and/or 11 modify it under the terms of the GNU Lesser General Public 12 License as published by the Free Software Foundation; either 13 version 2.1 of the License, or (at your option) any later version. 14 15 This library is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 Lesser General Public License for more details. 19 20 You should have received a copy of the GNU Lesser General Public 21 License along with this library; if not, write to the Free Software 22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 */ 24 25 /* 26 ------------------------------------------- 27 Classe Messenger 28 29 @description : 30 31 Singleton 32 33 34 @author Jens Halm copyright http://www.spicefactory.org/ 35 @author erixtekila copyleft http://www.v-i-a.net 36 ------------------------------------------- 37 version history : 38 1.2.0 : 04/02/05 39 - Portage en actionscript 2 pour le 40 compile time type checking 41 - Implémentation du modèle Singleton 42 (abstraite auparavant) 43 - Accès méthodes aux classes Singleton 44 ------------------------------------------- 45 */ 46 47 import org.omus.util.EventDispatcher; 48 import org.omus.util.iObservable; 49 import org.omus.util._Error; 50 import org.omus.util._Class; 51 52 import org.omus.msg.Message; 53 import org.omus.msg.MessageFilter; 54 import org.omus.msg.EnvelopeFactory; 55 import org.omus.msg.Envelope; 56 57 import org.omus.core.Session; 58 import org.omus.core.Group; 59 60 /** 61 * Cette classe gère l'envoi, la réception et le filtrage des messages. 62 * Contrairement à Mail, elle ne gère que les messages non persistants tels ceux d'un chat. 63 * Un mécasnisme de publication/abonnement est inclus pour la distribution des messages. 64 * 65 * <p> 66 * Evénements auxquels s'abonner : 67 * <ul> 68 * <li>onSubscribe Généré lorsqu'un nouveau email est enregistré côté serevur. 69 * </li><li>onUnsubscribe Généré lorsqu'un nouveau mot de passe est enregistré côté serveur. 70 * </li><li>onUnsubscribeAll Généré lorsque de nouvelles permissions sont enregistrées côté serveur. 71 * </li><li>onAdminMessage 72 * </li><li>onMessage 73 * </li><li>onError(error:_Error) Généré en cas d'erreur à cause d'une mauvaise modification de propriété persistante. 74 * </li></ul></p> 75 * Cette classe est implémentée en suivant le modèle Singleton. 76 * Un accès global à son instance est obtenu grâce à la méthode getInstance. 77 * Elle est aggrémentée par composition des méthodes des sources d'événements (EventDispatcher). 78 * @see org.omus.util.EventDispatcher 79 * @see org.omus.util.iObservable 80 * 81 * @author Jens Halm copyright http://www.spicefactory.org/ 82 * @author erixtekila copyleft http://www.v-i-a.net 83 * @version 1.2.0 84 */ 85 class org.omus.msg.Messenger implements iObservable 86 { 87 //-------------------- 88 // PROPRIETES 89 //-------------------- 90 /** 91 * Liste des filtres. 92 */ 93 private var filters:Array; 94 95 /** 96 * Liste des sujets avec abonnements. 97 */ 98 private var subscr:Array; 99 100 /** 101 * Liste des gestionnaires d'accusé de réception. 102 */ 103 private var handlers:Object; 104 105 /** 106 * Référence au système de génération d'événements. 107 */ 108 private var _eventSource:EventDispatcher; 109 110 /** 111 * Référence à l'unique instance de la classe permise. 112 */ 113 private static var _instance:Messenger; 114 115 //-------------------- 116 // CONSTRUCTEUR 117 //-------------------- 118 /** 119 * L'objet Messenger dispose des méthodes d'EventDispatcher par composition. 120 * TODO : Implémenter MessageHandler ? 121 */ 122 private function Messenger() 123 { 124 // Composition avec EventDispatcher 125 _eventSource = new EventDispatcher(); 126 127 // Propriétés 128 filters = new Array(); 129 subscr = new Array(); 130 131 // Gestionnaires 132 // Redirige les messages vers une méthode de gestion de l'accusé de réception ? 133 // TODO : Implémenter MessageHandler ? 134 var hd = new Object(); 135 hd["msg.toServer"] = "handleError"; 136 hd["customMsg"] = "handleCustomMsg"; 137 hd["adminMsg"] = "handleAdminMsg"; 138 hd["msg.subscr"] = "handleSubscribe"; 139 hd["msg.unsubscr"] = "handleUnsubscribe"; 140 hd["msg.unsubAll"] = "handleUnsubscribeAll"; 141 handlers = hd; 142 143 // trace(this+ " installé."); 144 } 145 146 147 //-------------------- 148 // METHODES PUBLIQUES 149 //-------------------- 150 /** 151 * Utilisé dans un contexte littéral 152 * @return Une chaine définissant l'objet 153 */ 154 public function toString():String 155 { 156 return "[Objet Messenger]"; 157 } 158 159 /** 160 * N'envoie un message qu'à un seul utilisateur. 161 * 162 * @param msg Message à pfaire parvenir. 163 * @param username Nom de l'utilisateur. 164 * @param sameGroup true si l'utilisateur doit obligatoirement appartenir au même groupe pour recevoir le message. 165 */ 166 public function sendToUser (msg:Message, username:String, sameGroup:Boolean):Void 167 { 168 // TODO : Accès Singleton 169 var session = Session.getInstance(); 170 var envFactory = EnvelopeFactory.getInstance(); 171 var clazz = _Class.getInstance(); 172 173 var argCheck = [ 174 [msg, Message, true], 175 [username, "string", true], 176 [sameGroup, "boolean", true] 177 ]; 178 if (! clazz.checkArguments("org.omus.messenger.sendToUser", argCheck)) return; 179 // Redirige le message en cas d'obligation d'appartenir au même groupe. 180 var type = (sameGroup) ? "grp.toUser" : "msg.toUser" ; 181 182 session.sendMessage(envFactory.getOutgoing(msg, type, username)); 183 } 184 185 /** 186 * Envoie un message à tous les membres d'un seul groupe. 187 * 188 * @param msg Le message à envoyer. 189 * @param groupName Optionnel. Le groupe devant recevoir le message. 190 * Si le nom du groupe n'est pas précisé, le groupe actuel reçoit le message. 191 */ 192 public function sendToGroup (msg:Message, groupName:String):Void 193 { 194 // TODO : Accès Singleton 195 var session = Session.getInstance(); 196 var envFactory = EnvelopeFactory.getInstance(); 197 var clazz = _Class.getInstance(); 198 var group = Group.getInstance(); 199 200 var argCheck = [ 201 [msg, Message, true], 202 [groupName, "string", false] 203 ]; 204 if (! clazz.checkArguments("org.omus.messenger.sendToGroup", argCheck)) return; 205 // groupe différent ou actuel ? 206 var type = (groupName == undefined || groupName == group.getName()) ? "grp.toGroup" : "msg.toGroup" ; 207 208 session.sendMessage(envFactory.getOutgoing(msg, type, groupName)); 209 } 210 211 /** 212 * Envoie un message à tous les utilisateurs loogés au serveur. 213 * 214 * @param msg Message à envoyer à tous. 215 */ 216 public function sendToAll (msg:Message):Void 217 { 218 // TODO : Accès Singleton 219 var session = Session.getInstance(); 220 var envFactory = EnvelopeFactory.getInstance(); 221 var clazz = _Class.getInstance(); 222 223 var argCheck = [[Message, true]]; 224 225 if (! clazz.checkArguments("org.omus.messenger.sendToAll", argCheck)) return; 226 227 session.sendMessage(envFactory.getOutgoing(msg, "msg.toAll")); 228 } 229 230 /** 231 * N'envoie un message qu'au serveur. 232 * Il est nécessaire de créer une extension pour recevoir ce message. 233 * 234 * @param msg Message à envoyer au serveur. 235 */ 236 public function sendToServer (msg:Message):Void 237 { 238 // TODO : Accès Singleton 239 var session = Session.getInstance(); 240 var envFactory = EnvelopeFactory.getInstance(); 241 var clazz = _Class.getInstance(); 242 243 var argCheck = [[Message, true]]; 244 245 if (! clazz.checkArguments("org.omus.messenger.sendToServer", argCheck)) return; 246 247 session.sendMessage(envFactory.getOutgoing(msg, "msg.toServer")); 248 } 249 250 /** 251 * Publie un message à tous les membres abonnés au sujet. 252 * 253 * @param msg Le message à envoyer. 254 */ 255 public function publish (msg:Message):Void 256 { 257 // TODO : Accès Singleton 258 var session = Session.getInstance(); 259 var envFactory = EnvelopeFactory.getInstance(); 260 var clazz = _Class.getInstance(); 261 262 var argCheck = [[Message, true]]; 263 264 if (! clazz.checkArguments("org.omus.messenger.publish", argCheck)) return; 265 266 session.sendMessage(envFactory.getOutgoing(msg, "msg.publish")); 267 } 268 269 /** 270 * S'abonne à tous les messages du sujet spécifié. 271 * Génère un événement onSubscribe aux abonnés. 272 * 273 * @param subject Sujet du message auquel s'abonner. 274 */ 275 public function subscribe (subject:String):Void 276 { 277 // TODO : Accès Singleton 278 var session = Session.getInstance(); 279 var envFactory = EnvelopeFactory.getInstance(); 280 var clazz = _Class.getInstance(); 281 282 if (! clazz.checkArguments("org.omus.messenger.subscribe", [[subject, "string", true]])) return; 283 if (isSubscribed(subject)) 284 { 285 // Broadcast 286 fireEvent("info", "onSubscribe", subject); 287 return; 288 } 289 290 // Envoie un message au serveur qui sera récupéré ensuite par gestionnaire d'accusé 291 session.sendMessage(envFactory.getOutgoing(new Message(subject), "msg.subscr")); 292 } 293 294 /** 295 * Se désabonne à un sujet. 296 * Génère un événement onUnsubscribe aux abonnés. 297 * 298 * @param subject Sujet auquel se désabonner. 299 */ 300 public function unsubscribe (subject:String):Void 301 { 302 // TODO : Accès Singleton 303 var session = Session.getInstance(); 304 var envFactory = EnvelopeFactory.getInstance(); 305 var clazz = _Class.getInstance(); 306 307 if (! clazz.checkArguments("org.omus.messenger.unsubscribe", [[subject, "string", true]])) return; 308 if (! isSubscribed(subject)) 309 { 310 fireEvent("info","onUnsubscribe",subject); 311 return; 312 } 313 314 // Envoie un message au serveur qui sera récupéré par accusé de réception. 315 session.sendMessage(envFactory.getOutgoing(new Message(subject), "msg.unsubscr")); 316 } 317 318 /** 319 * Se désabonne de tous les messages. 320 * Génère un événement onUnsubscribeAll aux abonnés. 321 */ 322 public function unsubscribeAll () { 323 if (subscr.length == 0) 324 { 325 fireEvent("info", "onUnsubscribeAll"); 326 } else { 327 // TODO : Accès Singleton 328 var session = Session.getInstance(); 329 var envFactory = EnvelopeFactory.getInstance(); 330 331 // Message serveur 332 session.sendMessage(envFactory.getOutgoing(new Message(""), "msg.unsubAll")); 333 } 334 } 335 336 /** 337 * Renvoie une liste de tous les sujets auquel le poste client s'est abonné. 338 * 339 * @return Une liste de tous les abonnements. 340 */ 341 public function getSubscriptions ():Array 342 { 343 return subscr.slice(0,this.subscr.length); 344 } 345 346 /** 347 * Renvoie si cet utilisateur a souscrit au sujet d'un message. 348 * 349 * @param subject Le sujet souhaité. 350 * @return true si le client est abonné à ce sujet. 351 */ 352 public function isSubscribed (subject:String):Boolean 353 { 354 var l = this.subscr; 355 var n = l.length; 356 for (var i = 0; i < n; i++) 357 { 358 if (l[i] == subject) return true; 359 } 360 return false; 361 } 362 363 /** 364 * Rajoute un filtre pour ne recevoir qu'une partie des messages. 365 * 366 * @param filter Un nouveau filtre. 367 */ 368 public function addFilter (filter:MessageFilter):Void 369 { 370 // TODO : Accès Singleton 371 var clazz = _Class.getInstance(); 372 if (! clazz.checkArguments("org.omus.messenger.addFilter", [[filter, MessageFilter, true]])) return; 373 // Doublon du filtre ? 374 if (indexOf(filter) == -1) filters.push(filter); 375 } 376 377 /** 378 * Supprime un filtre. 379 * 380 * @param filter Le filtre à supprimer. 381 */ 382 public function removeFilter (filter:MessageFilter):Void 383 { 384 // TODO : Accès Singleton 385 var clazz = _Class.getInstance(); 386 if (! clazz.checkArguments("org.omus.messenger.removeFilter", [[filter, MessageFilter, true]])) return; 387 var idx = indexOf(filter); 388 if (idx != -1) filters.splice(idx,1); 389 } 390 391 //*** Implémentation de iObservable ***\ 392 /** 393 * Notifie les observateurs d'un événement. 394 * 395 * @param logLevel Une chaine caractérisant le niveau de logging de l'information. 396 * @param eventName Nom de l'événement. 397 * @param arg1 [option] Autant de paramêtres de voulu. 398 */ 399 private function fireEvent (logLevel:String, eventName:String):Void 400 { 401 _eventSource.fireEvent.apply (_eventSource, arguments); 402 } 403 404 /** 405 * Ajoute un nouvel observateur. 406 * 407 * @param listener Référence de l'observateur. 408 * @return Un booléen indiquant la réussite de l'opération. 409 */ 410 public function addListener (listener:Object):Boolean 411 { 412 return _eventSource.addListener(listener); 413 } 414 415 /** 416 * Supprime un observateur. 417 * 418 * @param listener Référence de l'observateur. 419 * @return Un booléen indiquant la réussite de l'opération. 420 */ 421 public function removeListener (listener:Object):Boolean 422 { 423 return _eventSource.removeListener(listener); 424 } 425 426 /** 427 * Supprime tous les abonnés. 428 */ 429 public function removeAllListeners ():Void 430 { 431 _eventSource.removeAllListeners(); 432 } 433 434 /** 435 * Retourne le nombre d'observateurs. 436 * 437 * @return Le nombre d'observateurs enregistrés. 438 */ 439 public function countListeners ():Number 440 { 441 return _eventSource.countListeners(); 442 } 443 444 445 // Gestionnaires d'accusé de réception des messages serveur. 446 /** 447 * Gestionnaire d'accusé de réception de tout les messages serveur. 448 * Dirige le message vers le bon gestionnaire. 449 * TODO : Par l'air d'être utilisé ?! 450 * Voir si ce n'est pas un oubli d'implémenation de iMessageHandler. 451 * 452 * @param env Enveloppe renvoyée par le serveur. 453 */ 454 public function handleMessage (env:Envelope):Void 455 { 456 var mName = handlers[env.getType()]; 457 if (mName == undefined) { 458 // Logs internes 459 _global.oregano.iLog.error("clj-050", "envelope = " + env); 460 return; 461 } 462 // Active le gestionnaire. 463 this[mName](env); 464 } 465 466 /** 467 * Gestionnaire d'accusé de réception de message d'erreur. 468 * Génère un événement onError aux abonnés. 469 * 470 * @param env Enveloppe retournée par le serveur. 471 */ 472 private function handleError (env:Envelope):Void 473 { 474 // Broadcast 475 //!! TODO : Syntaxe. env.getMessage() should be Array. 476 fireEvent("error", "onError", new _Error("msg-003", "sendToServer", [env.getMessage()])); 477 } 478 479 /** 480 * Gestionnaire d'accusé de réception de message Administrateur. 481 * Génère un événement onAdminMessage aux abonnés. 482 * 483 * @param env Enveloppe retournée par le serveur. 484 */ 485 private function handleAdminMsg (env:Envelope):Void 486 { 487 var text = env.getMessage().getAttachment().text; 488 // Broadcast un événement (non renseigné dans la documentation) 489 fireEvent("info", "onAdminMessage", text); 490 } 491 492 /** 493 * Gestionnaire d'accusé de réception de message avec ou sans filtrage. 494 * Génère un événement onMessage aux abonnés ou à ceux de messageFilter. 495 * 496 * @param env Enveloppe du message retourné. 497 */ 498 private function handleCustomMsg (env:Envelope):Void 499 { 500 var msg = env.getMessage(); 501 var subj = msg.getSubject(); 502 var fil = filters; 503 var len = fil.length; 504 var filtered = false; 505 // Applique le filtre sur le message 506 for (var i = 0; i < len; i++) 507 { 508 var f = fil[i]; 509 if (f.subjects[subj]) 510 { 511 filtered = true; 512 // Broadcast 513 f.fireEvent("info","onMessage",msg); 514 } 515 } 516 // Broadcast 517 if (! filtered) fireEvent("info", "onMessage", msg); 518 // Logs internes 519 if (_global.oregano.iLog.infoEnabled()) _global.oregano.iLog.logLocal("INFO", "clj-051", "subject = " + subj); 520 } 521 522 /** 523 * Gestionnaire d'accusé de réception d'abonnement à un sujet. 524 * Génère un événement onSubscribe aux abonnés. 525 * 526 * @param env Enveloppe du message retourné. 527 */ 528 private function handleSubscribe (env:Envelope):Void 529 { 530 var subj = env.getMessage().getSubject(); 531 subscr.push(subj); 532 // Broadcast 533 fireEvent("info", "onSubscribe", subj); 534 } 535 536 /** 537 * Gestionnaire d'accusé de réception de désabonnement à un sujet. 538 * Génère un événement onUnsubscribe aux abonnés. 539 * 540 * @param env Enveloppe du message retourné. 541 */ 542 private function handleUnsubscribe (env:Envelope):Void 543 { 544 var subj = env.getMessage().getSubject(); 545 // Supprime l'abonnement. 546 removeArrElem(subscr, subj); 547 // Broadcast 548 fireEvent("info", "onUnsubscribe", subj); 549 } 550 551 /** 552 * Gestionnaire d'accusé de réception du désabonnement de tous les sujet. 553 * Génère un événement onSubscribeAll aux abonnés. 554 * 555 * @param env Enveloppe du message retourné. 556 */ 557 private function handleUnsubscribeAll (env:Envelope):Void 558 { 559 subscr = new Array(); 560 fireEvent("info", "onUnsubscribeAll"); 561 } 562 // Fin des gestionnaires d'accusé de réception. 563 564 //-------------------- 565 // METHODES PRIVEES 566 //-------------------- 567 /** 568 * Supprime un élément d'une liste. 569 * 570 * @param arr La liste originale. 571 * @param elem L'élément à enlever. 572 */ 573 private function removeArrElem (arr:Array, elem:String) { 574 for (var i = arr.length; i >= 0; i--) 575 { 576 if (arr[i] == elem) 577 { 578 arr.splice(i,1); 579 return; 580 } 581 } 582 } 583 584 /** 585 * Renvoie l'index d'un élément de la liste des filtres. 586 * 587 * @param filter Le fitre à trouver. 588 * @return L'index numérique de l'élément ou -1 s'il n'est pas référencé. 589 */ 590 private function indexOf (filter:MessageFilter):Number 591 { 592 var ls = filters; 593 var len = ls.length; 594 for (var i = 0; i < len; i++) 595 { 596 if (ls[i] == filter) return i; 597 } 598 return -1; 599 } 600 601 //-------------------- 602 // METHODES STATIQUES 603 //-------------------- 604 /** 605 * Utilisé dans un contexte littéral 606 * 607 * @return Une chaine définissant l'objet. 608 */ 609 public static function toLog():String 610 { 611 return "[Objet Messenger]"; 612 } 613 614 /** 615 * Accès global à la référence du Singleton 616 * @return Une référence à la classe 617 */ 618 public static function getInstance():Messenger 619 { 620 if(_instance == undefined) _instance = new Messenger(); 621 return _instance; 622 } 623 } 624