Gestione di una coda di mail in architettura SOA con WCF
←
→
Trascrizione del contenuto della pagina
Se il tuo browser non visualizza correttamente la pagina, ti preghiamo di leggere il contenuto della pagina quaggiù
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