1 AOP Sandro Pedrazzini Approfondimento Aspect Oriented Programming (AOP)
2 AOP Sandro Pedrazzini Aspect Oriented Programming Paradigma di programmazione, che prevede linserimento di funzionalità comune (cross-cutting concerns) a funzionalità esistente Lo scopo è quello di separare aspetti comuni (logging, security, gestione eccezioni, ecc.) da business logic nel senso più stretto
3 AOP Sandro Pedrazzini Aspect Oriented Programming (AOP) Tutti i paradigmi, in particolare il paradigma a oggetti, prevedono lincapsolamento di funzionalità in entità separate, creando astrazioni (metodi, classi). Esistono però funzionalità che potrebbero non rientrare facilmente in unorganizzazione di questo tipo, perché più trasversali (cross-cutting) Esempio: logging
4 AOP Sandro Pedrazzini Cross-cutting concerns tipici Sincronizzazione Gestione della memoria Persistenza Security Caching Logging Monitoring Business rules …
5 AOP Sandro Pedrazzini Implementazioni Le implementazini più diffuse per Java sono AspectJ e Hyper/J AOP viene integrata in diversi framework, soprattutto attraverso limplementazione di AspectJ –Spring –JBoss AOP –Guice
6 AOP Sandro Pedrazzini Terminologia di base Cross-cutting concern Funzionalità comune (trasversale) da condividere tra più funzioni. Esempio: funzionalità di logging, identica, allinterno di metodi o classi diverse, in cui, ad esempio, un log devessere eseguito in entrata e uno in uscita. Advice Codice aggiuntivo da applicare al modello di business esistente. Esempio: codice di logging, unico, da chiamare separatamente quando necessario.
7 AOP Sandro Pedrazzini Terminologia di base (2) Pointcut Punto di esecuzione allinterno dellapplicazione. Punto in cui un cross-cutting concern devessere applicato. Esempio: punto raggiunto quando un certo metodo inizia (logging di entrata) o termina (logging di chiusura) Aspect Modulo che combina la descrizione del pointcut (join-point) e il body delladvice. Esempio: aggiungiamo un aspect allapplicazione, specificando modalità di logging e quando questa va eseguita
8 AOP Sandro Pedrazzini Logging Tipicamente una funzionalità da aspect come logging la si trova in più parti nel codice, rendendo difficoltosa la sua comprensione e la sua manutenzione Una modifica al logging può significare modificare diversi moduli, classi e metodi
9 AOP Sandro Pedrazzini Logging (2) Codice in ogni modulo
10 AOP Sandro Pedrazzini Logging (3) Centralizzazione in un unico aspect
11 AOP Sandro Pedrazzini Esempio Funzionalità di pagamento con carta di credito public class BillingService implements IBillingService { … public Receipt chargeOrder(Order order, CreditCard card) throws Exception { ChargeResult result= fCreditCardProcessor.process(order.getAmount(), card); return...; }
12 AOP Sandro Pedrazzini Esempio (2) Abbiamo però anche bisogno di controlli di security e logging public Receipt chargeOrder(User user, Order order, CreditCard card) throws Exception { logger.info(Inizio pagamento…); if (!checkUserPermission(user) { logger.info(Utente non autorizzato…); throw new UnauthorizeException(); } ChargeResult result= fCreditCardProcessor.process(order.getAmount(), card); logger.info(Fine pagamento…); return...; }
13 AOP Sandro Pedrazzini Esempio (3) Nel codice precedente, elementi di logging e di security sono da considerare cross-cutting concerns Cosa succede se dobbiamo modificare elementi di security nellapplicazione? Siccome questi elementi sono sparsi in tutta lapplicazione, le modifiche saranno parecchie. Meglio sarebbe poterli gestire in modo centrale
14 AOP Sandro Pedrazzini Aspect AOP spinge a risolvere il problema della presenza di singoli elementi in più parti del codice permettendo di esprimere questi cross-cutting concerns attraverso moduli chiamati aspects Un aspect contiene advice (codice da eseguire) e pointcut (dichiarazione di quando gli advice vanno eseguiti) Esempio: un aspect potrebbe contenere –codice di verifica della security –specifica che la verifica viene eseguita ogni volta prima della chiamata a charge()
15 AOP Sandro Pedrazzini Aspect (2) Possibile esempio in AspectJ pseudocode public aspect SecurityCheck { before() : within(Receipt IBillingService.chargeOrder( User user, Order order, CreditCard card)) && call(ChargeResult ICreditCardProcessor.charge( long, CreditCard)) { if (!checkUserPermission(user) { logger.info(Utente non autorizzato…); throw new UnauthorizeException(); }
16 AOP Sandro Pedrazzini Aspect (3) AspectJ permette tutta una serie di modalità dichiarative per specificare i join points I joint point sono lelemento più critico, perché attraverso la loro dichiarazione, espressa anche attraverso regular expressions, si specifica il match Espressioni complesse rendono il codice poco prevedibile e quindi anche poco mantenibile
17 AOP Sandro Pedrazzini Esempi di pointcut execution(* set*(*)) match con lesecuzione di tutti i metodi il cui nome inizia con set e hanno un unico parametro di qualsiasi tipo within(ch.supsi.*) limita lo scope del pointcut a qualsiasi cosa (classe, metodo) nel package ch.supsi this(CreditCard) Questo pointcut risolve quando loggetto attuale in esecuzione (this) è unistanza di CreditCard execution(* set*(*)) && within(ch.supsi.*) && this(CreditCard) Combinazione dei tre criteri precedenti
18 AOP Sandro Pedrazzini Implementazioni Esistono fondamentalmente due tipi di implementazione per AOP –Class-waving Integra le implementazioni degli aspect direttamente nelle classi in cui devono essere eseguite. Il weaving può essere applicato sia a livello di compilazione, che loading o runtime. –Proxy La chiamata al metodo di un oggetto viene intercettata. Vengono usati Java dynamic proxy o CGLIB (code generation lib) I framework che si basano sul proxy sono generalmente più semplici e si basano quasi esclusivamente sul meccanismo di method interceptor.
19 AOP Sandro Pedrazzini Implementazione: proxy (1) Viene generata una sottoclasse della classe da estendere, o dinamicamente, a runtime (Java dynamic proxy), oppure modificando il bytecode durante la generazione (CGLIB) Subject Proxy request()... request()... RealSubject request()...
20 AOP Sandro Pedrazzini Implementazione: proxy (2) Il meccanismo di Java dynamic proxy può essere applicato solo se esiste uninterfaccia della classe da estendere Con CGLIB, invece, il proxy eredita dalla stessa classe che estende RealSubject Proxy request()... request()... RealSubject request()...
21 AOP Sandro Pedrazzini AOP in framework Framework come Spring, Guice o altri, integrano le funzionalità di AspectJ, creando se possibile un livello di astrazione –Spring: ne facilita lutilizzo, pur lasciando aperta la possibilità di accedere a funzionalità avanzate e specifiche di AspectJ –Guice: ne facilita e ne delimita lutilizzo, allo scopo di promuoverne limpiego dove questo migliora il codice, ma evitarne labuso, con conseguenze nefaste nel codice
22 AOP Sandro Pedrazzini AOP con Guice Guice prevede AOP attraverso il meccanismo di method interception In questo modo si può eseguire un advice ogni volta che uno specifico metodo viene invocato In Guice si parla di –Matcher: elemento esegue il match => pointcut –MethodInterceptor: parte di codice da eseguire => advice
23 AOP Sandro Pedrazzini AOP con Guice (2) Matcher Dichiarazione che permette di accettare o rifiutare un valore. In Guice servono due matcher: uno per specificare la classe e uno per specificare il metodo. MethodInterceptor Eseguito allinvocazione di un metodo che risolve il match. Riceve informazioni sulla chiamata: il metodo, i suoi argomenti, e listanza delloggetto invocante.
24 AOP Sandro Pedrazzini Esempio Riutilizzare lesempio del pagamento con carta di credito già usato per DI Eseguire durante lesecuzione del pagamento un controllo attraverso AOP per verificare i giorni in cui può avvenire il pagamento Implementato: specificare uno o più giorni in cui il servizio non può essere usato
25 AOP Sandro Pedrazzini Esempio (2) Codice attuale public class BillingService implements IBillingService { … public Receipt chargeOrder(Order order, CreditCard card) throws Exception { ChargeResult result= fCreditCardProcessor.process(order.getAmount(), card); return...; }
26 AOP Sandro Pedrazzini Matcher Definiamo unannotation per specificare il Matcher. Servirà a segnalare il NotOnWeekends { }
27 AOP Sandro Pedrazzini Matcher (2) Utilizzo dellannotation public class BillingService implements IBillingService { public Receipt chargeOrder(Order order, CreditCard card) throws Exception { ChargeResult result= fCreditCardProcessor.process(order.getAmount(), card); return...; }
28 AOP Sandro Pedrazzini Interceptor public class WeekendBlocker implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { Calendar today = new GregorianCalendar(); String todayDisplayName = today.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.ENGLISH); Order order = (Order) invocation.getArguments()[0]; if (todayDisplayName.equals(Sunday")) { throw new IllegalStateException( invocation.getMethod().getName() + " for " + order.getAmount() + " not allowed on " + todayDisplayName); } return invocation.proceed(); }
29 AOP Sandro Pedrazzini Interceptor (2) Chiamata al metodo vero e proprio invocation.proceed(); Accesso ai parametri invocation.getArguments() Accesso alle informazioni del metodo invocation.getMethod()
30 AOP Sandro Pedrazzini Modulo di bind Guice utilizza un modulo per specificare i vari binding public class NotOnWeekendsModule extends AbstractModule { protected void configure() { bindInterceptor(Matchers.any(), Matchers.annotatedWith(NotOnWeekends.class), new WeekendBlocker()); bind(ITransactionLog.class). toProvider(DatabaseTransactionProvider.class); bind(ICreditCardProcessor.class). to(PaypalCreditCardProcessor.class); bind(IBillingService.class).to(BillingService.class); }
31 AOP Sandro Pedrazzini Modulo di bind (2) bindInterceptor( Matchers.any(), Matchers.annotatedWith(NotOnWeekends.class), new WeekendBlocker()); Match con ogni classe Solo i metodi con questa annotation Oggetto contenente il metodo interceptor
32 AOP Sandro Pedrazzini Main public class Main{ public static void main(String[] args) { Injector injector = Guice.createInjector(new NotOnWeekendsModule()); … IBillingService billingService = injector.getInstance(IBillingService.class); … }
33 AOP Sandro Pedrazzini Esempio 2: security Controllo se un certo utente cerca di eseguire una determinata azione e se ha il diritto di farlo Le azioni che un utente può eseguire vengono specificate seguendo un approccio che utilizza ruoli Specifichiamo con annotation quali ruoli hanno diritto ad eseguire una determinata azione
34 AOP Sandro Pedrazzini Esempio 2: security (2) Un interceptor viene creato per verificare se lutente che chiama lazione ha il ruolo necessario (in un caso reale, ruolo e azione sono legati con permission, perciò andrebbero verificate anche queste) User manager che mantiene le informazioni sullutente registrato: public interface IUserManager { void setCurrentUser(User user); User getCurrentUser(); }
35 AOP Sandro Pedrazzini User Manager Allinterno di unapplicazione multiutente lo user manager utilizzerebbe la sessione per gestire gli utenti registrati In unapplicazione desktop, invece, basta un singleton Lo user possiamo rappresentarlo come una semplice classe che gestisce il nome dellutente (informazione minima) e un set di ruoli appartenenti allutente
36 AOP Sandro Pedrazzini User public class User { private String fName; private Set fRoles; public User(String name, Set roles) { fName = name; fRoles = roles; } public String getName() { return fName; } public Set getRoles() { return fRoles; } … }
37 AOP Sandro Pedrazzini Ruoli Per specificare ruoli in modo semplice e controllato, usiamo una enumeration public enum Role { CUSTOMER, EMPLOYEE }
38 AOP Sandro Pedrazzini Annotazione delle azioni Le annotation ci permettono di segnalare quali ruoli sono richiesti per eseguire una certa operazione Usata per segnalare un metodo e accessibile a runtime value() serve a specificare il RequiresRole { Role value(); }
39 AOP Sandro Pedrazzini Azioni Supponendo ora di avere una classe con delle azioni da eseguire, le annotation ci permettono di specificare con quale ruolo public class VideoRental … }
40 AOP Sandro Pedrazzini Azioni (2) public class VideoRental IUserManager public void rentMovie(long movieId) { System.out.println(String.format( "Movie %d rented by user %s., movieId, fUserManager.getCurrentUser())); public void registerNewMovie(String name) { System.out.println(String.format( "New movie \"%s\" registered by user %s.", name, fUserManager.getCurrentUser())); }
41 AOP Sandro Pedrazzini Interceptor A questo punto ci vuole un interceptor che faccia uso delle informazioni specificate dalle annotation per verificare i ruoli public class RoleValidationInterceptor implements MethodInterceptor private IUserManager fUserMgr; public Object invoke(MethodInvocation invocation) throws Throwable { Role requiredRole = invocation.getMethod().getAnnotation(RequiresRole.class).value(); if (fUserMgr.getCurrentUser() == null || !fUserMgr.getCurrentUser().getRoles().contains(requiredRole)){ throw new IllegalStateException(…); } return invocation.proceed(); }
42 AOP Sandro Pedrazzini Module Bisogna inoltre specificare quando viene usato linterceptor public class ExampleModule extends AbstractModule { public void configure() { bind(IUserManager.class).to(UserManager.class).in(Scopes.SINGLETON); RoleValidationInterceptor roleValidationInterceptor = new RoleValidationInterceptor(); bindInterceptor(any(), annotatedWith(RequiresRole.class), roleValidationInterceptor); //necessario, per in rileValidationInterceptor requestInjection(roleValidationInterceptor); }
43 AOP Sandro Pedrazzini Module (2) Prima di tutto associamo unimplementazione di IUserManager allinterfaccia. Definendola come singleton, specifichiamo che lo stesso oggetto usato in VideoRental venga usato anche in RoleValidationInterceptor In seguito associamo linterceptor a tutti i metodi annotati con RequiresRole Lultimo passaggio è necessario per iniettare nellinterceptor le sue dipendenze (nel nostro caso con UserManager)
44 AOP Sandro Pedrazzini Creazione di un framework AOP