domenica 21 settembre 2008

OpenLaszlo e Flex

OpenLaszlo e Flex sono stati due dei framwerok di sviluppo di RIA che avevamo inzialmente preso in considerazione. Li scartammo quasi immediatamente per il legame che questi framweork hanno (o meglio, nel caso di Laszlo, avevano) con Flash e per lo sviluppo in Javascript/Actionscript, e non Java.

Flex è un framework rilasciato da Adobe per la creazione di ria con un linguaggio markup proprio, MXML, basato su XML, utilizzato per descrivere il layout dell'interfaccia utente e i comportamenti, mentre ActionScript 3, linguaggio di programmazione orientato agli oggetti, utilizzato per creare la logica client. Il tutto compilato poi per la creazione di oggetti SWF per l'esecuzione tramite Flash Player.

OpenLazlo oper in maniera piuttosto simile, anch'esso con un linguaggio simil XML condito con un linguaggio di programmazione, Javascript, anch'esso poi compilato in file SWF.
Attualmente però la nuova versione di OpenLaszlo supporta anche la possibilità di sviluppare ria anche senza l'uso di flash, ma con dhtml/ajax (come gwt, in poche parole), ma questo supporto è stato introdotto solo con la versione 4.1, rilasciata a Luglio 2008... decisamente molto più tardi di quando la nostra scelta di frawork base su cui lavolare è caduta su GWT.

E' comunque interessante dare uno sguardo a questa tecnologia.
Grossomdo il paradigma di programmazione non è così distante da GWT... si programma in un linguaggio, il framework mette a disposizione un compilatore che processa i file sorgente e produce del codice che verrà poi eseguito sul client.
Il linguaggio di programmazione usato per OpenLaszlo è un markup language, LZX, linguaggio risultante dalla fusione di aspetti dichiarativi, derivanti dall' XML, con altri tipici della programmazione imperativa Object-Oriented, derivanti da JavaScript. Nei file sorgenti di OpenLaszlo infatti si utilizza una sintassi molto simile a quella di un classico file XML, con riferimenti alle API messe a disposizione del framwork, con l'aggiunta di codice JavaScript.
Il compilatore poi, processando questi file sorgenti, crea un file SWT (quindi un file per Macromedia Flash), che possa quindi essere eseguito da qualsiasi browser con il Flash Player. Recentemente è stato anche introdotto il supporto per la compilazione di codice slegato da Flash,
generando quindi codice DHTML/AJAX.
Comunque sia, il codice simil-Javascript scritto nei file LZX non è quello eseguito dal browser, ma viene intepretato dal compilatore e resituito come codice actionscript di Flash, nel caso si vada poi ad utilizzare la tecnologia di Macoromedia, o generando del nuovo codice Javascript nel caso di utilizzo del classico AJAX.

Pro:
  • La realizzazione dell'interfaccia utente risulta abbastanza semplice da realizzare, lineare e abbastanza riusabile. Il tutto viene realizzato con una serie di tag LZX che definiscono i componenti dell'interfaccia, la loro posizione, e le loro carattaristiche, il tutto riutilizzabile con la logica della programmazione ad oggetti.
    Per esempio questo codice
    < canvas>
    <button onclick="vw.setWidth(200)">Expand
    Width</button>
    <button x="120"
    onclick="vw.setWidth(100)">Reset</button>
    <view id="vw" y="30" width="100" height="100">
    <!-- onwidth is sent anytime width is changed -->
    <handler name="onwidth">
    this.setAttribute('bgcolor', (this.width
    > 100 ? red : blue));
    </handler>
    </view>
    </canvas>
    permette la creazione di due pulsanti, e relativi eventi.
  • Ci sono a disposizione numerevoli componenti, personalizzabili e riusabili, e le api, lmanoe per quanto riguarda l'implementazione con flash, è ben supportata e testata
Contro
  • Per dare un minimo di interattività alla pagina bisogna comunque utilizzare dell'actionscript, quindi bisogna conoscere questo linguaggio
  • Il supporto DHTML non è ancora maturo... quindi si può produrre stabilmente solo con flash, e quindi ci si lega troppo a questo prodotto




Fonti:
OpenLaszlo White Paper: http://www.openlaszlo.org/whitepaper/LaszloWhitePaper.pdf

venerdì 29 agosto 2008

Gwt-Log

Una delle cose più noiose nel logging con applicazioni GWT è che, lato client, non si riesce ad usare nativamente la console di loggin, ne quella di GWT in Hosted mode, nè quella del server. Il che diventa noioso, poichè spesso si ha la necessita di avere un minimo di tracciabilità delle attività svolte dal codice, e si vuole subito avere un feddback con il loggin, ma con gwt, almeno lato client, sono sempre stato obbligare ad usare i fastidiosi "windows alert".
Certo, da una parte hanno la loro utilità, come quella di bloccare, di fatto, l'esecuzione dell'applicazione e questo può avere un suo aspetto positivo... ma sono troppo invadenti... e soprattutto se ce si li dimentica nel codice, passando poi il prodotto al cliente, si posson avere situazioni imbarazzanti.

Ho trovato, in questi giorni, una libreria davvero interessante per il loggin con GWT


Che gestisce in maniera del tutto trasparente il loggin su più livelli... infatti, lasciando fare tutto a lui (ci sono comunque dei setting da poter usare per disabilitare/abilitare i vari tipi di logger), si può avere il log lato server (anche gli errori lato client possono essere spediti in amniera del tutto trasparente al server con rpc), con la normale console errore del server (errori lato server, come con il normale System.out o System.err), sia nella console dell'Hosted Browser di GWT (come con il classico GWT.log)(client e server), sia un log da leggere con al console di FireBug (un ottima estensione di Fiorefox), sia con un interessante "finestrella div" che compare all'interno delal applicazione stessa, un div semitrasparente draggabile...
Insomma, si può fare veramente di tutto e con in pratica nessuna riga di codice particolare... si importa la libreria nel progetto, si inseriscono alcune righe nel file gwt.xml... e basta usare Log(stringa) e via che si logga dappertutto.
Stupendo.


Aggiornamento: Strani Errori
Ieri sembrava funzionare tutto perfettamente, mentre oggi, aggiornando il progetto a GWT 1.5, cambiando quindi librerie eccetera, ho forse commesso qualche errore... e non si compilava più il modulo gwt... continuava a lamentarsi che non trovava la libreria di gwt-log, in particolare che non trovava il file di confiugurazione gwt.xml del progetto gwt-log
L'errore era:
[echo] Module: com.fdlservizi.sse.HelpDeskGWT
[java] Loading module 'com.fdlservizi.sse.HelpDeskGWT'
[java] Loading inherited module 'com.allen_sauer.gwt.log.gwt-log'
[java] [ERROR] Unable to find 'com/allen_sauer/gwt/log/gwt-log.gwt.xml' on your classpath; could be a typo, or maybe you forgot to include a classpath entry for source?
[java] [ERROR] Line 7: Unexpected exception while processing element 'inherits'
[java] com.google.gwt.core.ext.UnableToCompleteException: (see previous log entries)
[java] at com.google.gwt.dev.cfg.ModuleDefLoader.nestedLoad(ModuleDefLoader.java:225)
[java] at com.google.gwt.dev.cfg.ModuleDefSchema$BodySchema.__inherits_begin(ModuleDefSchema.java:194)
[...]

Non so per quale motivo, ma ignorava totalmente la presenza della libreria, che invece avevo normalmente selezionato tar le librerie esterne in IntelliJ Idea.
Poi, guardando bene il forum di grails (in particolare questa discussione), non ho capito bene perchè, ho scoperto che dovevo inserire tale libreria nella directory /lib/gwt.
Cosa mai fatta, anche perchè eventualmente le librerie le avrei messe in /lib, dentro al quale la directory gwt non esisteva nemmeno.
Ora invece, con l file messo lì dentro, funziona senza problemi.... misteri di grails.

venerdì 22 agosto 2008

Alcuni problemi con gli id composti e i valori di default con Hibernate

Stamattina, dopo aver passato due giorni a studiarmi il "paradigma di programmazione" per le GUI usato in sap (potrebbe tornarci utili, è uno interessante punto di riferimento a cui potremmo ispirarci), sono tornato a fare qualche esperimento con Hibernate.
In particolare ho continuato a giocare con le select e gli insert con le nostre classi POJO create per ORM.

Ho generato una pagina gsp di esperimento, con alcuni tasti gwt che usano una rpc, che "stimola" sue servizi lato server, uno per la ricerca delle classi (sempre "chiamata") e una per salvarla.
Nessun problema nella ricerca di chiamata, sia con il findall, il list, il find, i criteria, eccetera...
Bhe, in realtà ci sono dei problemi quando nel database ci sono dei null di troppo... ma è un problema correlato a quello che descvrivo qui in seguito.

Infatti nel momento dell'inserimento abbiamo trovati i primi problemi. Essendo il nostro database costruito con delle doppie chiavi primarie (ogni tabella ha un id, nemmeno numerico autoincrementale, come sarebbe meglio fosse, ma come stringa, correlato sempre ad un altro campo presente in ogni tabella "az" che fa riferimento ad una tabella, az), ogni volta che noi abbiamo una chiave esterna, questa chiave esterna è anch'essa costruita con due campi... e anche al tabella a cui facciamo riferimento ha un id primario composto. Questo comporta che nel ricavare i dati di uno di queste colonne, devo fare un doppio passaggio. Nella colonna ricavo l'id delal classe associata, nella quale ricavo poi l'id, che è quello che viee di fatto immagazzinato nella colonna stessa.
Ma all'atto dell'inserimento questo doppio passaggio non funziona. In pratica non riesco a memorizzare i campi se sono chiavi secondarie esterne... e questo ovviamente è un grosso problema. Il fatto è che non tutto il database è costruito in maniera perfetta, per esempio la tabella chiamata non ha riferimenti di chiave secondarie che puntano ad azienda, altre colonne sì... quindi il toll per il reverse enginering è andato un po' in pappa e non riesce a gestire in maniera completa queste doppie chiavi.
Il tutto dovrebbe risolversi attuando una modifica al database che già volevamo attuare, cioè eliminare totalmente tutte le chiavi primarie composte con quel campo az, che di fatto non serve a nulla. Era stato introdotto tempo addietro quando il programma doveva essere multiazienda, cosa che è stata in seguito abbandonata.
Non toglieremo nella tabelle il campo az (presente in tutte) e nemmeno la tabella az (per retrocompatibilità, alcune query presenti qua e là nel programma fanno riferimento a questa colonna), ma modificheremo tutte le chiavi primarie, facendole diventare uniche e non più composte (composte cioè dal solo campo id) e elimineremo ogni chiave secondaria esterna, in ogni tabella, che faccia riferimento ad az.

Stamattina mi sono accorto però di un nuovo problema... la generazione automatica delle classi di dominio POJO, usate da Hibernate, non ha tenuto conto dei valori di default impostati nel database... essendo in sql server sono impostate con delle costraint di questo tipo
ALTER TABLE [dbo].[chiamata] ADD DEFAULT ('N') FOR [libretto]
in questo caso la colonna libretto della tabella chiamata è impostata con un valore di default = N (tra l'alro la colonna ha come proprietà il poter accettare valori null).
In hibernate di fatto non esistono modi per specificare nel file di mapping delle classi i valori di default... esisterebbe ma non è portabile sui vari databasi, prchè bisognerebbe specificare il formato di colonna, diverso per ogni tipologia di database, qundi da evitare.
Di fatto, in una situazione del genere, se si vuole memorizzare nel database una classe di dominio per la quale non si specificano valori per un determinato fields, se questo accetta nel mapping valori null, hibernate lo inizializza automaticamente con null... infatti hibernate, quando deve memorizzare una classe sul database, nella query sql insert, specifica tutti i campi della classe stessa che nella proprietà del mapping abbiano insert=true
Ovviamente, se questa proprietà non viene specificata, hibernate automaticamente le popola con il valore null se la proprietà stessa l'accetta, o con al string avuota nel caso sia specificata not null
Questo significa che di fatto vengono ignorati i valori di default specificati nel database, perchè di fatto al database arriva una query con quei campi specificati... quindi inserisce quelli e non i valori che lui ha come default.
Se per esempio voglio forzare il database ad inserire i valori di dafault, dovrei fare in modo che hibernate non gli passi proprio quel campo... cosa possibile da fare, basta specificare insert=false



facendo così hibernate ignora il campo libretto, e il database, non ricevendolo nella query insert, lo inserisce con il valore di default
Così facendo però non è possibile inserire alcun valore... quindi se io volessi, all'atto della insert, specificare un valore per quella propietà, di fatto questa viene ignorata.

Nel nostro caso, in effetti, c'è un modo molto semplice per risolvere il problema... basta mettere nel costruttore della classe di dominio il valore di default, o nel mio caso, dato che per le classi di dominio che vengono utilizzate anche lato client gwt, devo costruire una determinata classe che viene poi serializzata ed usata nel passaggio dati nella rpc, dichiaro quel dato field con il valore di default, così facendo, anche se lato client non viene specificato alcun valore, di fatto hibernate lo riceve con il valore preimpostato e lo passa al database.

public class DatiChiamata implements IsSerializable {
public String id;
public String apertaDa;
public String tipoChiamata;
public String gradoDisservizio;
[...]
public String noteAssegnazione;
public String libretto = "N";
[...]
}

giovedì 7 agosto 2008

JAVA DAO, ORM, POJO

Abbiamo deciso di approfondire lo studio di Hibernate, effettivamente potrebbe esserci molto di aiuto, sia per la normale interrogazione di database (sia in selezione che in salvataggio) sia perché potrebbe evitarci i problemi di compatibilità con i due database che vogliamo supportare: Oracle e SQL Server.

Abbiamo deciso di non affidarci totalmente a GORM (Grails Object Relational Mapping) perchè groovy si rileva meno performante in alcune condizioni, e abbiamo paura che sia limitante nel futuro.

Quindi la scelta ricade sulla creazione di un ORM tramite JAVA DAO (Java Data Access Object) create con delle classi java POJO (Plain Old Java OBject).
Questo significa mappare tutto il nostro database con delle semplici classi java che devvano rispecchiare determinate carrateristiche.
In particolare Hibernate non ha richieste eccessive, ma secondo la logica del convention over configuration, è sempre meglio cercare di rispettare più richieste possibili, tra le quali:

  • Ogni classe dovrebbe avere un suo identificare univoco, chiamato Id (e qui per noi è un problema... perchè in pratica ogni tabella del nostro database ha un id composto da due campi stringa... decisamente lontano dagli standard che prevedono un id numerico autoincrementale... lo so lo so)
  • Ogni classe dovrebbe prevedere un costruttore a zero argomenti, che sarà utilizzato da Hibernate per istanziare la classe
  • Tutti i campi della tabella dovrebbero essere trasformari in campi della classe. Quindi ogni classe dovrebbe avere una serie di campi da considerare persistenti (quindi direttamente mappabilis ul database), preferibilmente da settare come privati, e quindi definire i getter e i setter per ognuno di questi campi. In particolare ogni campo dopvra rispettare le classiche convenzioni ORM, quindi nel caso di un nome campo (o tabella) composto d apiù nomi uniti con l'underscore, si dovrà converire con nome univoco con le lettere maiuscole per ogni parola che lo compone... esempio il campo data_di_nascita sarà convertito in dataDiNascita. Inoltre per convenzione Hibernate si aspetta di trovare i getter e i setter così composti getDataDiNascita e setDataDiNascita.
  • Le classi non dovrebbero essere dichiarate final, o in tal caso caso, dovrebbero implementare un interfaccia che dichiara tutti i metodi pubblici.
  • Nel caso di id composti conviene dichiarare l'id della classe come istanza di un'altra classe, convenzionamente chiamata nomeclasseId, nella quale vengono specificati i singoli campi delal chiave primaria composta.

Ci siamo affidati, per la stesura di queste classid i dominio, ad Hibernate Tool, un plugin per Eclipse appositamente studiato dallo staff di hibernate.
Seguendo i consigli di questa pagina, abbiamo proceduto con questi passi
  • dopo aver installato il plugin per eclipse (semplicemente scompattato lo zip preso dalla pagina di cui supra), ho creato un progetto grails, vuoto, con la classica procedura (nuova directory, grails create-app)
  • Ho messo nella cartella lib del progetto il jar con il driver jdbs del database sqlserver (jtds-1.2.2.jar preso su sourceforge). Avviato eclipse ho importato il progetto appena creato.
  • tasto destro sul progetto, ho creato un nuovo file di configurazione per hibernate: File -> New -> Other -> Hibernate -> Hibernate Configuration File.
    Il file di configurazione l'ho salvato nella directory grails-app/config/hibernate
    Come dialect ho scelto SQL Server, che poi sarà salvato nel file come org.hibernate.dialect.SQLServerDialect
    Come classe del driver ho scelto net.sourceforge.jtds.jdbc.Driver
    Come connection url jdbc:jtds:sqlserver://localhost/Sacci_Gabry;instance=SQLEXPRESS (ho instalato il database in locale)
    Il resto ho lasciato in bianco
  • ancora tasto destro e quindi ho settato al configuarazione della console di Hibernate (File -> New -> Other -> Hibernate -> Hibernate Console Configuration)
    Ho lasciato tutto come proposto, inserendo però nel classpath la lib inserita prima nella directory lib del progetto (il driver jdbc)
  • ancora tasto destro e quindi ho creato un file per il reverse enginering (di fatto quello che poi creerà i file) [File -> New -> Other -> Hibernate -> Hibernate Reverse Engineering File(reveng.xml) ]
    Il file l'ho inserito come prima in grails-app/config/hibernate
    dopo aver selezionato la configurazione e aspettato che il database venisse letto, ho selezionato solo le tabelle del dbo
  • Fatto questo ho fatto partire la compilazione (Lunch -> Hibernate Code Generation -> Open Hibernate Code Generation Dialog)
    Come outpout folder ho scelto src\java, e come nome di package gorm
    Come exporter ho selezionato le domain code, hibernate xml mappings, hibernate xml configuration, e lo Schema Documentation (decisamente opzionale, ma carino, genera della documentazione html interessante e facile da consultae)
    La generazione del codice impiega un po' ad eseguirsi.
  • A questo punto bisogna spostare qualche file per seguire le convenzioni hibernate/grails
    Il file hibernate.cfg.xml appena generato, diverso dal precedente in quanto conteiene tutti i riferimenti ai file di maping, va spostato nella directori config/hibernate, sovrascrivendo quindi il precedente
    Tutti i file hbl.xml vanno spostati nella stessa directorty grails-app/config/hibernate
A questo punto tutto è pronto per lavorare... si possono visualizzare e quindi utilizzare le view di eclipse predisposte per hibernate, consultare il database, la session factory, fare query HQL ecc..

martedì 29 luglio 2008

Hibernate, session, Dynamic Methods + Inject

Ci siamo accorti che così come utilizziamo noi sql, creiamo una differente connessione al database per ogni servizio, cosa non molto gradevole in effetti.
Infatti, sino ad ora, abbiamo raramente usato GORM e Hibernate, data la "stramba" costruzione del database già esistente per il programma (id stringa, più campi per chiavi primarie, eccetera), e quindi risulta più "facile" usare sql diretto piuttosto che gorm e hibernate. Quindi usiamo groovy.sql.Sql per la creazione di query sql on the fly.
Abbiamo tentanto la strada della memorizzazione delle sessioni nella session http, cosa che funziona, ma rende poco pratica la gestione delle sessioni stesse, perchè ogni volta che serve richiamare la connessione, bisogna scrivere svariate righe di codice, per ogni metodo dei dervizi che utilizzano sql...
Per farlo abbiamo usato questa strategia: nella prima connessione al database creata, quella del login, creiamo un istanza SQL come normalmente si dovrebbe fare in ogni service
import groovy.sql.Sql
def sql = Sql.newInstance("jdbc:jtds:sqlserver://192.168.0.22/sacco;instance=SQLSSE", "sa", "fdl", "net.sourceforge.jtds.jdbc.Driver")
e poi la memorizziamo nella sessione come altre informazioni (user, profilo, eccetera)
def session = RCH.currentRequestAttributes().session
session.connessioneDB = sql
E il tutto sembra funzionare... per poi richiamare questa connessione sql, basta fare in ogni servizio
def session = RCH.currentRequestAttributes().session
def sql= session.connessioneDB
per poi usare l'oggetto sql normalmente (quindi sql.execute eccetera)
Questo portava anche al vantaggio si avere la stringa di connessione al database in uan sola classe, e non in ogni servizio (maggiore mantenibilità)

Abbiamo quindi provato un'altra strada... essendoci hibernate comodamente pronto nel framework grails, abbiamo provbato ad usare le sessioni di hibernate per fare una cosa simile, in più abbiamo provato ad utilizzare i metodi dinamici di groovy iniettando nei servizi un oggetto sql già pronto all'uso... il tutto funziona... solo che non usando nativamente hibernate (ma lo usiamo solo per ricavare connessioni e non per gestire gli accessi al database) pare che non si riescano a recuparare connessioni univoche... ma che hibernate ne crei una nuova ogni volta che viene invocato con il metodo che più avanti spieghiamo.
E' stato comunque interessante provare questo approccio perchè ci ha permesso di verificare la possibilità di iniettare metodi (e proprietà) in svariate classi (singole, nominali, tutti i servizi, i controller, le classi di dominio eccetera)... ed addirittura provare la creazione di plugin (che pare essere l'unica strada efficace per effettuare l'iniettamento di metodi dinamici)
Questo approccia, nonostante non persegue il fine di riutilizzare una connessione unica, ha il vataggio di mettere a disposizione l'oggetto sql ad ogni servizio, senza riga di codice alcuna, e di utilizzare il file DataSource.groovy normalmente presente nei progetti grails... così si possono utilizzare i tre environment di questo file (development, production, test)

Abbiamo quindi creato un nuovo plugin in (seguendo la procedura indicata qui: http://docs.codehaus.org/display/GRAILS/The+Plug-in+Developers+Guide), dentro al quale abbiamo inserito il seguente codice:

def doWithDynamicMethods = {applicationContext ->
System.err.println("il metodo dinamico è partito1");
def sf = applicationContext.sessionFactory
com.fdlservizi.sse.SSEService.metaClass.getDbConn = {->
def sessione = sf.currentSession
def connessione = sessione.connection()
return connessione
}
}

Il quale inietta nella classe SSEService il metodo gerDbConn. In Grails inoltre vige la convenzione che ad ogni metodo get e set, corrisponda immediatamente, anche se non dichiarata, la relativa proprietà... quindi, specificando per una determinata classe il metodo getProprietà e/o setProprietà, automaticamente si può richiamare il metodo getProprietà semplicemente definendo: def proprietà (oppure utilizzando direttamente proprietà)

La classe SSEService, posta nella directory src/groovy/com.fdlservizi.sse/ è così composta


package com.fdlservizi.sse

import groovy.sql.Sql
import java.sql.Connection

class SSEService {

Sql getSql(){
def globalSql = new Sql((Connection)dbConn)
globalSql.execute("set dateformat ymd")
System.err.println("connection = " + (Connection)dbConn);
return globalSql
}

Connection getConnection(){
System.err.println("connection = " + dbConn);
return dbConn
}

org.hibernate.impl.SessionImpl getDbSession(){
return dbSess
}
}

In questa classe, quindi, per quello detto prima, semplicemente richiamando "dbConn", è come se richiamassimo getDbConn iniettato dal plugin.
Questa classe la usiamo poi per estendere ogni servizio che intenda usare una connessione sql... in questo modo, all'interno del servizio, possiamo semplicemente usare la sintassi che fino a quel momento usavamo, senza mai dichiarare l'oggetto sql.... perchè di fatto iniettato nella classe che estendono, quindi ereditato

In ogni servizio infatti si può tranquillamente scrivere, per esempio
sql.eachRow("select * from imp_tipo")
e si sottoindente GetSql, che a sua volta, sottointende getDbConn, che a sua volta sottointende applicationContext.sessionFactory.currentSession.connection() che restituisce la connessione di hibernate (presa dal file DataSource.groovy)
Questo però, come detto prima, al contrario delle nostre aspettative, sembra aprire sempre nuove connessioni.
Inoltre, nella classe SSEService, facciamo ad ogni richiesta di connessione, l'esecuzione del setdateformat, necessario per l'esecuzione delle nostre query, anche questo vantaggio notevole, dato che senza questo accorgimento bisognava eseguirla, in ogni servizio che utilizzava le date (DateTime, TimeStamp) e simili nelle query.

Abbiamo comunque deciso di concetrarci ora allo studio del dialetto di Hibernate, per utilizzare direttamente le sue potenzialita, demandando l'uso di sql diretto solo nei momenti in cui sarà strettamente necessario. Questo ci permetterà, presumibilmente, di demandare ad hibernate la gestione del pool di connession al database e le differenze tra i due database che dovremmo abdare a supportare (sql server e oracle).

giovedì 26 giugno 2008

Versioning

Sto provando ora l'utilizzo delle versioni per i progetti grails.
Da riga di comando, si può manipolare il file application.properties, settando quindi la versione, con
grails set-version n
dove n sta per il numero di versione.
Questo va a modificare la proprietà app.version all'interno del file sopracitato.

Successivamente si può utilizzare questo numero, all'interno dei progetti grails, con

da un controller con
def version = grailsApplication.metadata['app.version']
Oppure con le righe

import grails.util.*
def grailsVersion = GrailsUtil.grailsVersion
Oppure, all'interno di pagine gsp, con



Una volta che è stata settata al versione dell'applicazione, la creazione del war con il comando
grails war
rifletterà questi cambiamenti, nominando il war come nomeapplicazione.n.war, dove n sarà il numero di versione (in realtà questo per ora non succede, non so perchè, rimane sempre con il numero di versione 0.1; sul forum mi hanno consigliato di usare il comando
grails clean prima di grails war, ma nulla)

Durante il deploy si può inoltre specificare la directory di deploy, in quanto, di default, viene messo in una directory con lo stesso nome del war.
Per farlo basta usare il comando
asadmin deploy --contextroot /HelpDesk nomewar

Prima però bisogna fare l'undeploy dell'eventuale app già installata in quella directory, lo si fa con
asadmin undeploy nomeapp
nel nostro caso

asadmin undeploy HelpDeskGwt.1.2.6

AGGIORNAMENTO del 1 Luglio
Ho trovato l'errore del war... era colpa mia, nel file di configurazione config.groovy avevo inserito una riga di codice:
grails.war.destFile = """HelpDeskGwt-0.1.war"""
e questo forzava il nome del war... ora funziona tutto

venerdì 13 giugno 2008

Anche in Italia si muove qualcosa

Negli ultimi giorni anche il gruppo (mailing list su yahoo gropuc: http://it.groups.yahoo.com/group/Grails-IT/) italiano di Grails si sta animando, e oggi è stato rilasciato un tutorial interessante (mannaggia... se ci fosse stato un mesetto fa, avrei faticato molto meno)

Questo il messaggio integrale

Ciao,

ieri è stato pubblicato su Mokabyte il primo articolo su Grails e Groovy.

http://www2.mokabyte.it/cms/article.run?articleId=DYG-V69-HKE-Z3O_7f000001_10553237_7391cad5

Per come è fatto Grails, una Demo vale più di mille parole. Anche
l'articolo riprende questa impostazione alla "sporchiamoci subito le
mani", nei prossimi numeri andrò più in profondità sui vari argomenti
chiave.

Ovviamente, qualsiasi domanda o suggerimento per i prossimi numeri è
bene accetto.

Buon weekend!

Brando