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 2008SOA 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 9F 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 2008SOA 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 11F 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 2008SOA 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 13F 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 2008SOA 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 15F 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 2008SOA 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 17F 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 2008Puoi anche leggere