UNIVERSITÀ DEGLI STUDI DI TRIESTE




              FACOLTÀ DI INGEGNERIA

              Corso di laurea triennale in
    ...
Sommario



Capitolo 1: Introduzione ................................................................ 5
  Il contenuto di ...
Capitolo 4: Analisi dei problemi e definizione delle metodologie
            risolutive .....................................
Capitolo 1: Introduzione

                                               In    questa   tesi   vengono   descritti   gli
 ...
producono delle informazioni successivamente inviate ad un altro sistema
informatizzato, ad esempio tramite un web service...
Luogo di svolgimento del lavoro documentato

     Messa sul mercato il 22 maggio 2007, OnLife è stata in Italia la prima
p...
La migrazione della parte batch del sistema informativo DM2 di Onlife è
l‟oggetto di questo documento.
     Le caratterist...
Le ragioni della migrazione

    La migrazione dei meccanismi di workflow rientra in un più ampio
progetto di migrazione d...
Capitolo 2: Il sistema informativo di OnLife

                                                  Questo     breve   capitol...
   Sistemi host di L.A. Vita: la parte di sistema informativo che non è
            specifica di Onlife. I dati presenti ...
È il risultato della compilazione del questionario anonimo, che serve a
dare al cliente una misura orientativa del premio ...
Capitolo 3: Descrizione dei requisiti

                                               In questo capitolo viene data una vi...
soluzione) inevitabilmente allunga anche l‟MTTR2, ovvero il tempo medio
necessario per ripristinare le funzionalità del si...
portare degli effettivi vantaggi. Ad esempio nel sistema informativo DM2
l‟automatismo che elabora i messaggi di posta ele...
A proposito della soluzione adottata prima della migrazione va detto che,
pur essendo funzionale allo scopo, non offriva g...
Sincronizzazione degli status
      Come detto, il sistema informativo di Onlife è diviso in due parti: il sistema
informa...
In entrambi i casi è necessario sfruttare la funzionalità di generazione di
documenti PDF per inserire all‟interno dei ris...
MessaggiTesti         Workflow                Query
      ID            ID Messaggio                 ID
  standard
   Ogge...
Capitolo 4: Analisi dei problemi e definizione
                     delle metodologie risolutive

                        ...
comunque può essere ridotta testando approfonditamente i singoli moduli
parallelamente al lavoro di codifica), bisogna con...
La cause di possibili problemi
     Come già accennato, una probabile fonte di anomalie di funzionamento è il
mancato risp...
A) “Creare un applicativo batch che riesca a gestire opportunamente
  situazioni impreviste successe durante il normale fu...
C) “Creare un applicativo batch che riesca a gestire opportunamente
         situazioni impreviste successe durante il nor...
   InnerException: è un riferimento ad un‟altra eccezione.
           StackTrace: contiene una rappresentazione testuale...
7. Il “dettaglio dell‟errore” che deve essere salvato sul log è composto
   dai seguenti elementi:
      o Un messaggio de...
9. Normalmente i dettagli di ogni errore devono essere salvati sul
   database. Se questo non è disponibile, devono essere...
la gestione di nuove situazioni di errore. Questa caratteristica
              affronta il punto C del problema.




    N...
29
Esempio pratico di log:
Rilevanza dell’errore:
         25

Messaggio di errore:
         Errore durante l’esecuzione del ...
Dall‟esempio di log riportato si deducono immediatamente le seguenti
informazioni:
                   Si sta avendo a che...
La soluzione
     Inizialmente si è ipotizzato l‟utilizzo di OpenOffice, una soluzione open
source che offre anche delle A...
stato ritenuto opportuno porsi il problema di come raggiungere una migliore
efficienza.
     In questa sezione viene dunqu...
   Accorpare più interrogazioni in una sola. Sapendo che un algoritmo
    richiede sicuramente un certo insieme di dati, ...
Capitolo 5: Implementazione nell‟ambiente
                                         target

                               ...
condividono un insieme di funzioni sostanzialmente limitato alle sole funzioni
di accesso al database e di logging.


    ...
numero di email inviate in seguito all‟esecuzione del workflow di
            inizio giornata.
           Valori da 10 a ...
public sealed class Logger
{
      static Logger istanza = null; // la variabile statica privata che
                     ...
Riprendendo quanto definito nel capitolo 4, l‟insieme dei dettagli di ogni
errore è costituito da:
           Un messaggi...
La memorizzazione nel database avviene tramite la chiamata di una stored
procedure. Il codice utilizzato per richiamarla è...
else
            {
                   debugInfo = Stack delle chiamate:rn +
                                            En...
scelto di inserire nella classe Onlife una variabile statica contenente il
riferimento alla connessione al database. La co...
funzioniDaEseguire = new string[1];
                funzioniDaEseguire[0] = args[0];
                dacmd = true;
       ...
In questa particolare funzione l‟accesso ai dati del server su cui sono
presenti le quotazioni avviene tramite una query p...
Un esempio di ottimizzazione che è stato possibile fare su questa funzione
è il seguente: precedentemente la funzione di r...
La funzionalità di invio di messaggi di posta elettronica, richiede che venga
eseguito un certo numero di operazioni sul d...
AS
BEGIN
         SET NOCOUNT ON;
         DECLARE @PA_direzione AS nvarchar(3)
         DECLARE @PA_letta AS bit
        ...
allegato = new FileInfo(allegatoStr);
       if (!allegato.Exists)
       {
          string msg = AttachAttachmentsToMail...
/// summary Default = NULL /summary
    public int? idMessaggioStandard;

    /// summary Default = EMAIL /summary
    pub...
Lettura della posta

     La gestione automatizzata della posta in ingresso è una caratteristica
molto importante, dato ch...
   Il risultato dell‟interrogazione è un DataReader, il cui riferimento
               viene passato a diverse funzioni, ...
Diverse campagne possono essere raggruppate, per poter essere lanciate
secondo un ordine prestabilito, in automatismi. Qua...
La parte di sistema informativo che si occupa del calcolo del premio non è
inclusa in DM2, ma fa parte del sistema informa...
xmlReader.Close();
      ClientWs.Close();

      string[] premio_splitted = premio.Split(';');

      if (risultato.ToUpp...
Eliminazione dei dati sensibili

     Analogamente al controllo dei RID respinti, l‟eliminazione dei dati sensibili
è stat...
Sincronizzazione degli status

     La sincronizzazione periodica degli status dei contratti viene realizzata
accedendo al...
OPEN sorgDifferenze

     FETCH NEXT FROM sorgDifferenze
           INTO @ID, @DataDecorrenza, @DataDecorrenza_onlife, @Da...
Migrazione dei meccanismi di workflow di un sistema informativo assicurativo verso l'ambiente SQL Server e .NET
Migrazione dei meccanismi di workflow di un sistema informativo assicurativo verso l'ambiente SQL Server e .NET
Migrazione dei meccanismi di workflow di un sistema informativo assicurativo verso l'ambiente SQL Server e .NET
Migrazione dei meccanismi di workflow di un sistema informativo assicurativo verso l'ambiente SQL Server e .NET
Migrazione dei meccanismi di workflow di un sistema informativo assicurativo verso l'ambiente SQL Server e .NET
Migrazione dei meccanismi di workflow di un sistema informativo assicurativo verso l'ambiente SQL Server e .NET
Migrazione dei meccanismi di workflow di un sistema informativo assicurativo verso l'ambiente SQL Server e .NET
Upcoming SlideShare
Loading in...5
×

Migrazione dei meccanismi di workflow di un sistema informativo assicurativo verso l'ambiente SQL Server e .NET

2,068

Published on

Tesi di laurea triennale in ingegneria informatica.

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
2,068
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
9
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Migrazione dei meccanismi di workflow di un sistema informativo assicurativo verso l'ambiente SQL Server e .NET

  1. 1. 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
  2. 2. 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 3
  3. 3. 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 4
  4. 4. 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 5
  5. 5. 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 6
  6. 6. 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. 7
  7. 7. 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. 8
  8. 8. 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. 9
  9. 9. 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 10
  10. 10.  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 11
  11. 11. È 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. 12
  12. 12. 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 13
  13. 13. 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 14
  14. 14. 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. 15
  15. 15. 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. 16
  16. 16. 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. 17
  17. 17. 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: 18
  18. 18. MessaggiTesti Workflow Query ID ID Messaggio ID standard Oggetto ID Query Descrizione Testo Standard StringaSQL ID Automatismo Allegato1 Destinatari Allegato2 SELECT Nome, Cognome, Email FROM Partner WkflAutomatismi WHERE ..... ID Codice ALTRE Nome Note TABELLE 19
  19. 19. 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 20
  20. 20. 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. Stato del Istante in cui il servizio Istante in cui il servizio servizio viene ripristinato cessa di funzionare Funzionamento Tempo di Tempo tra due riparazione malfunzionamenti Riparazione Tempo 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. 21
  21. 21. 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. 22
  22. 22. 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. 23
  23. 23. 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. 24
  24. 24.  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. 25
  25. 25. 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). 26
  26. 26. 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 27
  27. 27. 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. 28
  28. 28. 29
  29. 29. 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,”dest@email.it”,...): 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 30
  30. 30. 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. 31
  31. 31. 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. 32
  32. 32. 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. 33
  33. 33.  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. 34
  34. 34. 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 35
  35. 35. 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 36
  36. 36. 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: 37
  37. 37. 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. 38
  38. 38. 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 + rn + dettagliEccezione(ex.InnerException); else return ex.TargetSite.Name + : + ex.Message; } 39
  39. 39. 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:rn + Onlife.dettagliEccezione(ex); debugInfo += rnrnStack delle chiamate:rn + getInnermostException(ex).StackTrace.ToString(); } 40
  40. 40. else { debugInfo = Stack delle chiamate:rn + 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(================================================ rn); } } else { logTxt.WriteLine(messaggio + -- Call stack:); logTxt.WriteLine(debugInfo); logTxt.WriteLine(================================================ rn); } } } 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 è 41
  41. 41. 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) { 42
  42. 42. 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 43
  43. 43. 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. 44
  44. 44. 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 45
  45. 45. 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 46
  46. 46. 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++; 47
  47. 47. 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; 48
  48. 48. /// 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]; } } 49
  49. 49. 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) 50
  50. 50.  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 (KeyValuePairstring, AcroFields.Item campo in documento.AcroFields.Fields) { Array.Resize(ref nomi, nomi.Length + 1); nomi[nomi.Length - 1] = campo.Key; } documento.Close(); return nomi; } 51
  51. 51. 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 52
  52. 52. 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; } } 53
  53. 53. 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 54
  54. 54. 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 55
  55. 55. 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] 56
  56. 56. 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 57

×