N° 65 - Luglio 2002

 javabase

Costrutti decisionali Condizioni booleane Blocco di istruzioni if - else if - else annidati if - else concatenati Il costrutto switch - case Espressioni condizionali Conclusioni
Un costrutto decisionale permette al programmatore di vincolare l'esecuzione di un'istruzione (o di un blocco di istruzioni) ad una condizione booleana. Prima di analizzare l'essenza di tali costrutti, è bene chiarire cosa si intenda esattamente con i termini "espressione booleana" e "blocco di istruzioni". Una condizione booleana è un'espressione della quale si può dire se sia vera o falsa (in inglese true o false). Abbiamo già illustrato gli esempi più semplici, che fanno uso degli operatori di uguaglianza, maggiore e minore. In questa sede vale la pena di approfondire la possibilità di combinare le condizioni booleane utilizzando gli operatori logici AND, OR e NOT. In Java per l'operatore AND viene rappresentato dal carattere "&". L'AND logico opera su due parametri, e restituisce true solamente se entrambi sono true. Se vogliamo che l'istruzione x = x + 1 venga eseguita solo se il valore della variabile x è maggiore di 10 e contemporaneamente minore di 100 (ossia compreso tra 10 e 100) scriveremo: if(x >= 10 && x <= 100) x = x + 1; Si noti che nell'esempio l'operatore & viene ripetuto due volte: questa variante dell'AND, denominata short circuit, segnala al calcolatore che può interrompere la valutazione dell'espressione non appena sia stata verificato il suo valore di verità, migliorando l'efficienza di esecuzione. Ad esempio, se durante la valutazione dell'espressione il calcolatore scopre che il primo parametro di una AND è falso, tutta l'espressione risulterà falsa, indipendentemente dal valore del secondo parametro. L'operatore OR, rappresentato in Java con il carattere "|", restituisce true se uno o entrambi i parametri sono veri, mentre restituisce false solamente quando entrambi i parametri sono falsi. Pertanto, se vogliamo che l'istruzione x = x * 2 venga eseguita solo se x e' uguale a 7 o ad 8, dovremo scrivere if ( x == 7 || x == 8 ) x = x * 2; Anche in questo caso si è fatto ricorso all'operatore di short circuit "||" al fine di rendere più efficiente la valutazione: se il primo parametro è vero, l'espressione è vera indipendentemente dal valore del secondo parametro. L'operatore XOR (Or Esclusivo), rappresentato in Java con il carattere "^", restituisce true solo se uno dei due parametri e' vero e l'altro falso; se al contrario i parametri sono entrambi veri o entrambi falsi, l'espressione restituisce false. Si noti che non esiste un operatore shortcut per l'Or Esclusivo, dal momento che è necessario valutare entrambi i parametri per fornire un valore di verità. Infine l'operatore NOT (in Java il carattere "!") permette di negare una qualunque espressione, restituendo in tal modo un valore true se l'espressione è false e viceversa. Si noti che possiamo scrivere espressioni complicate a piacere, combinando tra loro un numero qualunque di espressioni più semplici e ricorrendo alle parentesi per rendere esplicite le precedenze. La seguente istruzione azzera la variabile x se il suo valore è compreso tra 10 e 20 o tra 30 e 40, estremi inclusi: if((x>=10 && x <= 20) || (x >= 30 && x <= 40)) x = 0; Negli esempi visti fino ad ora, l'istruzione if vincolava l'esecuzione di una unica istruzione. Come ci si deve comportare se si desidera fare in modo di vincolare un numero maggiore di istruzioni? In casi come questi si deve definire un blocco, ossia un insieme di istruzioni racchiuso tra parentesi graffe, che vengono trattate dal compilatore Java come se fossero un'istruzione unica. Ecco allora che, se vogliamo azzerare le variabili x, y e z qualora una di esse superi il valore di 100, possiamo scrivere un frammento di codice del tipo: if ( x >= 100 || y >= 100 || z >= 100 ) { // se la condizione e' vera, tutte le // seguenti istruzioni verranno eseguite x = 0; y = 0; z = 0; } Per convenzione, quando si apre una parentesi graffa, le righe successive vengono indentate di un paio di spazi. Tale convenzione è del tutto arbitraria: niente impedisce di riscrivere il frammento di codice precedente in questo modo: if ( x >= 100 || y >= 100 || z >= 100 ){ x = 0; y = 0; z = 0; } Come già visto in precedenza, le convenzioni di impaginazione aiutano a rendere il codice più leggibile, e di conseguenza più facile da correggere o da mantenere. Una particolarità dei blocchi, è che al loro interno è possibile definire delle variabili locali. Tali variabili hanno una ciclo di vita ridotto, che termina non appena il calcolatore esce dal blocco. Pertanto, in un caso come il seguente: if ( x != y ) { int t = x; x = y; y = t; } t = 0; // ERRORE! l'ultima istruzione è errata perché fa riferimento alla variabile t, definita all'interno del precedente blocco, che al di fuori di esso ha cessato di esistere. Il costrutto condizionale più usato in Java è l'if, che può essere usato nelle due varianti con o senza else. Vediamo di ripassarli brevemente if ( condizioneBooleana ) istruzione; esegue l'istruzione se la condizione booleana è vera, mentre prosegue senza fare niente in caso contrario. La variante con l'else ha una forma del tipo: if ( condizioneBooleana ) istruzione1; else istruzione2; e permette di specificare, oltre all'istruzione da eseguire in caso di successo, anche quella da eseguire in caso di fallimento. Come già spiegato nel paragrafo precedente, se vogliamo che venga eseguita più di una istruzione dobbiamo ricorrere ad un blocco: if ( condizioneBooleana ) { istruzione1a; istruzione2a; istruzione3a; } else { istruzione1b; istruzione2b; istruzione3b; } Il costrutto if può comparire anche all'interno di un altro costrutto if, creando strutture nidificate anche molto complesse: vediamo di analizzare alcuni esempi. Partiamo da un frammento di codice con due if concatenati: if( x >= 0 ) if( x <= 10 ) System.out.println("x è compreso tra 0 e 10"); Il primo di questi dice "se la variabile x è maggiore o uguale a 0, esegui l'istruzione seguente"; l'istruzione successiva è a sua volta un if che dice "se la variabile x è minore o uguale a cento, esegui l'istruzione successiva": pertanto la terza istruzione verrà eseguita solamente se entrambe le condizioni precedenti risultano vere. Se a questo punto inserisco un else dopo queste istruzioni, a quale dei due if farà riferimento? Nel linguaggio Java, un'istruzione else fa sempre riferimento all'ultimo if della catena (quello più interno). Per sottolineare il concetto, si usa allineare l'else al corrispondente if: if( x >= 0 ) if( x <= 10 ) System.out.println("x è compreso tra 0 e 10"); else System.out.println("x è maggiore di 10"); Se ora aggiungiamo un ulteriore else, esso farà riferimento al primo if if( x >= 0 ) if( x <= 10 ) System.out.println("x è compreso tra 0 e 10"); else // riprende l'istruzione if( x <= 10 ) System.out.println("x è maggiore di 10"); else // riprende l'istruzione if( x >= 0 ) System.out.println("x è minore di 0"); Come ci dobbiamo comportare se volessimo forzare un else a far riferimento ad un if esterno? Come possiamo rimuovere dall'esempio precedente il primo else, facendo in modo che quello che rimane faccia ancora riferimento all'if più esterno? Per ottenere questo effetto dobbiamo racchiudere l'if più interno in un blocco: in questo modo l'if interno verrà trattato come un'istruzione a se stante, priva di else: if( x >= 0 ) { if( x <= 10 ) System.out.println("x è compreso tra 0 e 10"); } else // riprende l'istruzione if( x >= 0 ) System.out.println("x è minore di 0"); E' buona norma di programmazione evitare di ricorrere pesantemente alla nidificazione di istruzioni if, data la confusione che spesso ne segue. La formulazione di combinazioni condizionali troppo complesse spesso lascia scappare degli errori che è poi molto difficile correggere. Con un pò di ragionamento, di non è difficile produrre una forma più leggibile ricorrendo agli operatori booleani. Ad esempio il frammento di codice if( x >= 0 ) if( x <= 10 ) System.out.println("x è compreso tra 0 e 10"); può tranquillamente essere sostituito dal seguente, in tutto equivalente: if( x >= 0 && x <= 10 ) System.out.println("x è compreso tra 0 e 10"); Un caso più semplice di combinazione condizionale si ha quando si fa seguire ad un else una if. In questo caso, la verità di ogni if prescinde dalla verità di tutte le if precedenti, creando in tal modo una catena di alternative: if( x <= 0 ) System.out.println("x è minore o uguale a 0"); else if( x <= 10) System.out.println("x è maggiore di 0 e minore o uguale a 10"); else if ( x <= 20) System.out.println("x è maggiore di 10 e minore uguale a 20"); else System.out.println("x è maggiore di 20"); Si noti che in questo caso l'ultimo else comprende tutti i casi non considerati dalle precedenti istruzioni. Il costrutto switch permette di gestire tutte quelle situazioni in cui dobbiamo prendere scelte diverse a seconda del valore di un'espressione. switch (espressione) { case val1: istruzione_1a; istruzione_2a; .... istruzione_na; break; case val2: istruzione_1b; istruzione_2b; .... istruzione_nb; break; .... default: istruzione_1default; istruzione_2defefault; .... istruzione_ndefault; break; } L'espressione contenuta tra le parentesi dello switch deve essere di tipo intero (int, byte, short o char); ogni istruzione case lavora su di un particolare valore, e fornisce una sequenza di istruzioni da eseguire in quella circostanza. Tale sequenza termina usualmente con l'istruzione break, che forza il computer a uscire dallo switch, senza verificare i valori sucessivi. Nonostante il break sia opzionale, il suo uso è fortemente consigliato. Dopo aver specificato un numero qualunque di case, si può chiudere l'elenco specificando il blocco di default, ovvero una sequenza di istruzioni da eseguire se non si è verificato nessuno dei casi precedenti. Il blocco default è opzionale, e pertanto verrà inserito solamente nelle circostanze nelle quali risulti necessario. Il seguente esempio stamperà uno specifico messaggio se x vale 1, 2 o 3, mentre stamperà un messaggio di default in tutti gli altri casi. switch (x) { case 1: System.out.println("x è uguale a 1"); break; case 2: System.out.println("x è uguale a 2"); break; case 3: System.out.println("x è uguale a 3"); break; default: System.out.println("x è diverso da 1, 2 e 3"); break; } L'operatore ? può essere usato all'interno di espressioni matematiche, dove una delle sotto espressioni sia vincolata ad una particolare condizione booleana. espressioneBooleana ? espressione1 : espressione2; Vediamo ad esempio un'istruzione che assegna alla variabile y il valore assoluto di x, ossia il valore x se x è positivo, -x se x è negativo: y = x < 0 ? -x : x; Ovviamente è sempre possibile costruire una forma equivalente ricorrendo ad un costrutto if-else: if ( x < 0 ) y = -x; else y = x; L'uso di una forma o di un'altra deve sempre prescindere da criteri di leggibilità del codice risultante. Questo mese abbiamo approfondito l'uso dei costrutti condizionali in Java, costrutti che permettono di vincolare l'esecuzione di un'istruzione ad una particolare condizione. Abbiamo inoltre approfondito l'uso delle condizioni booleane e introdotto l'uso dei blocchi. Il mese prossimo parleremo di costrutti iterativi.

Dopo aver introdotto il concetto di variabile e di array, è giunto il momento di analizzare a fondo i restanti costrutti del linguaggio Java. Come già visto nel...

   di Andrea Gini
 j2me

Il mondo java embedded
VI parte: Il sistema di comunicazione del MIDP il package javax.microedition.io

Il MIDP (Mobile Information Device Profile), come più volte detto nei precedenti articoli apparsi su MokaByte, è dedicato al settore dei dispositivi embedded ed...

   di Marco Tomà
 devj2ee

Introduzione La Business Logic si sposta MVC con EJB: dalle action ai metodi remoti Come integrare i vari prodotti Conclusioni Bibliografia
Dopo gli articoli dei mesi precedenti dedicati alla realizzazione dello strato web tramite il modello MVC, vediamo queso mese come sia possibile integrare il tutto con uno strato applicativo basato su EJB Nei mesi scorsi abbiamo effettuato una introduzione piuttosto approfondita sulla realizzazione di applicazioni web basate sul modello MVC. Il filo conduttore degli articoli fino qui pubblicati è sempre stato quello di mostrare l'importanza di una buona progettazione della architettura complessiva al fine di rendere il più semplice possibile la fase di implementazione e sopratutto di manutenzione. Come forse sarà apparso fin da subito questo modo di lavorare, benché sia estremamente potente e molto flessibile, inzialmente richiede uno sforzo di comprensione forse non sempre banale. I benefici però sono molto importanti e facilmente verficabili quando si deve evolvere verso qualcosa di più complesso: il pattern MVC isola in determinati moduli (il Model, la View ed il Controller) le funzionalità specifiche della applicazione permettendo in tal modo di modificare o sostituire uno solo di tali moduli senza che il resto della applicazione ne sia interessata. Questo mese vedremo come sia semplice e praticamente indolore, migrare la business logic da una struttura basata esclusivamente su servlet/JSP MVC (riprendendo in esame l'esempio dei mesi scorsi la logica era tutta inglobata nelle classi action), alla integrazione di una architettura EJB in cui la business logic sia inserita in enterprise beans in esecuzione all'interno di un container apposito. L'utilizzo della tecnologia EJB introduce un livello di complessità sicuramente molto alto, per cui non sempre tale passaggio è giustificato: per una completa analisi di questi aspetti elencazione sarebbe necessario molto più spazio di quello qui a disposizione. Per brevità ricordiamo semplicemente alcuni concetti fondamentali che possono giustificare l'utilizzo di EJB: Transazionalità sicurezza e solidità: le principali caratteristiche del modello EJB possono essere indispensabili in uno scenario complesso in cui i molti client accedono contemporaneamente alle risorse condivise Riutilizzo: se si spinge ulteriormente il processo di isolamento della Business Logic in componenti remoti, questi potranno essere progettati in modo da essere utilizzati in modo ancora più autonomo dal resto della applicazione, e quindi ad essere utilizzati più volte. Scalabilità: è indubbio che l'utilizzo di un sistema EJB permette di scalare verso l'alto in modo molto più potente di quanto non sia possibile con un applicazione monoblocco. Per maggiori chiarimenti sull'uso di EJB è sufficiente leggere un qualsiasi manuale che spieghi cosa esso sia e perché sia così importante in ogni applicazione enterprise. Questo mese cercheremo di comprendere da un lato come sia possibile integrare una applicazione web con lo strato EJB ed al contempo di vedere alcune possibili scelte che si possono effettuare per progettare al meglio l'applicazione. Come per gli articoli precedenti vorrei ricordare che quanto verrà qui mostrato è frutto di considerazioni limitate al caso in esame, e che sia la parte di progettazione come quella delle tecniche di programmazione non devono essere considerate come il meglio possibile, ma solo come spunti per iniziare ad impratichirsi con queste tecnologie. Come ormai è stato detto più volte detto, l'utilizzo del modello MVC consente di isolare, in classi apposite, tutta la business logic della applicazione. Riconsidendo per un momento il grafico riportato in figura 1 e già pubblicato in precedenza, si potrà notare come le varie Action siano interpellate tutte le volte che deve essere eseguita una operazione non banale (la business logic appunto), eliminando dalle pagine JSP tutto quello che riguarda le operazioni di controllo dei dati, di gestione del database e cose di questo tipo. Figura 1 - L'organizzaizone di una web application secondo il modello MVC prevede l'isolamento della logica di esecuzione in apposite classi Il solo serlvet/JSP container non offre nessun supporto per quanto riguarda la sicurezza, la transazionalità, la persistenza e la resistenza agli incidenti, aspetti per i quali sicuramente EJB è più utile visto che fin dall'inizio è stato pensato appositamente per questi scopi. Il grosso beneficio di aver isolato la business logic in classi opportune permette di spostare la business logic dal servlet container ad un EJB container in cui siano stati installati session ed entity beans. Questo passaggio, se da un lato permette di non stravolgere completamente la struttura della applicazione, richiede alcune importanti considerazioni in modo da scegliere i componenti giusti e la migliore sequenza di operazioni da effettuare. Al fine di semplificare la trattazione si concentrerà l'attenzione sullo stesso esempio già visto in precedenza, ovvero la gestione di una community tramite pagine web: tale sistema deve prevedere la possibilità di effettuare una nuova registrazione, la modifica di un profilo già immesso, la rimozione di un utente ed infine il recupero della password tramite email. In questo caso si limiterà l'attenzione alla fase di registrazione di un nuovo utente: dopo che l'utente ha immesso i propri dati, il sistema effettuerà i dovuti controlli su tali dati e procederà alla registrazione solo se tutti i vincoli imposti su tali dati saranno rispettati. Il flusso della applicazione è riportato sommariamente in figura 2, mentre nella figura successiva è presentato in modo più dettagliato il passaggio delle varie pagine ed azioni secondo lo schema tipico presentato negli articoli precedenti. Figura 2 - grafico sommario del flusso della web application Figura 3 - grafico dettagliato del flusso della web application: in questo caso nei rettangoli verdi sono riportate le associazioni fra i codici di risposta delle action e la pagina da visualizzare, così come il nome del codice di una azione da eseguire ed il nome della classe corrispondente. Riconsiderando quanto visto nei mesi precedenti, tali associazioni sono quelle inserite ad esempio nei file actions.xml o routing.xml Si immagini di utilizzare nella applicazione JSP un oggetto UserCommunity, oggetto che conterrà tutti i campi necessari a rappresentare un utente registrato nella comunità virtuale, come nome, cognome, email, indirizzo fino a id e password. Su alcuni di tali campi si potranno imporre dei vincoli: che l'anno di nascita sia effettivamente un numero, o che la posta elettronica rispetti determinate regole di sintassi; sicuramente si dovrà imporre che lo uid sia unico all'interno della community, ed eventualmente anche la password. Similmente anche la posta elettronica dovrà essere unica, perché altrimenti non si potrà implementare il meccanismo di password-recovery tramite email, tipico di ogni sistema di community. Il rispetto di tali vincoli verrà garantito, al momento della registrazione, da una serie di controlli messi in atto una classe apposita: in questo esempio quindi il metodo perform() della classe AddNewCheckAction.class rappresenta la business logic della applicazione JSP. Anche altre funzioni (e quindi altra business logic) saranno inglobate all'interno della applicazione, ma per il momento è sufficiente concentrarsi su questa classe. Registrazioni concorrenti Nel modello MVC senza EJB il metodo perform() potrebbe non fornire sufficienti garanzie sulla correttezza finale dei dati: ad esempio potrebbe accadere che due utenti effettuino la registrazione in contemporanea, immettendo gli stessi valori per alcuni campi importanti. Semplificando al massimo si potrebbe avere che Thread A corrispondente all'utente A 1. controlla se uid è libero 2. uid è libero 3. controlla altri campi 4. controlla password libera 5. password è libera 6. controlla altri campi 7. procedi alla registrazione nel database dei dati A patto di non implementare tecniche manuali per la gestione della transazione, non si può avere la certezza che le operazioni dal punto 1 al 7 avvengano senza interruzioni e che un altro thread bruci sul tempo tale thread e proceda alla registrazione di un uid dopo che se ne era controllata la disponibilità. Questi e molte altri aspetti sono solo alcuni dei problemi che tipicamente entrano in gioco in scenari concorrenti: per chi fosse interessato ad approfondire tali componenti può eventualmente leggere [jconc] [ejb3]. Risulta chiaro quindi che per il solo supporto transazionale, spostare le operazioni del metodo perform dentro un container EJB potrebbe essere una buona scelta. Per prima cosa si deve cercare di capire quali siano i soggetti in causa nella applicazione web MVC e come trasformarli in modo da integrare EJB: questa scelta non è del tutto semplice ed automatica, dato che coinvolge anche un secondo aspetto fondamentale, ovvero come collegare i due sistemi. A tale scopo si possono considerare due elementi della web application MVC: la classe action che contiene la business logic in esame, ed il bean gestito dalle pagine JSP. Quest'ultimo, nell'esempio in esame il bean CommunityUserBean, non è altro che una raccolta di campi, le proprietà dell'utente, e non ingloba nessuna logica di lavoro. Il suo scopo è infatti raccogliere i vari dati immessi dall'utente tramite form, o viceversa visualizzarli a video. Non è necessario preoccuparsi anche dei servlet che implementano la funzione di controller, dato che non verranno toccati da questo processo di integrazione della logica in EJB. Le action invece verranno sostituite con successo da un unico enterprise session bean, i cui metodi andranno a sostituire i vari perform. Dato che in questo caso la transazionalità è data automaticamente dal container, si può risolvere brillantemente il problema di cui sopra. Ovviamente dato che il sistema JSP dovrà interagire in qualche modo con la parte EJB ed in particolare con i metodi remoti del session, le action continueranno ad esistere anche se svolgeranno la funzione di gateway verso il session bean (figura 4). Figura 4 - Le action che prima racchiudevano la business logic, adesso funzionano come strumento di integrazione fra lo strato EJB e quello JSP. Ogni metodo perform, invocato dall'ActionServlet, provvederà ad inoltrare la chiamata al corrispondente metodo remoto del session bean. Altra importante funzione delle action è quella di prelvare dal contesto i bean da passare come parametri ai metodi remoti, e di reimetterli dopo averli ricevuti indietro modificati. Rimane da chiedersi se utilizzare uno stateless o uno stateful session bean. Questa domanda implica la scelta di chi e come mantiene la sessione con il client remoto, se il container JSP (e quindi utilizzare un session stateless) oppure il container EJB (utilizzando incece un session stateful). Per poter rispondere a questa domanda conviene prendere in esame l'altro componente da trasformare, o meglio da rivedere, il bean CommunityUser. Per quella che la teoria di EJB insegna, un oggetto senza business logic, ma che rappresenta esclusivamente una collezione di proprietà è un ottimo candidato per essere rappresentato da un entity bean. Anche in questo caso non è possibile, ne sarebbe corretto, interagire da una pagina JSP direttamente con un entity; il bean CommunityUser quindi continuerà ad essere presente nella applicazione e continuerà ad essere utilizzato con lo stesso spirito di prima, ovvero come raccoglitori di dati: da una parte le pagine JSP ne popoleranno i campi con istruzioni del tipo <jsp:setProperty name="CommunityUserBean" property="*" /> dall'altro per la visualizzazione si potrà scrivere in una pagina .jsp qualcosa del tipo <b>Nome:</b> <jsp:getProperty name="CommunityUserBean" property="userFirstName"/> Il session bean, dovendo interagire con i dati immessi dall'utente, dovrà accedere ai campi del bean: una soluzione potrebbe essere quella di mantenere la sessione tramite il container JSP, utilizzando l'oggetto CommunityUserBean con scope session. In questo caso il bean verrebbe passato ai metodi del session tutte le volte che sia necessario effettuare determinate operazioni di business logic sui dati di CommunityUserBean. In questo caso il session sarebbe di tipo stateless ed i suoi metdo si potrebbero considerare semplici metodi remoti di servizio. Per capire meglio, il metodo perform() della action AddNewCheckData.class potrebbe diventare // ricava il bean CommunityUserBean, come attributo, // dalla sessione JSP user =(CommunityUserBean)httpServletRequest.getSession() .getAttribute("CommunityUserBean"); // inzializza il contesto EJB Context ctx = new InitialContext(); Object ref = ctx.lookup("CommunityRegistrator"); CommunityRegistratorHome communityRegistratorHome; communityRegistratorHome = (CommunityRegistratorHome) PortableRemoteObject.narrow(ref, CommunityRegistratorHome.class); // si crea un session CommunityRegistratorRemote // per servire lo strato client, JSP CommunityRegistratorRemote registrator = communityRegistratorHome.create(); // invoca il metodo remoto EJB per il controllo dei dati sul CommunityUserBean user=registrator.checkDataUser(user); // ripone nella sessione il bean CommunityUserBean httpServletRequest.getSession() .setAttribute("CommunityUserBean",user); Si noti come la action prima di invocare il metodo remoto, ricavi dalla sessione il bean CommunityUserBean. In questo caso il session bean, in caso di dati non corretti, modifica un suo campo particolare contenente tutti i messaggi di errore relativi ai dati immessi. Dato che tale messaggio se presente dovrà essere visualizzato in una pagina JSP, è necessario riporre il bean modificato nella sessione. Se si fosse voluto mantenere la sessione tramite il container EJB, in modo da sfruttarne le maggiori potenzialità, si sarebbe dovuto utilizzare uno stateful session, tenendo a scope di pagina il bean CommunityUserBean; risulta ovvio in questo caso per ogni modifica ad uno dei campi del bean in una qualsiasi pagina JSP si dovrebbe passare tale oggetto al session, con notevole traffico in rete, considerando che in ogni caso un session è da considerarsi remoto, cosi come le invocazioni dei metodi ed il passaggio dei parametri. Per quanto riguarda l'utilizzo di un entity bean, si deve procedere in modo più accorto. Infatti se si considera ad esempio l'operazione di registrazione di un utente, essa non deve avvenire in un sol colpo: l'utente, dopo aver immesso i propri dati per la registrazione e non ha ancora terminato la procedura di registrazione, potrebbe volere ricontrollare i dati prima del salvataggio finale. Se poter disporre di un bean a scope session nel contesto JSP è una operazione comoda perché permette di portare i dati da una pagina all'altra, creare un entity corrisponde invece parte dei casi alla creazione di una o più righe direttamente nel database e questo potrebbe essere un comportamento non desiderato. Per risolvere questo problema le possibilità possono essere molte, e non è detto che una strada sia completamente sbagliata a confronto di un'altra. Si prenda ad esempio il caso del controllo dei dati utente: se prima il tutto avveniva all'interno del metodo perform della classe AddNewCheck.class, adesso tali controlli si sono spostati all'interno del metodo checkDataUser(). Anche se tale metodo è transazionale, l'invocazione dei metodi da parte del container JSP avviene in modo prettamente asincrono e la lentezza della rete internet non fa che amplificare il concetto di asincronia delle operazioni effettuate da un utente. Il problema è presente in ogni applicazione basata su interfaccia utente, dove si ipotizza che l'operazione di controllo dei dati sia separata da quella di salvataggio finale: dopo l'immissione dei dati l'utente vuole sempre ricontrollare i dati immessi prima del salvataggio finale. Per questo checkDataUser() e saveDataUser()saranno due metodi separati del session bean e sebbene sia possibile, è fortemente sconsigliato dar vita a transazioni che ricadano su più metodi di un session. Riassumendo il problema la sequenza delle operazioni potrebbero essere: Un utene A inizia la procedura di registrazione. Viene creato per lui un nuovo contesto, corrispondente ad una nuova sessione A; la action JSP invoca il metodo remoto del session bean per controllare fra le altre cose se uid=pippo è disponibile; il metodo del session bean effettua molti controlli, fra cui uid=pippo; uid=pippo non è stato assegnato a nessun utente; l'utente A, per un qualsiasi motivo ritarda la conclusione della registrazione: il collegamento ad internet potrebbe essere improvvisamente rallentato oppure l'utente sospende per rispondere al telefono; un altro utente B, associato alla sessione B, esegue tutte le operazioni precedenti, e sceglie la uid=pippo. Tale valore è ancora disponibile; si procede con la registrazione; l'utente della sessione B conclude la registrazione con uid=pippo; viene creato un entity RemoteCommunityUser utilizzando i dati presenti nel bean JSP CommunityUser. Nel database adesso è presente un record in cui uid=pippo. L'utente della sessione A riprende la registrazione e la conclude con uid=pippo credendo che sia ancora disponibile. Viene creato un entity RemoteCommunityUser utilizzando i dati presenti nel bean JSP CommunityUser, ma i dati sono sovrapposti. Si noti come al punto 9 per l'utente A non viene infatti rieffettuato nessun controllo sulla disponibilità di uid, dato che in ogni caso non si potrebbe avere la certezza matematica che dopo il controllo non si verifichino altre pause (sempre dando per ipotesi che check e save siano due procedure separate temporalmente). In questo caso il metodo checkDataUser() potrebbe essere ... if (UserBean.getUserFirstName()!=null && UserBean.getUserFirstName().length()==0){ UserBean.addErrorMessage("Nome utente non valido: "+UserBean.getUserFirstName()+";"); } ... Context ctx = new InitialContext(); Object ref = ctx.lookup("CommunityUser"); CommunityUserHome remoteuser; remoteuser = (CommunityUserHome) PortableRemoteObject.narrow(ref,CommunityUserHome.class); try { CommunityUserRemote UserfindById = remoteuser.findByPrimaryKey(xxx, "pippo"); // Se si arriva qui c'è già un utente UserBean.addErrorMessage("UID "+UserBean.getUserId()+ "assegnato ad un altro utente"); } catch (Exception ex) { ... } Il controllo sulla presenza di un utente con uid=pippo è stato fatto effettuando una ricerca con il metodo findByPrimaryKey() al quale è stato passato l'id da controllare, oltre ad altri parametri necessari per la ricerca. Questo scenario, tanto frequente (facilmente risolvibile anche senza particolari supporti EJB tramite l'utilizzo di flag nel database), potrebbe essere risolto tramite la creazione al punto 2 di un entity il cui valore del campo uid sia pippo. Questa operazione impedirebbe ad una seconda ricerca di avere risposta negativa, anche se ci si trovasse nella condizione di cui prima. Il codice appena visto quindi, dopo aver effettuato una findByPrimaryKey(), potrebbe procedere alla create() dell'entity. In questo caso, anche se l'utente interrompese la procedura di registrazione, nel database si troverebbe un record di un utente fantasma, ed il valore pippo non sarebbe più disponibile anche se non associato a nessun utente. L'utilizzo di un flag status come campo dell'entity RemoteCommunityUser, da impostare a "saved" a procedura completata potrebbe evitare l'insorgere di utenti fantasma nel database, avendo ovviamente l'accortezza di rimuovere periodicamente o all'abbandono dell'utente, tutti record che abbiano status=temporary. L'utilizzo dei bean temporizzati (ovvero in grado di eseguire certe operazioni a determinati intervalli di tempo) disponibili dalla prossima versione della specifica EJB potrebbero risolvere questo problema in modo semplice. Volendo implementare controlli di unicità anche sugli altri campi, sarà sufficiente procedere in modo analogo, dopo aver fornito l'entity bean di metodi di ricerca opportuni, come findByEmail() o findByPassword(). A fine procedura il bean della sessione JSP invocherà il metodo saveUser() del session bean in modo da rendere definitiva la registrazione. Nel caso in cui si sia utilizzato un entity fin dall'inizio, tale metodo non dovrà far altro che impostare status ad un valore divero da temporary, convenzionalmente potrebbe essere saved. Nel caso in cui invece non si sia creato l'entity, lo si dovrà fare in questa sede. In entrambi i casi la creazione del bean entity produce la scrittura nel database dei dati utente, permettendo di disinteressarsi delle problematiche di accesso ai dati che invece saranno relegate nei metodi ejbStore() o ejbLoad() dell'entity. Molti sconsigliano un uso pesante o sconsigliano del tutto gli entity beans, ed in uno scenario come questo l'utilizzo di un flag nel database porterebbe ad un risultato analogo, rimandando la creazione dell'entity. Per quanto riguarda l'integrazione fra i due container, il JSP e EJB, si dovrebbe scendere molto nel dettaglio dell'utilizzo dei prodotti scelti per questo tipo di applicazione. Anche se non è possibile fare una trattazione esauriente in uno spazio umanamente gestibile, si tenga presente che il punto di contatto fra il session bean e la action che ne invoca i metodi remoti, è dato da una lookup tramite un sistema di naming JNDI. A tal proposito, una cosa che non è forse stata rimarcata è che la action dovrebbe sempre interagire con un enterprise bean di tipo session e mai con un entity direttamente. Il session infatti è il servitore fedele di ogni applicazione client, ed esporta verso il mondo esterno tutte le funzionalità di logica o di gestione dati che sono inglobate nello strato EJB. E' interessante notare come, proprio grazie all'utilizzo di un pattern potente ed universalmente accettato come l'MVC, la parte di interfaccia grafica, qui costituita dallo strato web servlet/JSP, potrebbe essere sostituita da una applicazione stand alone con gui Swing. La business logic non cambia ed anzi essendo inglobata in un robusto sistema basato su EJB, potrà essere utilizzata in contesti differenti senza troppe modifiche. E' vero però che, per permettere una così alta flessibilità e potenza, lo strato EJB dovrebbe essere modellato a sua volta in strati e sottostrati. Ad esempio a volte si indica una buona architettura come costituita da 4 livelli EJB, in cui i livelli più bassi sono legati alle funzionalità universalmente utilizzate, mentre quelli dei livelli più alti saranno modellati per il caso in esame. Nella peggiore delle ipotesi, volendo riutilizzare la parte EJB in un contesto completamente differente da quello per cui era stato pensato originariaente, sarà sufficiente modificare al più il primo strato EJB, quello relativo allo use case in esame. Quello che si è appena visto in questo articolo è solo un piccolo esempio di come EJB possa essere utilizzato concretamente all'interno di una applicazione più strutturata e che preveda l'interazione con l'utente finale tramite interfaccia grafica. Ovviamente per un lettore esperto, quanto visto potrebbe apparire scontato, e per certi versi banale: purtroppo per poter scendere ad un livello di approfondimento maggiore sarebbe necessario molto più spazio e tempo di quello a disposizione qui questo mese. Questi aspetti sono molto importanti, in genere non si trovano nella bibliografia ufficiale che si limita ad esporre la teoria di EJB in modo asettico, e sono invece il pane quotidiano di un buon progettista. E' praticamente impossibile poter trattare in modo universale tali concetti, ma nei prossimi mesi si cercherà di dare un po' di indicazioni anche in tal senso. [jconc] "Concurrent Programming in Java(TM): Design Principles and Pattern (2nd Edition)" di Doug Lea, Co; ISBN: 0201310090 [ejb3] "Enterprise Java Beans" di Richard Monson-Haefel, Ed. O'Reilly ISBN: 0596002262;

Dopo gli articoli dei mesi precedenti dedicati alla realizzazione dello strato web tramite il modello MVC, vediamo queso mese come sia possibile integrare il tu...

   di Giovanni Puliti
 pattern

J2EE Patterns
Il Pattern Business Delegate

Da completare.

   di Stefano Rossini & Luca Dozio
 soapchat

Una Chat basata su SOAP - parte terza

In questo numero si conclude la miniserie dedicata alla Chat basata su SOAP, con la realizzazione del codice client. Una volta costruita la Servlet JAXM che fun...

   di Massimiliano Bigatti

MokaByte® è un marchio registrato da MokaByte s.r.l.
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale
Per comunicazioni inviare una mail a info@mokabyte.it