Un tool per il tracing di codice Java basato su Aspect Oriented Programming

Pagina creata da Alessio Ferro
 
CONTINUA A LEGGERE
Un tool per il tracing di codice Java basato su Aspect Oriented Programming
Scuola Politecnica e delle Scienze di Base
Corso di Laurea in Ingegneria Informatica

Elaborato finale in Programmazione II

Un tool per il tracing di codice Java
basato su Aspect Oriented Programming
Anno Accademico 2016/2017

Candidato:
Saverio Milo

matr. N46002459
Un tool per il tracing di codice Java basato su Aspect Oriented Programming
Tesi triennale

Indice

Introduzione                                                                                                                          3

1   Paronamica degli argomenti trattati                                                                                               5
    1.1   Logging . . . . . . .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    .   .   .   .   . 5
    1.2   Rule-based Logging .    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    .   .   .   .   . 7
    1.3   AOP . . . . . . . . .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    .   .   .   .   . 9
    1.4   AspectJ . . . . . . .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    .   .   .   .   . 10
    1.5   Sommario . . . . . .    .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .    .   .   .   .   . 11
2   Descrizione della tecnica implementata                                                                                           12

3   Scenari d'uso                                                                                                                    16
    3.1   Descrizione programma test . . . . . . . . . . . . . . . . . . . . .                                                       16
    3.2   Esecuzione Nominale . . . . . . . . . . . . . . . . . . . . . . . . .                                                      16
    3.3   Interruzione Servizio (SER) . . . . . . . . . . . . . . . . . . . . .                                                      18
    3.4   CMP e gestione eccezioni . . . . . . . . . . . . . . . . . . . . . .                                                       20
          3.4.1 Caso 1: gestione all'interno del metodo . . . . . . . . . .                                                          20
          3.4.2 Caso 2: gestione nel caso in cui l'eccezione venga rilanciata                                                        20
4   Conclusioni                                                                                                                      22

                                                                                                                   Milo Saverio
Tesi triennale

Introduzione

Durante l'attività di sviluppo di un prodotto software, aspetto cruciale da te-
nere in considerare è la sua adabilità, difatti esistono servizi i quali devono
essere disponibili continuativamente, come programmi utilizzati ad esempio :
per la gestione di transizioni nanziare, monitoraggio delle prestazioni di un
veicolo (stato dei freni, funzionamento delle luci etc.), veicolare il traco aereo.
Tale problematica è sentita sopratutto dai software legati ad attività critiche
e per tali infrastrutture il presentarsi di malfunzionamenti, possono provocare
danni all'ambiente o a persone, difatti vengono adottate dierenti tecniche per
incrementare il loro grado di adabilità, ricordiamo: la replicazione dello strato
hardware, rendendo così le decisioni maggiormente robuste a possibili guasti in
uno degli apparati su cui il sistema esegue, essendo la decisione presa solo se
la maggioranza delle infrastrutture è concorde; far realizzare parti del codice
che adempiono allo stesso scopo da dierenti team ed eseguirle su diversi strati
replicati, riducendo la possibilità di introdurre nel codice errori comuni. Sudette
tecniche vengono adottate per limitare il propagarsi di errori, i quali possono
evolvere in fallimenti dell'intero sistema provocati da difetti nel codice introdotti
durante le vari fasi del suo sviluppo.
Sono state realizzate dierenti tecniche atte a registrare gli eventi salienti del-
l'esecuzione di un'applicazione software, riuscendo a determinare in tempo reale
l'andamento del usso di esecuzione per avere dati riguardanti: le risorse uti-
lizzate, i tempi d'esecuzione di specici eventi, informazioni relative alla reale
messa in opera del software; tali metodologie hanno successo quando il rapporto
generato è accurato, permettendo la rilevazione dei problemi di cui il software
è aitto.
Una delle principali tecniche adottate è generare eventi di log. Sono informa-
zioni raccolte in precisi punti del codice atte a descriverne il suo andamento,
succesivamente salvate in una struttura logica di tipo le, oppure inviate su rete
per poter poi essere analizzate.

I risultati riscontrati da un approc-                      f
cio al logging non ben strutturato
portano ad avere informazioni non
sempre accurate, difatti i fallimen-
ti catturati dalle attività di log-
ging sono solo una parte di quelli
che avvengono e non sempre quel-
li registrati sono veritieri, derivan-
ti dalla rilevazione di errori che in
realtà, sono gestiti o comunque non
discriminano un fallimento, riscon-
                                            Figura 1: Propagazione errori
trando risultati non soddisfacenti,
sopratutto per prodotti in cui la rilevazione dei fault debba essere un'attività
primaria.

                                                                       Milo Saverio
Tesi triennale

Anché l'attività di rilevazione dei fallimenti possa essere eciente, non può
essere rimandata durante la stesura del codice e le istruzioni che riguardano la
cattura delle informazioni per il logging, devono essere separate dal resto del
codice dell'applicativo essendo dei servizi che il softwate non implementa ma ne
usufruisce.
Un metodologia che migliora l'attività di tracing, risolvendo il primo problema
citato sopra, è adottato dal rule-based logging, fornendo una tecnica di cattu-
ra delle informazioni maggiormente adabile e precisa. Di suddetto approccio
ne esiste una versione scritta in C, utilizzata dal framework LogBus, il quale
la sfrutta per la rilevazione dei fallimenti. Il framework discrimina diverse ti-
pologie di errori che portano il software a comportarsi in modo non previsto,
introducendo opportune chiamate di funzione atte alla registrazione di eventi.
LogBus   non separa i diversi aspetti del codice, difatti l'immissione delle chia-
mate alla scrittura degli eventi di log nei vari le che verranno eseguiti, avviene
tramite un opportuno code parser a cui una volta datogli in pasto: il sorgente
di cui si vogliono analizzare i fallimenti; le entità che fanno parte del sistema e
dove si trova la loro denizione, riesce ad inserire automaticamente le opportune
chiamate di funzione che occorrono per la registrazione degli eventi di logging,
rendendo il codice meno comprensibile e aumentando lo sforzo per una sua fu-
tura modica.
L'AOP è un approccio realizzato per separare i diversi interessi all'interno di un
codice, problematica che aigge approcci alla programmazione in cui il software
viene rappresentato come un insieme di funzionalità (procedurale) o composizio-
ne di entità (oggetti), con l'utilizzo degli aspetti invece, si riesce a determinare
porzioni del usso di esecuzione e aggiungere funzionalità in modo trasparente
al codice che realizza la nostra business logic. L'approccio legato agli aspetti
purtroppo, non è maturato come quello ad oggetti, per tale ragione viene af-
ancato all'OOP, cosicché sia possibile utilizzare le peculiarità migliori dei due
per la stesura della piattaforma software.
Scopo di quest'elaborato è quello di testare la possibilità di realizzare un tool
per la rilevazione dei fallimenti in Java, il quale possa essere utilizzato indie-
rentemente dal prodotto che si vuole andare a produrre e abbia la capacità di
separare la rilevazione dei eventi di logging dalla business logic.
L'elaborato sarà organizzato in tre sezioni: una prima, in cui verrà discusso del
logging e degli approcci sviluppati successivamente in tale elaborato; verranno
poi forniti i dettagli con i quali si è realizzato il tool ed inne un insieme di
esempi pratici, per testare le capacità dello strumento realizzato.

                                                                      Milo Saverio
Tesi triennale

1     Paronamica degli argomenti trattati

1.1    Logging
Il logging é una delle tecniche maggiormente diuse per la raccolta di informa-
zioni durante l'esecuzione delle varie funzionalità di un sistema, utilizzata anche
per la rilevazione dei fallimenti. In quest'ultimo caso sudetta metodologia viene
impiegata per scoprire dinamicamente i fault introdotti durante la fase di pro-
duzione che successivamente evolvono in fallimenti, rendendo l'attività di debug
più immediata rispetto ad analizzare staticamente il codice per determinare di-
fetti in esso. Difatti essendo un'attività esegutita quando il software non è in
esecuzione, molti dei fault non vengono rilevati, poiché alcuni sono scatenati
soltanto con l'interazione di più attività che il software ore o in determinati
situazioni di carico del sistema. La tecnica essenzialmente si basa sulla produ-
zione di log entry, informazioni che descrivono un evento che è avvenuto durante
il funzionamento del software, successivamente i dati vengono salvati in un le
di testo ed analizzando quest'ultimo si dovrebbe identicare quale parte del co-
dice abbia scatenato il fallimento.
L'approccio al logging non è un attività ben denita, non esistono metodolo-
gie speciche a supporto di tale attività, per suddetto motivo le informazioni
rilevate dal log potrebbero essere inaccurate e imprecise. Un difetto potrebbe
provocare la scrittura di più eventi nel le, includendo a priori un'operazione
di ltraggio delle informazioni registrate prima del loro reale utilizzo, anché
sia possibile discriminare la causa del fallimento del sistema software. Esistono
alcuni framework a supporto di tali attività tra cui log4x di Apache che cercano
di uniformare la creazione degli eventi di log, supportando inoltre le operazioni
di ltraggio dei dati.
I framework forniscono solo delle API utili alla gestione degli eventi. Il posi-
zionamento dei punti in cui viene eettuato il logging viene lasciato nelle mani
degli sviluppatori rimandando l'attività di gestione dei fallimenti alla fase di
codica, quindi il modo in cui questi punti vengono attivati mutua da team a
team di sviluppatori anche se principalmente i pattern per la cattura di eventi
sono tre:
    • try{} catch(){write_log}

      la funzione di logging è avviata alla cattura di un'eccezione;
    • if(cond) write_log

      di solito if contiene singole condizioni logiche legate insieme da operatori
      logici che non vengono modicati se il codice esegue correttamente;
    • else write_log

      la scrittura su log avviene nel ramo else di un costrutto if;

                                                                       Milo Saverio
Tesi triennale

       Listing 1:   Esempio di cattura degli eventi di log con l'utilizzo di try catch() in
                                            wildy
 1    @Override
 2       protected Context getDefaultInitCtx() throws NamingException {
 3           if (!gotDefault) {
 4               // if there is an initial context factory prop in the
                  ,→ env use it to create the default ctx
 5               final String factoryClassName = myProps != null ?
                  ,→ (String)
                  ,→ myProps.get(Context.INITIAL_CONTEXT_FACTORY) :
                  ,→ null;
 6               if(factoryClassName == null ||
                  ,→ InitialContextFactory.class.getName()
                  ,→ .equals(factoryClassName)) {
 7                   defaultInitCtx = new
                      ,→ DefaultInitialContext(myProps);
 8               } else {
 9                   final ClassLoader classLoader =
                      ,→ WildFlySecurityManager.
                      ,→ getCurrentContextClassLoaderPrivileged();
10                   try {
11                       final Class factoryClass =
                          ,→ Class.forName(factoryClassName, true,
                          ,→ classLoader);
12                       defaultInitCtx =
                          ,→ ((javax.naming.spi.InitialContextFactory)
                          ,→ factoryClass.newInstance())
                          ,→ .getInitialContext(myProps);
13                   } catch (NamingException e) {
14                       throw e;
15                   } catch (Exception e) {
16                       throw
                          ,→ NamingLogger.ROOT_LOGGER.failedToInstantiate(e,
                          ,→ "InitialContextFactory",
                          ,→ factoryClassName, classLoader);
17                   }
18               }
19               gotDefault = true;
20           }
21           return defaultInitCtx;
22       }
     questi approcci non intercettano la causa del fallimento ntantoché non viene
     sollecitata la parte del codice istruita a farlo, se fosse presente ad esempio un
     loop innito in un blocco di istruzioni, il usso di esecuzione stagna in un punto
     preciso non permettendo la rilevazione di tale loop di esecuzione, difatti la mag-
     gior parte dei fallimenti rilevati non è dovuta a difetti del software, ma fallimenti
     provocati da risorse limitate che vengono utilizzate per l'esecuzione dello stesso
     o comunque dall'ambiente in cui viene avviato.

                                                                               Milo Saverio
Tesi triennale

Per suddetti motivi esistono altri metodi per il rilevamento dei fallimenti ad
esempio heartbeat message e in alcuni casi, si aiuta l'attività di monitoraggio
con il supporto hardware (watchdog).
Tutti gli approcci però lasciano libero il programmatore nel decidere dove at-
tivare le procedure di logging, quindi la qualità della rilevazione dei fallimenti
dipende dall'esperienza di quest'ultimo, se consideriamo che un prodotto è rea-
lizzato da diversi team, ognuno di loro potrebbe avere un approccio dierente.
Il rule-based logging vuole disiplinare l'attività della cattura dei fallimenti: indi-
viduare prima della stesura del codice quali sono i punti salienti dell'esecuzione,
categorizzare i vari tipi di errori che possono essere rilevati, esonerando così il
programmatore e poter rendere tale attività automatizzata.

1.2    Rule-based Logging
Questa metodologia, con l'ausilio della modellazione UML, riesce ad individuare
quali saranno le posizioni in cui dovrà essere eettuato l'inserimento dei punti
di avvio delle procedure di log già durante la progettazione. Difatti con una
rappresentazione del sistema in termini delle entità che lo costituiscono e delle
loro interazioni, è possibile discriminare quali sono le interazioni o le operazioni
che possono generare fallimenti nel software, determinando inoltre una proce-
dura ben denita per la rilevazione.
Lo scopo è quindi quello di creare una tecnica generalizzata, essendo l'attività
di modellazione una buona prassi per la creazione di un software e quindi in-
clusa nella maggior parte dei prodotti di qualità, estendendo la possibilità di
riusarla per progetti completamente diversi invece di dover trovare approcci ad-
hoc, molto spesso non ecaci o che comunque richiedono uno sforzo di sviluppo
elevato.
Il punto chiave di questa metodologia è quello di basarsi su un modello specico
per la rilevazione degli errori e l'utilizzo di regole ben denite, che stabiliscono:
dove eettuare l'operazione di generazione di eventi di log (quelli che devono
scatenare la procedure atte alla loro gestione) e cosa deve essere tracciato, sal-
vato (il tipo di evento che l'ha suscitato). Per la brevità dell'elaborato saranno
rilevati solo i seguenti eventi:
    • Service STart(SST): evento da loggare al primo ingresso nella funzionalità,
      prima ancora dell'avvio di quest'ultima, mette in evidenza che quel servizio
      è stato avviato correttamente;
   • Service ENd(SEN): deve essere loggato prima di ogni terminazione del
      servizio valida, evidenza che il software si è comportato nel modo voluto;
Questi eventi permettono di determinare la durata prevista di un servizio: ogni
volta che una coppia di eventi SST e SEN per un servizio sono rilevati, vie-
ne salvata la durata dell'esecuzione di quel servizio ed utilizzata per calcola-
re ∆n , la durata della ennesiama esecuzione stimata a partire dalla formula
∆n = (1 − α) ∗ ∆n−1 + α ∗ ∆l dove ∆l denisce la durata dell'ultima esecuzione
riscontrata. Il parametro da tenere in considerazione è α perché all'aumentare
di questo si dà maggiore peso all'ultima esecuzione corretta, invece della storia
delle esecuzioni, facendo oscillare la durata prevista del servizio.

                                                                        Milo Saverio
Tesi triennale

   Gli errori deniti dalla metodologia sono i seguenti:
   • Service error (SER): I service error non permettono ad una frazione di
     codice che è in esecuzione, di raggiungere un exit point (escludendo la sua
     normale terminazione);
   • Service complaint (CMP): notica che un servizio è terminato per mezzo
     di un dirty exit point, cioé un punto di terminazione dell'attività non
     voluto;
   • Interaction error (IER): tale tipologia di errore notica che vi è stata
     un'invocazione di un metodo da un'entità è il controllo non ritorna a
     questa;
   • Crash error (CER): un servizio è terminato inaspettatamente;

In tale elaborato verrano trattate solo le prime due tipologie di errore.
Gli eventi citati sopra, uniti ad un approccio regolato da timeout, permettono la
rilevazione del SER. Un SER error viene generato solo quando non è riscontrato
l'evento SEN associato ad esso dopo un tempo pari ns ∗ ∆n (dove ns è un valore
maggiore di 1), da quando si è registrato un SST, segnalando che il servizio
non riesce a terminare. Un CMP error viene registrato ogni qual volta un
metodo rilancia l'eccezione ad un altro o in caso di una runtime exception,
poiché il usso di esecuzione termina prima del punto di ritorno che stabilisce
la corretta esecuzione del metodo, nel caso in cui la gestione dell'eccezione di
questo avvenga all'interno dello stesso, viene considerato un'esecuzione corretta
del codice e per tale ragione non viene generato un errore di tipo CMP. La
rilevazione di tale evento comporta l'esclusione del SEN.
La realizzazione della metodologia indicata sopra verrà implementata sfruttando
il paradigma legato agli aspetti, utilizzato per separare la logica del prodotto
da realizzare, dai servizi di cui usufruisce.

                                                                    Milo Saverio
Tesi triennale

1.3    AOP
L'approccio allo sviluppo software più utilizzato ad oggi è quello legato alla pro-
grammazione ad oggetti, ormai aermatosi anche nell'ambito industriale.
Questo paradigma di programmazione è comunque non perfetto ed aetto da
diversi problemi, uno tra questi legato alla realizzazione di servizi orizzontali
(servizi che occorrono all'applicativo per funzionare, ma non fanno parte del
suo reale scopo di realizzazione), utili allo sviluppo della piattaforma software,
tra cui ricordiamo: la persistenza delle informazioni prodotte, la sicurezza ed
anche le attività di logging.
Nel caso di quest'ultime, la parte di codice adibita a generare informazioni ri-
guardanti l'esecuzione di operazioni dell'entità è unita alla business logic della
stessa, andando a non rispettare uno dei principi della stesura di un buon codice,
la modularità. Questa prassi si ripresenta anche per gli altri servizi orizzontali,
provocando un degrado della qualità del codice, oltre ad una replicazione di esso
che potrebbe essere evitata, riducendo così la complessità e il tempo per il suo
sviluppo.
La programmazione legata agli aspetti è stata ideata per la separazione dei di-
versi concern all'interno di un codice introducendo appunto gli aspetti.
Secondo l'OOP la realizzazione di un software avviene componendo gli ogget-
ti, ognuno adibito ad esprimere le entità che sono essenziali alla creazione del
prodotto. Nel caso di attività comuni, vengono utilizzate le funzionalità di og-
getti che orono supporto a queste, usufruendo anche di più istanze dello stesso
oppure riscrivendo parti del codice ogni qual volta occorrono (l'approccio mag-
giormente utilizzato), gli aspetti invece riescono ad intercettare la richiesta di
servizi comuni e li servono evitando la riutilizzazione di risorse o la duplicazione
di codice, così viene evitato l'introduzione di istruzioni non inerenti alla logica
applicativa. La programmazione legata agli aspetti non è da intendersi un pa-
radigma esterno a quello della programmazione ad oggetti, invece le deve essere
da supporto per migliorare la qualità del software realizzato, difatti con il solo
utilizzo dell'AOP non è possibile realizzare un software funzionale.
Il framework utilizzato è AspectJ, che estende la sintassi del linguaggio Java,
rendendolo compatibile con il paradigma orientato agli aspetti.

 Figura 2:   Dierenza tra approccio ad oggetti ed ad aspetti. Immagine tratta da:
          http://kospiotr.github.io/wiki/spring-aop-presentation/

                                                                      Milo Saverio
Tesi triennale

1.4     AspectJ
Questo framework, disponibile al download sul sito di Eclipse, basa tutto il suo
funzionamento su precisi punti chiave:
   • Aspect

   • Join Point
   • Pointcut
   • Advice

Gli Aspect permettono di denire dei tipi di cross-cutting, dando così la ca-
pacità allo strato software di catturare le richieste per l'utilizzo di un servizio
voluto da più oggetti, gli Aspect verranno sfruttati per denire un aspetto che
intercetti, punti del codice di cui si vuole fare logging.
I Join Point sono punti ben deniti nell'esecuzione del programma, quali pos-
sono essere: la chiamata di un metodo, la sua esecuzione, la chiamata di un
costruttore etc.; permettendo di denire quali porzioni di codice, della business
logic, devono attivare l'esecuzione di codice orizzontale.
I Pointcut sono quella parte dell'infrastruttura che permettono la denizione
dei Join Point da catturare. Viene fornito anche un oggetto, thisJoinPoint il
quale unito all'utilizzo del pattern reection, dà la possibilità di avere infor-
mazioni riguardanti: il Join Point catturato, identicativo dell'oggetto, nome
della classe, metodo che ha generato l'esecuzione del Pointcut e molte altri dati
relativi al Join Point che è stato eseguito.
Gli Advice sono dei comportamenti aggiuntivi che si vogliono collocare nel nor-
male usso di esecuzione dell'applicativo, in modo trasparente a quest'ultimo
senza appesantirlo o renderlo confuso, ne esistono di tre tipologie:
   •   before  , prima dell'esecuzione del Join Point;
   •       , dopo l'esecuzione del Join Point;
       after

   •   around   , vengono eseguite al posto del Join Point;
after permette di determinare punti del codice più specici da catturare, come
ad esempio: dopo il lancio di un eccezione, aancandoli la clausola throwing()
o appena dopo la terminazione di un metodo con returning().
Around è la clausola più potente, riesce a "wrappare" il Join Point che l'ha
scatenata, dando la possibilità di eseguire operazioni sia prima che dopo
quest'ultimo, oltre che a dierenza delle altre Advice, permette di avere un
parametro di ritorno nella sua dichiarazione e di poter modicare il valore
dei parametri di un metodo, tramite le variabili passate come argomento al
metodo proceed(..), che riavvia l'esecuzione del usso di istruzioni intercettato.

                                                                     Milo Saverio
Tesi triennale

    Un listato di codice che mostra un utilizzo di tali strumenti viene riportato qui
    sotto:
1   aspect myAspect //definizione di una Aspect
2   {
3        pointcut myPointcut(): execution(public static void
          ,→ main(..)); //definizione di un pointcut
4        before(): myPointcut() //definizione di una Advice
5        {
6          System.out.println("Eseguo prima del main"); //codice da
            ,→ eseguire per quella particolare Advice
7        }
8   }
    in questo esempio si viene a creare una Aspect, che prima di ogni esecuzione
    di un metodo la cui rma corrisponde a quella indicata come parametro di
    execution, viene eseguita una stampa a video. Si può notare che la denizione
    di quest'ultima è molto simile a quella di una classe, anche se prima di poter
    eseguire questo codice si ha bisogno di un compilatore specico. Per Java vi è la
    presenza di un altro framework che dà la possibilità di estendere il linguaggio con
    il paradigma dell'AOP, Spring, il quale ha meno capacità espressive di AspectJ
    nella denizione dei Pointcut, ma può essere eseguito senza ausilio di compilatori
    non Java.

    1.5    Sommario
    In conclusione il software realizzato vuole essere di supporto alla rilevazione dei
    fallimenti, evitando ai programmatori l'onere di introdurre nel codice istruzioni
    dedicate alla rilevazione, con utilizzo del rule-based logging, separando inoltre
    la business logic del prodotto da voler realizzare dalla logica del framework per
    la rilevazione dei fallimenti, utilizzando la programmazione legata agli aspetti
    implementata con l'ausilio del framework AspectJ nel linguaggio Java.

                                                                         Milo Saverio
Tesi triennale

2     Descrizione della tecnica implementata

Anché sia possibile utilizzare il framework prodotto, bisogna congurare
l'ambiente Eclipse come indicato:

    • Avviare l'IDE, recarsi nella sezione help, Install new software;
    • Aggiungere una nuova repository dove si andrà a scaricare il necessario;
      cliccando sul tasto add, si aprirà una nuova nestra, nel campo nome
      inserire un nome che si ritiene idoneo, nel campo URL il seguente indirizzo:
      http://download.eclipse.org/tools/ajdt/47/dev/update;
    • Attendere anché compaiano i pacchetti che è possibile installare, bisogna
      selezionarli tutti dopodiché cliccare su avanti;
    • Accettare le condizioni ed attendere l'installazione;
    • Per vericare che l'operazione sia andata a buon ne, selezionare New
      project, Other e controllare se è presente una sezione denominata AspectJ;
    • Nel progetto includere il package contenete il codice attuo al tracing e
      precedere ad ogni metodo la annotation @Log,anché ne venga fatta una
      tracciatura;

     Figura 3:   Class diagram delle principali classi che realizzano il framework

Il package che va incluso per eettuare la tracciatura, è stato realizzato in
linguaggio Java, con l'ausilio di AspectJ e delle annotation. Nell'infrastruttura
è presente una aspect che viene attivata per ogni funzionalità di cui si vuole
eettuare il logging essendo questa preceduta da @Log, dando la possibilità
di testare solo le funzionalità di cui si è realmente interessati, rendendo la
generazione del log consona all'esecuzione del codice che ci interessa tracciare,
altrimenti si verebbe a generare un le di log con molte entry rendendolo di
dicile lettura. Le annotation inoltre sono state introdotte per un successiva
gestione delle varie operazioni di rilevamento da parte di un container.

                                                                         Milo Saverio
Tesi triennale

Il software durante la sua esecuzione, non appena giunge ad un punto di interesse
per il logging, il suo usso di esecuzione viene indirizzato alla sezione di codice
che si occupa del tracing e rilevamento errori, principalmente le diverse sezioni
in cui l'esecuzione può essere spostata sono composte: da una sezione atta alla
scrittura delle informazioni sul le di log ed un'altra invece esegue le attività
utili per la rilevazione di errori.
Tutte le entry hanno questa sezione di informazioni di utilità in comune:
timestamp - [tipo del messaggio] - tipo di evento loggato
i seguenti Pointcut, deniscono quali metodi il framework riesce a gestire:
pointcut main(): execution(public static void main(..));
pointcut run() : execution(public void run(..));
pointcut allMethod():execution(* *(..));

   • main, cattura l'esecuzione del punto di lancio dell'intero software, gli even-
      ti di SST e di SEN per tale metodo vengono solo tracciati, senza tener
      conto dei parametri per la rilevazione dei SER, essendo eseguito una sola
      volta;
   • run, i thread vengono gestiti dierentemente dagli altri metodi, le
      informazioni che occorrono per la loro tracciatura sono diverse;
   • allMethod, qualsiasi altro metodo avviato viene gestito da questo Pointcut;

Le informazioni relative ai metodi eseguiti che occorrono al framework per rile-
vare le varie tipologie d'errore, vengono salvate in due HashMap: una di queste
(timerrecord nel class diagram) occorre per conoscere i riferimenti temporali dei
vari metodi e parametri relativi al funzionamento del framework, difatti è stato
realizzato un oggetto RecordTime in cui vengono memorizzati le informazioni
relative: al tempo di inizio del metodo, la sua durata stimata, quante volte è
stato eseguito; invece l'altra Map (timer) contiene i TimerTask utilizzati per
la rilevazione dei SER, questi iniziano ad essere eseguiti solo dopo un numero
adeguato di SST e SEN, difatti questi eventi scatenano il salvataggio di in-
formazioni nella prima HashMap riguardanti il metodo eseguito, quando i dati
raccolti sono sucienti a determinare il tempo di esecuzione del servizio, viene
istanziato un TimerTask e salvato nella seconda Map, esso in realtà è un nuo-
vo thread a cui è associato un timer, che viene eseguito solo dopo che è stato
trascorso un tempo pari ns ∗ ∆n e notica l'avvenimento del SER, altrimen-
ti all'arrivo del SEN il TimerTask viene eliminato e questa esecuzione viene
considerata corretta.
  Identicativo metodo in esecuzione                                RecordTime
                                              SSTt            durata istanza(ms) numero di esecuzioni terminate
       AspectJT.run()0Thread-1        31/07/2017 11:59:49.503         220                      2
ClasseEsempio.m1(..)648303415Thread-5 31/07/2017 11:59:50.046         107                      6

                            Tabella 1:     Esempio hashtable timerrecord

                                                                                              Milo Saverio
Tesi triennale

before(): allMethod() && @annotation(provaAspectJ.Log) &&
 ,→ !main() && !run()
       {
              Loggable.writeLog(thisJoinPoint,
               ,→ "SST",Thread.currentThread().getName());
       }
Rileva l'evento SST di tutti i metodi diversi da main e run, viene lanciata la
funzionalità di writeLog, la quale avvia la memorizzazione delle informazioni re-
lative al Pointcut, che vengono fornite dall'oggetto thisJoinPoint, un esempio
di evento registrato ha la seguente forma:
 metodo che ha generato l'evento, ID dell'oggetto, nome del
  ,→ thread che lo ha eseguito
I thread a dierenza di un normale metodo gli viene assegnato ID oggetto 0,
perché per ogni thread viene creato un oggetto diverso quindi un ID dierente,
procurando una entry diversa nel le di log, quando in realtà viene avviato un
thread che ha lo stesso comportamento di quello precedente.
after() : allMethod() && @annotation(provaAspectJ.Log) &&
 ,→ !main() && !run() &&
 ,→ if(!nex.contains(thisJoinPoint.getSignature()
 ,→ .toShortString()+thisJoinPoint
 ,→ .getThis().toString()+Thread.currentThread().getName()))
       {
              Loggable.writeLog(thisJoinPoint,
               ,→ "SEN",Thread.currentThread().getName());
       }
Frazione del codice per la rilevazione dei SEN, non viene avviata se nella strut-
tura dati nex è presente una funzionalità che ha generato un errore di tipo
CMP, altrimenti verrebbe generato un evento che segnala la corretta termina-
zione del metodo compromettendo il signicato della traccia, essendo lanciato
in luogo ad una terminazione non ordinaria della funzionalità.
metodo che ha generato l'evento, ID dell'oggetto, nome del
 ,→ thread che lo ha eseguito, durata dell'esecuzione
Alla entry relativa ad un SEN viene inclusa anche la durata dell'esecuzione di
quella istanza, usata per discriminare casi di falsi positivi oppure generazioni di
errori causate da una durata maggiore dell'esecuzione della funzionalità ma che
in realtà non é fallita.

                                                                     Milo Saverio
Tesi triennale

after() throwing(Exception e) : allMethod() &&
 ,→ @annotation(provaAspectJ.Log)
       {
              nex.add(thisJoinPoint.getSignature().
               ,→ toShortString()+thisJoinPoint
               ,→ .getThis().toString());
              Loggable.writeLogCMP(thisJoinPoint,e.toString(),
               ,→ Thread.currentThread().getName());
       }
Gli eventi che generano CMP vengono salvati nella struttura nex per evitare
l'avvio di un evento SEN .
metodo che ha lanciato l'eccezione, l'eccezione lanciata
La piattaforma ha anche la capacità di rilevare la cattura e la gestione di una
eccezione all'interno di un metodo della business logic:
 before(): he()
       {
               String e=CatchClauseSignature.class.cast
                ,→ (thisJoinPoint.getSignature()).toLongString();
               Loggable.writeLogCE(thisJoinPoint,e,Thread.
                ,→ currentThread().getName());
       }

 metodo che ha lanciato l'eccezione, l'eccezione catturata
un futuro utilizzo, potrebbe essere quello di invalidare il tempo di esecuzio-
ne dell'istanza di quel metodo oppure, iniziare a tener traccia di quest'ultimo
considerando la gestione dell'eccezione.

                                                                  Milo Saverio
Tesi triennale

3     Scenari d'uso

Per testare il funzionamento del framework sviluppato, verrà utilizzato un esem-
pio di riferimento scritto in Java, in cui verranno introdotti dei fault in posizio-
ni speciche del usso di esecuzione, anché si possa determinare l'adabilità
dello strumento.

3.1    Descrizione programma test
Nell'esempio utilizzato vengono avviate le seguenti attività : il main esegue
10 thread del tipo AspectjT che a loro volta richiamano di un oggetto c1, il
metodo m1. Lo stesso thread ne crea altri 10 del tipo AspectjT1 che eseguono
il metodo m1 dell'oggetto c2, i due oggetti sono istanziati nel main e passati ai
thread tramite costruttori.
La scelta di creare due istanze della stessa classe, serve a dimostrare che il
framework riesce ad identicare l'esecuzione dello stesso metodo da parte di
oggetti diversi, invece quella di generare più ussi di esecuzione mette in luce
la sua capacità di operare anche in ambiente multithreading. Questa facoltà di
discernere due diverse istanze dello stesso oggetto o diversi ussi di esecuzione,
rende più chiara quale parte del software ha subito un fallimento, difatti se si
avesse solo il nome del thread, si riuscirebbe a capire quale usso si è interrotto
ma non per colpa di quale oggetto ciò è avvenuto, nel caso contrario invece
avere solo l'ID dell'oggetto fa comprendere quale istanza di questo ha causato
un usso di esecuzione non voluto, ma non quale sia il usso, nel caso in cui ne
siano presenti altri che vengano eseguiti concorrentemente, per tale ragione le
informazioni vengono salvate entrambe.

3.2    Esecuzione Nominale
Viene mostrato in prima istanza dei graci che descrivono la normale esecuzione
del codice in cui successivamente introdurremo dei fault, utili per confrontarli
poi con quelli che provengono da una esecuzione non corretta, sulle ordinate
è rappresentato il tempo di esecuzione per quel metodo salvato sul log, invece
sulle ascisse il numero dell'esecuzione in ordine di inizio attività:

                                                                      Milo Saverio
Tesi triennale

metodo: AspectJT.run() (ms)

                               200

                               100

                                0
                                     1   2   3   4      5     6      7   8   9   10
                                                     ID esecuzione
metodo: c1.m1 (ms)

                               200

                               100

                                0
                                     1   2   3   4      5     6      7   8   9   10
                                                     ID esecuzione
metodo: AspectJT1.run() (ms)

                               200

                               100

                                0
                                     1   2   3   4      5     6      7   8   9   10
                                                     ID esecuzione
metodo: c2.m1 (ms)

                               200

                               100

                                0
                                     1   2   3   4      5     6      7   8   9   10
                                                     ID esecuzione

                                                                                      Milo Saverio
Tesi triennale

3.3    Interruzione Servizio (SER)
Verrano introdotti fault per simulare delle situazioni di fallimento, mettendo in
evidenza le reali capacità del framework.
Provocheremo prima un errore di tipo SER. Verrà allungata l'esecuzione dei
vari servizi con l'introduzione di chiamate al metodo sleep in punti precisi del
codice, ciò serve a simulare il blocco di un'attività software per un malfunzio-
namento anche se in realtà non accadrà.
Negli esempi sottostanti verrà prolungata di due secondi l'esecuzione della
settima istanza del thread AspectJT.run() e di cinquecento millisecondi
l'esecuzione dell'ottava chiamata del metodo m1 dell'oggetto c2. Il valore
ns utilizzato è pari a tre, per la determinazione del tempo di timeout che il
software gestisce.
Analizzando i graci sottostanti, l'esecuzione della settima istanza del thread
AspectJT.run() e del metodo m1 di c2 sono aumentate ed il framework ha
segnalato la durata anomala dei servizi, lanciando un errore di tipo SER
(situato al di sotto dei rispettivi graci). La durata del thread AspectJT1
risulta anche essa maggiore, osserviamo che il nome del thread è lo stesso che
avvia il metodo m1, quindi il usso deve attendere la ne del metodo prima di
poter terminare.
La bontà del framework è vericata, essendo riuscito a rilevare tempi di
esecuzione più lunghi, per tale ragione sicuramente riuscirà a determinare
bloccaggi nel codice. Il sorgente del framework e dell'esempio vengono avviati
nella stessa JVM, per avere risultati migliori il framework dovrebbe essere
eseguito da una JVM diversa o addirittura eseguito su una macchina diversa,
infatti in un reale utilizzo, avviare i codici nello stesso ambiente diminuisce la
capacità del framework nella rilevazione dei fallimenti, perché in casi in cui ad
esempio durante la mancanza della fornitura elettrica per il sistema, ma non
nella sala dove è presente l'elaboratore su cui sta avvenendo la raccolta dati,
questo evento non verrebbe rilevato.

31/07/2017 11:59:50.903      - [INFO] - SST ClasseEsempio.m1(..)
 ,→ 517415723 Thread-33
31/07/2017 11:59:50.932      - [INFO] - SST WatchDog.run() 0 Timer-8
31/07/2017 11:59:50.933      - [WARNING] - SER AspectJEsameT.run()
 ,→ Thread-21 0
31/07/2017 11:59:50.934      - [INFO] - SEN WatchDog.run() 0 Timer-8 2
31/07/2017 11:59:51.015      - [INFO] - SEN ClasseEsempio.m1(..)
 ,→ 517415723 Thread-33      113
31/07/2017 11:59:51.015      - [INFO] - SEN AspectJEsameT1.run() 0
 ,→ Thread-33 215
31/07/2017 11:59:52.572      - [INFO] - SST ClasseEsempio.m1(..)
 ,→ 648303415 Thread-21
31/07/2017 11:59:52.685      - [INFO] - SEN ClasseEsempio.m1(..)
 ,→ 648303415 Thread-21      113

                       Figura 4:   Esempio rilevazione SER

                                                                     Milo Saverio
Tesi triennale

metodo: AspectJT.run() (ms)

                                    2,000

                                    1,500

                                    1,000

                                          500

                                           0
                                                1     2    3     4      5     6      7   8   9      10
                                                                     ID esecuzione

                                                           SER AspectJT.run() Thread-23 0
           metodo: c1.m1 (ms)

                                          200

                                          100

                                           0
                                                1     2    3     4      5     6      7   8   9      10
                                                                     ID esecuzione
           metodo: AspectJT1.run() (ms)

                                          600

                                          400

                                          200

                                           0
                                                1     2    3     4      5     6      7   8   9      10
                                                                     ID esecuzione

                                                          SER AspectJT1.run() Thread-27 0
           metodo: c2.m1 (ms)

                                          400

                                          200

                                           0
                                                1     2    3     4      5     6      7   8   9      10
                                                                     ID esecuzione

                                                    SER ClasseEsempio.m1(..) Thread-27 1616246432

                                                                                                     Milo Saverio
Tesi triennale

3.4      CMP e gestione eccezioni
Per generare entry di tale tipo sono state create tre classi:
   • CheckedException, lancia un'eccezione catturata all'interno del suo corpo;
   • ThrowsCheckedException, rilancia un'eccezione all'oggetto che avvia il
        metodo di questa classe;
   • UncheckedException, genera una      NullPointerException      ;
tali classi servono a simulare la terminazione di un'attività per mezzo di exit
point non voluti, in Java a dierenza di altri linguaggi di programmazione le
eccezioni sono divise in categorie ben dierenziate tra loro, in base al tipo e al
loro modo di essere gestite, questi esempi sono esaustivi per qualsiasi generazione
di eccezione possa avvenire, tutti i metodi m1 dei vari oggetti sono eseguiti nel
main.
I comportamenti addottatti dal framework sono 2, in base a se l'eccezione è
gestita nel metodo oppure venga rilanciata.

3.4.1     Caso 1: gestione all'interno del metodo

13/07/2017 01:31:06.990 - [INFO] -
 ,→ SSTmainprovaAspectJ.AspectJEsame
13/07/2017 01:31:07.025 - [INFO] - SST CheckedException.m1()
 ,→ 1828972342 main
13/07/2017 01:31:07.029 - [INFO] - gestione eccezione
 ,→ provaAspectJ.CheckedException catch(java.io.IOException)
13/07/2017 01:31:07.032 - [INFO] - SEN CheckedException.m1()
 ,→ 1828972342 main 19
13/07/2017 01:31:07.055 - [INFO] - SENmain
All'avvio del metodo m1, viene lanciata un eccezione di tipo IOException
tramite la clausola throw, come vediamo essa è gestita ed il metodo termina
naturalmente, perché questa non determina la ne del metodo essendo cattu-
rata, ma solo di un usso operativo diverso.

3.4.2     Caso 2: gestione nel caso in cui l'eccezione venga rilanciata

13/07/2017 01:32:34.368 - [INFO] -
 ,→ SSTmainprovaAspectJ.AspectJEsame
13/07/2017 01:32:34.385 - [INFO] - SST
 ,→ ThrowsCheckedException.m1(..) 931919113 main
13/07/2017 01:32:34.387 - [WARNING] - CMP
 ,→ provaAspectJ.ThrowsCheckedException@378bf509
 ,→ java.io.IOException
13/07/2017 01:32:34.388 - [INFO] - gestione eccezione
 ,→ provaAspectJ.AspectJEsame catch(java.io.IOException)
13/07/2017 01:32:34.391 - [INFO] - SENmain

                                                                        Milo Saverio
Tesi triennale

Questa volta l'IOException viene rilanciata al metodo chiamante, che si pre-
occupa di catturare l'eccezione, invece il metodo chiamato data la sua ne di
esecuzione non ordinaria genera un errore CMP, qui il metodo che lancia l'ec-
cezione termina perché ha raggiunto un punto di esecuzione che non è capace
di gestire, terminando prima del suo punto di ritorno ordinario.
13/07/2017 01:28:33.971 - [INFO] -
 ,→ SSTmainprovaAspectJ.AspectJEsame
13/07/2017 01:28:34.003 - [INFO] - SST UncheckedException.m1()
 ,→ 1828972342 main
13/07/2017 01:28:34.006 - [WARNING] - CMP
 ,→ provaAspectJ.UncheckedException@6d03e736
 ,→ java.lang.NullPointerException
13/07/2017 01:28:34.007 - [INFO] - SENmain
La generazione della eccezione unchecked è stata eettuata creando un ogget-
to a cui poi è stato assegnato al suo riferimento il valore null, richiamando
succcessivamente l'avvio di un metodo usando quel riferimento, generando così
un NullPointerException. In questo caso nessun metodo cattura e gestisce
l'eccezione non essendo a carico del programmatore, si è in grado comunque di
rilevarla.

                                                                Milo Saverio
Tesi triennale

4    Conclusioni

La programmazione legata agli aspetti è sicuramente un approccio alla scrittura
di codice orizzontale più elegante e funzionalmente più ecace di quello ad og-
getti, purtroppo però questa losoa di programmazione non è ben supportata,
infatti non esistono strumenti potenti per l'ingegnerizzazione del software, se si
pensa che l'approccio ad oggetti viene utilizzato sin dall'attività di analisi del
prodotto software da realizzare. Legando però l'AOP ad un approccio sistema-
tico come il rule-based logging può portare a risultati soddisfacenti, ovviamente
qui non si è riusciti a descrivere l'intera potenzialità di questo stile, difatti due
tipologie di errore ad esempio, non sono state analizzate, però riscontrando la
semplicità con cui sia possibile analizzare del codice in esecuzione, rende l'AOP
uno strumento estremamente potente per la realizzazione di framework orizzon-
tali che aiutino lo sviluppo di un prodotto software. Un futuro test potrebbe
essere eettuato su un progetto open source come ActiveMQ, rilevando come
si comporta il software in casi più complessi di quelli simulati in questo elabo-
rato, sfruttando ad esempio bug noti della piattaforma ed osservando se questi
vengano rilevati.

                                                                       Milo Saverio
Bibliografia
     [1]   Kiczales, G.; Lamping, J.; Mendhekar, A.; Maeda, C.; Lopes, C.; Loingtier, J. M.;

     Irwin, J. Aspect-oriented programming. ECOOP’97. Proceedings of the 11th European

     C onference on Object-Oriented
     [2]   Cinque M.; Cotroneo D.; Pecchia A.; Event Logs for the Analysis of
     Software Failures: A Rule-Based Approach.IEEE transactions on software
     engineering, vol. 39, no. 6, June 2013

     [3]   The Eclipse Foundation. "The AspectJ TM Programming Guide".

     http://www.eclipse.org/aspectj//doc/next/progguide/index.html. 30/08/2017

     [4]   Antonio Pecchia; Marcello Cinque; Gabriella Carrozza; Domenico Cotroneo.

     Industry Practices and Event Logging: Assessment of a Critical Software Development

     Process; ICSE 2015. International Conference on Software Engineering

     [5] The Apache Software Foundation. "Apache Log4j 2 ™". https://logging.apache.org/log4j/2.x/.

     30/08/2017
Puoi anche leggere