Transactions
Brought to you by:
revittorio76
Ogni operazione eseguita a partire da una sessione deve, per prima cosa, creare un oggetto di tipo Transaction che freeza tutta la configurazione della sessione, in modo che se questa viene modificata mentre la trasformazione è in esecuzione, la transazione non risente delle suddette modifiche
Exists another way to create transaction without clone everytimes Session context? Is possible to create a context that keep changes inside them?
Possible solution: every transaction use real session context that keep track of how many transactions are currently active;
NOTE: All depends in how many transactions will be active contemporaneously and how frequent session will be 'without any work' and how frequently context will be changed at runtime
What happen if a transaction require a context change?
STATO INIZIALE
La sessione ha un solo contesto di riferimento
Le nuove transazioni che partono devono basarsi su questo
contesto
Le richieste di modifica del contesto dall'esterno non dovrebbero essere molte
Una volta impostato un contesto non dovrebbe più venire modificato
Le transazioni in corso potrebbero essere parecchie, anche contemporaneamente
I ragionamenti sulle performance devono tenere molto in considerazione questo aspetto, che deve essere privilegiato
La clonazione di un contesto all'avvio di ogni transazione è da valutare attentamente.
ARCHITETTURA
PREMESSA
L'idea di questa proposta si basa sulla gestione degli oggetti fatta dalla JVM:
si crea un contesto principale per la sessione che sarà il contesto di riferimento;
PROPOSTA TECNICA
La sessione ha un riferimento ad un contesto unico detto MASTER, che verrà inizializzato come da SessionManager;
Alla richiesta di creare una nuova transazione, questa prenderà il riferimento al contesto SLAVE (non al MASTER);
NOTA:
Alla richiesta di modifica del contesto della sessione dall'esterno, viene modificato il contesto MASTER e, contestualmente, viene posto a NULL il puntatore al contesto SLAVE.
NOTA:
Con questo sistema, si hanno molteplici vantaggi:
infatti, se un contesto viene rimosso dal puntatore SLAVE di una sessione, quanto tutte le transazioni che lo utilizzano si concludono, questo oggetto si troverà automaticamente a non aver più alcun riferimento attivo, il che lo rende automaticamente eliminabile dal GC.
PROBLEMATICHE APERTE
Come gestire le modifiche ad un contesto fatte da ogni singola transazione?
Gestire il problema dei MAPPING DUPLICATI
Una soluzione potrebbe essere quella di dichiarare che i mapping aggiunti all'interno di una transazione NON MODIFICANO IL CONTESTO DELLA SESSIONE; questo, però, potrebbe portare a dei problemi di performance in quanto, l'esecuzione di molti mapping dello stesso tipo in cascata, ogniuno dei quali richiede l'aggiunta di sotto-mapping, potrebbe costringere ad un sacco di lavoro aggiuntivo la libreria.
Soluzione a ciò, potrebbe essere quella di rendere disponibile una opzione che controlli l'auto-aggiunta dei mapping delle transazioni nel contesto di sessione questa opzione deve essere DISABILITATA di default e attivabile solo espressamente dall'utente questo, se non ci sono mapping eseguiti parallelamente, potrebbe essere utile per migliorare le performance e semplificare il processo di TUNING devono essere previsti nell'interfaccia grafica di runtime degli appositi strumenti che aiutino l'utente in fase di tuning ad identificare quali mapping vengono aggiunti dalle transazioni e quanto di frequente avviene questo servirebbe per permettere all'utente di decidere se aggiungere determinati mapping alla sessione, in modo da velocizzare il lavoro di conversione
Da questa soluzione nasce, però, un ulteriore problema: una transazione che ha un mapping specifico DIVERSO da quello che si vuole aggiungere alla sessione per motivi di performance, potrebbe avere degli errori di conversione. che ne usano un altro, ammesso che n>>m, posso caricare in sessione il sotto-mapping utilizzato da <n> dichiarando di escluderlo per i tipi e aggiungere il sotto-mapping tipo dichiarandolo valido solo per i tipi oppure non aggiungere affatto quest'ultimo, lasciando che le transazioni se lo carichino autonomamente. </n> </n></t>
Come gestire questo problema?
In fase di tuning dovrebbe essere possibile aggiungere un mapping specifico, ma decidere di utilizzarlo solo per determinate transazioni ovvero escluderlo in determinate altre.
Questo farebbe in modo che, date <t> tipologie di transazioni, <n> che utilizzano un sottomapping ed
NOTA:
le soluzioni proposte risolverebbero anche il problema della gestione delle modifiche al contesto fatte da una singola transazione (punto precedente)
Last edit: Vittorio 2014-07-25
Accettata la soluzione che implica che le modifiche del contesto apportate all'interno di una transazione NON MODIFICANO IL CONTESTO DELLA SESSIONE di appartenenza.
Questa soluzione, però, comporta anche che una transazione non può avere un semplice riferimento ad un contesto, ma deve avere un oggetto più complesso, che mantenga il riferimento al contesto base, ma che permetta di aggiungere mapping.
NOTA
Non si può semplicemente clonare il contesto base, altrimenti si perderebbero i benefici di performance dati dall'architettura a doppio contesto implementata nella sessione
CONCORRENZA
Il problema della concorrenza interviene soprattutto durante la modifica del contesto dovuto ad aggiunte di mapping esterne.
La creazione di nuove transazioni non crea alcun problema se si usa l'accortezza di porre a NULL il contesto SLAVE solo DOPO che il contesto MASTER è stato aggiornato. In questo modo, tutte le nuove transazioni che partono durante l'aggiornamento, utilizzeranno il contesto SLAVE NON ANCORA AGGIORNATO e questa cosa ha senso se si inserisce nella documentazione tecnica che il nuovo mapping aggiunto sarà attivo, NON da momento della chiamata al metodo di aggiunta, ma dal momento del RITORNO di tale metodo.
PROBLEMA
Il problema reale riguardo all'aggiunta di nuovi mapping si ha se non esiste un contesto SLAVE già definito.
In questo caso, infatti, una eventuale nuova transazione dovrebbe clonare il MASTER che, però, è in fase di aggiornamento.
SOLUZOINE: una soluzione potrebbe essere quella di bloccare la clonazione del contesto MASTER durante l'aggiornamento dello stesso e, viceversa, bloccarne l'aggiornamento durante la clonazione.
Accettata la soluzione del doppio blocco, in quanto non dovrebbe dare particolari problematiche essendo entrambe le operazioni (aggiornamento del contesto e clonazione dello stesso) due operazioni che avvengono con frequenze molto basse.
L'unica controindicazione del tutto accettabile è che le transazioni che vengono aperte durante un'aggiunta di un mapping, devono rimanere in attesa fino al termine di questa operazione, ovviamente, SOLO se non esiste un precedente contesto SLAVE.
NOTA
A causa di questa scelta architetturale, nella documentazione tecnica è da aggiungere che, come buona pratica, è consigliabile aggiungere tutti i mapping necessari al corretto funzionamento di una sessione, subito dopo la sua creazione, in modo da non rallentare l'esecuzione delle transazioni.
PROBLEMA
In che contesto aggiungere i mapping fatti 'on the fly'? Vedere Implement 'on the fly' mapping definition
SOLUZIONE molto probabilmente vanno aggiunti al contesto della transazione, in modo da non modificare il contesto di sessione
Related
Tickets: #3
Accettata la soluzione che implica di aggiungere i mapping fatti 'on the fly' direttamente nella transazione.
Questa soluzione allinea il funzionamento alla gestione dei mapping caricati all'interno di una transazione (che non sono visibili a livello di sessione).
NOTA
In fase di sviluppo della parte WEB di monitoraggio, sarà da tenere conto di questa tipologia di mapping e gestirli correttamente
CONTESTI
Ci sono vari tipi di contesti all'interno dell'applicativo:
Lo scopo fondamentale di tutti questi contesti è quello di contenere tutte le definizioni di mapping caricate fino al momento del loro utilizzo.
La differenziazione tra i vari contesti sta nell'utilizzo che si fa dei mapping in esso contenuti; in particolare, i primi tre contesti (Configurazione, Session Manager e Sessione) dovranno semplicemente essere clonati per generare il mapping del livello inferiore, mentre l'ultimo (Transazione) dovrà avere dei metodi di ricerca del mapping da utilizzare per una specifica conversione.
Un'altra importante funzionalità che devono avere tutti i mapping è quella di controllo dei duplicati; infatti, in base a determinate regole generali, un mapping viene classificato come duplicato e, una volta identificato, bisogna decidere se lanciare un'eccezione o meno.
MAPPING DUPLICATI
Un mapping è considerato DUPLICATO quando ha classe di origine, classe di destinazione, nome e modulo di appartenenza uguale ad uno esistente.
Un mapping duplicato può causare problemi in quanto può rendere ambigua una conversione.
NOTA
Il modulo di appartenenza viene tenuto in considerazione in quanto viene gestita una priorità dei moduli.
Questa priorità fa si che a partita di classe di origine, classe di destinazione e nome, venga scelto il mapping appartennete al modulo caricato per primo.
RICERCA DI UN MAPPING
La ricerca di un mapping si svolge per passi successivi; sarà necessario, infatti, suddividere l'operazione in una sequenza di step da eseguire in cascata, che, ad ogni passo, riduca la lista di mapping iniziale (tutti i mapping caricati nel contesto) ad uno, cioè quello da utilizzare per la specifica conversione.
I passi da eseguire sono i seguenti:
Le operazioni sopra citate, dovranno essere configurabili per contesto allo stesso modo delle definizioni di mapping.
NOTA: nello step 2, se i moduli sonno scandini in ordine di priorità e se trovo un mapping perfettamente aderente alla conversione richiesta (priorità massima), posso ritornarlo immediatamente: partendo dal presupposto che non possono esserci mapping duplicati (al netto del modulo di appartenenza), non potrò mai trovare un altro mapping con priorità uguale o maggiore a quello appena identificato.
NOTA: il metodo di ricerca DEVE necessariamente ritornare uno ed un solo mapping; in tutti gli altri casi bisognerà generare un errore che potrà essere di mapping non trovato oppure mapping ambiguo. Internamente, però, i vari stadi filtri dovranno necesasriamente passarsi una lista di mapping (compresa l'informazione del modulo di appartenenza) in modo tale da poter effettuare dei filtri incrementali.
Questa modalità di ricerca, introduce dei concetti che dovranno essere specificati nel dettaglio in seguito; tali concetti sono:
priorità dei mappingvicinanza delle classiQuesti concetti saranno sviluppati in seguito.
priorità dei moduli
La priorità dei moduli è definita in base all'ordine di caricamento all'interno della sessione; due mapping uguali in due moduli differenti verranno disambiguati utilizzando il mapping presente nel modulo caricato per primo.
conformità di un mapping ad una conversione
Un mapping si dice conforme ad una conversione quando:
NOTA in quest'ultimo caso potrebbe essere lanciata una eccezione di MAPPING AMBIGUO
Last edit: Vittorio 2014-08-06
RICERCA DI UN MAPPING
PRIORITA' DEI MODULI
La priorità dei moduli è semplicemente legata all'orgine di inserimento di ogni singolo modulo nel contesto.
Un modulo inserito prima ha priorità maggiore di uno inserito successivamente.
CONFORMITA' DI UN MAPPING AD UNA CONVERSIONE
Una conversione è caratterizzata da una delle seguenti combinazioni:
Un mapping, invece, è caratterizzato dai seguenti parametri:
Per CONFORMITA' si intende la capacità di eseguire la conversione richiesta utilizzando il mapping dato, cioè, di poter scrivere un ipotetico metodo
DEST map(ORIG);
con:
ed eseguire la conversione
OBJ_DEST convDest = map(OBJ_ORIG)
con:
Più formalmente, si intende CONFORME un mapping ad una determinata conversione quando:
la classe di origine del mapping è uguale, superclasse o interfaccia di quella della conversionela classe di destinazione del mapping è uguale o sottoclasse di quella della conversioneil nome del mapping è uguale a quello richiesto dalla conversione, se specificato oppure è quello di DEFAULTLast edit: Vittorio 2014-08-06
CLASSI DI ORIGINE E DESTINAZIONE
TUTTE LE CLASSI DEVONO AVER DEFINITO UN MAPPING per poter essere utilizzate con BackBone; questo mapping può essere definito anche vuoto e esternamente tramite file XML, ma deve essere presente.
L'unica eccezione a questa regola sono le super-classi di classi con mapping definito, che non hanno necessità di fare alcun mapping (es.: la classe Object); in questo caso, infatti, sarà possibile omettere la definizione del mapping e verrà automaticamente considerato come se avessero un mapping vuoto.
Se si tenta di fare una conversione da o verso una classe su cui non è stato definito alcun mapping, verrà generato un errore. Questo è valido anche se la classe in questione è generata a runtime da un buildMethod o una factoryClass.
Per spiegare il funzionamento delle mappature, partiamo da un esempio (vedi allegato esempioClassi.png).
In questo esempio abbiamo un albero di classi per un layer MW e una sola classe piatta, corrispondente ad una semplice tabella, per il layer di persistenza.
In questo caso, definiamo il mapping che va da DMammifero a PAnimale che riporta i 3 campi presenti e due mapping specifici per DCane e DUomo, sempre verso PAnimale, che aggiunge il mapping specifico dell'attributo aggiuntivo.
Viceversa, al contrario, definiamo il mapping da PAnimale verso DMammifero, DCane e DUomo, definendo come costruttore un metodo 'buildMethod' in DMammifero (ereditato dai suoi figli) che, data la razza, costruisce la corrispondente classe a runtime.
Non definiamo alcun mapping per le classi DAnimale e DGatto.
In questo esempio deve essere possibile effettuare un mapping da una classe qualsiasi dell'albero MW verso PAnimale, con l'unica eccezione per DGatto che darà errore in quanto non ha mapping definito.
Il fatto che DAnimale non abbia mapping definito non genera errori in quanto non è una classe istanziabile (quindi avrà sempre delle sotto-classi a runtime) e non ha alcun campo da spostare.
In realtà, sarebbe più sensato spostare il buildMethod all'interno di questa classe, anzichè tenerlo in DMAmmifero, cosi questo metodo potrebbe preoccuparsi di creare tutti gli oggetti di tipo DAnimale, anche non figli di DMammifero.
La conversione inversa, invece, permette di creare qualsiasi oggetto di tipo DMammifero, ma NON DI TIPO DAnimale; questo perche, chiamando il metodo map(..) con secondo parametro DAnimale.class, verrebbe generato un errore di mapping non definito.
Chiamando, invece, il metodo map(..) con secondo parametro DMammifero.class, la conversione funziona, a meno che la classe generata non sia DGatto; in questo caso, infatti, non avendo DGatto un mapping definito, verrà lanciata l'eccezione di mapping non definito.
NOTA anche se invoco il metodo map(..) passando la classe DMammifero.class, è ovvio che, a seconda della classe istanziata a runtime (sia essa DCane, DGatto o DUomo) debba essere invocato il corrispettivo mapping; Questo significa che, il mapping deve essere fatto in vari step successivi:
CONCLUSIONI
A conclusione di ciò, ricordando che TUTTE le classi coinvolte nelle conversioni devono avere un mapping definito, si può affermare che, per essere un mapping CONFORME ad una conversione, questo debba avere:
classe di origine uguale alla classe dell'oggetto da convertire
in esempioClassi.png, se provo a convertire DGatto in PAnimale, verrebbe lanciata un'eccezione di mapping non definito.
classe di destinazione uguale alla classe indicata come classe attesa della conversione
Inoltre, in questo caso, dovrà anche essere definito un mapping con classe di destinazione uguale alla classe effettiva dell'oggetto convertito.
Last edit: Vittorio 2014-08-05
SELEZIONE DEL MAPPING PER NOME
Quando si invoca una conversione, si può specificare il nome di un mapping da utilizzare; se viene indicato il nome, il mapping corrispondente deve essere utilizzato, se questo non esiste, deve essere lanciata un'eccezione di mapping non trovato.
Se nessun nome è indicato, viene implicitamente utilizzato il mapping con nome di default; in altre parole è come se l'invocazione della conversione avvenisse indicando come nome del mapping quello di default.
NOTA Poniamo un esempio: una classe ha definito un mapping specifico (con nome diverso da quello di default) e contiene diversi campi al suo interno tra cui, ad esempio, uno dove è definito solo il mapping di default (ad esempio una stringa che deve essere semplicemente riportata in un campo destinazione di tipo stringa).
Nel caso appena citato, la conversione va invocata con il nome del mapping specifico, ma è chiaro che al suo interno deve poter utilizzare i mapping specifici definiti per i vari campi, anche se non hanno lo stesso nome.
Per ovviare a ciò, ogni definizione di mapping si porta dietro un elenco di nomi di mapping da utilizzare come seconde scelte per la conversione dei campi interni; la prima scelta sarà sempre il nome del mapping stesso.
Ne consegue che i metodi di mapping riceveranno NON un nome di mapping singolo, ma un elenco ordinato di nomi; la prima chiamata fatta dall'utente avrà il solo nome del mapping della classe esterna (ma anche l'utente potrà, volendo, indicare una serie di nomi), mentre, per i mapping interni, verranno riportati, oltre al nome del mapping esterno, tutti i nomi indicati come seconde scelte nel mapping stesso.
NOTA se l'utente indica una serie di nomi di mapping, o, in generale, una conversione che riceve un elenco di nomi di mapping dovrà cercare tra tutti i mapping disponibili quello che ha un nume indicato nell'elenco; una vota trovato un mapping valido, ai campi interni verrà passato, come prima scelta, il nome trovato e, come seconde scelte, l'elenco dei mapping aggiuntivi per quel specifico mapping. Tutti gli altri mapping indicati in input alla conversione, a questo punto, verranno scartati e non riportati nelle sotto-conversioni dello specifico campo.
L'elenco delle seconde scelte di una definizione di mapping conterrà il nome del mapping di default, se non diversamente specificato dall'utente;
In questo modo, se l'utente non specifica un comportamento diverso, invocando una conversione di una classe con un determinato nome di mapping, per i campi interni della classe verrà utilizzato il nome del mapping indicato oppure il nome del mapping di default.
Da rilevare anche il caso in cui un campo specifico di una classe debba essere convertito con un mapping particolare, il cui nome non debba essere indicato come seconda scelta per tutta la classe, ma come scelta obbligata per quel specifico campo.
Questo caso verrà gestito indicando, nella definizione di mapping per quel campo specifico, il nome del mapping della classe contenitrice in cui debba essere impiegato.
Questa configurazione, ovviamente, sovrascriverà le impostazioni della classe contenitrice e comporterà che la conversione di quel particolare campo venga invocata con il solo nome di mapping specifico di quel campo (cioò comporta che per le conversioni dei sotto-campi di quel particolare campo, verranno perse le informazioni del mapping della classe contenitrice, riportando solamente il nome del mapping del campo stesso).
Quest'ultimo valore, di default, sarà vuoto, in modo tale da non sovrascrivere, se non espressamente indicato dall'utente, le configurazioni del mapping della classe contenitrice; inoltre, questo valore potrà anche contenere un elenco di mapping che, però, avrà un significato diverso dall'elenco di mapping di una conversione; infatti questo elenco servirà per dire che quel specifico mapping è scelta obbligata per ognuno dei mapping della classe principale in elenco.
NOTA da notare che gli elenchi di mapping devono essere degli elenchi di nomi perarati da un carattere speciale (ad esempio |) che, quindi, dovrà essere identificato come carattere speciale e dovrà dare errore se indicato all'interno del nome di un mapping.
NOMI DEI MAPPING:
AL MOMENTO VIENE IMPLEMENTATO SOLO LA GESTIONE DI UN SINGOLO NOME DI MAPPING
, demandando ad una versione futura l'eventuale gestione di più nomi di mapping.In questa versione, la gestione del nome di mapping è definita come segue:
Il caso base deve essere la possibilità di dare un nome ad un mapping e richiamarlo espressamente tramite un apposito parametro del metodo "map", ma deve rimanere la possibilità di non specificare alcun nome per un mapping (nome di default) e per il metodo "map", lasciando a BackBone l'onere di trovare il mapping migliore.
Date queste due premesse, si hanno i seguenti casi base:
1- Il caso più semplice è una conversione a cui si passa un nome di mapping specifico: in questo caso verrà invocato il mapping con quel determinato nome, se esiste, altrimenti viene lanciato un errore di Mapping Not Found.
2- Se invece, alla conversione non si indica alcun nome, si vuole utilizzare il mapping "migliore", quindi NON verrà applicato alcun filtro per nome.
NOTA NON è possibile richiamare esplicitamente un mapping con nome di default
Una corretta strutturazione del codice consiglierebbe di dare sempre un nome ad ogni mapping, in modo tale che sia possibile utilizzarlo sia in modalità generica, non indicando alcun nome alla conversione, sia in modalità diretta, specificando il nome da utilizzare.
Casi particolari, comunque, come mapping generici, cioè mapping di default da utilizzare solo se non diversamente indicato, ovvero situazioni in cui ci sia uno ed un solo mapping specificato per ogni coppia di classi, potrebbero permettere la definizione di mapping senza alcun nome.
Casi particolari potrebbero verificarsi durante l'esecuzione delle conversioni, tra i quali ci sono i seguenti:
4- Ad una conversione viene indicato espressamente il nome di default; questo caso viene gestito esattamente come se non fosse indicato alcun nome. Questo comportamento è dovuto anche al fatto che, in alcuni casi, nella configurazione tramite XML potrebbe NON essere possibile distinguere tra attributo non specificato e attributo con valore vuoto. In ogni caso, comunque, non si vuole rendere invocabile direttamente un mapping anonimo.
Ulteriori casi legati alle logiche dei nomi riguardano i mapping dei campi interni ad una classe e i mapping padre (gerarchia di mapping).
MAPPING DEI CAMPI INTERNI
Il caso d'uso è il seguente:
Se un campo interno ad un mapping ha necessità di una conversione particolare, posso specificare il nome del mapping da utilizzare nella configurazione del mapping stesso; questo comporta che la conversione di quel particolare campo dovrà ignorare il nome del mapping "esterno" per utilizzare il nuovo nome specificato.
Se, altrimenti, nessun nome viene indicato per il mapping di un campo specifico, significa che quel campo deve essere mappato nel modo migliore, cioè invocando il metodo "map" SENZA ALCUN NOME DI MAPPING SPECIFICATO.
Questo comporta che il nome del mapping utilizzato per il campo figlio possa essere differente dal nome del mapping utilizzato per la classe contenitrice, a meno che non sia espressamente dichiarato in configurazione; in altre parole, per far si che una classe e tutti i suoi campi vengano convertiti con una serie di mapping tutti con lo stesso nome, bisogna indicarlo espressamente nella configurazione.
TODO Da chiarire se ha senso creare una keyword specifica da utilizzare come nome di mapping da usare per un campo figlio, per indicare il nome del mapping della classe contenitrice (in modo che a runtime, per mappare un campo figlio venga usato un mapping con lo stesso nome del mapping usato per mappare la classe esterna) e, di conseguenza, un flag nel mapping della classe esterna che indica di applicare di default questo comportamento a tutti i sotto-campi.
MAPPING PADRE
TODO
Last edit: Vittorio 2015-01-13
MODULI E MAPPING
Il funzionamento di BackBone è basato su una serie di definizioni di mapping pre-configurate e caricate al momento dell'avvio (in uno dei tre livelli: Configuration, SessionFactory o Session) oppure all'interno di una singola transazione (queste ultime verranno perse alla chiusura della transazione).
NOTA ogni livello dei precedenti descritti, ha un suo specifico contesto dove i dati (mapping e moduli) vengono caricati; le modifiche di un livello NON influiscono il livello successivo, se questo è già creato; per allineare la configurazione di un livello inferiore ad uno superiore è necessario CHIUDERE il livello inferiore e rigenerarlo;
sarà, dunque, necessario predisporre un metodo di chiusura di un livello che deve andare ad eliminare anche il riferimento al figlio presente nel livello superiore.
Le definizioni di mapping, per essere eseguite, hanno bisogno di un MODULO.
Ci sono vari tipi di moduli, ognuno con un ser di definizioni di mapping a se stante e con delle logiche di funzionamento specifiche.
Per far funzionare correttamente BackBone, l'utente deve caricare nel contesto (Configuration, SessionFactory o Session) corretto, per prima cosa l'elenco di moduli che intende utilizzare, in seguito dovrà caricare l'elenco di mapping che questo contesto dovrà gestire.
Durante la fase di caricamento dei mapping, BackBone cercherà per ognuno a quale modulo appartiene (tra l'elenco dei moduli caricati), invocando un apposito medoto di CHECK (partendo dal primo modulo caricato, in avanti). Appena un modulo riconosce il mapping come SUO, questo viene compilato, trasformato in un oggetto eseguibile da BackBone e restituito al contesto che provvederà ad archiviarlo in un'apposita cache, associato alla chiave di ricerca (classe origine, classe destinazione, mapping name, modulo).
NOTA gli elenchi di oggetti eseguibili dovranno essere necessariamente suddivisi in differenti liste, a seconda del modulo di appartenenza; questo implica che la chiave diventa (classe origine, classe destinazione, mapping name).
Al momento dell'utilizzo di uno specifico mapping, verrà creata una transazione con un contesto specifico di transazione. Questo contesto erediterà il contesto base con tutti i suoi dati e sarà in grado di CERCARE all'interno di questi dati uno specifico mapping.
Per effettuare questa ricarca, ogni modulo deve essere associato ad un motore di ricerca (restituito da un metodo del modulo stesso, in modo da essere eventualmente configurabile, se necessario). Il contesto di transazione, per ogni modulo presente nel contesto, prenderà il rispettivo Mapping-Finder e, dato in ingresso l'elenco dei mapping caricati, cercherà quello specifico secondo dei criteri privati del modulo stesso (ogni modulo può adottare dei criteri specifici). Questo Finder, dovrà occuparsi di determinare l'elenco dei mapping compatibili con la conversione da fare e filtrarli per priorità (se definita); al termine dell'operazione di ricerca, questo Filter potrà avere uno dei seguenti risultati:
1-NULL: nessun mapping trovato
2-Mapping: trovato uno ed un solo mapping
3-AmbiguousMapping: trovati più di un mapping senza possibilità di scelta di uno prioritario
Nel caso 1, il contesto interrogherà i moduli successivi, se ce ne sono, altrimenti darà l'eccezzione MappingNotFound
Nel caso 2, invece, il contesto utilizzerà il mapping trovato senza interrogare i moduli successivi (ATTENZIONE a caricare correttamente i moduli nel contesto, seguendo il corretto ordine di priorità)
Nel caso 3, infine, verrà interrotta la ricerca e ritornata l'eccezione al chiamante.
NOTA il contesto di transazione NON ha alcuna necessità di gestire moduli aggiuntivi, in quanto non possono essere aggiunti moduli all'interno di una transazione in corsa.
CONFIGURAZIONE DEI MODULI
Ogni modulo deve avere una configurazione a se stante e indipendente da quella di BackBone, in quanto si vuole incentivare la presenza di moduli creati da terze parti e per i quali non è possibile prevedere tutte le possibili configurazioni implementabili.
Al momento dell'aggiunta di un nuovo modulo, deve essere possibile aggiungere una classe già istanziata, come una nuova classe (per nome) istanziata tramite costruttore vuoto o tramite costruttore che prende in input un nome di file di configurazione.
PROPOSTA In questo modo potrebbe essere possibile indicare la configurazione del modulo direttamente dentro l'XML di configurazione di BackBone e passarlo come input al costruttore del modulo al momento della sua creazione.
TIPOLOGIE DI CONTESTI
La gestione dei moduli sopra descritta, identifica due tipologie di contesti:
-Il primo contesto (Configuration, SessionFactory e Session) dovrà essere in grado di mantenere un elenco di definizioni di mapping, un elenco di moduli, le proprietà specifiche del contesto e un elenco di mapping compilati.
-Il secondo tipo di contesto (Transaction) dovrà contenere un contesto normale (NON modificabile) con, inoltre, un elenco di definizioni di mapping specifiche della transazione e un elenco di mapping compilati.
Il contesto di transazione, inoltre, dovrà essere in grado di ricercare una definizione di mapping tra i mapping compilati ed aggiungere un nuovo mapping, se richiesto.
NOTA nella realtà, la ricerca di un mapping avviene sempre date DUE liste di definizioni di mapping; infatti questa ricerca verrà sempre effettuata dentro una transazione che avrà la lista dei mapping di sessione più quella dei mapping di transazione.
Sarà, quindi, necessario definire un nuovo tipo di lista (da utilizzare solo all'interno della transazione) che permetta facilmente di gestire due liste al suo interno.