lunedì 31 gennaio 2011

Fase 5 : Problema Stateful Session Bean

Parallelamente al modulo web ho continuato a sviluppare il modulo ejb. Ho deciso di utilizzare dei Session Beans stateless per eseguire azioni atomiche (sulle entità o semplicemente per elaborare alcuni dati). Invece ho preferito utilizzare un Session Bean stateful che fungesse da carrello per l'utente. Fin dal primo istante mi sono accorta che richiamando queste due righe di codice ad ogni reload della pagina mi veniva assegnata ogni volta una nuova istanza dell'EJB.

Context context = new InitialContext();
this.carrelloEJB = (CarrelloSBRemote) context.lookup("WiiShop/CarrelloSB/remote");


Il mio scopo, però, era mantenere un riferimento sempre allo stesso EJB stateful, contenente lo stato del carrello. Allora, dopo molte ricerche, ho scoperto che bastava memorizzare in sessione un reference all'EJB per richiamarlo in caso di bisogno.

this.carrelloEJB = (CarrelloSBRemote) session.getAttribute("shopSFSB");
// se in sessione non c'è un'istanza dell'EJB allora accedo all'EJB e memorizzo un reference nella sessione
if (this.carrelloEJB == null)
 {
    Context context;
    try {
        context = new InitialContext();
        this.carrelloEJB = (CarrelloSBRemote) context.lookup("WiiShop/CarrelloSB/remote");
                    session.setAttribute("shopSFSB", this.carrelloEJB);
            } catch (NamingException ex) {
            }
 }


Se, invece che stateful, CarrelloSBRemote fosse stato stateless e avessi utilizzato la stessa tecnica, avrei sbagliato. Testando il codice potreste avere la sensazione che tutto funzioni, ma un stateless Session Bean può essere cancellato in qualunque istante dal sistema e se voi vi salvate un riferimento ad esso non è detto che quando andate a richiamarlo sia lo stesso di prima.

lunedì 24 gennaio 2011

Fase 4 : Architettura applicazione web e prime fasi di sviluppo

Questa fase è stata molto laboriosa, soprattutto data la mia lentezza nello sviluppare un'interfaccia abbastanza accettabile dal punto di vista grafico. Questo è ciò che sono riuscita ad ottenere prendendo in prestito alcune immagini trovate girovagando tra i siti sulla Wii.


Per quanto riguarda l'architettura, la mia web application si compone di pagine JSP, Servlets e Java Bean (molto utili per JSF e Richfaces). Inoltre ho impostato un filtro (Filter) per controllare gli accessi all'area riservata.
Grazie a Jaxb e alle sue annotazioni ho potuto creare l'rss per il WiiShop, creandone anche una versione Html tramite l'uso di un file xsl e dei Transformers.

martedì 11 gennaio 2011

Fase 3 : Interfaccia Web, JSF e Richfaces

A questo punto ho provato a generare alcuni Session Beans per capirne bene il funzionamento. Facendo uso di EJB3 ho trovato tutto molto semplice e piuttosto intuitivo, anche se ho trascorso parecchio tempo a studiare le differenze tra EJB2 e EJB3.
Prima di procedere con il modulo ejb e le entità ho deciso di dedicarmi all'applicazione web ed in particolare alla sua interfaccia. Ho, quindi, creato un template html+css per capire dove avrei posizionato i vari componenti grafici.
Dopo un po' di studi, la scelta della tecnologia da utilizzare è ricaduta su Java Server Faces e RichFaces. Nonostante fossi già venuta in contatto con queste due tecnologie o riscontrato non pochi problemi.
Innanzitutto tengo a precisare che JBoss 5 già contiene al suo interno JSF 1.2. Se inserite le JSF 1.2 di NetBeans otterrete solo conflitti. Per quanto riguarda RichFaces, ho usato le 3.3.0 perchè sono le uniche che sono riuscita a far funzionare.

mercoledì 5 gennaio 2011

Fase 2 : Configurazione, Entità e prime problematiche

Ora che ero in possesso di una struttura per il mio progetto avevo bisogno di identificare gli "attori" che avrebbero popolato il mio database. Ho deciso, quindi, di creare una piccola applicazione java per costruire il database facendo uso di Hibernate e dei suoi file di mappatura.
Le tabelle di cui avrei avuto bisogno sarebbero state: User, Order, Product.
Ecco la classe che ho utilizzato per caricare i file .hbm.xml e generare così la struttura per il mio database.

public class SchemaGenerator
{
    // System constants for the current platform directory token
    static String fileSep = System.getProperty("file.separator");

    // We use this session factory to create our sessions
    public static SessionFactory sessionFactory;

    static String[] db_dialects = {"Mysql", "org.hibernate.dialect.MySQL5InnoDBDialect",};
  
    public static void main(String[] args)
    {
        initialization();
    }
  
    /**
    * Loads the Hibernate configuration information, sets up the
    * database and the Hibernate session factory.
    */
    public static void initialization()
    {
        System.out.println("initialization");
        try
        {
            Configuration myConfiguration = new Configuration();
  
            /**
             * Insert here your beans classes for which you want to generate the SQL script
             * one for each bean for each table
             **/
            myConfiguration.addClass(main.Product.class);
            myConfiguration.addClass(main.User.class);
            myConfiguration.addClass(main.Order.class);
          
            Properties myProperties = new Properties();

            for (int i = 0; i < db_dialects.length; i = i + 2)
            {
                String dialect_name = db_dialects[i];
                String dialect_class = db_dialects[i + 1];

                String dialect_file = dialect_name.toLowerCase();
                dialect_file = dialect_file.replace(' ', '_');
                dialect_file += (".sql");

                System.out.println("Generating " + dialect_name);

                // Note that this is the only Hibernate property
                // set. In particular, there is no JDBC
                // connectivity data, nor are we specifying a
                // driver!
                myProperties.put("hibernate.dialect", dialect_class);
                try
                {
                    // Load the *.hbm.xml files as set in the
                    // config, and set the dialect.
                    SchemaExport mySchemaExport = new SchemaExport(myConfiguration, myProperties);
                  
                    mySchemaExport.setDelimiter(";");

                    // Despite the name, the generated create
                    // scripts WILL include drop statements at
                    // the top of the script!
                    mySchemaExport.setOutputFile("/...your path.../create_"+ dialect_file);
                    mySchemaExport.create(false, false) ;

                    // Generates DROP statements only
                    mySchemaExport.setOutputFile("/...your path.../drop_"+ dialect_file);
                    mySchemaExport.drop(false, false);

                    System.out.println(dialect_name + " OK.");

                } catch (Exception e)
                {
                    e.printStackTrace();
                    System.out.println(e.getMessage());
                }
            }
        } catch (Exception e)
        {
            e.printStackTrace();
        }
    }  
}

Di seguito un esempio di file .hbm.xml.

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 17-nov-2010 15.44.15 by Hibernate Tools 3.4.0.Beta1 -->
<hibernate-mapping>
    <class name="main.Order" table="orders">
        <id name="orderId" type="java.lang.Long">
            <column name="order_id" />
            <generator class="increment" />
        </id>
        <many-to-one name="userId" class="main.User" cascade="all" lazy="false">
            <column name="user" />
        </many-to-one> 
        <property name="totalCost" type="int">
            <column name="total_cost" />
        </property>
        <property name="date" type="java.lang.String">
            <column name="order_date" />
        </property>
        <set name="products" table="order_product" cascade="all" lazy="false">
            <key column="order_id"/>
            <many-to-many column="product_code" class="main.Product" />
        </set>
        <property name="deleted" type="boolean">
            <column name="deleted" length="0" />
        </property>
    </class>
</hibernate-mapping>

La classe Product altro non è che un semplice bean.

Una volta creato il database MySql ho importato il file generato dall'applicazione.
Attenzione a quando utilizzate parole come "Order" o "date" all'interno dei vostri file di mappatura poichè sono parole riservate per MySql e quando tenterete di importare gli schema generati vi verranno restituiti messaggi d'errore.
Nel mio database ora avevo le tabella Orders, Products, Users e una tabella di join tra Orders e Products. Quindi ho creato una nuova connessione con il database da NetBeans (Servizi --> Database --> New Connection). Finalmente potevo iniziare ad inserire i primi file di configurazione all'interno di NetBeans.
Pulsante destro sul modulo ejb: Nuovo --> Altro --> Persistenza --> Schema Database (ho inserito la connessione al db appena creata). Quindi ho creato anche un'unità di persistenza (stesso percorso di prima). Di seguito come si presenta il mio file di configurazione con una piccola aggiunta per poter utilizzare Hibernate:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
  <persistence-unit name="WiiShop-ejbPU" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>java:/wiishopDS</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
         <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
         <property name="hibernate.connection.isolation" value="RepeatableRead"/>
      </properties>
  </persistence-unit>
</persistence>

Nella stessa posizione ho dovuto copiare a mano il file jboss-ds.xml che riporto di seguito.

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
  <local-tx-datasource>
    <jndi-name>wiishopDS</jndi-name>
    <connection-url>jdbc:mysql://localhost/WiiShop</connection-url>
    <driver-class>com.mysql.jdbc.Driver</driver-class>
    <user-name>root</user-name>
    <password/>
    <min-pool-size>5</min-pool-size>
    <max-pool-size>20</max-pool-size>
    <idle-timeout-minutes>5</idle-timeout-minutes>
    <transaction-isolation>TRANSACTION_REPEATABLE_READ</transaction-isolation>
  </local-tx-datasource>
</datasources>

Purtroppo esso viene generato dentro setup, ma una volta che farete deploy il server non troverà questo file e restituirà eccezione. Ho perso molto tempo per capire il motivo di questo problema che potrebbe essere dovuto al fatto che NetBeans si aspetta di lavorare con JBoss 4 e non 5. Spostandolo dalla cartella setup a src/conf non ho più avuto problemi.

A questo punto ero già in grado di generare le mie entità dal database. Ho creato un package e cliccando con il destro su di esso ho scelto Nuovo --> Classi Entità dal Database. Niente di più semplice per ottenere le entità corrette con tutte le opportune annotazioni.
Un consiglio: fate partire JBoss 5 da shell separatamente da NetBeans. Per fare deploy non dovrete far altro che pulire e costruire l'applicazione JEE. Copiare il file .ear (generato dentro dist) all'interno di JBoss5/server/default/deploy. In un attimo JBoss si accorgerà del file e lo caricherà. Ogni tanto ricordate anche di cancellare un po' di file da JBoss5/server/default/tmp.

lunedì 3 gennaio 2011

Fase 1 : Architettura

Decidere un dominio per il progetto è stata la parte più divertente. Ho deciso di creare un e-commerce che simulasse un WiiShop.
Per prima cosa ho dovuto eseguire uno schizzo sull'architettura che avrebbe seguito il progetto, identificando tutti i componenti e il loro ruolo:
ejb-container: contenente entità per gestire la persistenza su database attraverso Hibernate e dei session beans (stateless e statefull) per eseguire la logica di alcune operazoni (es. mantenere il carrello dell'utente, gestire semplici o complesse operazioni CRUD con le entità...)
web-container: una web application che, attraverso l'uso di servlets, filtri, Java Beans, JSF e richfaces, permette all'utente di interagire con il WiiShop tramite browser.
Il server che ho scelto di utilizzare è stato JBoss 5.1. Esso è in grado di simulare l'esecuzione dell'applicazione JEE come applicazione distribuita poichè l'applicazione web viene caricata sul Tomcat contenuto all'interno di JBoss.
Con NetBeans (6.9.1) ho creato una nuova applicazione JEE, scegliendo di includere all'interno di essa un modulo EJB e un modulo per la web application. Inoltre ho scelto come server: JBoss (quello che avevo scaricato a parte: 5.1).