home >> mb149
di

Angelo Ferraro

Nella stessa sezione>
gxt-2
cloud-2
graniteds-2
mobileapps-2
ws_spring-3
aspettisociali-1
Allegati>
graniteds_esem...
Nella stessa serie
graniteds-1
graniteds-2
graniteds-3
graniteds-4
News on Feed


Bookmarking
bookmark on Delicious bookmark on Digg bookmark on Furl bookmark on Reddit bookmark on Slashdot bookmark on Technorati
 
 


MokaByte 149 - Marzo 2010

GraniteDS: Flex incontra Java

II parte: EJB3 con Externalization

Nel precedente articolo, abbiamo fatto una panoramica sulle caratteristiche principali di GraniteDS. In questo articolo e in quelli che seguiranno, iniziamo a vedere, tramite esempi pratici, come usare tale framework per integrare applicativi Flex e Java.

Introduzione

Abbiamo già visto le caratteristiche principali di GraniteDS, un framework open source che facilita l’integrazione tra Flex e Java. Iniziamo a vedere ora i passi per installare GraniteDS e configurarlo al meglio. Creeremo una semplice applicazione Flex di esempio che diverrà l’interfaccia web della nostra applicazione lato server in Java. Nel caso di interfacce semplici, è possibile compilare i sorgenti Flash ed ottenere il file SWF utilizzando un editor per la scrittura e i task ant presenti nell’SDK per la compilazione. Ovviamente, nel caso di interfacce più elaborate, lavorare con un semplice editor diventa un limite, per cui nasce l’esigenza di usare strumenti più potenti. Proprio per questo creeremo e configureremo un progetto in Flex Builder 3, l’IDE di Adobe costruito su Eclipse (e quindi di facile utilizzo per tanti programmatori Java).

L’applicazione di esempio

In questo articolo concentreremo la nostra attenzione nel tentativo di integrare l’interfaccia web (Flash) con un server basato su EJB. L’applicazione che andremo a creare si occuperà di gestire il salvataggio, la modifica e la visualizzazione di una serie di prodotti, e per far questo utilizzeremo Eclipse. Come è possibile vedere dalle figure seguenti, l’interfaccia è molto semplice. Abbiamo una semplice 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 ed uno per eliminarlo.
Quando apriamo la pagina web dell’applicazione, la prima operazione effettuata è il caricamento di tutti i prodotti presenti nel database.

Figura 1 - Finestra principale dell’interfaccia web.



Altre operazioni che dovremo implementare saranno poi l’inserimento, la modifica e la cancellazione di un prodotto

Figura 2 - Finestra principale dell’interfaccia web.



Lato server, creeremo un business layer in EJB, ovvero utilizzeremo dei Session Bean per implementare la logica dell’applicativo e un Entity Bean per rappresentare i dati. Come persistence provider utilizzeremo Hibernate.

Server Side

Per implementare la logica della nostra applicazione occorre creare l’entity bean che rappresenti i dati.

Questo entity avrà un campo, id, a sola lettura, auto incremented e primary key nel database, più una serie di campi modificabili. La struttura sarà del tipo

@Entity
@Table(name = "product")
@NamedQueries({...})
public class Product implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;
    @Column(name = "name")
    private String name;
    @Column(name = "descr")
    private String descr;
    @Column(name = "price")
    private Double price;
    @Column(name = "quantity")
    private Integer quantity;
         public Product(Integer id) {
        this.id = id;
     }
    //Metodi Getter e Setter
    ...
}

Notiamo che, tra il back end ed il front end, oltre all’invocazione dei metodi di business logic, ci sarà uno scambio di oggetti di questo tipo (o di collezione degli stessi). Questo comporterà che, per quanto detto nel precedente articolo a proposito degli externalizers, sarà necessario ricreare lato client un oggetto Flash da mappare, in tutte le operazioni di serializzazione e deserializzazione, ad ognuno di questi oggetti Java. Per fare questo utilizzeremo il generatore automatico di codice Gas3 (sezione successiva).
Passiamo alla logica dell’applicativo che dovrà consentire la lettura di tutti i prodotti dal DB, l’inserimento, la modifica e la cancellazione di un prodotto. Quindi l’interfaccia remota o locale dovrà esporre questi metodi:

...
public interface ProductService {
    public List<Product> findProductList();
    public Product saveProduct(Product p);
    public int deleteProduct(Product p);
}

L’implementazione la faremo con uno Stateless che userà l’interfaccia precedente

...
@Stateless
@Local(ProductService.class)
public class ProductServiceBean implements ProductService {    
    @PersistenceContext(unitName = "GranitedsUnit")
     protected EntityManager manager;
    @SuppressWarnings("unchecked")
    @Override
    public List<Product> findProductList() {
        Query query = manager.createNamedQuery("Product.findAll");
        return query.getResultList();
    }

    @Override
    public Product saveProduct(Product p) {
        try {
            return p = manager.merge(p);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;    
    }
         @Override
    public int deleteProduct(Product p) {
        try {
            Product a = manager.find(Product.class,p.getId());
            if(a!=null) manager.remove(a);
            return 0;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return -1;    
    }
}

Avendo deciso di creare il front end in Flex, e di voler utilizzare il framework GDS, occorre effettuare il download delle librerie necessarie, ovvero di "granite.jar", "granite-hibernate.jar" e "granite-essentials.swc", "granite.swc" [1]. Per far questo preleviamo l’ultima versione del progetto, che attualmente è "graniteds-2.1.0.GA.zip". Le librerie richieste si trovano nella cartella build del progetto; copiamo i jar nella cartella "WEB-INF/lib" del nostro progetto Eclipse, e i file swc nella cartella Flex/libs (la cartella Flex interna al progetto Eclipse è quella che conterrà poi il progetto Flex).

Gas3

Come abbiamo detto precedentemente, per deserializzare campi protetti o privati occorre usare il meccanismo della externalization. Con GraniteDS è possibile effettuare l’externalization dei nostri bean in automatico, senza cioè modificarli manualmente per fare in modo che implementino l’interfaccia "java.io.Externalizable". L’externalzation e la scelta delle classi da esternalizzare verrà fatta successivamente attraverso i file di configurazione. Inoltre grazie al generatore di codice Gas3 possiamo scrivere in automatico anche le classi ActionScript3 che fungono da controparte ai nostri bean.
Vediamo come procedere con il nostro esempio. La prima cosa da fare è effettuare il download del generatore Gas3, presente sia come Ant task che come plugin per Eclipse [1]. Occorre prelevare l’ultima versione attualmente disponibile, "org.granite.builder_2.1.0.GA.jar", e installarlo copiandolo semplicemente nella cartella plugins di Eclipse (dopo aver cancellato eventuali versioni precedenti). Riavviando Eclipse e cliccando col tasto destro sul nostro progetto, troviamo la voce "Add GraniteDS Nature" nel menu contestuale.
Cliccando su questa voce viene avviato un wizard che ci accompagna nel processo di creazione delle classi ActionScript3. Nel primo passaggio, cliccando su "Add Folder", scegliamo le cartelle contenenti le classi da riprodurre in AS3 (nel nostro caso solo la cartella "src" di "common").

Figura 3 - Scelta delle classi da riprodurre in AS3.



Dopo aver scelto le cartelle, occorre definire dei filtri che ci consentono di selezionare solo i file che ci interessa riprodurre, ovvero gli entity. Per fare questo selezioniamo il nodo "Included" e clicchiamo su "Edit". In questa finestra è possibile impostare la cartella di destinazione dei file generati, quindi, poichè nel nostro caso il progetto Flex Builder verrà messo in una sottocartella del progetto eclipse, impostiamolo a "Flex/src".

Figura 4 - Impostazione dei filtri e della cartella di destinazione.

 

Clicchiamo su Finish per generare le classi AS3. Da questo momento in poi, qualsiasi modifica fatta agli entity provocherà l’avvio del processo di generazione e quindi l’aggiornamento della classi AS3.
Se andiamo a vedere nella cartella specificata, troviamo i file generati. Nel nostro caso l’unica classe che soddisfa i criteri è "Product.java", e quindi troveremo 2 file "Product.as" e "ProductBase.as". Nel caso in cui si rende necessario l’inserimento di codice AS3, è necessario inserirlo in "Product.as" perchè la classe "ProductBase.as" verrà rimpiazzato ogni qualvolta il processo di generazione viene innescato.

Figura 5 - File AS3 generati.

 

Front End

Per creare il front end, utilizzeremo l’ambiente Flex Builder. Creiamo un nuovo progetto Flex, impostando come "Location", la cartella precedentemente utilizzata per generare le classi AS3 (la cartella Flex sotto il progetto Eclipse) e assicuriamoci di settare il campo "Application Server Type" a  "None".
Cliccando col tasto destro sul progetto andiamo su "Proprietà" e dopo su "Flex Compiler" per settare alcuni parametri di compilazione. Nel campo "Additional compiler arguments" inseriamo

-includes=flex.samples.common.entity.Product  -include-libraries
"D:/Applicazioni/Java/eclipse/workspace/GDSEjb/Flex/libs/granite-essentials.swc"
"D:/Applicazioni/Java/eclipse/workspace/GDSEjb/Flex/libs/granite.swc"
-locale en_US
-services "D:/Applicazioni/Java/eclipse/workspace
       /GDSEjb/Web/public/WEB-INF/flex/services-config.xml" -context-root "/GDSEjb"

In tal modo oltre ad impostare alcuni parametri ("locale", "services", e "context-root"), obblighiamo il compilatore a inserire nell’swf la classe "Product" e i due componenti (in realtà "granite.swc" potrebbe essere semplicemente linkata).

Nel file principale del progetto disegniamo l’interfaccia e definiamo un "RemoteObject" che successivamente legheremo al session bean attraverso i file di configurazione. Il codice AS3, che si occupa di gestire gli eventi associati ai componenti dell’interfaccia, si trova nel file "ProductAdmin.as".

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
creationComplete="init()">
<mx:Script source="ProductAdmin.as"/>
<mx:RemoteObject id="srv" destination="productService">
    <mx:method name="findProductList"
     result="productListHandler(event)"
     fault="faultHandler(event)"/>
    <mx:method name="saveProduct"
     result="saveResponseHandler(event)"
     fault="faultHandler(event)"/>
    <mx:method name="deleteProduct" result="deleteResponseHandler(event)"
fault="faultHandler(event)"/>
</mx:RemoteObject>
<mx:Label x="10" y="10" text="GraniteDS - Ejb Example"
    fontSize="16" color="#E9F3F5"/>
<mx:DataGrid id
="productGrid" dataProvider="{_productData}" bottom="10" right="10" top="68" left="10">
    <mx:columns>
    <mx:DataGridColumn width="70" headerText="ID" dataField="id"/>
    <mx:DataGridColumn width="200" headerText="Product" dataField="name"/>
    <mx:DataGridColumn headerText="Description" dataField="descr"/>
    <mx:DataGridColumn width="100" headerText="Quantity" dataField="quantity"/>
    <mx:DataGridColumn width="100" headerText="Price" dataField="price"/>
    <mx:DataGridColumn width="100" headerText="Manage"
itemRenderer="ManageRenderer"/>
    </mx:columns>
    </mx:DataGrid>
    <mx:Button click="displayNewHandler()" label="Add New Product"
right="10" top="38"/>
    </mx:Application>

File di configurazione

Finora, quello che abbiamo fatto è creare un back end in Java EE ed un front end in Flex. Nel creare questi due progetti abbiamo definito l’interfaccia di comunicazione tra i due progetti (le firme dei metodi esposti dal session bean e gli oggetti serializzati). Il passo successivo sarà quello di impostare opportunamente alcuni file di configurazione.

Cominciamo col creare il file "granite-config.xml", nella cartella "WEB-INF/granite", con il seguente contenuto

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE granite-config PUBLIC "
-//Granite Data Services//DTD granite-config internal//EN"
    "http://www.graniteds.org/public/dtd/2.0.0/granite-config.dtd">
<granite-config>
    <class-getter type="org.granite.hibernate.HibernateClassGetter"/>
    <externalizers>
        <externalizer type="org.granite.hibernate.HibernateExternalizer">
            <include annotated-with="javax.persistence.Entity"/>
        </externalizer>
    </externalizers>
</granite-config>

Come abbiamo anticipato, l’esternalizzazione degli entity con GraniteDS viene fatta in automatico attraverso questo file di configurazione. Nella sezione "externalizers" indichiamo di esternalizzare tutte le classi annotate con l’annotazione "@Entity". Per individuare le classi da esternalizzare si possono usare anche altri metodi, ad esempio specificando direttamente la classe

<include type="flex.samples.common.entity.Product"/>

Oppure, per evitare di elencare tutte le classi, si può indicare di esternalizzare tutte le istanze di una superclasse

<include instance-of=" flex.samples.common.entity.AbstractProduct"/>

In questo specifico caso l’externalization viene fatta utilizzando l’externalizer "org.granite.hibernate.HibernateExternalizer" che fornisce un meccanismo di Lazy Initialization . Questo externalizer viene usato quando si utilizza come persistent provider Hibernate e richiede l’inclusione nel classpath della libreria "granite-hibernate.jar". In base al persistent provider utilizzato occorre impostare l’externalizer adatto, e GDS ne fornisce diversi [4].

Il secondo file di configurazione è services-config.xml, nella cartella "WEB-INF/flex", con il seguente contenuto

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
    <services>
        <service
            id="granite-service"
            class="flex.messaging.services.RemotingService"
            messageTypes="flex.messaging.messages.RemotingMessage">
            <destination id="productService">
                <channels>
                    <channel ref="my-graniteamf"/>
                </channels>
                <properties>
                    <factory>ejbFactory</factory>
                </properties>
            </destination>
        </service>
    </services>
    <factories>
        <factory id="ejbFactory"
class="org.granite.messaging.service.EjbServiceFactory">
            <properties>
                <lookup>GDSEjb/{capitalized.destination.id}Bean/local</lookup>
            </properties>
        </factory>
    </factories>
    <channels>
        <channel-definition id="my-graniteamf"
class="mx.messaging.channels.AMFChannel">
            <endpoint uri
            ="http://{server.name}:{server.port}/{context.root}/graniteamf/amf"
            class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
    </channels>
</services-config>

Questo è il punto dove il RemoteObject as3, productService, viene legato al session bean. Quando il RemoteObject viene usato nel file mxml, viene effettuato, tramite l’ejbFactory org.granite.messaging.service.EjbServiceFactory, il lookup dell’EJB usando la stringa GDSEjb/ProductServiceBean/local. La stringa {capitalized.destination.id} infatti viene sostituita a runtime con il nome della destinazione e  la prima lettere viene messa in maiuscolo (capitalized). E questo è proprio il nome JNDI con cui il nostro session viene registrato.

Infine nel file web.xml occorre inserire

    ...
    <!-- read services-config.xml file at web application startup -->
    <listener>
        <listener-class>org.granite.config.GraniteConfigListener</listener-class>
    </listener>
    <!-- handle AMF requests ([de]serialization) -->
    <filter>
        <filter-name>AMFMessageFilter</filter-name>
        <filter-class>org.granite.messaging.webapp.AMFMessageFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AMFMessageFilter</filter-name>
        <url-pattern>/graniteamf/*</url-pattern>
    </filter-mapping>
    <!-- handle AMF requests (execution) -->
    <servlet>
        <servlet-name>AMFMessageServlet</servlet-name>
        <servlet-class>
            org.granite.messaging.webapp.AMFMessageServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>AMFMessageServlet</servlet-name>
        <url-pattern>/graniteamf/*</url-pattern>
    </servlet-mapping>
    ...

Conclusioni

In questo articolo abbiamo visto un esempio completo (scaricabile dal menu in alto a sinistra) dell’utilizzo del framework GraniteDS per integrare un front end in Flex e un back end in Java EE, e precisamente con EJB3. Nel prossimo articolo vedremo altri esempi pratici di utilizzo di GDS in scenari diversi.

Riferimenti

[1] Download della distribuzione del progetto GraniteDS
http://sourceforge.net/projects/granite/files/granite/

[2] Il sito ufficiale del progetto GraniteDS
http://www.graniteds.org/

[3] GraniteDS Blog
http://graniteds.blogspot.com/

[4] Externalizers
http://www.graniteds.org/confluence/display/DOC20/2.+Externalizers

Invia un messaggio alla redazione su questo articolo

Mittente (facoltativo - immetti una email valida se desideri ricevere una risposta)

Come hai trovato questo articolo?

 

Mi sono addormentato

Interessante con il giusto taglio

Argomento non interessante

Molto interessante

Argomento interessante, ma non approfondito

Efficace, ha risolto i miei problemi

Interessante

Mi sono commosso

 

Lascia un commento su questo articolo