Migrazione dei meccanismi di workflow di un sistema informativo assicurativo verso l'ambiente SQL...
-
Upload
donato-clun -
Category
Technology
-
view
2.038 -
download
0
description
Transcript of Migrazione dei meccanismi di workflow di un sistema informativo assicurativo verso l'ambiente SQL...
UNIVERSITÀ DEGLI STUDI DI TRIESTE
FACOLTÀ DI INGEGNERIA
Corso di laurea triennale in
Ingegneria Informatica
Migrazione dei meccanismi di workflow di un sistema informativo assicurativo
verso l’ambiente SQL Server e .NET
Laureando: Relatore:
Donato Clun Chiar.mo Prof. Leonardo Felician
Anno accademico 2009 / 2010
3
Sommario
Capitolo 1: Introduzione ................................................................ 5
Il contenuto di questo documento ................................................................ 5
Luogo di svolgimento del lavoro documentato .............................................. 7
Dettagli della migrazione.............................................................................. 7
L‟ambiente sorgente ............................................................................................... 7
L‟ambiente obiettivo ............................................................................................... 8
Le ragioni della migrazione .......................................................................... 9
Precisazioni su questo documento ............................................................... 9
Capitolo 2: Il sistema informativo di OnLife ................................. 10
L‟ambiente di esecuzione prima della migrazione ....................................... 10
La base di dati attuale: concetti chiave dello schema dei dati ..................... 11
Prospect .............................................................................................................. 11
Partner ................................................................................................................ 11
Quotazione .......................................................................................................... 11
Carico contabile ................................................................................................... 12
Capitolo 3: Descrizione dei requisiti ............................................ 13
Vincoli non funzionali ................................................................................ 13
Affidabilità dell‟applicativo ................................................................................... 13
Prestazioni ........................................................................................................... 14
Descrizione delle funzioni .......................................................................... 15
Generazione di PDF ............................................................................................. 15
Caricamento delle nuove quotazioni e dei nuovi contratti ...................................... 16
Elaborazione delle mail in ingresso ....................................................................... 16
Sincronizzazione degli status ............................................................................... 17
Eliminazione dei dati sensibili .............................................................................. 17
Elaborazione rendiconti intermediari .................................................................... 17
Invio di E-mail ..................................................................................................... 18
Automazione di campagne di comunicazione ........................................................ 18
4
Capitolo 4: Analisi dei problemi e definizione delle metodologie risolutive ................................................................... 20
Disponibilità del servizio e robustezza del software..................................... 20
Il problema .......................................................................................................... 20
La cause di possibili problemi .............................................................................. 22
Definizione di una metodologia risolutiva ............................................................. 22
Creazione di documenti PDF ...................................................................... 31
La soluzione ........................................................................................................ 32
Miglioramento delle prestazioni .................................................................. 32
Analisi della causa ............................................................................................... 33
Metodologia risolutiva .......................................................................................... 33
Capitolo 5: Implementazione nell‟ambiente target ....................... 35
Strategia di sviluppo software adottata ...................................................... 35
“Logger”.......... ........................................................................................... 36
Creazione della classe Logger ............................................................................... 37
Funzioni di accesso al database ................................................................. 41
Separazione tra thread principale e thread supervisore .............................. 42
Caricamento delle quotazioni ..................................................................... 43
Invio della posta ........................................................................................ 45
Lettura della posta..................................................................................... 50
Automazione di campagne di comunicazione ............................................. 51
Esecuzione della procedura dei rinnovi ...................................................... 52
Controllo RID respinti ................................................................................ 54
Eliminazione dei dati sensibili.................................................................... 55
Sincronizzazione degli status ..................................................................... 56
Elaborazione automatica degli incassi ........................................................ 58
Elaborazione degli estratti conto ................................................................ 59
Capitolo 6: Conclusioni ............................................................... 62
Bibliografia........ ........................................................................... 63
5
Capitolo 1: Introduzione
In questa tesi vengono descritti gli
obiettivi, analizzati i problemi affrontati e
descritte le metodologie risolutive adottate,
nell‟ambito del lavoro di migrazione della logica
funzionale di OnLife, la prima polizza vita ad
essere venduta direttamente ai clienti tramite
internet.
Il contenuto di questo documento
Per definire chiaramente la parte del sistema informativo oggetto della
migrazione descritta in questa tesi è necessaria una premessa. Per qualunque
organizzazione disporre di un buon sistema informativo informatizzato non
significa soltanto poter disporre delle informazioni necessarie in tempi rapidi,
ma significa anche poter automatizzare parte dei processi operativi aziendali. In
quest‟ottica un criterio lecito (che non considera come è realizzato il sistema,
ma solo l‟aspetto operativo) secondo il quale è possibile distinguere diversi
insiemi di funzionalità in un sistema informativo è il grado di intervento umano
necessario alla raccolta ed elaborazione dei dati e, successivamente, alla
produzione e distribuzione delle informazioni a chi (o a cosa) ne ha bisogno.
Con il progredire della tecnologia di elaborazione automatica delle
informazioni e la crescente disponibilità di questi strumenti, la possibilità di
automatizzare parte dei processi ha infatti assunto un ruolo sempre più
importante. In alcuni casi l‟interazione con gli utenti può essere ridotta a zero,
come nel caso di un sistema che al verificarsi di una predeterminata condizione
dia autonomamente luogo ad una sequenza di operazioni di elaborazione che
6
producono delle informazioni successivamente inviate ad un altro sistema
informatizzato, ad esempio tramite un web service.
In un sistema informativo complesso è quindi possibile individuare un
insieme di automatismi che hanno luogo con un elevato livello di autonomia
rispetto alla necessità di input da parte dell‟utente (quindi che elaborano
solamente dati provenienti dalla base di dati), e che non hanno inizio a causa
di una interrogazione esterna, ma a causa del verificarsi di una predeterminata
condizione. Tali automatismi possono essere definiti batch, termine
comunemente utilizzato in informatica per descrivere l‟esecuzione di una serie
di programmi senza intervento manuale. Nell‟ambito di un sistema informativo
gli automatismi batch sono dunque il risultato dell‟automazione di una parte
dei meccanismi di workflow operativo.
In questo documento viene trattata proprio la migrazione, da un ambiente
software già esistente e funzionante a un nuovo ambiente in grado di
soddisfare meglio le necessità dell‟azienda, degli automatismi batch di OnLife,
la prima polizza vita ad essere venduta direttamente ai clienti tramite internet
in Italia.
Nell‟ambito di questa tesi la definizione di automatismo batch non va
intesa in maniera rigorosa, dato che sono stati sviluppati anche alcuni
automatismi che vengono eseguiti su richiesta dell‟utente, tuttavia rimane
evidente la contrapposizione con la parte del sistema informativo, molto più
ricca di interfacce, che dipende dall‟utilizzo interattivo da parte di un utente.
La migrazione effettuata non è stata di tipo 1:1, ma ha puntato a
migliorare diversi aspetti del sistema e a soddisfare nuove necessità incontrate
dall‟azienda. E‟ stato dunque necessario separare il lavoro in più parti:
Individuazione degli obiettivi
Analisi dei problemi
Elaborazione di metodologie risolutive
Fase di sviluppo software
7
Luogo di svolgimento del lavoro documentato
Messa sul mercato il 22 maggio 2007, OnLife è stata in Italia la prima
polizza vita ad essere venduta ai clienti utilizzando internet come unico canale
di vendita diretta, segnando un ulteriore passo nella direzione della vendita
senza intermediari già intrapresa nel 1994 da Genertel. OnLife è un prodotto di
L.A. Vita S.p.A. (controllata al 100% dal gruppo Allianz), società per cui è stato
fatto il lavoro oggetto di questa tesi. Essendo stato utilizzato all‟interno
dell‟azienda, il risultato di questa tesi ha dunque avuto utilità pratica.
Dettagli della migrazione
Di seguito viene data una visione d‟insieme delle caratteristiche del
sistema informativo “sorgente” e del sistema informativo “obiettivo”. Ulteriori
dettagli riguardanti il sistema informativo iniziale verranno specificate nel
capitolo 2, mentre informazioni ancora più dettagliate si possono trovare in
(Gorjan, 2006), anche se relative ad una versione con meno funzionalità
rispetto a quella più recente, oggetto della migrazione.
L’ambiente sorgente
La disponibilità sul mercato di Onlife è arrivata solamente dopo la
conclusione dello sviluppo di un sottosistema informativo specifico dedicato
esclusivamente ad Onlife chiamato “DM2” che, affiancando il sistema
informativo di L.A. Vita, ha fornito tutti gli strumenti necessari alla sua
gestione. Da quanto nel 2007 DM2 è entrato in produzione ha subìto
importanti modifiche che hanno aggiunto ulteriori funzionalità per gli utenti e
ulteriori automatismi, di pari passo con l‟incremento del numero di polizze
vendute.
8
La migrazione della parte batch del sistema informativo DM2 di Onlife è
l‟oggetto di questo documento.
Le caratteristiche tecniche principali dell‟ultima versione di DM2 sono le
seguenti:
L‟interfaccia è sviluppata in Access 2003, che grazie ai suoi
strumenti integrati ha permesso ottenere rapidamente un sistema
completo e funzionante.
Utilizza il DBMS Microsoft SQL Server 2005 (migrato dal Microsoft
Jet di Access, che è stato il primo DBMS utilizzato)
Le “business rules” e i meccanismi di workflow sono programmati in
VBA all‟interno di Access.
La produzione di documenti PDF avviene tramite una stampante
virtuale che riceve l‟input da una istanza di Microsoft Word gestita
dal codice VBA in Access.
L’ambiente obiettivo
Il nuovo ambiente software e gli strumenti di sviluppo utilizzati sono stati
imposti dall‟azienda, che ha scelto Microsoft SQL Server 2005 come DBMS e
Microsoft Visual Studio 2008, .NET Framework 3.5 e il linguaggio C# come
strumenti di sviluppo software. L‟azienda ha anche fornito delle librerie da
utilizzare obbligatoriamente per l‟apertura della connessione con il database e,
se necessario, anche per sfruttare l‟infrastruttura di logging di Allianz. Non
sono stati imposti ulteriori vincoli, ed è quindi stato quindi possibile valutare
l‟utilizzo di librerie open source per realizzare alcune delle funzionalità
richieste.
9
Le ragioni della migrazione
La migrazione dei meccanismi di workflow rientra in un più ampio
progetto di migrazione dell‟intero sistema informativo DM2 deciso dall‟azienda.
Le ragioni di questa scelta si possono riassumere nei seguenti punti chiave:
Il vecchio applicativo batch non rispettava gli standard aziendali,
che prevedono l‟utilizzo di strumenti software completamente
diversi.
A causa dell‟inadeguatezza del primo DBMS utilizzato (Microsoft
Jet), ed a causa della necessità di migliorare le prestazioni
dell‟interfaccia utente (passando da Access ad una nuova interfaccia
web) era già stata effettuata la migrazione della base di dati verso
SQL Server 2005. Si è dunque prefigurata la possibilità di sfruttare
le funzionalità di questo DBMS per migliorare ulteriormente le
prestazioni complessive del sistema, sia lato batch, sia lato
interattivo.
Precisazioni su questo documento
Tutti i moduli software descritti in questo documento sono stati sviluppati
dall‟autore, ad eccezione della libreria iTextSharp per la creazione di PDF, e la
libreria fornita da Allianz per interfacciarsi con l‟architettura aziendale
(nell‟ambito di questo progetto è stata utilizzata solo la funzione che recupera
la connessione al database).
Contenendo questo documento alcune parti del codice sorgente creato
nell‟ambito di questo lavoro, in alcuni casi è stato necessario oscurare delle
parti di codice per garantire la riservatezza delle informazioni riguardanti le
misure di sicurezza adottate in Allianz. Tali modifiche saranno comunque
evidenziate.
10
Capitolo 2: Il sistema informativo di OnLife
Questo breve capitolo fornisce le
principali informazioni necessarie alla
comprensione della struttura della parte del
sistema informativo che è stata oggetto della
migrazione descritta in questa tesi.
L’ambiente di esecuzione prima della migrazione
Il sistema informativo che permette la vendita e la gestione della polizza
Onlife è suddiviso in due parti: il sistema informativo DM2, che è specifico di
Onlife e che fornisce principalmente funzioni di CRM1 con i clienti (ex, attuali e
potenziali), e il sistema informativo di L.A. Vita, che si occupa esclusivamente
del workflow interno all‟azienda.
A questi due elementi si aggiunge un altro server indipendente che è
l‟unico ad essere visibile da internet.
Il risultato è una struttura divisa nei seguenti elementi distinti:
Server raggiungibile da internet, con funzionalità di web server e
mail server. Il web server esegue la web application che rende
disponibile il questionario tramite il quale i potenziali clienti e i
broker di assicurazione possono ricevere una quotazione. Il mail
server si occupa della ricezione e dell‟invio dei messaggi di posta
elettronica, utilizzando a questo scopo due tabelle (MailIn e MailOut)
sul database interno che contengono i messaggi in ingresso ed in
uscita.
Database server inaccessibile da internet, su cui è in esecuzione il
DBMS Microsoft SQL Server 2005, e che contiene il database del
sottosistema informativo DM2 specifico di Onlife.
1 CRM: Customer Relationship Management
11
Sistemi host di L.A. Vita: la parte di sistema informativo che non è
specifica di Onlife. I dati presenti su questi sistemi vengono esposti
al sistema informativo DM2 con delle query pass through sul
database server, mentre alcuni servizi più complessi, (come il
calcolo del premio) vengono resi disponibili tramite un web service.
Server di automazione di Onlife su cui è in esecuzione il software
(completamente sviluppato in Access in VBA) che esegue gli
automatismi batch necessari.
La base di dati attuale: concetti chiave dello schema dei dati
Maggiori dettagli relativi alle singole entità facenti parte dello schema dei
dati del sistema informativo DM2 (che è composto da 70 tabelle), verranno
illustrati contestualmente alla definizione dei requisiti.
È comunque opportuno introdurre alcuni concetti base relativi alle entità
più ricorrenti nell‟ambito di questo lavoro ma poco “intuitive”.
Prospect
Dal punto di vista commerciale la caratteristica più importante di Onlife,
dopo il prezzo di vendita, è la possibilità di fare un preventivo anonimo in un
tempo brevissimo, chiedendo al potenziale cliente solamente i dati
indispensabili al calcolo del premio. L‟individuo che interagisce con Onlife
senza fornire le proprie generalità deve essere comunque identificato, e per
questo scopo si utilizza l‟entità prospect che lo identifica tramite l‟indirizzo
email, la data di nascita e il sesso.
Partner
Il partner è l‟individuo di cui si conoscono anche i dati anagrafici. È
identificato dal codice fiscale. In questa categoria rientrato clienti, ex-clienti,
dipendenti, fornitori e broker.
Quotazione
12
È il risultato della compilazione del questionario anonimo, che serve a
dare al cliente una misura orientativa del premio da pagare qualora decida di
stipulare una polizza.
Carico contabile
È un movimento contabile che può rappresentare una stipula, un rinnovo,
o la variazione di una polizza.
13
Capitolo 3: Descrizione dei requisiti
In questo capitolo viene data una visione
d‟insieme sulle funzionalità che è stato
necessario realizzare, e sui vincoli non
funzionali che hanno inciso sullo sviluppo del
sistema.
Vincoli non funzionali
Ancor prima di entrare nel dettaglio dei requisiti funzionali richiesti è
necessario evidenziare i requisiti non funzionali che hanno caratterizzato
maggiormente tutto il lavoro di migrazione.
Affidabilità dell’applicativo
Come già evidenziato l‟obiettivo del lavoro svolto era quello di migrare un
sistema batch, che per essere considerato tale doveva soddisfare il requisito di
indipendenza dall‟interattività con l‟utente. Questo requisito fondamentale
doveva essere rispettato sia nelle condizioni di utilizzo normale, sia, per quanto
possibile, in condizioni eccezionali: va infatti considerato che l‟accesso “fisico”
al calcolatore su cui l‟applicativo batch viene eseguito deve essere ridotto a
rarissimi casi veramente eccezionali, anche perché all‟interno di realtà di
dimensioni molto grandi come Allianz, è impensabile che non vengano poste
restrizioni a questo proposito.
A ciò si aggiunge il fatto che l‟allungarsi del tempo necessario ad ottenere
l‟accesso al calcolatore in seguito al verificarsi di una condizione eccezionale
(sia per raccogliere informazioni sul problema da risolvere, sia per attuare la
14
soluzione) inevitabilmente allunga anche l‟MTTR2, ovvero il tempo medio
necessario per ripristinare le funzionalità del sistema.
In ambito assicurativo l‟indisponibilità di un certo servizio per un certo
periodo di tempo può facilmente tradursi, più o meno direttamente, in una
perdita di denaro, di conseguenza l‟MTTR è un indicatore chiave che è stato
importante ridurre, adottando le opportune straregie di sviluppo.
Da queste considerazioni si sono ricavati i seguenti requisiti:
Robustezza: il software deve comportarsi in maniera ragionevole
anche rispetto a situazioni non previste, onde evitare che errori di
lieve entità blocchino il servizio.
MTTR ridotto: in caso di errore deve essere possibile individuarne
rapidamente la causa.
Affidabilità: è necessario minimizzare la percentuale di tempo in cui
il servizio non è accessibile (è una conseguenza dei due punti
precedenti).
Prestazioni
Uno dei motivi che hanno reso necessario questo lavoro di migrazione era
la sicurezza di avere un buon margine per sostenere la futura crescita di
Onlife. Pur non essendosi ancora verificata la situazione limite in cui il tempo
di esecuzione di un ciclo di automatismi avesse superato l‟intervallo di tempo
con cui tale ciclo di automatismi doveva essere avviato, bisogna considerare
che la durata complessiva dell‟esecuzione di un ciclo ha comunque influenza
sulla bontà del sistema.
Infatti se è vero che alcuni degli automatismi devono essere eseguiti su
base temporale precisa (ad esempio la generazione degli estratti conto per gli
intermediari deve essere svolta una volta al mese), altri invece possono essere
avviati con una frequenza arbitraria, ed una loro esecuzione più frequente può
2 MTTR: Mean Time To Restore
15
portare degli effettivi vantaggi. Ad esempio nel sistema informativo DM2
l‟automatismo che elabora i messaggi di posta elettronica in ingresso potrebbe
essere eseguito più frequentemente, riducendo così il tempo medio di risposta
ed aumentando quindi il grado di soddisfazione del cliente.
Descrizione delle funzioni
I successivi requisiti presentati nel resto di questo capitolo sono tutti di
tipo funzionale, e nella maggior parte dei casi sono stati dedotti dall‟analisi del
codice sorgente del software che precedentemente eseguiva gli automatismi
necessari.
Una trattazione dettagliata di tutte le specifiche funzionali sarebbe
eccessivamente lunga. Dato che lo scopo di questo documento è evidenziare le
parti più originali del lavoro svolto, la maggior parte di essi sarà descritta in
maniera sommaria.
Generazione di PDF
La generazione di documenti PDF con contenuti personalizzati (da inviare
ad esempio ai clienti o agli intermediari) è una funzionalità chiave di DM2. Il
vecchio sistema di generazione di PDF, basato su Microsoft Word e su una
stampante virtuale in grado di salvare i documenti nel formato desiderato, era
in grado di soddisfare le necessità di Onlife, ma la migrazione verso il nuovo
ambiente software ha reso necessario un lavoro di riprogettazione.
L‟azienda infatti ha deciso che nel nuovo ambiente di produzione non
sarebbe stato installato né Microsoft Office né la stampante virtuale, che invece
erano elementi indispensabili nella vecchia soluzione. Si è dunque presentata
la necessità di trovare una soluzione alternativa a questo problema.
16
A proposito della soluzione adottata prima della migrazione va detto che,
pur essendo funzionale allo scopo, non offriva grandi prestazioni in termini di
velocità di produzione dei documenti, ed è quindi presumibile che in futuro,
con l‟aumentare del numero di clienti, si sarebbe rivelata inadeguata.
Il lavoro di riprogettazione resosi necessario in seguito al cambiamento
dell‟ambiente software ha dunque offerto la possibilità di sviluppare una
soluzione che possa sostenere la crescita futura di Onlife.
Caricamento delle nuove quotazioni e dei nuovi contratti
Il compito del server web accessibile da internet è esclusivamente quello di
raccogliere le nuove proposte e quotazioni richieste dai clienti o dai broker e
salvarle sul proprio database, ma prima di poter essere inserite nel sistema
DM2, queste informazioni devono essere normalizzate per rispettare la
struttura dati del sistema informativo. La lettura, la normalizzazione e il
salvataggio dei dati in DM2 sono a carico di questa funzionalità.
Elaborazione delle mail in ingresso
Il mail server è configurato per memorizzare tutti i messaggi di posta
elettronica ricevuti all‟interno della tabella “MailIn” presente nel database del
sistema DM2. Essendo Onlife un‟iniziativa basata su internet come unico
canale di vendita e contatto, tutti i nuovi messaggi in ingresso vengono
sottoposti ad un automatismo che li elabora, inviando delle risposte
automatiche quando possibile e minimizzando lo sforzo necessario alla risposta
manuale quando l‟elaborazione automatica non ha avuto esito positivo.
17
Sincronizzazione degli status
Come detto, il sistema informativo di Onlife è diviso in due parti: il sistema
informativo DM2 che si occupa prevalentemente del CRM di Onlife, e il sistema
informativo di L.A. Vita, che si occupa del workflow interno all‟azienda. Dato
che questi due sistemi si basano su due database indipendenti, è
indispensabile mantenere sincronizzati gli status dei contratti, dato che la
mancata sincronizzazione potrebbe comportare gravi errori, come il mancato
rendiconto di un contratto in vigore o il rendiconto di un contratto stornato.
Di conseguenza una funzionalità richiesta è la sincronizzazione periodica
degli status dei contratti, attuata accedendo al database di L.A. Vita tramite
una query pass through e confrontando gli status dei contratti con i dati
contenuti nel database di DM2, evidenziando i casi in cui è stata rilevata una
differenza di status e procedendo con il necessario aggiornamento.
Eliminazione dei dati sensibili
L‟algoritmo di calcolo del premio assicurativo necessita di alcuni dati che
secondo il Codice sulla protezione dei dati personali (D. Lgs. 196/2003) sono
considerati “dati sensibili”. È necessario che tali dati vengano eliminati da tutte
le proposte che non sono diventate polizze, dopo che sia trascorso un periodo
di tempo prestabilito.
Elaborazione rendiconti intermediari
L‟obiettivo di questa funzione è la generazione degli estratti conto da
inviare agli intermediari di vendita, e l‟invio delle disposizioni di pagamento a
beneficio degli intermediari che hanno diritto a una provvigione.
18
In entrambi i casi è necessario sfruttare la funzionalità di generazione di
documenti PDF per inserire all‟interno dei rispettivi modelli di documento i dati
estratti dal database.
Invio di E-mail
L‟invio di un messaggio di posta elettronica non consiste solamente nel
suo inoltro al mail server opportuno (operazione a carico del mail server
presente sul server internet di Onlife), ma richiede anche una serie di
operazioni da svolgere all‟interno del sistema DM2. Il sistema informativo
prevede che venga associato ad ogni Prospect o Partner ogni messaggio inviato
o ricevuto che lo riguarda. In questo modo viene minimizzato lo sforzo
necessario a gestire la comunicazione, essendo possibile avere per ogni
soggetto lo storico completo dei messaggi scambiati.
Automazione di campagne di comunicazione
L‟obiettivo di questa funzione è automatizzare l‟invio di grandi moli di
email consentendo la loro più libera ed assoluta personalizzazione secondo le
diverse esigenze. Si tratta di una funzionalità importantissima di DM2, che
viene utilizzata sia per l‟invio di campagne di marketing sia per l‟invio di
comunicazioni di massa ai clienti. Si basa su dei modelli di messaggio
standard, delle query che determinano gli insiemi dei destinatari, e delle
“campagne” (delle coppie modello di messaggio / query dei destinatari).
Lo schema dei dati alla base di queste funzionalità è il seguente:
19
MessaggiTesti
standard ID
Oggetto
Testo
Allegato1
Allegato2
WkflAutomatismi
ID
Codice
Nome
Note
Query
ID
Descrizione
StringaSQL
Workflow
ID Messaggio
Standard ID Query
Destinatari ID Automatismo
SELECT Nome, Cognome, Email FROM Partner WHERE .....
ALTRE
TABELLE
20
Capitolo 4: Analisi dei problemi e definizione
delle metodologie risolutive
In questo capitolo vengono analizzate in
dettaglio le singole problematiche affrontate e
vengono illustrate le metodologie risolutive
adottate.
Disponibilità del servizio e robustezza del software
Nel capitolo 3 è stata messa in luce l‟importanza di produrre dei moduli
software robusti (ovvero che gestiscano ragionevolmente situazioni impreviste)
e facilmente riparabili. Vista la complessità del progetto è stato necessario
studiare una strategia per affrontare il problema in maniera coerente durante
tutto l‟arco del lavoro di sviluppo.
Il problema
Il problema affrontato può essere così riassunto:
“Creare un applicativo batch che riesca a gestire opportunamente situazioni
impreviste successe durante il funzionamento, minimizzando i casi in cui queste
possano dar luogo alla sospensione del servizio, e agevolando il più possibile il
lavoro di debug e ripristino.”
Il vecchio sistema di automazione sviluppato in Microsoft Access era
costituito da oltre 300 funzioni per un totale di circa 15 mila righe di codice.
Oltre alla probabilità di bug dovuti a errata codifica del software (che
21
comunque può essere ridotta testando approfonditamente i singoli moduli
parallelamente al lavoro di codifica), bisogna considerare che il buon
funzionamento di molti moduli dipende da fattori esterni non controllabili dal
software, come il rispetto delle specifiche da parte dei dati in ingresso, oppure
l„accessibilità di alcune risorse necessarie all‟esecuzione (database, file, ecc).
In questo capitolo con il termine “disponibilità” si intende la percentuale di
tempo in cui il servizio è correttamente funzionante. Tale quantità dipende da
due valori fondamentali: MTBF e MTTR.
L‟MTBF (Mean Time Between Failures) è il tempo che, in media, passa tra
l‟istante in cui il sistema viene messo in funzione e l‟istante in cui il
funzionamento viene interrotto a causa di un problema non previsto.
L‟MTTR (Mean Time To Repair) è il tempo che, in media, è necessario per
ripristinare le funzionalità del sistema in seguito ad un malfunzionamento.
Tempo
Stato del
servizio
Funzionamento
Riparazione
Istante in cui il servizio
viene ripristinato
Istante in cui il servizio
cessa di funzionare
Tempo tra due
malfunzionamenti
Tempo di
riparazione
La disponibilità media del servizio dipende dalle due quantità sopra
descritte, secondo la relazione
Disponibilità media =𝑀𝑇𝐵𝐹
𝑀𝑇𝐵𝐹 + 𝑀𝑇𝑇𝑅
dalla quale, ricordando che il risultato ideale è una disponibilità media del
100%, si ha immediata conferma di due fatti intuitivi:
Riducendo l‟MTTR si migliora la disponibilità media.
Aumentando l‟MTBF diminuisce l‟incidenza dell‟MTTR sulla
disponibilità media.
22
La cause di possibili problemi
Come già accennato, una probabile fonte di anomalie di funzionamento è il
mancato rispetto delle specifiche di programma da parte dei dati da elaborare.
Di conseguenza, la possibilità che il sistema debba affrontare una situazione
non prevista non risulta essere eliminabile neanche in seguito ad una accurata
fase di test.
Il sistema di automazione dell‟invio di messaggi di posta elettronica
personalizzati, ad esempio, deve elaborare i tag contenuti in un testo standard
caricato dal database, e deve eseguire una stringa SQL anch‟essa definita nel
database. Se i tag contenuti nel messaggio non rispettano le specifiche oppure
se la query SQL dà luogo ad un errore, il sistema si trova di fronte ad una
situazione non prevista, che non può essere risolta in maniera interattiva con
un utente, dato che l‟applicativo è batch.
E‟ possibile individuare una stretta relazione tra la robustezza (termine
con cui si indica la misura in cui il sistema si comporta in modo ragionevole in
situazioni impreviste), MTTR e MTBF. Un sistema che risponde in maniera
“ragionevole” ad una situazione inattesa infatti non si fermerà se la situazione
di errore non è critica e anche in questa eventualità non si bloccherà senza
averne dato opportuna segnalazione: nel primo caso avrà evitato di mettere
fuori servizio l‟intero sistema per un errore di poco conto (aumentando quindi
l‟MTBF), mentre nel secondo caso la segnalazione dell‟errore permetterà di
reagire tempestivamente (riducendo l‟MTTR).
Definizione di una metodologia risolutiva
Il problema è stato diviso in tre punti, corrispondenti a tre parti della frase
che lo sintetizza. Nei prossimi paragrafi ad ogni parte del problema verranno
associati dei principi da rispettare.
23
A) “Creare un applicativo batch che riesca a gestire opportunamente
situazioni impreviste successe durante il normale funzionamento,
minimizzando i casi in cui queste possano dar luogo a sospensione del
servizio, e agevolando il più possibile il lavoro di debug/riparazione.”
Il software deve prendersi l‟onere di verificare i dati in input, in
modo da riconoscere le situazioni anomale.
Ad ogni situazione di errore deve corrispondere una strategia di
gestione opportuna. Ogni componente deve contenere il codice
necessario per gestire autonomamente il problema. Se questo non è
possibile, la gestione dell‟errore diventa di competenza del
componente chiamante.
La gestione delle situazioni di errore deve essere progettata tenendo
in mente l‟integrità dei dati e delle operazioni svolte.
B) “Creare un applicativo batch che riesca a gestire opportunamente
situazioni impreviste successe durante il normale funzionamento,
minimizzando i casi in cui queste possano dar luogo a
sospensione del servizio, e agevolando il più possibile il lavoro di
debug/riparazione.”
E‟ necessario adottare una strategia di codifica che permetta di
distinguere gli errori gravi che impediscono il proseguimento degli
automatismi dagli errori non gravi che non ne pregiudicano il
funzionamento.
L‟applicativo deve sospendere l‟esecuzione di un automatismo
solamente nell‟eventualità di un errore definito “grave” secondo la
distinzione del punto precedente.
24
C) “Creare un applicativo batch che riesca a gestire opportunamente
situazioni impreviste successe durante il normale funzionamento,
minimizzando i casi in cui queste possano dar luogo a sospensione del
servizio, e agevolando il più possibile il lavoro di
debug/riparazione.”
Mai, in nessun caso, il software deve terminare in maniera anomala
senza averne dato segnalazione.
Quando possibile le segnalazioni di errore devono essere
memorizzate nel database del sistema DM2, in modo da essere
immediatamente evidenziate e facilmente consultabili.
Per evitare di perdere informazioni potenzialmente molto utili, se
non dovesse essere possibile collegarsi al database, le segnalazioni
devono essere memorizzate su un altro supporto.
Deve essere possibile individuare immediatamente la gravità
dell‟errore, in modo da dare subito la giusta priorità alla sua analisi.
Ogni segnalazione di errore deve dare il maggior numero possibile di
informazioni significative necessarie all‟individuazione del problema
e conseguente soluzione.
Il meccanismo di lancio/gestione delle eccezioni contenuto nei più comuni
linguaggi di programmazione correntemente utilizzati, è stato uno strumento
che ha fornito delle funzionalità di base molto utili al raggiungimento dello
scopo.
Premessa: nel linguaggio C# le istanze della classe Exception contengono
diverse proprietà, ma le seguenti sono ampiamente utilizzate nella soluzione
del problema:
Message: è una stringa di testo che contiene la descrizione
dell‟errore.
25
InnerException: è un riferimento ad un‟altra eccezione.
StackTrace: contiene una rappresentazione testuale dello stack delle
chiamate.
La soluzione adottata si basa su una serie di norme da seguire durante la
codifica dei moduli:
1. Il dettaglio di ogni errore deve essere salvato sul log (la definizione di
“dettaglio dell‟errore” verrà data in seguito)
2. Il compito di salvare il dettaglio dell‟errore spetta sempre alla
funzione che lo gestisce, e mai alla funzione che lo genera, a meno
che generazione e gestione avvengano nella stessa funzione.
3. Il passaggio del controllo del flusso di esecuzione deve essere
passato da una funzione ad un‟altra tramite eccezioni solo ed
esclusivamente in caso di errore.
4. Quando una funzione riceve un‟eccezione che non è in grado di
gestire, deve creare una nuova eccezione con tutti i dettagli del caso
(punto 6). Se invece è in grado di gestire l‟eccezione, scrive il
dettaglio sul log (per rispettare il punto 2).
5. Quando una funzione incontra una situazione anomala, se non è in
grado di gestirla lancia un‟eccezione contenente tutti i dettagli del
caso (punto 6). Se è in grado di gestirla, scrive il dettaglio sul log
(per rispettare il punto 1).
6. Come già accennato nei punti 4 e 5, ogni funzione che incontra una
situazione anomala o che riceve un‟eccezione da una funzione
chiamata, e non è in grado di gestire la situazione, deve lanciare
una eccezione così strutturata: la proprietà Message deve contenere
una stringa che definisca l‟errore e la proprietà InnerException deve
contenere un riferimento all‟eccezione eventualmente ricevuta
(altrimenti deve essere null). Il risultato è che ogni errore genera una
lista concatenata di eccezioni, rappresentata nella seguente figura.
26
7. Il “dettaglio dell‟errore” che deve essere salvato sul log è composto
dai seguenti elementi:
o Un messaggio definito dalla funzione che ha gestito l‟errore,
che indica di che errore si tratta, in che contesto è avvenuto, e
che implicazioni ha avuto.
o La sequenza dei messaggi di errore specifici presi da ogni
elemento della lista concatenata di eccezioni.
o Lo stack delle chiamate preso dall‟eccezione alla fine della
catena.
o Un indicatore della gravità dell‟errore, determinato dalla
funzione che gestisce l‟errore.
8. Per evitare che un loop infinito possa bloccare il servizio per un
tempo indeterminato, l‟esecuzione degli automatismi deve avvenire
in un thread separato, detto “principale”. Un altro thread, detto
“supervisore” si occupa di segnalare l‟eccessivo prolungarsi
dell‟esecuzione di un automatismo, ed ha la possibilità di
interrompere la sua esecuzione (solo in casi estremi, dopo un‟attesa
molto lunga).
27
9. Normalmente i dettagli di ogni errore devono essere salvati sul
database. Se questo non è disponibile, devono essere salvati su un
file di log.
Adottando le norme descritte, il codice avrà le seguenti caratteristiche:
Durante la codifica dei moduli software per qualunque situazione
anomala si è liberi di programmare il lancio di un‟eccezione. La
gravità non sarà intrinseca dell‟errore in sé, ma dipenderà dal
livello a cui viene bloccata. Questo fatto è coerente con l‟idea
intuitiva che un errore provocato, ad esempio, durante l‟invio di
una singola email può essere considerato non critico nell‟ambito
di una campagna pubblicitaria, mentre è senz‟altro critico
nell‟ambito dell‟invio di un ordine di pagamento. Questa
caratteristica affronta i punti A e B del problema.
L‟unica condizione in cui termina l‟esecuzione di un automatismo
si ottiene quando l‟eccezione risale fino al thread principale; tale
condizione indica che il software ha incontrato una situazione
critica che non può essere risolta. Questa caratteristica affronta
il punto B del problema.
Il log conterrà molte informazioni utili all‟individuazione
dell‟errore e delle cause che lo hanno scatenato, e conterrà un
indicatore della gravità dell‟errore rilevato. In questo modo il
tempo necessario a ripristinare il sistema sarà inferiore, e sarà
possibile agire tempestivamente in caso di un errore grave.
Questa caratteristica affronta il punto C del problema.
L‟adozione di una metodologia di gestione degli errori coerente in
tutto il codice ne facilita la lettura e la manutenzione. Una volta
compresa questa strategia di base, risulta molto facile aggiungere
28
la gestione di nuove situazioni di errore. Questa caratteristica
affronta il punto C del problema.
Nelle prossime pagine vi sono alcuni esempi che illustrano l‟utilizzo pratico
della metodologia applicata.
29
30
Esempio pratico di log:
Rilevanza dell’errore:
25
Messaggio di errore:
Errore durante l’esecuzione del workflow di apertura. Impossibile
Lanciare la campagna con ID = 23
Impossibile continuare.
DebugInfo:
Dettagli eccezione:
- lanciaCampagna(23, 74, 0, ""): errore durante l’invio del
messaggio!
- inviaMessaggioStandard(23,”[email protected]”,...): errore durante il
mail merge.
- EseguiMailMerge(“oggetto”,...): tag NUMERO_CONTRATTO non trovato.
Stack delle chiamate:
at OnLifeBatch.Onlife.lanciaCampagna([parametri]) in
Campagne.cs:line 246
at OnLifeBatch.Onlife.inviaMessaggioStandard([parametri]) in
EMAIL – Funzioni principali.cs:line 453
at OnLifeBatch.Onlife.EseguiMailMerge([parametri]) in
EMAIL – Merge.cs: line 187
31
Dall‟esempio di log riportato si deducono immediatamente le seguenti
informazioni:
Si sta avendo a che fare con un errore grave che richiede
immediata attenzione (nell‟implementazione di Onlife, una
rilevanza maggiore di 19 indica un problema critico)
L‟errore è avvenuto durante il lancio di una campagna,
nell‟ambito del workflow di inizio giornata.
L‟errore riguarda il mail merge del messaggio standard con ID
uguale a 23.
L‟errore riguarda il mail merge dell‟oggetto del messaggio. È
stato trovato un tag non presente nella query dei destinatari.
Se dovesse essere un bug, sono immediatamente disponibili le
righe del codice sorgente in cui ha avuto luogo l‟errore.
Creazione di documenti PDF
La generazione di PDF personalizzati è un problema che è stato necessario
affrontare rispettando i nuovi standard aziendali imposti da Allianz per il nuovo
ambiente di produzione.
Il sistema di generazione dei PDF deve soddisfare i seguenti requisiti:
I documenti devono essere prodotti a partire da dei modelli che ne
impostano il contenuto e ne contengono la parte statica.
All‟interno dei modelli di documento devono essere presenti delle
aree personalizzabili, in cui verranno inserite le parti dinamiche.
Non può essere utilizzato Microsoft Office o una stampante virtuale,
perché non rispetterebbe gli standard aziendali.
La velocità di produzione deve essere maggiore o uguale a quella
della soluzione precedentemente adottata.
32
La soluzione
Inizialmente si è ipotizzato l‟utilizzo di OpenOffice, una soluzione open
source che offre anche delle API utilizzabili per l‟automazione. I primi test
hanno però messo in luce seri problemi quando venivano creati molti
documenti nell‟arco di una sessione di automatismi: a causa di un evidente
memory leak3, il consumo di memoria dell‟applicativo batch cresceva
notevolmente ad ogni singolo documento creato, e dopo un certo numero di
documenti prodotti, il software cominciava a produrre documenti corrotti.
Questo problema unito anche alla scarsa documentazione di queste API ha
portato all‟abbandono di questa soluzione.
La seconda alternativa analizzata è stata l‟utilizzo delle librerie open
source iTextSharp, delle librerie che permettono di creare dei documenti PDF ex
novo, e permettono anche di modificare dei form PDF (analoghi ai classici PDF
ma contenenti dei campi di testo in cui è possibile scrivere) creati ad esempio
con OpenOffice, e salvarli come PDF classici.
I test di generazione di PDF fatti su questa soluzione hanno dato risultati
al di sopra delle aspettative: nell‟ambito di un test di generazione di documenti
personalizzati di una pagina, è stata raggiunga la velocità di circa 50
documenti al secondo, e i documenti prodotti non hanno mostrato difetti come
poteva accadere con la prima soluzione.
Miglioramento delle prestazioni
Come è già stato sottolineato, la velocità di esecuzione del vecchio sistema
batch era considerata soddisfacente, di conseguenza il problema delle
prestazioni deve essere considerato di importanza decisamente inferiore
rispetto agli altri due già affrontati.
Tuttavia per garantire un buon margine di crescita futura, al solo costo
del rispetto di qualche accorgimento durante la fase di sviluppo sviluppo, è
3 Un particolare tipo di consumo non voluto di memoria dovuto alla mancata
deallocazione di variabili/dati non più utilizzati da parte dei processi.
33
stato ritenuto opportuno porsi il problema di come raggiungere una migliore
efficienza.
In questa sezione viene dunque studiata una metodologia da utilizzare per
velocizzare l‟esecuzione delle operazioni batch.
Analisi della causa
Dato che la potenza di calcolo degli attuali calcolatori si misura in decine
di miliardi di istruzioni al secondo, l‟anello debole su cui si è concentrata
l‟attenzione è l‟accesso ai dati.
Il fatto che l‟applicativo batch e il DBMS vengano eseguiti su due
calcolatori diversi inevitabilmente introduce un tempo di latenza ogni volta che
viene effettuata un‟interrogazione al database di DM2. L‟ordine di grandezza
della somma del tempo necessario affinché l‟interrogazione arrivi al DB server e
del tempo necessario affinché la risposta partita dal DB server sia resa
disponibile all‟applicativo batch, generalmente è nell‟ordine di diversi
millisecondi; di conseguenza nell‟ambiente di Onlife tale tempo è sicuramente
superiore a 8 ms, dato che questo è il tempo misurato con il comando ping, che
misura il Round Trip Time con pacchetti di dimensioni molto ridotte.
Se si considera che l‟automatismo di caricamento delle quotazioni esegue
svariate decine di interrogazioni per ogni singola quotazione che deve essere
elaborata, risulta evidente che il numero di interrogazioni è un fattore da tenere
sotto controllo.
Metodologia risolutiva
Non ripetere interrogazioni inutili. Nel vecchio applicativo capitava
che una stessa interrogazione venisse eseguita più volte all‟interno
di un ciclo. Le interrogazioni ridondanti vanno assolutamente
evitate, facendo solo una interrogazione e salvando il dato in una
variabile.
34
Accorpare più interrogazioni in una sola. Sapendo che un algoritmo
richiede sicuramente un certo insieme di dati, tali dati vanno
ricavati subito tutti in un‟unica interrogazione.
Utilizzare JOIN e (NOT) IN per unire più query. Nel vecchio
applicativo capitava di fare in più query successive esattamente
quello che avrebbe fatto una query che utilizza dei JOIN.
Utilizzare stored procedure e stored functions per spostare parte
della logica all‟interno del database. Questi elementi vengono
eseguiti direttamente dal DBMS, e quindi permettono di ridurre
ulteriormente il numero di interrogazioni necessarie.
35
Capitolo 5: Implementazione nell‟ambiente
target
In questo capitolo viene affrontato il
problema dell‟implementazione delle
funzionalità richieste. In particolare:
l‟applicazione delle metodologie elaborate nel
capitolo precedente, la definizione della
strategia di sviluppo software, e la descrizione
del lavoro svolto, mostrando anche qualche
parte del codice prodotto.
Strategia di sviluppo software adottata
Lo sviluppo dell‟applicativo è stato diviso nelle seguenti fasi:
Sviluppo delle funzioni di base di accesso al database e logging.
Suddivisione del progetto in diversi settori funzionali, evidenziando i
moduli che sarebbe stato necessario programmare per primi a causa
della presenza di altri moduli che da essi dipendono.
Programmazione del singolo settore funzionale, seguendo la
strategia di sviluppo che sarà indicata in seguito.
Con il termine “settore funzionale” si intende l‟insieme di funzioni
necessarie allo sviluppo di una particolare funzionalità. Questo termine ha
particolarmente senso in questo contesto perché l‟insieme delle funzioni
automatizzate di Onlife è suddivisibile in diversi insiemi che tra loro hanno un
accoppiamento (ovvero dati e funzioni comuni) estremamente basso. Ad
esempio l‟elaborazione degli estratti conto ed il caricamento delle quotazioni
36
condividono un insieme di funzioni sostanzialmente limitato alle sole funzioni
di accesso al database e di logging.
Nell‟ambito di un singolo settore funzionale, la strategia di sviluppo
adottata non è ben inquadrabile nei classici schemi top-down o botton-up, ma
è stata una strategia di tipo misto. Inizialmente tutti i problemi più complessi
che sono stati affrontati, sono stati prima suddivisi in elementi di più facile
gestione, applicando quindi una strategia top-down, mentre successivamente è
stata adottata una strategia finalizzata al permettere un precoce debug delle
funzioni create.
“Logger”
Un elemento fondamentale per rispettare la metodologia indicata nel
precedente capitolo, è il sistema che permette di salvare il log degli errori o di
altre informazioni che si ritiene opportuno salvare.
Per questa funzionalità è stato necessario modificare la struttura della
tabella Log già presente nel vecchio sistema informativo, ed utilizzata per scopi
analoghi. La vecchia tabella prevedeva un campo ID determinato da un
contatore, un timestamp per indicare l‟istante in cui è stato aggiunto
l‟elemento, e un campo di testo contenente la descrizione dell‟errore o il
messaggio informativo. Per poter memorizzare separatamente anche le
informazioni di debug (sequenza dei messaggi delle eccezioni e stack delle
chiamate in forma testuale) e la rilevanza dell‟errore, si è deciso di aggiungervi
due campi: un campo di testo per le informazioni di debug, e un valore intero
per la rilevanza.
Il significato del valore di rilevanza è così stabilito:
Valori da 0 a 9 – Non indicano una situazione di errore ma hanno
contenuto puramente informativo. Ad esempio possono indicare il
37
numero di email inviate in seguito all‟esecuzione del workflow di
inizio giornata.
Valori da 10 a 19 – Indicano una situazione di errore che è stata
gestita con successo, ma essendo una situazione anomala richiede
l‟attenzione da parte di un responsabile. Ad esempio se durante
l‟invio di una campagna di email, il campo Email di un record
restituito dalla query dei destinatari è NULL, quel record sarà
semplicemente saltato. Non avrebbe senso interrompere il lancio
della campagna a causa di un solo errore, ma siccome potrebbe
essere il sintomo di una query dei destinatari da rivedere, è
opportuno che venga salvato un messaggio di errore.
Valori da 20 a 29 – Indicano una situazione di errore grave, che ha
provocato il blocco di un automatismo e che richiede un intervento
tempestivo. Ad esempio se all‟inizio dell‟automatismo di rinnovo, la
query che individua i carichi contabili da rinnovare produce un
errore, la procedura di rinnovo non può continuare.
Creazione della classe Logger
Per avere garanzia del fatto che esista solamente un‟unica istanza della
classe Logger, è stato utilizzato il design pattern Singleton (Vlissides, et al.,
1994). Questo design pattern prevede che la classe Logger abbia un solo
costruttore che ha la caratteristica di essere privato; in questo modo non è
possibile creare direttamente una nuova istanza della classe. Per far si che
possa comunque esistere un‟istanza, la classe fornisce una proprietà statica di
sola lettura, a cui è associato un metodo get che restituisce il riferimento
all‟istanza della classe. Questo design pattern rende tra l‟altro possibile la lazy
initialization (inizializzazione “pigra”), ovvero la creazione dell‟istanza della
classe solamente al momento della prima chiamata.
Di seguito viene riportata l‟implementazione della classe Logger:
38
public sealed class Logger
{
static Logger istanza = null; // la variabile statica privata che
// contiene l'unica istanza di Logger
// è necessario evitare l'esecuzione concorrente di alcune parti
// critiche quindi dichiaro un oggetto per il lock
static readonly object loglock = new object();
DbConnection LogConnection;
StreamWriter logTxt;
Logger() //costruttore privato
{
LogConnection = [codice necessario all'utilizzo dei
sistemi di sicurezza di Allianz]
logTxt = new
StreamWriter(Config.getSetting(settingsNames.PercorsoLog), false);
logTxt.WriteLine("----- Log dei messaggi che non è stato
possibile memorizzare sul database. -----");
}
public static Logger Istanza // La proprietà statica di sola lettura
// che fornisce accesso all'unica istanza
{
get
{
lock (loglock)
{
if (istanza == null) // la classe viene istanziata
{ // al primo utilizzo
istanza = new Logger();
}
return istanza;
}
}
}
...
funzioni della classe Logger
...
}
Dal codice sorgente si nota anche che sono state prese le precauzioni
necessarie per gestire l‟esecuzione concorrente di più richieste dell‟istanza della
classe, dato che entrambi i thread (principale e supervisore) ne fanno uso.
39
Riprendendo quanto definito nel capitolo 4, l‟insieme dei dettagli di ogni
errore è costituito da:
Un messaggio definito dalla funzione che ha gestito l‟errore, che
indica di che errore si tratta, in che contesto è avvenuto, e che
implicazioni ha avuto.
La sequenza dei messaggi di errore specifici presi da ogni elemento
della lista concatenata di eccezioni.
Lo stack delle chiamate preso dall‟eccezione alla fine della catena.
Un indicatore della gravità dell‟errore, determinato dalla funzione
che gestisce l‟errore.
Il compito della classe Logger è quello di scrivere queste informazioni sul
log. Di seguito viene illustrata l‟implementazione delle funzioni necessarie.
Avendo a che fare con una lista concatenata di eccezioni, è necessario
individuare la prima eccezione che è stata lanciata, ovvero l‟ultima nella catena
delle eccezioni. Per far questo è stata sviluppata la semplice funzione ricorsiva
getInnermostException:
static Exception getInnermostException(Exception ex)
{
if (ex.InnerException == null) return ex;
else return getInnermostException(ex.InnerException);
}
Dalla lista concatenata di eccezioni è necessario ricavare anche la
sequenza dei messaggi presi da ogni eccezione della lista. Questo compito viene
svolto dalla funzione ricorsiva dettagliEccezione:
public static string dettagliEccezione(Exception ex)
{
if (ex.InnerException != null) return ex.TargetSite.Name + ": " +
ex.Message + "\r\n" + dettagliEccezione(ex.InnerException);
else return ex.TargetSite.Name + ": " + ex.Message;
}
40
La memorizzazione nel database avviene tramite la chiamata di una stored
procedure. Il codice utilizzato per richiamarla è il seguente:
void StoredProcedureAddToLog(dbInputParam[] parametri)
{
DbCommand comando = LogConnection.CreateCommand();
if (parametri.GetLength(0) > 0) aggiungiParametriAComando(parametri,
comando);
comando.CommandType = CommandType.StoredProcedure;
comando.CommandText = "[dbo].[addToLog]";
comando.ExecuteNonQuery();
}
void aggiungiParametriAComando(dbInputParam[] parametri, DbCommand comando)
{
foreach (dbInputParam parametro in parametri)
{
DbParameter paramDaAggiungere = comando.CreateParameter();
paramDaAggiungere.ParameterName = parametro.nome;
paramDaAggiungere.Direction = ParameterDirection.Input;
paramDaAggiungere.DbType = parametro.tipo;
paramDaAggiungere.Value = parametro.valore;
comando.Parameters.Add(paramDaAggiungere);
}
}
L‟insieme di funzioni fin‟ora indicate è stato infine utilizzato per creare la
funzione addToLog, che riceve le informazioni necessarie dalle funzioni che
hanno la necessità di loggare qualcosa, e scrive il tutto sulla tabella Log del
database (se il database è accessibile) oppure sul file di log (se il database non
è accessibile).
public void addToLog(string messaggio, Exception ex, Int16 rilevanza)
{
lock (loglock)
{
string debugInfo;
//se viene passata un'eccezione, ne scrivo i dettagli. Altrimenti
scrivo solo lo stack delle chiamate.
if (ex != null)
{
debugInfo = "Dettagli eccezione:\r\n" +
Onlife.dettagliEccezione(ex);
debugInfo += "\r\n\r\nStack delle chiamate:\r\n" +
getInnermostException(ex).StackTrace.ToString();
}
41
else
{
debugInfo = "Stack delle chiamate:\r\n" +
Environment.StackTrace.ToString();
}
if (LogConnection.State == ConnectionState.Open)
{
try
{
dbInputParam[] parametri = new dbInputParam[3];
parametri[0] = new dbInputParam("@messaggio",
DbType.String, messaggio);
parametri[1] = new dbInputParam("@debugInfo",
DbType.String, debugInfo);
parametri[2] = new dbInputParam("@rilevanza",
DbType.Int16, rilevanza);
StoredProcedureAddToLog(parametri);
}
catch
{
logTxt.WriteLine(messaggio + " -- Call stack:");
logTxt.WriteLine(debugInfo);
logTxt.WriteLine("================================================
\r\n");
}
}
else
{
logTxt.WriteLine(messaggio + " -- Call stack:");
logTxt.WriteLine(debugInfo);
logTxt.WriteLine("================================================
\r\n");
}
}
}
Funzioni di accesso al database
Un altro elemento fondamentale che è stato preparato durante la prima
fase dello sviluppo dell‟applicativo batch, è l‟insieme delle funzioni di accesso al
database, essendo questo un elemento indispensabile per tutti gli automatismi.
Dato che il batch resta in esecuzione solamente fin quando ci sono
automatismi da eseguire, e dato che tutti gli automatismi necessitano di
accesso al database, ne consegue che la necessità di una connessione al
database accompagna tutta l‟esecuzione del batch. Per questa ragione si è
42
scelto di inserire nella classe Onlife una variabile statica contenente il
riferimento alla connessione al database. La connessione verrà aperta
immediatamente dopo l‟inizio dell‟esecuzione, e verrà chiusa immediatamente
prima della fine.
Oltre alle funzioni di apertura, chiusura e verifica della connessione, è
stato creato un ampio insieme di funzioni e di strutture dati, volte a facilitare la
scrittura del resto del codice.
Separazione tra thread principale e thread supervisore
Nel capitolo precedente si è deciso che per evitare che un loop infinito
possa bloccare il servizio per un tempo indeterminato, l‟esecuzione degli
automatismi deve avvenire in un thread separato, detto principale, mentre un
altro thread, detto supervisore si occupa di segnalare l‟eccessivo prolungarsi
dell‟esecuzione di un automatismo.
Questa separazione avviene nella procedura principale, dopo che è stata
caricata la lista di automatismi da eseguire:
static void Main(string[] args)
{
if (Onlife.openDB() == false) {
Logger.Istanza.addToLog("Impossibile collegarsi al database.",
25);
Logger.Istanza.Chiudi();
return;
}
Config.load();
Onlife.loadIntMasterData();
// Dichiaro il thread che eseguirà gli automatismi necessari.
// Onlife.eseguiFunzione(nomeAutomatismo) è la funzione principale
da cui parte il thread.
Thread threadAutomatismo;
DateTime istanteInizioEsecuzione;
string[] funzioniDaEseguire;
bool dacmd;
if (args.Length > 0)
{
43
funzioniDaEseguire = new string[1];
funzioniDaEseguire[0] = args[0];
dacmd = true;
}
else
{
funzioniDaEseguire = Onlife.getListaAutomatismiDaEseguire();
dacmd = false;
}
foreach (string nomeFunzione in funzioniDaEseguire)
{
threadAutomatismo = new Thread(new
ParameterizedThreadStart(Onlife.eseguiFunzione));
istanteInizioEsecuzione = DateTime.Now;
Console.WriteLine("Eseguo " + nomeFunzione);
threadAutomatismo.Start(nomeFunzione);
TimeSpan tempoEsecuzione;
while (true)
{
Thread.Sleep(1000);
if (threadAutomatismo.ThreadState == ThreadState.Stopped)
break;
Console.Write('.');
tempoEsecuzione =
DateTime.Now.Subtract(istanteInizioEsecuzione);
if (tempoEsecuzione.Seconds == 0 & tempoEsecuzione.Minutes
> 0) Logger.Istanza.addToLog("Sto eseguendo l'automatismo " + nomeFunzione +
" da " + tempoEsecuzione.Minutes + " minuti", 4);
//L'attuale implementazione non prevede la terminazione
forzata dopo un tempo limite
}
. . . . .
[codice che verifica l’esito della funzione chiamata]
. . . . .
}
Console.WriteLine();
Onlife.closeDB();
Console.WriteLine("Esecuzione terminata.");
Logger.Istanza.Chiudi();
}
Caricamento delle quotazioni
44
In questa particolare funzione l‟accesso ai dati del server su cui sono
presenti le quotazioni avviene tramite una query pass through definita in SQL
Server.
Il processo di caricamento delle nuove quotazioni può essere semplificato e
sintetizzato in questa sequenza di azioni:
1. Recupera una quotazione da elaborare dalla query pass
through. Se non ci sono più quotazioni da elaborare, termina
l‟esecuzione.
2. Salva i dati della nuova quotazione/preventivo nel sistema
DM2.
3. Elabora il canale di vendita.
4. Se non è una proposta o un contratto: salva il Prospect, salva
l‟indirizzo email, associa l‟indirizzo al Prospect, torna al punto
1.
5. Salva i dati della proposta o del contratto
6. Se il codice fiscale esiste già in DM2, aggiorna i dati del
partner, altrimenti aggiungi un nuovo Partner.
7. Salva l‟email e associala al Partner.
8. Se l‟email era già associata ad un Prospect, promuovi il
Prospect a Partner.
9. Torna al punto 1.
Questa funzionalità opera esclusivamente su dati provenienti dal
database, di conseguenza per seguire la metodologia di miglioramento delle
prestazioni indicata nel capitolo precedente, è stato deciso di realizzarla
completamente tramite stored procedure.
La complessità dell‟algoritmo ha reso indispensabile l‟utilizzo di un
cursore per leggere in sequenza i dati dalla query pass through
Query_Onlife_Quotazioni ed eseguire la sequenza di operazioni per ogni record
letto.
45
Un esempio di ottimizzazione che è stato possibile fare su questa funzione
è il seguente: precedentemente la funzione di ricaricamento delle quotazioni
che non è stato possibile caricare in un primo momento, doveva eseguire i
seguenti passaggi:
Veniva letto da LogQuotazioni l‟ID di una quotazione da ricaricare
Tale id veniva passato alla funzione di caricamento tramite un
parametro
La funzione eseguiva una query pass through verso il database del
server internet, per recuperare la quotazione.
Se c‟erano altre quotazioni da ricaricare, si ricominciava.
Tale problema è stato velocizzato notevolmente con una semplice modifica
della query di ricaricamento delle quotazioni:
SELECT CAST(NUMERO_QUOTAZIONE AS INT) AS NUMQUOT,
DATA_QUOTAZIONE, TIPO_RECORD,
ID_SESSIONE, E_MAIL,
SESSO, DATA_NASCITA,
CAPITALE, COD_FUMO,
CAP, PREMIO,
DA_DOVE_CONOSCIUTI, TIPO_RISCHIO,
FLAG_PROCAM, CANALE_VENDITA,
SUB_CANALE_VENDITA, ID_CONTRATTO,
FLAG_PRIV_COMM
FROM Query_Onlife_Quotazioni
WHERE CAST(NUMERO_QUOTAZIONE AS INT) IN (SELECT DISTINCT IDQuotazione FROM
logQuotazioni)
La parte evidenziata individua tutte le quotazioni che è necessario
ricaricare, che quindi verranno elaborate tramite una sola query. Ora è
sufficiente chiamare la funzione di caricamento delle quotazioni passando un
apposito parametro ricaricaLogQuotazioni settato a True, per ottenere con una
sola esecuzione della stored procedure, ciò che prima ne richiedeva una per
ogni quotazione.
Invio della posta
46
La funzionalità di invio di messaggi di posta elettronica, richiede che venga
eseguito un certo numero di operazioni sul database di DM2, al fine di
memorizzare ogni messaggio inviato nello storico del Prospect o al Partner
destinatario.
Tutte le funzioni relative alla posta elettronica in generale devono eseguire
operazioni anche i file allegati: questi elementi non sono memorizzati nel
database ma nel filesystem del sistema, quindi è stato necessario cercare una
soluzione che minimizzi il numero di richieste al database ma
contemporaneamente permetta di gestire i file.
La prima opzione presa in considerazione è stata l‟utilizzo del supporto
agli assembly CLR offerto da SQL Server. In questo modo le operazioni sui files
sarebbero state realizzate in dei moduli programmati in C# inseriti
direttamente nel DBMS, e richiamabili da stored procedure. Le restrizioni di
sicurezza imposte da Allianz hanno però obbligato l‟abbandono di questa
strada.
È stato necessario percorrere l‟unica alternativa rimanente: tutte le
funzioni che avevano a che fare con gli allegati sono state programmante
all‟interno del file batch, mentre le restanti funzioni che operavano solo sul
database sono stare realizzate tramite stored procedure.
Un esempio di funzione realizzata tramite stored procedure è la seguente:
CREATE PROCEDURE [dbo].[logComunication]
@idEmail int,
@direzionePosta int,
@tipoPosta int,
@Mittente varchar(200),
@IDEmailMittente int,
@Destinatario varchar(200),
@idEmailDestinatario int,
@Oggetto varchar(200),
@Testo varchar(max),
@idCampagna int = NULL,
@numAllegati int = 0,
@DataInvio datetime = NULL,
@DataRicezione datetime = NULL,
@RisultatoInvio int = 0,
@WebCons int = NULL,
@IdCasella int = NULL,
@flg_errore bit = 0
47
AS
BEGIN
SET NOCOUNT ON;
DECLARE @PA_direzione AS nvarchar(3)
DECLARE @PA_letta AS bit
DECLARE @PA_idposta AS int
DECLARE @PA_idPartnerTeam AS int
IF @direzionePosta = 1 SET @PA_direzione = 'IN'
ELSE IF @direzionePosta = 2 SET @PA_direzione = 'OUT'
ELSE IF @direzionePosta = 0 SET @PA_direzione = NULL
IF @direzionePosta = 2 OR @direzionePosta = 0 SET @PA_letta = 1
ELSE SET @PA_letta = 0
EXEC @PA_idposta = [dbo].LogPosta @direzionePosta, @tipoPosta,
@Mittente, @IDEmailMittente, @Destinatario, @idEmailDestinatario, @Oggetto,
@Testo, @numAllegati, @DataInvio, @DataRicezione, @IdCasella, @flg_errore
SET @PA_idPartnerTeam = 0
INSERT INTO PostaArchivio (IDMail, Direzione, IdPosta, IDCampagna,
"Letta?", RisultatoInvio, IDWebCons, IDPartnerTeam)
VALUES (@idEmail, @PA_direzione, @PA_idposta, @idCampagna,
@PA_letta, @RisultatoInvio, @WebCons, @PA_idPartnerTeam)
RETURN SCOPE_IDENTITY()
END
Mentre un esempio di funzione realizzata all‟interno dell‟applicativo batch,
che esegue operazioni sui files, è la funzione AttachAttachmentsToMailOut, che
riceve l‟ID del messaggio e i percorsi dei file da allegare, e copia gli allegati da
inviare nella apposita cartella degli allegati in uscita.
static void AttachAttachmentsToMailOut(int idEmail, string[] allegati)
{
string errInfo ="AttachAttachmentsToMailOut(" + idEmail + ", [array
allegati])";
FileInfo allegato;
FileInfo destfile;
FileInfo backup;
string destFileStr;
string backupStr;
int n = 0;
int i = 0;
foreach (string allegatoStr in allegati)
{
n++;
48
allegato = new FileInfo(allegatoStr);
if (!allegato.Exists)
{
string msg = "AttachAttachmentsToMailOut: ERRORE! L'allegato " +
allegatoStr + " non esiste!";
Logger.Istanza.addToLog(msg, 15);
throw new System.Exception("AttachAttachmentsToMailOut: ERRORE!
L'allegato " + allegatoStr + " non esiste!");
}
destFileStr = IM_DirectoryAllegatiOUT + getNomeAllegatoForIT(idEmail,
n) + allegato.Extension;
destfile = new FileInfo(destFileStr);
if (destfile.Exists)
{ //è già presente un file con lo stesso nome. lo rinomino.
backupStr = IM_DirectoryAllegatiOUT + getNomeAllegatoForIT(idEmail,
n) + "_backup" + allegato.Extension;
backup = new FileInfo(backupStr);
while (backup.Exists)
{
backupStr = IM_DirectoryAllegatiOUT +
getNomeAllegatoForIT(idEmail, n) + "_backup" + i + allegato.Extension;
backup = new FileInfo(backupStr);
i++;
}
i = 0;
destfile.MoveTo(backupStr);
Logger.Istanza.addToLog("Attenzione: stranamente, il file di
destinazione " + destFileStr + " esisteva già. È stato rinominato in " +
backupStr + " prima di salvare il nuovo file.", 15);
}
allegato.CopyTo(destFileStr);
} // foreach...
}
Per migliorare la manutenibilità del codice scritto è stata creata una classe
EmailOut; le istanze della quale rappresentano messaggi da inviare tramite
l‟apposita funzione InviaEmail.
public class EmailOut
{
public SqlString destinatario;
public SqlString oggetto;
public SqlString testo;
public bool daSpedire;
public string[] allegati = { };
/// <summary> Default = NULL </summary>
public SqlInt32 webcons;
49
/// <summary> Default = NULL </summary>
public int? idMessaggioStandard;
/// <summary> Default = EMAIL </summary>
public enumTipoPosta tipoPosta;
/// <summary> Default = TRUE </summary>
public bool archiviaEmailSN;
/// <summary> Default = TRUE </summary>
public bool salvaAllegatiInArchivioSN;
/// <summary> Default = 1 (non cambiare! nel vecchio sistema in
Access era fisso) </summary>
public SqlInt32 _tipoMessaggio;
/// <summary> Default = 1 (non cambiare! nel vecchio sistema in
Access era fisso) </summary>
public SqlInt32 _idAutorizzato;
public EmailOut()
{
idMessaggioStandard = null;
_idAutorizzato = 1;
_tipoMessaggio = 1;
tipoPosta = enumTipoPosta.email;
archiviaEmailSN = true;
salvaAllegatiInArchivioSN = true;
webcons = SqlInt32.Null;
}
public int numAllegati
{
get { return allegati.GetLength(0); }
}
public void aggiungiAllegato(string allegato)
{
int dimAttuale = allegati.GetLength(0);
Array.Resize(ref allegati, dimAttuale + 1);
allegati[dimAttuale] = allegato;
}
public override string ToString()
{
return "Destinatario: \"" + destinatario.ToString() + "\"
Oggetto: \"" + oggetto.ToString() + "\" Testo: \"" +
testo.ToString().Substring(0, 30) + "[..] \" DaSpedire: \"" +
daSpedire.ToString() + "\" Numero allegati: " + numAllegati;
}
public void resetAllegati()
{
allegati = new string[0];
}
}
50
Lettura della posta
La gestione automatizzata della posta in ingresso è una caratteristica
molto importante, dato che in Onlife questo è il mezzo di comunicazione
principale con i clienti.
I nuovi messaggi in ingresso devono essere sottoposti ad un automatismo
che li elabora ed invia delle risposte automatiche quando possibile, mentre
quando non è possibile li associa al giusto partner o prospect.
Ogni nuova email deve dunque essere sottoposta ai seguenti passaggi:
Si prova ad eseguire il trattamento automatico.
Se il trattamento automatico non ha dato esito positivo, analizzando
l‟indirizzo del mittente la mail viene archiviata associandola al
Prospect o al Partner da cui proviene, il quale viene messo “in
evidenza”.
Se non proviene da un Prospect o da un Partner noto, viene
aggiunta alla “posta da classificare”.
Il trattamento automatico avviene cercando nell‟oggetto del messaggio
delle parole chiave, ed eseguendo la funzione opportuna in base alle eventuali
corrispondenze trovate. Il meccanismo si basa su delle tabelle che indicano le
parole chiave e i nomi delle funzioni da chiamare.
Questa funzione è stata realizzata nel seguente modo:
Una funzione principale LeggiPosta interroga il database alla ricerca
di nuove email da elaborare (restituite da una vista definita sul
database, che per ottimizzare gli accessi al database esegue già
alcune stored function di base e restituisce i risultati nei campi della
vista)
51
Il risultato dell‟interrogazione è un DataReader, il cui riferimento
viene passato a diverse funzioni, secondo le necessità. Le funzioni
che lo ricevono però devono elaborare solo il record corrente, quindi
non possono nè chiudere il datareader nè eseguire il suo metodo
Read().
LeggiPosta contiene un ciclo di lettura che tenta di esegue il
trattamento automatico di tutti i messaggi in ingresso
Automazione di campagne di comunicazione
La funzione LanciaCampagna deve creare ed inviare grandi quantità di
messaggi personalizzati. In fase di invio infatti il testo di ogni messaggio deve
essere personalizzato per il singolo destinatario inserendo dati estratti dalla
query dei destinatari dal database al posto di specifici tag presenti nel
messaggio.
Dato che questa funzione deve poter gestire anche gli allegati delle email, è
stata programmata interamente all‟interno dell‟applicativo batch.
Se gli allegati sono dei form PDF è possibile personalizzarne il contenuto
analogamente a come viene fatto per il testo del messaggio. Per determinare se
il PDF deve essere personalizzato oppure no, si usa la funzione
nomiCampiInPDF, che utilizzando le librerire iTextSharp permette di
individuare gli eventuali campi del Form PDF:
public static string[] nomiCampiInPDF(string percorsoPDF)
{
string[] nomi = { };
PdfReader documento = new PdfReader(percorsoPDF);
foreach (KeyValuePair<string, AcroFields.Item> campo in
documento.AcroFields.Fields)
{
Array.Resize(ref nomi, nomi.Length + 1);
nomi[nomi.Length - 1] = campo.Key;
}
documento.Close();
return nomi;
}
52
Diverse campagne possono essere raggruppate, per poter essere lanciate
secondo un ordine prestabilito, in automatismi. Quando si avvia un
automatismo le campagne che ne fanno parte vengono lanciate in sequenza.
Questa funzione viene svolta da EseguiWorkflow:
public static int eseguiWorkflow(enumAutomatismo automatismo, int? valoreID,
bool? flg_log)
{
string errInfo = "eseguiWorkflow(" + automatismo.ToString() + ", " +
valoreID.ToString() + ", " + flg_log.ToString() + ")";
dbInputParam[] parametro = new dbInputParam[1];
parametro[0] = new dbInputParam("@idAutomatismo", DbType.Int32,
(int)automatismo);
IDataReader operazioni = dataReaderDaSQL("SELECT IDMessaggio, IDQuery,
StringaUpdate FROM Workflow WHERE [InVigore?] = 1 AND IDAutomatismo =
@idAutomatismo ORDER BY OrdineChiamata", parametro);
string strUpdate;
int mailInviate = 0;
while (operazioni.Read())
{
if (operazioni.IsDBNull(2)) strUpdate = "";
else strUpdate = operazioni.GetString(2);
try
{
mailInviate += lanciaCampagna(operazioni.GetInt32(0),
operazioni.GetInt32(1), 0, strUpdate);
}
catch (Exception ex)
{
operazioni.Close();
operazioni.Dispose();
throw new Exception(errInfo + ": Si è verificato un errore
durante l'esecuzione dell'automatismo " + automatismo.ToString() + ".
L'esecuzione è stata interrotta.", ex);
}
}
operazioni.Close();
return mailInviate;
}
In questa funzione si può tra l‟altro notare un esempio di applicazione
della metodologia di gestione degli errori precedentemente definita: se
LanciaCampagna invia un‟eccezione, questa viene concatenata all‟eccezione
definita nel blocco catch.
Esecuzione della procedura dei rinnovi
53
La parte di sistema informativo che si occupa del calcolo del premio non è
inclusa in DM2, ma fa parte del sistema informativo di L.A. Vita. Per
interfacciarsi con questo sistema, nel caso del calcolo del premio è stato
necessario utilizzare un web service.
La funzione calcoloPremioOL si occupa della connessione al web service e
della corretta interpretazione dei dati restituiti, lanciando un‟eccezione se il
risultato ricevuto ha un formato inatteso:
public static string calcoloPremioOL(int numProposta, double
capitaleAssicurato, DateTime dataDecorrenza)
{
string errInfo = "calcoloPremioOL(" + numProposta + ", " +
capitaleAssicurato.ToString() + ", " + dataDecorrenza + ")";
GrlWsOnlifeSoapClient ClientWs = new GrlWsOnlifeSoapClient();
ClientWs.ClientCredentials.UserName.UserName = [credenziali di accesso]
ClientWs.ClientCredentials.UserName.Password = [credenziali di accesso]
string risultatoXml = ClientWs.CalcoloPremioOL(numProposta,
capitaleAssicurato, dataDecorrenza);
XmlTextReader xmlReader = new XmlTextReader(new
StringReader(risultatoXml));
string risultato = "";
string errore = "";
string premio = "";
while (xmlReader.Read())
{
if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name
== "Result")
{
xmlReader.Read();
risultato = xmlReader.Value;
}
if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name
== "ErrorDescription")
{
xmlReader.Read();
errore = xmlReader.Value;
}
if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name
== "anyType")
{
xmlReader.Read();
premio = xmlReader.Value;
}
}
54
xmlReader.Close();
ClientWs.Close();
string[] premio_splitted = premio.Split(';');
if (risultato.ToUpper().Trim() == "TRUE" && premio_splitted.Length ==
4)
{
return premio_splitted[3];
}
else
{
addToBacheca(enumBacheca.segnalazioniDM2, "Premio non calcolato
[e altre informazioni]");
throw new Exception([messaggio di errore dettagliato]);
}
}
Per aumentare la velocità di elaborazione il resto della logica di rinnovo dei
contratti è stata programmata in una stored procedure.
Controllo RID respinti
Questa funzione si limita a verificare la presenza di RID respinti, contando
il numero dei contratti che hanno uno specifico status. Anche questo è un
esempio di funzione che è stato possibile realizzare molto semplicemente in
un‟unica stored procedure. In particolare questa funzione è talmente semplice
che poteva essere realizzata definendo una vista, tuttavia per garantire piena
libertà di modifica si è deciso di utilizzare comunque una stored procedure.
Attualmente il compito di scrivere sul log l‟informazione spetta alla
funzione chiamante, ma essendo una stored procedure in futuro si potrebbe
aggiungere una chiamata alla stored procedure che si occupa della scrittura
sulla tabella Log.
CREATE PROCEDURE ControllaRIDrespinti
AS
BEGIN
SET NOCOUNT ON;
DECLARE @ris INT
SELECT @ris = count(*) FROM Contratti WHERE STATUS = 226
RETURN @ris
END
55
Eliminazione dei dati sensibili
Analogamente al controllo dei RID respinti, l‟eliminazione dei dati sensibili
è stata realizzata tramite la stored procedure qui riportata:
DECLARE @dataElim datetime
SET @dataElim = GETDATE()
DECLARE @eliminati int
UPDATE Contratti
SET
QuestAltezza = NULL,
QuestPeso = NULL,
QuestPressioneMax = NULL,
QuestTrigliceridi = NULL,
QuestColesteroloTot = NULL,
QuestColesteroloHdl = NULL,
[QuestFlagDiabete?] = NULL,
[QuestFlagInfarti?] = NULL,
DataCancellazioneDatiSensibili = NULL,
Note=Note+'Scaduta validità proposta in data '+CAST(GETDATE() AS nvarchar)
WHERE NOT DataCancellazioneDatiSensibili IS NULL AND
DataCancellazioneDatiSensibili < DateAdd(d, 1, @dataElim) AND
Status <> 100 AND
Status <> 200 AND
NOT ID IN (SELECT NumeroProposta
FROM CarichiContabili
WHERE TipoCarico = 1) AND
Contratti.ID NOT IN (SELECT Contratti.ID
FROM Contratti
WHERE (((Contratti.IDPartner) IN
(SELECT DISTINCT Contratti.IDPartner
FROM Contratti INNER JOIN
CarichiContabili ON Contratti.ID = CarichiContabili.NumeroProposta
WHERE (((CarichiContabili.TipoCarico)=1)))))
)
SET @eliminati = @@ROWCOUNT
UPDATE IntMaster SET DataUltimaEliminazioneDatiSensibili = GETDATE()
DECLARE @risultato as nvarchar(50)
SET @risultato = 'Eliminati i dati sensibili di ' + CAST(@eliminati as
nvarchar) + ' proposte'
EXEC [dbo].[addToLog] @risultato, 'Stored procedure: eliminaDatiSensibili', 5
56
Sincronizzazione degli status
La sincronizzazione periodica degli status dei contratti viene realizzata
accedendo al database di L.A. Vita tramite una query pass through e
confrontando gli status dei contratti con i dati contenuti nel database di DM2.
L‟obiettivo è evidenziare i casi in cui è stata rilevata una differenza di
status e procedere all‟aggiornamento dei dati presenti in DM2.
Dato che questa funzione elabora solamente dati provenienti dal database
di DM2 e dal database di Onlife (a cui si accede tramite una query pass
through), si è deciso di realizzarla tramite una stored procedure che restituirà il
numero di differenze individuate.
CREATE PROCEDURE [dbo].[ControllaStatusContratti]
AS
BEGIN
SET NOCOUNT ON;
DECLARE @strModificatoStatus as nvarchar(1000)
SET @strModificatoStatus = ''
DECLARE @idPartner as int
DECLARE @numDifferenze as int
SET @numDifferenze = 0
DECLARE @ID as int
DECLARE @DataDecorrenza as datetime
DECLARE @DataDecorrenza_onlife as datetime
DECLARE @DataScadenza as datetime
DECLARE @DataScadenza_onlife as datetime
DECLARE @DataSottoscrizione as datetime
DECLARE @DataSottoscrizione_onlife as datetime
DECLARE @RataLorda as decimal(14,2)
DECLARE @RataLorda_onlife as numeric
DECLARE @Status as smallint
DECLARE @Status_onlife as smallint
DECLARE @PrestazioneIniziale_onlife as numeric
DECLARE @PrestazioneIniziale as decimal(13,2)
DECLARE sorgDifferenze CURSOR FAST_FORWARD FOR
SELECT ID
,DataDecorrenza, DataDecorrenza_onlife, DataScadenza
,DataScadenza_onlife ,DataSottoscrizione
,DataSottoscrizione_onlife ,RataLorda ,RataLorda_onlife
,Status ,Status_onlife ,PrestazioneIniziale_onlife
,PrestazioneIniziale
FROM [dbo].[qry_differenze_STATUS]
57
OPEN sorgDifferenze
FETCH NEXT FROM sorgDifferenze
INTO @ID, @DataDecorrenza, @DataDecorrenza_onlife, @DataScadenza,
@DataScadenza_onlife, @DataSottoscrizione, @DataSottoscrizione_onlife,
@RataLorda, @RataLorda_onlife, @Status, @Status_onlife,
@PrestazioneIniziale_onlife, @PrestazioneIniziale
WHILE @@FETCH_STATUS = 0 BEGIN
IF @Status_onlife <> 0 BEGIN
UPDATE Contratti
SET Status = @Status_onlife,
DataDecorrenza = @DataDecorrenza_onlife,
DataScadenza = @DataScadenza_onlife,
DataSottoscrizione = @DataSottoscrizione_onlife
WHERE ID = @ID
IF @RataLorda <> @RataLorda_onlife BEGIN
SET @strModificatoStatus = convert(nvarchar,
GETDATE(), 103) + ': Modificato il premio del contratto numero ' + cast(@ID
as nvarchar) + ' da € ' + cast(@RataLorda as nvarchar) + ' a € ' +
cast(@RataLorda_onlife as nvarchar) + char(13) + char(10)
UPDATE Contratti SET RataLorda =
@RataLorda_onlife WHERE ID = @ID
END
IF @PrestazioneIniziale <> @PrestazioneIniziale_onlife
BEGIN
SET @strModificatoStatus = @strModificatoStatus
+ convert(nvarchar, GETDATE(), 103) + ': Modificato il capitale del contratto
numero ' + cast(@ID as nvarchar) + ' da € ' + cast(@PrestazioneIniziale as
nvarchar) + ' a € ' + cast(@PrestazioneIniziale_onlife as nvarchar) +
char(13) + char(10)
UPDATE Contratti SET PrestazioneIniziale =
@PrestazioneIniziale_onlife WHERE ID = @ID
END
IF @Status_onlife = 200 UPDATE Contratti SET
DataCancellazioneDatiSensibili = NULL WHERE ID = @ID
IF NOT(@Status_onlife = 100 OR @Status_onlife = 200) BEGIN
SELECT @idPartner = IDPartner FROM Contratti WHERE ID
= @ID
SET @strModificatoStatus = convert(nvarchar,
GETDATE(), 103) + ': Modificato status contratto numero ' + cast(@ID as
nvarchar) + ' in ' + cast(@status_onlife as nvarchar) + char(13) + char(10)
EXEC dbo.evidenziaPartner @idPartner,
@strModificatoStatus
INSERT INTO Log (Testo, DebugInfo, Rilevanza) VALUES
(@strModificatoStatus, 'Stored procedure: ControllaStatusContratti', 5)
END
58
SET @numDifferenze = @numDifferenze + 1
END
SET @strModificatoStatus = ''
SET @idPartner = NULL
SET @ID = NULL
SET @DataDecorrenza = NULL
SET @DataDecorrenza_onlife = NULL
SET @DataScadenza = NULL
SET @DataScadenza_onlife = NULL
SET @DataSottoscrizione = NULL
SET @DataSottoscrizione_onlife = NULL
SET @RataLorda = NULL
SET @RataLorda_onlife = NULL
SET @Status = NULL
SET @Status_onlife = NULL
SET @PrestazioneIniziale_onlife = NULL
SET @PrestazioneIniziale = NULL
FETCH NEXT FROM sorgDifferenze
INTO @ID, @DataDecorrenza, @DataDecorrenza_onlife,
DataScadenza, @DataScadenza_onlife, @DataSottoscrizione,
@DataSottoscrizione_onlife, @RataLorda, @RataLorda_onlife, @Status,
@Status_onlife, @PrestazioneIniziale_onlife, @PrestazioneIniziale
END
CLOSE sorgDifferenze
DEALLOCATE sorgDifferenze
DECLARE @risultato as nvarchar(50)
SET @risultato = 'Differenze di STATUS: ' + CAST(@numDifferenze as
nvarchar)
EXEC [dbo].[addToLog] @risultato, 'Stored procedure:
ControllaStatusContratti', 5
RETURN @numDifferenze
END
Elaborazione automatica degli incassi
I bonifici da elaborare vengono ricevuti via email in un file nel formato di
Microsoft Excel, strutturato in una maniera predefinita.
L‟elaborazione automatica degli incassi prevede la ricerca di tale file tra le
email ricevute, e la successiva elaborazione della causale di ogni bonifico alla
ricerca di un numero di contratto per cui si è in attesa di pagamento.
Questa funzione è stata realizzata utilizzando OLEDB, una API sviluppata
da Microsoft per accedere a dati provenienti da una grande varietà di fonti
59
diverse in maniera uniforme. In questo caso la fonte di dati è direttamente il file
Excel:
string xlsConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source="
+ percorsoFileExcel + ";Extended Properties=\"Excel 8.0;HDR=Yes;IMEX=1\";";
OleDbConnection xlsConnection = new OleDbConnection(xlsConnectionString);
xlsConnection.Open();
Una volta fatte tutte le verifiche sul file sorgente e sui dati caricati, è stato
possibile utilizzare una stored procedure per eseguire le restanti operazioni di
elaborazione del bonifico.
Elaborazione degli estratti conto
Questa funzione deve generare gli estratti conto per gli intermediari di
vendita, deve inviarli ai rispettivi destinatari, e deve generare ed inviare gli
ordini di pagamento a beneficio degli intermediari che ne hanno diritto.
Per creare l‟ordine di pagamento, si è utilizzato il modulo di creazione di
PDF a partire da Form PDF già creato per le altre funzionalità di Onlife.
L‟estratto conto invece è un documento piuttosto complesso da creare,
perchè contiene una tabella creata dinamicamente di dimensioni molto
variabili. Precedentemente veniva creata sfruttando Microsoft Word, ma per le
restrizioni imposte da Allianz questa strada non era percorribile.
Si è quindi deciso di creare una nuova funzione
generaPDFContoIntermediario specifica per questa funzionalità, che utilizza
direttamente la libreria iTextSharp.
Un esempio autoesplicativo del codice creato, tratto dalla funzione in
questione, è il seguente:
//creo il paragrafo iniziale (intestazione)
Paragraph ParagrafoIntro = new Paragraph();
ParagrafoIntro.Font = Times13;
Phrase PhraseIntro = new Phrase();
60
PhraseIntro.Add(new Chunk("ONLIFE", Times13Bold));
PhraseIntro.Add(new Chunk(" - Estratto conto in € per", Times13));
PhraseIntro.Add(new Chunk(" " + descrizioneCanale, Times13Bold));
PhraseIntro.Add(new Chunk(" di data", Times13));
PhraseIntro.Add(new Chunk(" " + DateTime.Now.ToString("dd/MM/yyyy"),
Times13Bold));
ParagrafoIntro.SpacingAfter = 5;
ParagrafoIntro.Add(PhraseIntro);
//aggiungo il paragrafo iniziale al documento
document.Add(ParagrafoIntro);
//creo il separatore e lo aggiungo al documento
PdfPTable separatore = new PdfPTable(1);
separatore.WidthPercentage = 100;
separatore.DefaultCell.Border = 1;
separatore.DefaultCell.BorderWidth = 2;
separatore.AddCell(" ");
document.Add(separatore);
Costruire direttamente il documento PDF richiede più lavoro di
programmazione rispetto all‟utilizzo di un editor WYSIWYG, ma consente di
avere un migliore controllo sul risultato prodotto e completa libertà nella
definizione dei contenuti.
Un esempio di documento completo di tutti gli elementi, prodotto dalla
funzione di generazione degli estratti conti, è visibile nella pagina seguente
61
62
Capitolo 6: Conclusioni
Attualmente il nuovo applicativo batch è in avanzata fase di test. Fin‟ora
ha soddisfatto tutti i requisiti preposti, non sono stati riscontrati problemi di
funzionamento e ed ha soddisfatto gli obiettivi posti dall‟azienda.
È stato deciso di portare separatamente in fase di produzione le singole
funzionalità, in modo da concentrare gli sforzi su una alla volta e quindi
velocizzare il processo. L‟entrata in produzione delle prime funzionalità è atteso
a breve.
63
Bibliografia
Microsoft Developer Network. [Online] http://msdn.microsoft.com/.
Felician, Leonardo. Dispense del corso di Sistemi Informativi 2.
Felician, Leonardo. 1995. L'impresa rete. Workflow management e lavoro
cooperativo in azienda. : Franco Angeli, 1995.
Fermeglia, Maurizio. Dispense del corso di complementi di basi di dati.
Gorjan, Mauro. 2006. Onlife: progettazione di un sistema informativo per
l'assicurazione vita su internet. : Università degli studi di Trieste, tesi di laurea
in Ingegneria Informatica, 2006.
Sbroiavacca, Fulvio. Dispense del corso di Sistemi Informativi 1.
Troelsen, Andrew. 2007. Pro C# 2008 and the .NET 3.5 Platform, Fourth
Edition. : Apress, 2007.
Vargas, Enrique. 2000. High Availability Fundamentals. [Online] 2000.
www.sun.com/blueprints/1100/HAFund.pdf.
Vlissides, John, et al. 1994. Design Patterns: Elements of Reusable
Object-Oriented Software. : Addison-Wesley Professional, 1994. ISBN 0-201-
63361-2.