Gestione di una coda di mail in architettura SOA con WCF

 
Gestione di una coda di mail in architettura SOA con WCF
F            OCUS

    Gestione di una coda di mail
    in architettura SOA con WCF
    L’invio di email è una funzionalità molto diffusa nelle applicazioni moderne e la gestione
    di più applicazioni che devono inviare email è una condizione presente in molte aziende.

    di Omar Venturi

    I
          n questo articolo vengono analizzati alcuni sce-            delle soluzioni adottate nelle loro applicazioni per
          nari relativi ad applicazioni che inviano email             l’invio delle email, ne evidenziano le problematiche,
          agli utenti e vengono descritti i passaggi princi-          le analizzano, progettano una soluzione comune per
          pali per realizzare un servizio centralizzato con           sostituire quelle esistenti ed infine la realizzano, sof-
    gestione di una coda.                                             fermandosi sugli aspetti più importanti ed utilizzando
    Vengono esaminate alcune delle caratteristiche del-               esempi del mondo reale.
    l’architettura SOA (Service Oriented Architecure) e
    delle API di WCF (Windows Communication Foun-                        L’invio di email è una funzionalità molto diffusa
    dation), evidenziando gli aspetti che semplificano lo             nelle applicazioni moderne (si pensi ad esempio ad un
    sviluppo e la gestione della comunicazione tra appli-             sito web che invia una mail agli utenti al termine del
    cazioni e servizi.                                                processo di registrazione) e la gestione di più appli-
       L’articolo è di livello introduttivo e si rivolge a            cazioni che devono inviare email, spesso implemen-
    chi, pur conoscendo la programmazione ad oggetti,                 tando soluzioni differenti, è una condizione presente
    conosce poco il modello architetturale SOA e la                   in molte aziende. In particolare, le caratteristiche
    tecnologia WCF di Microsoft; l’obiettivo è di guidare             comuni di tali applicazioni sono:
    il lettore nella progettazione, nella realizzazione, ma           • La mail rappresenta un punto all’interno di un
    soprattutto nella comprensione di una soluzione                       flusso di lavoro fondamentale per il completa-
    reale che utilizza questi strumenti.                                  mento di un’attività complessa (registrazione
       Nella trattazione si vedrà il percorso ipotetico                   utenti, ordini, segnalazioni di eventi, etc);
    affrontato da alcuni sviluppatori che, discutendo                 • È indispensabile poter verificare se e quando la mail
                                                                          è stata inviata;
                                                                      • La presenza di soluzioni diverse per ciascuna appli-
      Omar Venturi              omar.venturi@gmail.com
                                                                          cazione può rappresentare un costo da ridurre.
      Si occupa di progettazione e sviluppo di soluzioni E-Learning
      e Cross Media Publishing con tecnologie Microsoft .NET.
      Lavora come Project Manager per ONION S.p.A. – società
                                                                        Spesso le applicazioni si appoggiano a componenti
      di servizi IT con sedi a Brescia, Milano e Torino. Laureato     esterni per l’invio della mail, oppure sfruttano le
      presso l’Università degli studi di Milano in Tecnologie per     funzionalità più semplici messe a disposizione dai
      la società dell’informazione. È MPC (Microsoft Certified        framework applicativi. Inoltre, benché le code siano
      Professional) dal 1999 e MCSD (Microsoft Certified Solution
      Developer) dal 2001.
                                                                      uno strumento per migliorare l’affidabilità dell’invio

8                                                                              Computer Programming - n. 177 - Marzo 2008
SOA con WCF

 della mail, non sempre le applicazioni le utilizzano;              diverse.
 talvolta perché si ritiene troppo onerosa l’integra-                 Mentre i nostri tornano alle loro scrivanie, fermia-
 zione, talvolta perché il componente o il framework                moci un momento a pensare alla situazione appena
 non le supporta.                                                   descritta. Le casistiche analizzate non evidenziano
   Iniziamo quindi analizzando il caso della “Software              problematiche tecnologiche; è vero che qualche mail
 Meraviglioso S.p.A.”, software house specializzata                 talvolta non arriva a destinazione (Matthew e Luisa
 nella realizzazione di applicazioni web-oriented.                  hanno preferito non parlare di questo), ma tutto som-
   Software Meraviglioso è un’azienda organizzata in                mato questo non rappresenta un grosso problema per
 molte aree, ognuna composta da più gruppi di lavoro.               le loro applicazioni.
 Tutti i gruppi hanno sviluppato almeno una volta un                  Il problema è invece l’adozione di soluzioni diffe-
 progetto per il quale è stato necessario inviare mail              renti per coprire le stesse esigenze, con conseguente
 agli utenti e ciascun team è soddisfatto della solu-               aumento dei costi sia in fase di sviluppo che in quella
 zione implementata.                                                di manutenzione. Inoltre, ogni scenario presenta pro-
                                                                    blematiche specifiche che proviamo a riassumere:
 Il problema                                                        • l’utilizzo di componenti esterni è certamente una
                                                                        buona soluzione poiché garantisce il riuso del
   Davanti alla macchinetta del caffè un giorno si sono                 software. Tuttavia questa soluzione impone dei
 incontrati Claudio, Luisa e Matthew (appartenenti a                    vincoli sulla tecnologia utilizzata. Il progetto di
 tre gruppi di lavoro differenti) e chiacchierando si                   Claudio utilizza un componente COM, il progetto
 sono accorti che:                                                      di Luisa una classe Java ed il progetto di Matthew
 • Il progetto di Claudio utilizza un componente                        una classe del framework .NET: il progetto ed i
    COM chiamato SuperMail che supporta le code                         suoi componenti devono utilizzare la stessa tec-
    ed ha un prezzo di 300$;                                            nologia;
 • Il progetto di Luisa, più recente, utilizza un com-              • i componenti che non supportano le code scaricano
    ponente open source chiamato YourPostman; è                         la responsabilità dell’invio della mail sull’applica-
    disponibile la versione 0.95 per .NET
    e la versione 0.98 per Java (quella uti-
    lizzata da Luisa). La documentazione            FIGURA 1 - La soluzione è ospitata all'interno di due servizi Windows
    è buona ed il componente è freeware.
    Il supporto per le code sarà imple-
    mentato con il prossimo rilascio;
 • Il progetto di Matthew utilizza invece
    il client SMTP presente nel name-
    space System.Net.Mail del framework
    .NET. Questo componente non offre
    supporto nativo per le code, ma il
    gruppo di Matthew doveva realizzare
    velocemente la funzionalità di invio
    di password dimenticata (si erano
    proprio dimenticati di implemen-
    tarla!) ed hanno deciso che questa
    era la soluzione migliore; avrebbero
    pensato successivamente alla gestione
    delle code.

   Nessun problema funzionale quindi,
 tutto il software gira che è una mera-
 viglia, però tre gruppi diversi hanno
 implementato tre soluzioni diverse per
 lo stesso problema e, immaginano i
 nostri amici, è probabile che anche gli
 altri team abbiano adottato soluzioni

Computer Programming - n. 177 - Marzo 2008                                                                                      9
F           OCUS

        zione stessa. Cosa succede se il server di mail resti-        senza preoccuparsi di quale componente è utilizzato o di
        tuisce un errore durante l’invio?                             come è gestita la coda”.
     • il codice infrastrutturale per l’utilizzo dei compo-              “Certo, però non vedo vantaggi rispetto a quello che
        nenti non può essere riutilizzato: istanziare ed              ho implementato” – ribatte Claudio – “attualmente sto
        utilizzare il componente COM richiede codice                  utilizzando SuperMail e non mi preoccupo di come lui
        differente rispetto a quello del componente .NET              gestisce le code. Semplicemente la mia applicazione crea
        o Java;                                                       un’istanza dell’oggetto SuperMail.Message, gli assegna i
     • implementare le funzionalità di invio di e-mail                valori corretti e quindi chiama il metodo Send. Più facile
        per un progetto ha un costo che si ripete per ogni            di così!”.
        progetto, con un valore diverso in funzione della                “Prova a pensare”, interviene Matthew “a cosa suc-
        soluzione adottata.                                           cederebbe se utilizzassi il tuo componente in altri progetti e
                                                                      fra un po’ volessi passare alla nuova versione, magari per
        “Un servizio! Ecco quello di cui abbiamo bisogno!”            .NET o Python. Dovresti sicuramente modificare le parti
     esclama ad alta voce Luisa e l’indomani si confronta             di codice che utilizzano SuperMail, poiché i tuoi progetti
     con i suoi colleghi per valutare gli impatti che questa          referenziano direttamente il componente. Se, come sug-
     soluzione comporta sulle applicazioni esistenti.                 gerisce Luisa, utilizzassimo un servizio esterno, le nostre
        Luisa spiega che il loro problema è comune e facil-           applicazioni non sarebbero più dipendenti dalla tecnologia
     mente risolvibile; la maggior parte delle applicazioni           del componente per l’invio delle mail, che potrebbe quindi
     presenti in azienda necessita di inviare una mail                essere aggiornato in maniera indipendente”.
     e tutti implementano una soluzione diversa, con                     “Beh, è un bel vantaggio, ma devo modificare tutto
     diverse tecnologie.                                              quello che ho già scritto?” chiede Claudio.
        “Non risparmieremmo tempo se in azienda fosse dispo-             “No”, risponde Luisa, “le applicazioni esistenti pos-
     nibile un sistema in grado di registrare le mail che le nostre   sono continuare con la soluzione attuale oppure passare
     applicazioni devono inviare gestendo le code ed offrendo         gradualmente alla nuova soluzione; dipende dal rapporto
     anche meccanismi per il controllo degli errori, della piani-     tra costi e benefici. Questo è un altro dei vantaggi offerti
     ficazione delle spedizioni e dei log? Le nostre applicazioni     dall’architettura SOA: è possibile gestire gli aggiorna-
     vedrebbero l’invio della mail come un servizio esterno,          menti in base alle proprie esigenze”.
                                                                                        Quelli della Software Meraviglioso si
                                                                                     mettono quindi al lavoro ed iniziano a
           FIGURA 2 - Il diagramma delle classi della soluzione                      progettare l’architettura della soluzione.

                                                                                    L’architettura ed il modello
                                                                                       I nostri bravi sviluppatori (che in
                                                                                    questo momento stanno ricoprendo il
                                                                                    ruolo di architetti) hanno pensato di
                                                                                    ospitare la soluzione all’interno di due
                                                                                    servizi Windows, come mostrato in
                                                                                    Figura 1:
                                                                                    • MailQueue_Service: è responsa-
                                                                                    bile della scrittura e della lettura dei
                                                                                    messaggi dalla coda. Le applicazioni
                                                                                    che hanno bisogno di inviare e-mail
                                                                                    accedono a questo servizio utilizzando
                                                                                    due interfacce, delle quali parleremo
                                                                                    tra poco. Queste due interfacce sono
                                                                                    l’unico punto pubblico del servizio.
                                                                                    • MailQueue_Sender: è il servizio
                                                                                    responsabile di estrarre un messaggio
                                                                                    dalla coda e di inviarlo al mail server
                                                                                    configurato per quel messaggio (le mail
                                                                                    possono essere inviate utilizzando server

10                                                                              Computer Programming - n. 177 - Marzo 2008
SOA con WCF

   differenti). Anche se questo servizio colloquia               più istanze delle altre classi: contiene un’istanza di
   direttamente con MailQueue_Service, è stato                   Message (il messaggio) che a sua volta può contenere
   disaccoppiato e caricato in un servizio diverso               delle istanze di MessageSchedule (le regole di sche-
   e la comunicazione tra i due avviene attraverso               dulazione). La busta può contenere inoltre degli alle-
   una terza interfaccia. In questo modo è possibile             gati (MessageAttachmentBuffered) ed i parametri di
   sospendere il servizio che invia le mail (ad esempio          connessione al server (ApplicationConfiguration).
   per manutenzione del server di mail), continuando             Infine, partendo dalla classe MessageStatus, la busta
   a registrare le mail nella coda.                              può accedere all’elenco di eventi legati al messaggio,
                                                                 relativamente all’invio (MessageAttempt) o alle
    Introdotta l’architettura, vediamo ora il modello: le        operazioni effettuate su di esso, come la creazione o
 classi utilizzate per modellare il dominio sono semplici        eventualmente la modifica (MessageHistory).
 ed intuitive. Oltre alla classe principale Messaggio,
 sono state definite altre classi che permettono alle               Analizziamo un po’ più in dettaglio le interfacce.
 applicazioni di controllare il messaggio da inviare.            L’interfaccia IMailQueueWriter prevede solo il metodo
    Innanzitutto ogni applicazione ha la possibilità             AddMessage(): questo è il metodo principale che
 di definire quale server SMTP utilizzare per l’invio            permette di registrare un messaggio nella coda. La
 dei messaggi (le applicazioni autorizzate all’invio             chiamata a questo metodo prevede due parametri:
 dei messaggi sono registrate nel sistema), inoltre è            un’istanza della classe Message e la chiave dell’appli-
 possibile specificare, per ogni messaggio, un insieme           cazione mittente (quest’ultima impedisce che appli-
 di caratteristiche che influiscono direttamente sul             cazioni non registrate possano inviare messaggi).
 posizionamento del messaggio nella coda.                           Per scelta architetturale, si è deciso che per allegare
    “Cosa? Le applicazioni possono controllare direttamente      documenti alla mail è necessario utilizzare un’in-
 la posizione del messaggio nella coda?”
 chiede Claudio sorpreso di questo strano
 comportamento, “Non è esatto” risponde                FIGURA 3 - Schema del database utilizzato per memorizzare i messaggi
 Matthew giustificando la scelta, “Le
 applicazioni non possono definire la posi-
 zione del messaggio, ma per rendere più fles-
 sibile l’applicazione possono richiedere che il
 messaggio sia inviato in uno specifico istante
 temporale – usando la proprietà Planning
 – oppure che venga inviato secondo una
 schedulazione predefinita, usando la classe
 MessageSchedule. Nel database i messaggi
 sono sempre memorizzati in tabella secondo
 l’ordine d’inserimento, ma da questa tabella
 sono recuperati in ordine crescente rispetto
 alla data di invio richiesta”.
     “Comunque” continua Matthew “in
 genere un’applicazione non richiede che
 lo stesso messaggio sia inviato più volte.
 Se i messaggi non contengono né una
 pianificazione, né uno scheduling, la coda
 si comporta come una classica FIFO: il
 primo messaggio inserito è il primo inviato.
 Abbiamo deciso di aggiungere queste
 funzionalità per rendere più flessibile il
 servizio”.
    In Figura 2 è presentato il diagramma
 delle classi della soluzione. La classe
 MessageEnvelope (la busta) rappresenta
 la radice del modello e contiene una o

Computer Programming - n. 177 - Marzo 2008                                                                                    11
F           OCUS

     terfaccia differente (IMailQueueAttachmentWriter).           proprietà Attempts della classe Message.
     Come si vedrà meglio tra poco, i metodi definiti in
     queste interfacce possono essere attivati inviando dei       La prima classe con WCF
     messaggi SOAP a determinate URL.
       WCF controlla queste URL e può gestire indirizzi             Abbiamo definito l’architettura della soluzione,
     e protocolli differenti per ciascuna di esse; in questo      abbiamo descritto le classi e sappiamo quali relazioni
     modo si possono controllare separatamente le chia-           esistono tra loro; possiamo iniziare a scrivere la classe
     mate che inviano un semplice messaggio (poche                Message.
     decine di KB) da quelle che trasferiscono megabyte.            Il Listato 1 contiene una versione molto semplice
     È possibile ad esempio configurare il servizio affinché      di questa classe dove, per brevità, sono stati omessi
     la dimensione dei messaggi non superi i 100 KB               alcuni campi. È stato inoltre definito un semplice
     mentre quella degli allegati non superi i 20 MB.             costruttore (attenzione a questo aspetto, lo appro-
       La terza interfaccia, IMailQueueReader, è utilizzata       fondiremo nel paragrafo Messaggi ed oggetti) che
     esclusivamente dal servizio MailQueue_Sender che             dovrebbe garantire la creazione di istanze coerenti
     ad intervalli regolari interroga la coda chiamando il        con la nostra logica.
     metodo NextMessageToSend() il quale restituisce un
     oggetto di tipo MessageEnvelope. Come descritto                 Torniamo dai nostri amici ai quali si è aggiunto
     poco sopra, questo oggetto contiene tutte le infor-          Giorgio, un giovane sviluppatore che sta parteci-
     mazioni necessarie ad inviare la mail. Dopo aver             pando insieme a loro ad un brainstorming. Giorgio
     ottenuto la risposta dal server (positiva o negativa         sta cercando di capire quello che avviene dietro le
     che sia), MailQueue_Sender registra l’esito dell’in-         quinte quando un’applicazione si collega al servizio
     vio utilizzando il metodo SetResponse(). Se l’esito è        e crea un’istanza della classe Message. Ha capito che
     negativo, il metodo NextMessageToSend() restituirà           Message, definita in MailQueue_Service, deve in
     nuovamente il messaggio per un nuovo tentativo; è            qualche modo essere resa disponibile anche alla sua
     possibile controllare il numero di tentativi di invio        applicazione affinchè questa possa crearne un’istanza.
     cui un messaggio può essere sottoposto tramite la            Ascoltiamolo: “…in effetti non è affatto banale che la
                                                                  mia applicazione possa istanziare una classe della quale
                                                                  non possiede la definizione. La classe Message è definita
       LISTATO 1 - Versione molto semplice della classe Message   nel servizio, ma la mia applicazione deve comunque essere
                                                                  in grado di crearla. Inoltre, una volta passata l’istanza al
       public class Message                                       servizio, la mia applicazione non deve più occuparsi di
       {
                                                                  come il messaggio venga inviato, giusto?”
         private int _id;
         private int _attempts;
                                                                     “Esatto Giorgio”, risponde Matthew, “hai compreso
         private string _subject;                                 l’aspetto fondamentale di questa architettura. Per farti
         private string _body;                                    un esempio, immagina di andare al ristorante ed ordinare
         private string[] _to;                                    una pizza: non è sufficiente entrare, dobbiamo anche
         private DateTime[] _planning;                            essere in grado di ordinare quello che vogliamo secondo il
                                                                  menu del ristorante. La pizza è il risultato che chiediamo
           public Message(
                                                                  al ristorante (il servizio) e l’ordine è quello che dobbiamo
             string Subject,
             string Body,
                                                                  ‘costruire e passare’ al cameriere. Dopo aver ‘costruito
             string To,                                           l’ordine’, possiamo tranquillamente aspettare; a noi inte-
             string From,                                         ressa mangiare, non come viene preparata la pizza”.
             DateTime DispatchDate)                                  Poi continua “La soluzione che utilizza la tua applica-
           {                                                      zione per creare un’istanza di Message è analoga a quella
             _id = -1;                                            che utilizziamo noi per ordinare: leggiamo il menu. Non
             _attempts = int.MaxValue;
                                                                  dobbiamo andare dietro il banco e fare la pizza, e non
             _subject = Subject;
             _body = Body;                                        dobbiamo nemmeno assumere un pizzaiolo privato a casa
             _to = new string[] {To};                             (cioè acquistare un componente tutto per noi), è suffi-
             _planning = new DateTime[]                           ciente chiedere il servizio che vogliamo seguendo il menu
                  {DispatchDate};                                 del ristorante”.
           }
       }                                                            Fantastico! Adesso che abbiamo capito i vantaggi

12                                                                          Computer Programming - n. 177 - Marzo 2008
SOA con WCF

 offerti dall’architettura orientata ai servizi ed i passi     mentre il secondo è uno standard per descrivere i
 fondamentali per scambiare dati con il servizio, siamo        dati serializzati. Il contratto infine, utilizza i primi due
 pronti per guardare un po’ più da vicino gli aspetti          per astrarre e semplificare la descrizione dei servizi
 legati alla comunicazione.                                    offerti da un’applicazione, dei dati scambiati tra gli
    Iniziamo con qualche domanda: come fa l’ap-                attori coinvolti e di altro ancora. In WCF sono pre-
 plicazione ad inviare i dati a MailQueue_Service?             viste cinque tipologie di contratto che introduciamo
 MailQueue_Service gestisce direttamente la comu-              brevemente:
 nicazione con MailQueue_Sender e le applicazioni              • Service contract: definisce le operazioni esposte
 oppure si appoggia ad altri componenti? Come è                   da un servizio, cioè il cosa viene offerto. IMail-
 possibile inviare un’istanza della classe Message, in            QueueWriter è definito ad esempio come service
 esecuzione su un server, ad un altro server sul quale è          contract (un service contract per il ristorante
 in esecuzione MailQueue_Service?                                 potrebbe essere “si fanno pizze!”).
    Le risposte sono fornite da WCF, un insieme di             • Data contract: descrive gli oggetti utilizzati dal ser-
 classi (un framework) che si occupa della comuni-                vizio e la loro struttura. Tutte le classi del nostro
 cazione tra applicazioni. Utilizzando WCF, le nostre             modello ad oggetti che devono essere utilizzate
 applicazioni non devono occuparsi di aprire connes-              dalle applicazioni sono definite come Data Con-
 sioni di rete, di gestire buffer di trasmissione, di seria-      tract.
 lizzare gli oggetti o altri aspetti del genere, ma è WCF      • Message contract: questo tipo di contratto è utiliz-
 che si occupa di tutto ciò.                                      zato per avere un controllo completo su come gli
    WCF non è la prima né l’unica tecnologia per                  oggetti viaggiano sulla rete, cioè su come vengono
 la gestione semplificata della comunicazione tra le              serializzati e descritti con SOAP; in pratica per-
 applicazioni; i Web Service ad esempio erano la solu-            mette di controllare il messaggio SOAP in tutti i
 zione precedente prodotta da Microsoft. WCF si pre-              suoi aspetti. È un po’ come quando lasciate chiuso
 senta come l’evoluzione dei Web Service con l’obiet-             il menu ed ordinate una pizza ingrediente per
 tivo di unificare, sotto un unico nome, le modalità              ingrediente. Niente menu, gli ingredienti li scelgo
 di comunicazione che utilizzano anche diversi pro-               io!
 tocolli come HTTP, TCP, MSMQ, NamePipe, etc.                  • Fault contract: descrive i messaggi di errore e per-
 Alla base di tutto ci sono i concetti di serializzazione,        mette alle applicazioni di interpretarli corretta-
 SOAP e contratto: il primo è il processo con il quale            mente.
 un oggetto viene salvato su un supporto oppure                • Callback contract: in genere la comunicazione parte
 inviato attraverso un canale di comunicazione (met-              dall’applicazione richiedente (il consumer) ed
 tendo in sequenza i dati che rappresentano l’istanza),           arriva al servizio. Questo contratto permette al
                                                                  consumer di farsi chiamare dal servizio, stabilendo
                                                                  una comunicazione bidirezionale ("Quando le pizze
   LISTATO 2 - DataContract e ServiceContract                     sono pronte chiamami al 338.123456").

    [DataContract]                                               È importante chiarire un aspetto fondamentale
    public class Message
    {
                                                               prima di proseguire: WCF è una tecnologia .NET
        [DataMember(Name=”Id”)]                                e richiede l’utilizzo di strumenti specifici per questo
      private int _id;                                         framework. Tuttavia le applicazioni che utilizzano i
        [DataMember(Name=”Attempt”)]
      private int _attempts;
        [DataMember(Name=”Subject”)]
      private string _subject;                                   LISTATO 3 - Definizione di IMailQueueWriter
        [DataMember(Name=”Body”)]
      private string _body;                                       [ServiceContract]
        [DataMember(Name=”To”)]                                   public interfaceIMailQueueWriter
      private string[] _to;                                       {
        [DataMember(Name=”Plannin”)]                                [OperationContract()]
      private DateTime[] _planning;                                  int AddMessage(
                                                                       Message Mail,
    /* Costruttore */                                                  string ApplicationKey);
    }                                                             }

Computer Programming - n. 177 - Marzo 2008                                                                                    13
F           OCUS

     servizi esposti tramite WCF possono essere realizzati             Il Data contract utilizza due attributi:
     con qualunque tecnologia in grado di comunicare                  • DataContract: da utilizzare sulla classe (o sulla
     con messaggi SOAP. Ecco perché, (riprendendo per                   struct)
     un istante l’inizio dell’articolo) mentre la scelta di un        • DataMember: da utilizzare sui campi o sulle pro-
     componente è vincolata dalla tecnologia del progetto               prietà
     (COM, Java, .NET, etc), l’utilizzo di un servizio è
     svincolato dalla tecnologia dell’applicazione consu-               Si noti che i campi esposti con l’attributo Data-
     mer.                                                             Member sono definiti come privati: i modificatori
                                                                      di visibilità non hanno effetto sul messaggio. Tutti i
       Affrontiamo adesso un aspetto pratico, quale la                campi (o le proprietà get/set) decorati con l’attributo
     definizione dei contratti.                                       DataMember sono pubblicati con il proprio nome o
       WCF fa ampio uso degli attributi .NET per questo               con quello definito dal parametro Name, indipenden-
     aspetto. Ogni tipologia di contratto prevede un                  temente dal fatto che siano dichiarati come privati.
     insieme definito di attributi con i quali decorare le
     proprie classi. Il run-time di WCF legge questi attri-            Il Service contract utilizza invece gli attributi:
     buti e crea automaticamente il codice necessario per             • ServiceContract: da utilizzare sulla classe o sull’in-
     la pubblicazione del servizio. Vediamo come definire               terfaccia
     un DataContract ed un ServiceContract (Listato 2).               • OperationContract: da utilizzare sui metodi

                                                                        Si è già accennato al fatto che i consumer acce-
                                                                      dono ai servizi attraverso le interfacce. Queste sono
       LISTATO 4 - Configurazione del servizio per IMailQueueWriter   definite come Service Contract ed esposte pertanto
                                                                      come servizi. IMailQueueWriter è definita ad esem-
                                                            pio come mostrato nel Listato 3.
                                                   classe concreta (MailQueueWriter) responsabile di
                                                                      using MailQueue.service;
        
                                                             public partial class MQHost:ServiceBase
                                                                        {
                                                               private static ServiceHost _mqH = null;
                                                        protected override void
                                                       OnStart(string[] args)
                                         ServiceHost(typeof(MailQueueWriter));
         
                                                         _ mqH.Open();
                                                             }

                                                               protected override void OnStop()
                                                          {
         
                                                                   _mqH.Close();
                                                         }
                                                             }

14                                                                              Computer Programming - n. 177 - Marzo 2008
SOA con WCF

 memorizzare il messaggio nella coda. Per completezza
                                                               LISTATO 6 - Descrizione degli errori restituiti
 in Figura 3 è riportato lo schema del database uti-
 lizzato per memorizzare i messaggi; gli aspetti che si
                                                               [ServiceContract]
 riferiscono alla memorizzazione dei dati nelle tabelle        public interface IMailQueueWriter
 esulano dagli obiettivi di questo articolo. È sufficiente     {
 in questo caso evidenziare l’esistenza di relazioni 1-          [OperationContract()]
 1 tra le tabelle e le classi del modello (Message,              [FaultContract(typeof(MailException))]
 Application, Attachment, etc) per comprenderne le               [FaultContract(typeof(Exception))]
 logiche di memorizzazione principali. Si noti inoltre            int AddMessage(
                                                                    Message Mail,
 che la tabella [MQ.MESSAGE.QUEUE] non ha una                       string ApplicationKey);
 classe relativa nel modello ad oggetti: questa tabella        }
 contiene la coda ed è la tabella dalla quale estrarre un
 messaggio ad ogni chiamata del metodo NextMessa-
 geToSend().                                                 letto. Anche i consumer della nostra applicazione
                                                             prima di poter comunicare con il servizio devono
 ABC: Address, Binding, Contract                             richiedere il contract al servizio e leggerlo: in questo
                                                             caso il contract è scritto in WSDL, un linguaggio
    Il nostro servizio è quasi pronto: abbiamo creato le
                                                             derivato dall’XML per la descrizione delle inter-
 classi ed abbiamo definito i contratti; dobbiamo ora
                                                             facce dei servizi web. WCF, a differenza di quello
 preparare l’ambiente necessario a pubblicarlo.
                                                             che accade con i Web Service, nasconde di default
    Sappiamo che i nostri amici della “Software Mera-
                                                             il contratto; affinché questo possa essere letto dai
 viglioso” hanno deciso di esporre il servizio come un
                                                             consumer è necessario impostare i valori appropriati
 Windows Service, ma non abbiamo detto ancora
                                                             all’interno della sezione Behavior del file di configu-
 niente sul protocollo utilizzato, su quale porta rimarrà
                                                             razione. I consumer hanno bisogno del WSDL per
 in ascolto e se, ad esempio, i messaggi necessitano di
                                                             chiamare il servizio e lo possono leggere con una
 criteri di protezione. Questi aspetti sono gestiti da
                                                             chiamata HTTP-GET.
 WCF e controllabili da un file di configurazione, il
                                                                Vediamo come configurare il servizio per la classe
 che semplifica molto le attività di pubblicazione e
                                                             MailQueueWriter. Nel Listato 4 sono presenti tre
 manutenzione del servizio.
                                                             sezioni utilizzate per configurare gli elementi fonda-
    L’infrastruttura di WCF che permette l’esecuzione
                                                             mentali del run-time di WCF:
 dei servizi è controllata da una tripletta di parametri
                                                             • services: questa è la sezione principale utilizzata per
 il cui acronimo è ABC (Address, Binding e Contract)
                                                                 configurare l’endpoint del servizio. Questa sezione
 ed alla quale ci si riferisce con il nome "endpoint". Il
                                                                 utilizza valori definiti nelle altre due sezioni,
 Contract lo abbiamo incontrato nel paragrafo prece-
                                                                 vedremo tra poco come sono costruiti i collega-
 dente, vediamo ora cosa sono Address e Binding.
                                                                 menti tra le sezioni;
    L’address è l’indirizzo (URL) al quale il servizio è
                                                             • behaviors: questa sezione è utilizzata in questo caso
 raggiungibile. Il formato dell’address è:
                                                                 per pubblicare il contratto WSDL. In particolare
 protocollo://server:porta/extension                             il valore dell’attributo httpGetUrl rappresenta
                                                                 l’indirizzo http dal quale il consumer può leggere il
   Ad esempio http://localhost:9190/MailWriter è un              WSDL;
 address valido per il nostro servizio, così come lo è       • bindings: questa sezione controlla alcuni aspetti
 net.tcp://192.168.0.5:9191/MailRead.                            di basso livello della comunicazione, come ad
                                                                 esempio la dimensione del buffer utilizzato per la
   Il binding è un elemento fondamentale di WCF                  comunicazione.
 poiché oltre al protocollo di comunicazione controlla
 un insieme di altre caratteristiche, quali ad esempio          Si noti infine che tutti i parametri definiti nel
 la capacità di stabilire comunicazioni bidirezionali        file di configurazione si possono impostare anche in
 (duplex) o a senso unico. Per un approfondimento di         maniera programmatica, potendo in questo modo
 questi aspetti si rimanda a [1].                            realizzare delle combinazioni molto flessibili.
   Prima di procedere, torniamo per un istante
 all’esempio della pizzeria ricordando che prima di            Il collegamento service-binding si ottiene con-
 poter ordinare abbiamo chiesto il menu e lo abbiamo         frontando il valore dell’attributo service/endpoint/

Computer Programming - n. 177 - Marzo 2008                                                                               15
F           OCUS

     @binding con il nodo figlio di bindings con lo stesso      e di passarle le richieste in accordo a quanto definito
     valore.                                                    nel file di configurazione.
       Il collegamento service-behavior si ottiene invece         Tutto quello che dobbiamo fare per attivare il servi-
     confrontando il valore dell’attributo service/@beha-       zio è presentato nel Listato 5 e riassunto di seguito:
     viorConfiguration con il valore dell’attributo             1. Creare in Visual Studio 2005 un nuovo progetto di
     behavior/@name.                                               tipo Windows Service;
                                                                2. Aggiungere un riferimento al componente .NET
       Il servizio configurato con questo file apre un socket      System.ServiceModel;
     TCP all’indirizzo mypc:8000/mqW/ e pubblica il             3. Aggiungere un riferimento al progetto contenente
     proprio WSDL all’indirizzo http://mypc:8080/mqW.              le classi che implementano i ServiceContract
                                                                   (MailQueue.service);
     Hosting del servizio                                       4. Dichiarare una variabile di tipo ServiceHost;
                                                                5. Creare un’istanza di ServiceHost e ed aprire il ser-
        Si deve ora attivare il servizio ed attendere le           vizio nel metodo OnStart() del Windows Service;
     richieste inviate dai consumer. Per essere attivato,       6. Chiudere il servizio nel metodo OnStop() del ser-
     un servizio deve essere ospitato all’interno di un pro-       vizio.
     cesso controllato dal runtime di WCF (si pensi al ser-
     vizio come ad una dll). Oltre ai servizi Windows che         Quando il servizio viene avviato, viene eseguito il
     utilizzeremo tra breve, un servizio può essere ospitato    metodo OnStart nel quale viene creata un’istanza di
     ad esempio in una Console Application, in una Win-         ServiceHost (gestita completamente da WCF) che
     dows Forms Application, in IIS ed altri ancora (per        prepara l’infrastruttura di rete necessaria alla comu-
     approfondimenti si veda [4]).                              nicazione con il servizio. La chiamata al metodo
        WCF fornisce la classe ServiceHost per la gestione      Open() apre la comunicazione e mette il servizio in
     del servizio e dell’infrastruttura di comunicazione.       attesa.
     È sufficiente passare al costruttore di questa classe
     il tipo del nostro servizio (in questo esempio Mail-       Messaggi ed oggetti
     QueueWriter) e WCF si fa carico di creare un’istanza
                                                                   Affrontiamo ora il punto che abbiamo lasciato
                                                                aperto relativo al costruttore della classe Message.
       LISTATO 7 - Il polling di MailQueue_Sender
                                                                Si affrontano qui alcuni argomenti di carattere gene-
                                                                rale che valgono anche al di fuori dell’applicazione
       _smtp = new SmtpClient();                                che stiamo realizzando. È importante comprenderli
       while (!_stop){ // polling                               poiché questi concetti hanno un impatto diretto sulle
        mailSnd =_client.NextMessageToSend();                   classi e sull’architettura delle nostre applicazioni.
        while (mailSnd!= null){ // coda                         Scegliere di realizzare un’applicazione con architet-
         try{                                                   tura SOA offre certamente dei vantaggi, ma richiede
          MailMessage mail = new MailMessage();                 anche attenzione e consapevolezza poiché non è pos-
        /* valorizza le proprietà della mail */
                                                                sibile applicare tutte le tecniche di programmazione
          mail.Subject = mailSnd.Subject;
          mail.Body = mailSnd.Body;                             utilizzate ad esempio con un’architettura basata sui
        /* altre proprietà... */                                componenti. Applicare un’architettura SOA senza
          smpt.Send(mail);                                      conoscerne le caratteristiche può portare a grossi
          _client.SetResponse(                                  problemi strutturali o al fallimento del progetto. Per
            mailSnd.CurrentAttemptId,                           ulteriori informazioni sull’architettura SOA si può
            string.Empty, true);                                fare riferimento a [3].
         }catch (Exception ex) {
          _client.SetResponse(
                                                                   Torniamo quindi al problema: l’obiettivo del
            mailToSend.CurrentAttemptId,                        costruttore era di permettere al consumer di creare
            ex.Message, false);}                                un’istanza della classe Message coerente con la nostra
         mailSnd =_client.NextMessageToSend();                  logica applicativa, garantendo quindi che tutte le
                                                                istanze di quella classe contenessero un valore per
        } // fine coda                                          i campi “corpo”, “mittente”, “oggetto” e “data di
       Thread.Sleep(pollingSecond);                             spedizione”.
       } // fine polling
                                                                   Riprendiamo per l’ultima volta l’esempio della

16                                                                       Computer Programming - n. 177 - Marzo 2008
SOA con WCF

 pizzeria per capire il problema
                                            LISTATO 8 - Come inserire un messaggio nella coda
 del costruttore: sappiamo che
 il menu descrive le pizze dispo-
                                             static void Main(string[] args)
 nibili e gli ingredienti utilizzati         {
 per ciascuna pizza, così come                 int messageId;
 il DataContract descrive le
 proprietà della classe. Menu e                mq.Message m = new mq.Message();
 DataContract non descrivono                   m.From = “omar.venturi@gmail.com”;
 il comportamento degli oggetti,               m.FromName = “Omar Venturi”;
                                               m.Html = false;
 ma solo le loro caratteristiche.              m.Subject = “Test message #1”;
 Ordinare una pizza “quattro                   m.Body = “Hello World”;
 formaggi senza gorgonzola”                    m.To = new string[1];
 significa, nell’ottica SOA, fare              m.To[0] = “o.venturi@onion.it”;
 una richiesta al pizzaiolo (il ser-
 vizio) e non alla pizza (la classe)           mq.MailQueueServiceClient client = new mq.MailQueueServiceClient();
 che in questo caso è un oggetto
                                               messageId = client.AddMessage(m, “DSKWSMPWPECFOC000334IKJ”);
 “stupido”. Analogamente, per                }
 verificare se una mail senza
 destinatari è valida, è necessario
 contattare il servizio, non la mail                             al consumer che dovrà essere in grado di gestirlo.
 stessa.                                                            Come accennato in precedenza, il Fault Contract è
    È chiaro quindi che lasciare il costruttore sulla            il tipo di contratto utilizzato per descrivere i messaggi
 classe Message non ha senso in questo contesto                  di errore che il servizio può restituire. I fault contract
 perché Message non deve implementare alcuna                     sono pubblicati attraverso il WSDL e di conseguenza
 funzionalità; sarebbe come se la pizza che ordiniamo            i consumer li conoscono in anticipo e li possono
 fosse in grado di rifiutarsi di cuocere se la combina-          gestire. Come mostrato nel Listato 6, FaultContract
 zione di ingredienti richiesta non fosse valida!                è un attributo che va utilizzato insieme ad Opera-
    Questo è il punto: le classi che decoriamo con gli           tionContract ed aggiunge ai metodi una descrizione
 attributi WCF non sono esposte come oggetti (cioè               di quali errori possono essere restituiti. Il parametro
 come entità con delle caratteristiche e dei comporta-           passato è un DataContract (si veda il codice allegato
 menti), ma come dei semplici messaggi. WCF non è                per maggiori dettagli) e pertanto il meccanismo di
 una tecnologia che permette di serializzare oggetti e           inserimento nel WSDL è analogo per i DataContract
 trasferirli attraverso la rete (questo è un compito che         e per i FaultContract, cambia solo la modalità di
 può svolgere ad esempio una tecnologia come Remo-               attivazione.
 ting del .NET Framework): WCF è una tecnologia
 che si occupa di trasferire dati, non comportamenti.               Sollevare un’eccezione nel servizio è un’operazione
    Quando un consumer legge il WSDL del servizio,               che richiede poche righe di codice:
 non riesce a leggere il codice all’interno del costrut-         • creare un’istanza della classe MailException;
 tore della classe (e nemmeno di un qualunque altro              • effettuare il throw di una nuova FaultException
 metodo) e pertanto non può utilizzarli, poiché l’in-                alla quale si passa l’istanza del punto precedente.
 frastruttura utilizzata non è semplicemente proget-
 tata per farlo. È necessario quindi implementare la
 validazione dei dati nel servizio; nel nostro caso, la          MailException me =
 validazione è realizzata all’interno del metodo Add-                 MailException.Create(“Istanza non valida”);
 Message(), cioè del punto d’ingresso del servizio.              throw new
                                                                    FaultException(me);
 Gestione degli errori
                                                                 Inviare un messaggio
   Se l’istanza del messaggio passata al metodo Add-
 Message() non è valorizzata correttamente, ad esem-               A questo punto non ci resta che creare il servizio
 pio non contiene alcun indirizzo di destinazione,               MailQueue_Sender per l’invio dei messaggi ed un
 allora il servizio può restituire un messaggio d’errore         semplice client per l’inserimento delle mail nella coda

Computer Programming - n. 177 - Marzo 2008                                                                                    17
F          OCUS

     (nella realtà i client saranno più complessi, come ad
     esempio applicazioni web).                                         int messageId;
       La struttura di MailQueue_Sender è ancora un                     mq.Message m = new mq.Message();
     progetto di tipo Windows Service. È in questo servi-               m.From = “omar.venturi@gmail.com”;
     zio che possiamo decidere la tecnologia da utilizzare              m.FromName = “Omar Venturi”;
     per inviare le mail: COM, Java, .NET o altro ancora.               m.Html = false;
     Nel nostro caso utilizziamo il client SMTP di .NET.                m.Subject = “Test message #1”;
       Dopo aver avviato il servizio MailQueue_Service,                 m.Body = “Hello World”;
     da Visual Studio 2005 è possibile aggiungere una                   m.To = new string[1];
     Service Reference inserendo l’url del WSDL dell’en-                m.To[0] = “o.venturi@onion.it”;
     dopoint che espone l’interfaccia IMailQueueReader:
     Visual Studio legge il contratto (ecco perché il servi-            mq.MailQueueServiceClient client =
     zio deve essere attivo) e crea automaticamente una                   new mq.MailQueueServiceClient();
     classe proxy che si interfaccia con il servizio. Questa
     classe, implementando l’interfaccia IMailQueueRea-
                                                                        messageId = client.AddMessage(
     der, espone il metodo NextMessageToSend() che
                                                                                         m, “DSKWSMPWPECFOC000334IKJ”);
     estrae dalla coda il primo messaggio.
                                                                    }
       MailQueue_Sender effettua un polling sul servizio
     MailQueue_Service con una frequenza configura-
     bile: se non ci sono messaggi pronti per essere inviati        Conclusioni
     si mette in attesa, altrimenti invia uno ad uno i                 Abbiamo visto come l’architettura SOA definisca
     messaggi utilizzando per ognuno il server SMTP asso-           linee guida che permettono di disaccoppiare i servizi
     ciato al messaggio, infine contatta nuovamente Mai-            utilizzati dalle applicazioni consumer. Naturalmente
     lQueue_Service per registrare l’esito dell’operazione,         SOA non è la soluzione a tutti i problemi ed in
     utilizzando il metodo SetResponse, come mostrato               questo articolo abbiamo dato alcuni elementi per
     nel Listato 7.                                                 capire quando può essere efficace questa scelta archi-
       Vediamo infine come inserire un messaggio nella              tetturale e quali problemi debbano essere affrontati
     coda. Creiamo un progetto di tipo Console Applica-             a seguito della sua adozione. Benché la tecnologia
     tion (ricordiamoci di registrare questa applicazione           WCF non sia stata creata esclusivamente per le
     nella tabella del DB delle applicazioni, assegnando            architetture SOA, ne è sicuramente un componente
     anche una chiave), aggiungiamo una Service Refe-               molto importante che permette ai progettisti di con-
     rence al contratto WSDL relativo all’interfaccia               centrarsi su quelle che sono le problematiche di busi-
     IMailQueueWriter (se vogliamo allegare documenti               ness dell’applicazione, demandando al framework le
     dobbiamo aggiungere una Service Reference anche                problematiche di comunicazione.
     a IMailQueueAttachmentWriter), quindi istanziamo
     un nuovo messaggio, valorizziamo le proprietà e pas-
     siamo l’istanza, insieme alla chiave dell’applicazione,            CODICE ALLEGATO
     al metodo AddMessage, come mostrato nel codice
     seguente e nel Listato 8.                                          ftp.infomedia.it                             SOA-WCF
                                                                        Sistema per la gestione di una coda centralizzata di email
                                                                        completamente configurabile. Il progetto consiste di due
     static void Main(string[] args)                                    servizi Windows (uno per la ricezione dei messaggi ed uno
     {                                                                  per l’invio) e di un database.

       BIBLIOGRAFIA E RIFERIMENTI
       [1] Juval Lowy – “Programming WCF Services”. O’Reilly, 2007
       [2] Scott Klein – “Professional WCF Programming”, Wrox, 2007
       [3] “Service Oriented Architecture (SOA) in the Real World”, http://msdn2.microsoft.com/en-us/architecture/
           bb833022.aspx
       [4] Chris Peiris, Dennis Mulder – “Hosting and Consuming WCF Services”, http://msdn2.microsoft.com/en-us/library/
           bb332338.aspx

18                                                                              Computer Programming - n. 177 - Marzo 2008
Puoi anche leggere