Dopo aver introdotto il framework Tide, vedremo in questo articolo tutti i passi da seguire per creare un semplice progetto che mostri l’integrazione tramite Tide di un server con tecnologia JBoss Seam ed il client in Flex.
Introduzione
In questo articolo creeremo da zero un semplice progetto tramite Eclipse e Flex Builder. Per i nostri scopi utilizzeremo i seguenti strumenti:
- Eclipse Galileo (versione 3.5.2)
- JBoss Tools (versione 3.1.0.GA)
- Flex Builder 3.0.0
- Granite Eclipse Builder 2.1.0
- JBoss Seam 2.2.0.GA
Utilizzeremo inoltre l’application server JBoss 4.2.3.GA configurato come server runtime in JBoss Tools. L’applicazione che andremo a creare sarà una rivisitazione di un’applicazione che abbiamo già visto in un articolo precedente della serie [1]. L’applicazione si occuperà di gestire il salvataggio, la modifica e la visualizzazione di una serie di prodotti, tramite una semplice interfaccia composta da una gliglia dati, che mostra tutti i prodotti caricati nel nostro database, e un pulsante in alto che permette l’inserimento di un nuovo prodotto. Ogni riga ha inoltre un piccolo pannello con due pulsanti, uno per aprire in modifica il prodotto in questione e uno per eliminarlo.
Creiamo il progetto Seam
Figura 1 – Nuovo Seam Web Project.
Nel passaggio successivo inseriamo il nome e lasciamo invariati tutti gli altri valori, assicurandoci che in Configuration sia impostato Dynamic Web Project con Seam 2.2:
Figura 2 – Wizard, impostazione dei parametri fondamentali.
Nei successivi 3 passaggi accettiamo i valori di default, e andiamo avanti fino ad arrivare al passaggio in cui occorre scegliere il tipo di pacchetto da creare. Qui scegliamo di creare un pacchetto EAR e selezioniamo il profilo di connessione al database MySQL (se non esiste lo creiamo).
Figura 3 – Wizard, impostazione dei parametri fondamentali.
Concludiamo il wizard cliccando su Finish.
Progetto Client in Flex Builder
Dentro il progetto precedentemente creato con Eclipse, creiamo una cartella, Flex, nel pacchetto GDSTide dove andremo a collocare il progetto Flex Builder.
Figura 4 – Cartella per il progetto del Client.
Successivamente apriamo Flex Builder e creiamo un nuovo Flex Project selezionando come location la cartella precedentemente creata all’interno del progetto Eclipse.
Figura 5 – Nuovo Flex Project
Nel passaggio successivo impostiamo come output folder direttamente la cartella WebContent del progetto Eclipse:
Figura 6 – Output folder del progetto Flex Builder.
E concludiamo il wizard impostando, come cartella per i sorgenti, flex_src.
GraniteDS Nature
Torniamo su Eclipse, clicchiamo con il tasto destro sul pacchetto GDSTide-ejb e selezioniamo Add GraniteDS Nature per avviare un altro wizard tramite il quale forniremo al Gas3 le informazioni neccessarie per creare le classi AS3 corrispondenti alle classi Java degli oggetti che verranno scambiati tra server e client.
Figura 7 – GraniteDS Nature
Gli oggetti di nostro interesse sono gli entity.
Figura 8 – Selezione del percorso contenente le classi da ricreare in AS3.
Impostiamo un filtro in maniera tale da selezionare solo gli entity, e indichiamo anche il percorso ove andare a posizionare le classi AS3 generate, Flex/flex_src del pacchetto principale (GDSTide).
Figura 9 – Percorso dove posizionare le classi AS3.
Nei due passaggi successivi accettiamo i valori di default, e andiamo avanti fino all’ultimo passaggio in cui impostiamo il template da utilizzare per la creazione delle classi AS3. Qui clicchiamo sul tasto Use Tide.
Figura 10 – Scelta del Template.
File di configurazione
Completiamo il progetto con le modifiche ad alcuni file di configurazione. Iniziamo dal file components.xml (nel pacchetto principale sotto la cartella WebContent/WEB-INF) aggiungendo le righe in grassetto
components.xml
xmlns_core="http://jboss.com/products/seam/core" ... xsi_schemaLocation="http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.2.xsd http://jboss.com/products/seam/persistence http://jboss.com/products/seam/persistence-2.2.xsd http://jboss.com/products/seam/drools http://jboss.com/products/seam/drools-2.2.xsd http://jboss.com/products/seam/bpm http://jboss.com/products/seam/bpm-2.2.xsd http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.2.xsd http://jboss.com/products/seam/mail http://jboss.com/products/seam/mail-2.2.xsd http://jboss.com/products/seam/web http://jboss.com/products/seam/web-2.2.xsd http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.2.xsd http://www.graniteds.org/config http://www.graniteds.org/public/dtd/2.1.0/granite-config-2.1.xsd"> ...
Aggiungiamo adesso il file granite-config.xml nella cartella WebContent/WEB-INF/granite del pacchetto principale. Qui attiviamo i componenti Seam, generati dal Seam Tools wizard, per la comunicazione con Tide. Questa parte è opzionale perchè basterebbe annotare i componenti da invocare con RemoteDestination o TideEnabled.
granite-config.xml
//DTD granite-config internal//EN" "http://www.graniteds.org/public/dtd/2.0.0/granite-config.dtd">
Aggiungiamo anche il file services-config.xml nella cartella WebContent/WEB-INF/flex del pacchetto principale, dove andremo a definire l’unica destinazione necessaria:
services-config.xml
class="flex.messaging.services.RemotingService" messageTypes="flex.messaging.messages.RemotingMessage"> tideSeamFactory ...
Tide può essere usato anche senza definire il file standard di configurazione services-config.xml. Infatti, dal momento che Tide incapsula un RemoteObject, è possibile definire programmaticamente il canale di comunicazione tramite l’uso del DefaultServiceInitializer implementato nel Tide Context
<!CDATA[ import org.granite.tide.service.DefaultServiceInitializer; Tide.getInstance().addComponent("serviceInitializer", DefaultServiceInitializer,{contextRoot: "/ context-root " }); ]]> ...
Aggiungiamo adesso le librerie granite.jar, granite-hibernate.jar e granite-seam21.jar nella cartella EarContent/lib del pacchetto GDSTide-ear (server), e granite-essentials.swc e granite.swc nella cartella Flex/libs del pacchetto GDSTide(client).
Per finire su FlexBuilder, dobbiamo impostare alcuni parametri di compilazione (tutto sulla stessa linea, senza a capo), ossia
-keep-as3-metadata=Bindable,Managed,ChangeEvent, NonCommittingChangeEvent, Transient,Id,Version,Name,In,Inject,Out,Observer,ManagedEvent,Destroy -includes=org.domain.gdstide.entity.Product -include-libraries "D:/Applicazioni/Java/eclipse-jee-galileo-SR2- win32/eclipse/workspace/GDSTide/Flex/libs/granite-essentials.swc" "D:/Applicazioni/Java/eclipse-jee-galileo-SR2-win32/eclipse /workspace/GDSTide/Flex/libs/granite.swc" -locale en_US -services "D:/Applicazioni/Java/eclipse-jee-galileo-SR2-win32/eclipse/workspace /GDSTide/WebContent/WEB-INF/flex/services-config.xml" -context-root "/GDSTide"
Figura 11 – Parametri di compilazione.
Per finire aggiungiamo il progetto alla lista di progetti da pubblicare sul server JBoss 4.2.3.
Figura 12 – Publish del progetto su JBoss.
Va notato che tutto quello che abbiamo fin qui creato può essere utilizzato come modello per i futuri progetti con Tide.
E ora scriviamo il codice
Utilizziamo il wizard Seam di Eclipse per creare l’entity Product. Il wizard creerà la classe Product e due componenti Seam ProductList and ProductHome. La classe Product avrà il codice seguente
package org.domain.gdstide.entity; import java.io.Serializable; ... @Entity @EntityListeners(DataListener.class) public class Product implements Serializable, org.granite.tide.IUID { @Id @GeneratedValue private Long id; @Version private Integer version; @Basic private String uid; @Basic private String name; @Basic private String descr; @Basic private Double price; @Basic private Integer quantity; public Long getId() { return id; } public Integer getVersion() { return version; } public String getUid() { return uid; } public String getName() { return name; } ... }
Nel codice sopra riportato ho messo in evidenza in grassetto due linee di codice (private Long id; e private String uid;) perchè necessitano di un approfondimento. In un’applicazione tipica a tre livelli, Flex, Business Layer, e DataBase, un’oggetto vive in tutti i tre livelli:
- il client Flex
- il contesto di persistenza Hibernate/JPA
- il DataBase
Durante la vita dell’oggetto, l’unico elemento invariante è l’id. L’id lega le differenti versioni dello stesso oggetto nei tre livelli. Quando si lavora su oggetti esistenti, o creati a livello di database, non ci sono problemi perchè l’id è già impostato e viene mantenuto invariato in tutti e tre i livelli durante le operazioni di serializzazione e deserializzazione.
Un problema si può avere quando un’oggetto viene creato nei due livelli superiori. Il nuovo oggetto non avrà un id fino a quando non verrà salvato nel DB e questo significa che tra la creazione dell’oggetto e il suo salvataggio, l’id passerà dal valore null a un valore reale. In questo modo non è possibile avere un legame tra l’oggetto che è stato creato e l’oggetto successivamente salvato.
La soluzione più comune è quella di creare un ulteriore id, l’uid, che viene creato e impostato dal client e salvato assieme all’oggetto. Quando ciò non è possibile, perchè stiamo lavorando su applicativi server già operativi e che non prevedevano tale proprietà, bisogna considerare che si possono verificare dei problemi nel creare degli oggetti da Flex.
Assicuriamoci che, nella cartella Flex/flex_src, sia stata creata la classe AS3 corrispondente. Poiche’ non useremo le conversations, in ProductHome, commentiamo il metodo create.
Passiamo ora al Client Flex. L’applicazione avrà lo stesso funzionamento che aveva nell’esempio del precedente articolo [1], ma la modificheremo per mostrare le caratteristiche di Tide (modifiche evidenziate in grassetto).
Scriviamo la pagina principale, che consisterà di un DataGrid e di alcuni tasti
layout="absolute" preinitialize="Seam.getInstance().initApplication()"> import org.granite.tide.events.TideUIEvent; import org.granite.tide.service.DefaultServiceInitializer; import org.granite.tide.seam.Seam; import org.granite.tide.seam.Context; import org.granite.tide.seam.framework.PagedQuery; import org.domain.gdstide.ProductsCtl; Seam.getInstance().addComponents([ProductsCtl]); Seam.getInstance().addComponentWithFactory( "productList", PagedQuery, { maxResults: 36 } ); [Bindable] [In] public var productList:PagedQuery; fontSize="16" color="#E9F3F5"/> bottom="10" right="10" top="68" left="10"> headerText="Manage" itemRenderer="ManageRenderer"/> click="dispatchEvent(new TideUIEvent('createProduct'));" right="10" top="38"/>
Diamo un’occhiata al codice evidenziato in grassetto
preinitialize="Seam.getInstance().initApplication()"
Inizializziamo il Tide context e registriamo l’MXML principale, e così anche tutti gli altri annotati con [Name]. Possiamo ora lanciare e intercettare eventi e usare l’injection. L’oggetto Context è il cuore di Tide e costituisce, per la parte client, la rappresentazione del server. Viene salvato in un’istanza singleton per cui è accessibile in qualsiasi punto dell’applicativo Flex. I componenti server possono essere referenziati tramite i loro nomi come proprietà del Context.
Seam.getInstance().addComponents([ProductsCtl]);
Registriamo nel context un componente che vedremo un pò più avanti.
Seam.getInstance().addComponentWithFactory("productList",PagedQuery,{maxResults:36});
Registriamo il componente productList come componente PagedQuery di Flex, ovvero una collezione in grado di interagire con gli oggetti lato server Query
[Bindable] [In] public var productList:PagedQuery;
Questa è una variabile injected
dispatchEvent(new TideUIEvent('createProduct'))
Viene lanciato un evento nel contesto Tide che verrà segnalato a tutti gli osservatori interessati a questo evento.
Vediamo le modifiche da apportare al form che ci consente di editare i prodotti:
layout="absolute" width="400" height="300" title="Products"> [Name("productEditor")] import org.granite.tide.events.TideUIEvent; import org.domain.gdstide.entity.Product; [Bindable] [In] public var product:Product; private function save():void { ... } ... click="save();"/> click="dispatchEvent(new TideUIEvent('closeProductEditor'))"/>
Questo MXML verrà registrato automaticamente nel Tide Context perchè annotato con [Name].
Vediamo infine parte della classe ActionScript che fungerà da controller, ProductCtl, dove, oltre a delle variabili che verranno injected ed outjected, ci sono i metodi Observer che verranno eseguiti al verificarsi di determinati eventi:
package org.domain.gdstide{ import flash.display.DisplayObject; ... [Name("productsCtl")] public class ProductsCtl { [In] public var application:DisplayObject; [Bindable] [In] [Out] public var product:Product = null; [In] public var productHome:Object; [In] public var productList:Object; private var productEditor:ProductEditor; [Observer("createProduct")] public function createProduct():void { product = new Product(); product.quantity=0; product.price=0; productEditor = PopUpManager.createPopUp(application, ProductEditor, true) as ProductEditor; PopUpManager.centerPopUp(productEditor); } ... }
Conclusioni
Con questo articolo, in cui abbiamo visto un esempio completo di utilizzo del framework GDS e Tide, concludiamo questa serie rivolta all’integrazione tra il mondo Flex e il mondo Java. Per coloro che intendono approfondire la tematica, molte informazioni possono essere trovate nella ricca e completa documentazione presente nel sito ufficiale del progetto [2] e nel blog [3].
Riferimenti
[1] Angelo Ferraro, “GraniteDS: Flex incontra Java – II parte: EJB3 con Externalization”, MokaByte 149, Marzo 2010
[2] Il sito ufficiale del progetto GraniteDS
[3] GraniteDS Blog