giovedì 12 novembre 2009

Grails validation, un incongruenza nella constraint size

Dopo mesi che non mi faccio più sentire (l'elezione a sindaco ha decisamente influito) ecco che mi ripropongo a voi.
Sembra difficile crederlo, ma siete davvero in molti quelli che seguite il blog... anche se poco aggiornato.
D'altronde in Italia non sono molti quelli che programmano in GWT e Grails, e in particolare con una combinazione di entrambi.

Oggi forse abbiamo trovato un incongruenza, o almeno qualcosa di poco intuitivo, nella validazione di grails.
Era un qualcosa che avevamo già intuito in passato, oggi abbiamo fatto alcuni test e possiamo essere sicuri di questo comportamento. Stiamo usando grails 1.2M4, ma credo il comportamento sia simili almeno per grails 1.0 in avanti.

In effetti la documentazione è poco chiara sulle constraint, in particolare proprio sull'interazione della constraint size con nullable e blank, verso i quali ha una frase piuttosto criptica:
Currently this constraint cannot be used in addition to blank or nullable, a custom validator may be added to perform this kind of constraints.

Il che apparentemente significherebbe, se il mio inglese non mi tradisce, che la constraint size non si può usare in aggiunta a nullable e blank... ma la cosa risulta falsa, infatti, prendiamo ad esempio questa classe

class Prova {
static constraints = {
prova1(size:0..16)
prova2(size:1..16)
}
String prova1
String prova2
}


e questo codice in un controller:

Prova provaA = new Prova()
provaA.validate()
println "Validazione ProvaA"
provaA.errors.each {println it}

Prova provaB = new Prova()
provaB.prova1 = ""
provaB.prova2 = ""
provaB.validate()
println "Validazione ProvaB"
provaB.errors.each {println it}


si ha questo output
Validazione ProvaA
org.springframework.validation.BeanPropertyBindingResult: 2 errors
Field error in object 'Prova' on field 'prova2': rejected value [null]; codes [Prova.prova2.nullable.error.Prova.prova2,Prova.prova2.nullable.error.prova2,Prova.prova2.nullable.error.java.lang.String,Prova.prova2.nullable.error,prova.prova2.nullable.error.Prova.prova2,prova.prova2.nullable.error.prova2,prova.prova2.nullable.error.java.lang.String,prova.prova2.nullable.error,Prova.prova2.nullable.Prova.prova2,Prova.prova2.nullable.prova2,Prova.prova2.nullable.java.lang.String,Prova.prova2.nullable,prova.prova2.nullable.Prova.prova2,prova.prova2.nullable.prova2,prova.prova2.nullable.java.lang.String,prova.prova2.nullable,nullable.Prova.prova2,nullable.prova2,nullable.java.lang.String,nullable]; arguments [prova2,class Prova]; default message [La proprietà [{0}] della classe [{1}] non può essere null]
Field error in object 'Prova' on field 'prova1': rejected value [null]; codes [Prova.prova1.nullable.error.Prova.prova1,Prova.prova1.nullable.error.prova1,Prova.prova1.nullable.error.java.lang.String,Prova.prova1.nullable.error,prova.prova1.nullable.error.Prova.prova1,prova.prova1.nullable.error.prova1,prova.prova1.nullable.error.java.lang.String,prova.prova1.nullable.error,Prova.prova1.nullable.Prova.prova1,Prova.prova1.nullable.prova1,Prova.prova1.nullable.java.lang.String,Prova.prova1.nullable,prova.prova1.nullable.Prova.prova1,prova.prova1.nullable.prova1,prova.prova1.nullable.java.lang.String,prova.prova1.nullable,nullable.Prova.prova1,nullable.prova1,nullable.java.lang.String,nullable]; arguments [prova1,class Prova]; default message [La proprietà [{0}] della classe [{1}] non può essere null]
Validazione ProvaB
org.springframework.validation.BeanPropertyBindingResult: 0 errors

Non avendo dichiarato nessun'altra contraint oltre a size, come per ogni proprietà della classe, si dovrebbe sottointendere "nullable: true"
E infatti sembrerebbe così, nel tentativo su ProvaA abbiamo i due errori sul valore nullo

Il size sulla stringa è inteso come un controllo sulla lenght() della stringa stessa. quindi può trarre in inganno il size(0..16) che potrebbe far pensare di aver concesso alla stringa un lenght 0... quindi come non avergli dato valore, quindi come aver sottinteso nullable:true
Ma non è così

Altra cosa strana è il size(1..0)... concedere ad una stringa lunghezza minima di 1, potrebbe far pensare di costringere la stringa ad avere un valore diverso dal valore vuoto, in parole povere di aver sottointeso "blank: false", ma non è così
come si vede dall'output precedente, un assegnazione prova2="" è accettata... anche se palesemente un lenght su una stringa = "" sarebbe 0.

Comportamento strano, non credete?

venerdì 22 maggio 2009

Accedere al DataSource di grails da dovunque

Mi è riservito dover riaccedere al datasource da un qualcosa che non fosse un controller o un service.
Infatti nel nostro progetto abbiamo una classe utility che deve avere accesso al database, per poter fare una query con HQL...

Per poterlo fare bisogna:

import org.codehaus.groovy.grails.web.context.ServletContextHolder as SCH
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes as GA
def ctx = SCH.servletContext.getAttribute(GA.APPLICATION_CONTEXT)
def dataSource= ctx.dataSource

e ora nel dataSource si ha una classe di tipo: http://static.springframework.org/spring/docs/2.0.x/api/org/springframework/jdbc/datasource/DriverManagerDataSource.html
e ora si può accedere alla connessione così:

def sql = new Sql((Connection)dataSource.getConnection())

Con grails 1.1 si può fare più semplicemente

import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
def static ctx = AH.application.mainContext
def dataSource= ctx.dataSource

Per poi continuare come scritto prima

giovedì 30 aprile 2009

Database Naming convention

Alcune convenzioni largamente utilizzate (in gran parte usate anche da grails e hibernate) per i nomi nei database.
Ho trovato questo libro, SQL Programming Style by Joe Celko
Che sembrerebbe essere interessante e si basa sullo standard ISO/IEC 11179-5:2005, che da quanto letto è lo standard per la "naming convention" in senso lato.

Lo standard iso, riassumento, dice tra le altre cose

1. Be unique (within any data dictionary in which it appears).
2. Be stated in the singular.
3. State what the concept is, not only what it is not.
4. Be stated as a descriptive phrase or sentence(s).
5. Contain only commonly understood abbreviations.
6. Be expressed without embedding definitions of other data elements
or underlying concepts.
7. Tables, sets, and other collections shall be named with a collective,
class, or plural name.
8. Procedures shall have a verb in their name.
9. A copy (alias) of a table shall include the base table name as
well as the role it is playing at that time.

Elenco qui, invece, alcune delle convenzioni trovate nel libro
  • Lunghezza dei nomi (tutti): dovrebbe essere inferiore ai 30 caratteri. Molti dbms sql accettano sino a 128 caratteri (come MS SQL) ma considerando il limite di oracle di 30 caratteri, conviene che tutti i nomi usati siano inferiori a questo limite. Comunque sembra una cifra più che sufficiente.
  • Evitare i caratteri speciali, come ad esempio $, #, @, alcuni dmbs li supportano, ma non tutti.
  • I nomi delle tabella è buona norma scriverli "capitalizzati" (prima lettera maiuscala, il resto minuscolo). Tutto il resto (nomi colonne, ecc...) tutto minuscolo. Questa però pare essere una convenzione piuttosto controversa.
  • Per nomi composti (sia tabella sia colonna) usare l'undescore come separatore, e non le "camel words" (cioè come in java, le parole attaccate capitalizzate, come NomeTabella). Per i nomi di tabella usare parole capitalizzate.
  • Essere coerenti nella scelta se usare nomi di tabella al singolare o al plurale. Ci sono alcune correnti di pensiero, alcuni pensano sia meglio usare nomi al plurale, dopotutto una tabella contiene in se solitamente più di un entità. Però nella modellazione una tabella rappresenta un oggetto, quindi espresso al sigolare. Inoltre scrivere tutto al singolare evita i problemi derivati proprio dalle particolare regole nel rendere plurarle una parola (person diventa people). Il libro consiglia nomi al plurale, anche se molti altri (e io mi aggiungo a questi) consigliano nomi al singolare.
  • Evitare i prefissi, soprattutto nei nomi di tabelle. Alcuni usano anteporre table_ o tbl_ ai nomi di colonne, ma è inutile, anzi scomodo. Sconsigliato anche anteporre il nome della tabella a tutte le colonne della tabella stessa.
  • Le tabelle di relazione dovrebbero avere nomi che riassumano al relazione stessa. Per esempio un'ipotetica relazione di "matrimonio" tra la tabella "uomo" e la tabella "donna" dovrebbe chiamarsi "matrimonio", e non "uomo_donna". Se non possibile, si usano i nomi della tabelle unite dall'underscore.
  • Le chiavi primarie, dove possibile, dovrebbero essere degli standard industriali, come per esempio l'ISBN, o simili.

giovedì 19 febbraio 2009

Grails Are Not Configured

Non è la prima volta che mi capita questo errore, e non è la prima volta che perdo decine di minuti per risolverlo... mi dimentico sempre la soluzione.
In questi giorni ho importato ed esportato svariati progetti... e quindi spesso ci sono incompatibilità con le configurazioni degli stessi (librerie, jdk, facet bla bla).

A volte capita che, nel tentare di far partire/compilare un progetto Intellij Idea segnali "Grails are not configured"
Per risolvere il problema bisogna verificare che:
  • Ci sia il Grails JDK configurato correttamente, per fare questo bisogna accedere alla struttura del progetto (ctr+alt+shift+s, oppure F4 se si clicca sulla root del progetto nel project explorer) , andare su moduli-> grails e verificare che sia richiamato un jdk di grails
  • sia settata, sia in IDEA che in windows la variabile GRAILS_HOME
e tutto dovrebbe risolversi :-D

venerdì 13 febbraio 2009

Novità in ambito GWT: Smart GWT, aggiornamenti a GWT-EXT e GWTMosaic

Negli ultimi mesi sono venute alla luce importanti novità nel modno di gwt.

Alcuni aggiornamenti alle librerie GWT-EXT: siamo ora alla 2.0.6, anche se nella home è dichiarata ancora la 2.0.5
Questa versione la trovate da un link posto in questo post: http://gwt-ext.com/forum/viewtopic.php?f=12&t=3624
Attenti però, le prime versioni di questo pacchetto avevano una struttura directory che richiamava la versione precedente.
Purtroppo questa release, anzi, in realtà anche quella precedente, è stata accompagnata da una notizia che ci ha un po' scioccati: GWT-EXT non verrà più aggiornato... o meglio, verranno corretti comunque eventuali bachi e, forse, mantenuta la compatibilità con futuri rilasci di GWT e di nuovi browser, ma di fatto lo sviluppo è fermo... questo perchè gli sviluppatori hanno deciso di propendere per lo sviluppo di una nuova tecnologia non più basata sulle librerie ext-JS.
Infatti viene caldamente consigliato al conversione a SmartGWT: http://www.jroller.com/sjivan/entry/smartgwt_1_0_released
Queste nuove librerie grafiche si basano sul framework SmartClient, che non ha, almeno per ora, i problemi delle licenze di EXT-js.

Molti più widget, un buon supporto al pattern MVC e una nuova e interessante struttura dati (molto simile sotto certi aspetti a quella che abbiamo sviluppato per la mia tesi).

Per ora non intendiamo migrare, ma in un futuro probabilmente lo faremo.
Comunque, di fatto, uno dei problemi di gwt-ext rimane: il codice che il sorgente deve scaricare è enorme, poichè tutto richiama librerie javascript che vanno comunque totalmente scaricate dal browser (e parliamo di 700K minimo, esclusa l'applicazione in sè).

Pochi giorni fa è stata rilasciato un nuovo aggiornamento di GWT-Mosaic, che ora si fa davvero interessante, si è arrivati alla versione 0.1.8 (http://groups.google.com/group/gwt-mosaic/browse_thread/thread/a114236cb7faaa37#)
Questo piccolosissima libreria gwt, piccolissima se paragonata a GWT-Ext o SmartGwt, è molto interessante perchè si appoggia nativamente e interamente su GWT... cioè non richiama codice javascript che il browser deve scaricare, ma lascia tutto in pasto al compilatore java-to-javascript di GWT... questo permettere di mettere in codice javascript (e quindi tutto peso che il browser deve scaricare) solo quello che in realtà si programma... e questo è un notevole vantaggio per applicazioni piccole o medio piccole
La differenza è notevolissima: per un mini progetto (giusto qualche pannello e qualche pulsante) con gwt mosaic, il codice (tra pagina html con js embedde e css) che il browser deve scaricare è di 92K, tra l'altro probabilmente molto riducibili, perchè si hanno due css da 28 k e molto probabilmente sono io l'imbranato che non ha capito come usarne solo uno, datyo che sono quasi identici, quindi possiamo dire che si arriverebbe a meno di 70 KB
Con gwt ext invece si tratterebbe di almeno 2MB (tra pagina html con js embedde, altri js e css) , poichè comunque bisogna scaricare il core  in js (che sono le librerie ext) di 770KB, più i js dell'applicazione, circa 10 KB, più almeno 100KB di css (in questo caso obbligatori)
insomma... si va dai 80-90 KB con gwtmosaic agli 800-900 di gwt-ext.

certo, per grandi applicazioni le dimensioni di un progetto gwt-ext non cambiamo moltissimo in proporzione (il nostro helpdesk, che è abbastanza corposo, arriva a 900-1000KB totali), però la differenza è enorme.

martedì 20 gennaio 2009

Latex

Ho iniziato ad impaginare al tesi con latex... in realtà non ho ancora terminato di scriverla, mi manca uno dei due capitoli centrali...
ma almeno inizio a scrivere seriamente il tutto.

Prendo appunti su alcuni settaggi:

Voglio usare una doppia bibliografia, così da costrurire la classica bibliografia, e successivamente, una sitografia
Uso ovviamente bibtex, decisamente comodo
lo affianco al pacchetto multibib

quindi nella classe caricata all'inizio del documento, scrivo questi due comandi

\usepackage{multibib}
\newcites{sito}{Sitografia}

dove il newcites introduce proprio la seconda bibliografia e una serie di comandi (simili a quelli di bibtex, ma con l'aggiunta ad onuno del suffisso "sito") appositi

all'interno del documento principale, subito dopo al creazione della bibliografia, creo la sitografia in questo modo

\bibliography{bibliografia} % inserisce la bibliografia e la prende in questo caso d bibliografia.bib

%sitografia, prendendo i dati da sitografia.bib, dentro il quale uso "misc"%
\bibliographystylesito{alpha}
\bibliographysito{sitografia} 

All'interno del documento, usando il normale comando \cite citerò i documenti della bibliografia normale, usando il comando \citosito, utilizzo al sitografia


Per poter avere anche la sitografia, però, non posso usare il comodo comando preimpostato all'interno di TeXnicCenter "Build and View" (che compila tutto, crea il pdf, e lo visualizza), ma devo andare di riga di comando

latex tesi
bibtex tesi
bibtex sito
latex tesi
latex tesi

e qui si produce il file dvi

con il comando

dvips primo.dvi -o

si produce il file PostScript ps

lunedì 29 dicembre 2008

Effective Java

Da qualche settimana siamo fermi sullo sviluppo del framework (e quindi anche la mia tesi), ne ho approfittato, oltre che per migliorare il prototipo già funzionanente presso un nostro cliente, e per implementare nuove features richieste dal cliente stesso, per studiarmi uno dei libri che mi ero ripromesso di studiare: Effective Java.
Interessante direi, molto utile in qualche consiglio, meno in altri, ma direi un must.
Alcuni dei consigli trovati nel libro li ho già "implementati" nel framework, migliorando alcuni aspetti che erano già stati realizzati. E sicuramente molti altri ci saranno in seguito utili.
Lo consiglio a tutti, soprattutto a chi come me non è espertissimo nella programmazione di grandi progetti.

martedì 25 novembre 2008

TeePee

Dopo un po' di giorni senza post, forse è il caso di aggiornare un po' chi segue questo blog (e guardando dalle statistiche di google analytics non siete nemmeno pochi, lasciatemi gongolare, dai).

Nell'ultimo mese ci siamo dati completamente allo sviluppo del framework che fino dall'inizio volevamo sviluppare. Prima d'ora era poco più che un abbozzo, ma l'esperienza fatta nello sviluppare l'applicazione che già dei clienti stanno usando, ci ha permesso di trovare i reali bisogni di cui necessitiamo, e le carenze dei framework esistenti.

Il mio collega e correlatore ha gettato le basi, su cui ho costruito il resto, sempre sotto la sua supervisione.
Abbiamo anche deciso di rendere disponibile questo core del framework, rilasciandolos su google code con licenza GNU Lesser General Public License.
Il tutto lo trovate qui: http://code.google.com/p/teepee/
Ovviamente attualmente non si trova molto online, tranne il codice nudo e crudo, ma presto, anche ai fini della tesi, rilasceremo i javadoc e qualche file esplicativo, oltre a quelche schema UML.

A tale proposito, qualcuno ha quelche strumento (opensource o freeware) da consigliare? Per ora ho provato 
  • violetUML, non male, ma di fatto non utile nei sequence diagram
  • ArgoUml, buono per il reverse enginering, mi ha generato tutti i diagramma di classe per tutti le classi del progetto, niente male, fa risparmiare tempo... ma poi poco usabile per creare i diagrammi
  • Visual Paradigm... niente male, ma a pagamento
  • StarUml... quello che sto usando ora, opensource, non male, ma i grafici non sono personalizzabili al 100%... alcune cose rimangono standard, come i punti di ancoraggio delle linee, frecce e simili.

mercoledì 8 ottobre 2008

Ancora su Orm

Sono riuscito nel tentativo del salvataggio di una chiamata con hibernate/orm... ho in pratica trovato il modo per superare, più o meno, le restrizioni evidenziate in questo post(Alcuni problemi con gli id composti e i valori di default con Hibernate).

In pratica ho usato degli DAO molto più semplici... che non considerano in pratica le relazioni molti a molti... in pratica, all'interno della classe che rappresenta la tabella nel database, ogni riferimento a chiavi esterne... è memorizzato come un campo qualsiasi, ignorando assolutamente se nei DAO esiste una classe che potrebbe sostiurire il campo.

In pratica, prima, per esempio, se nella tabella chiamata evavo un campo id_imp che era ovviamente una chiava esterna per la tabella imp, il DAO era costituito come un field costituito da un elemento stesso della classe Imp... che a suo volta però al suo interno aveva un ID composito rappresentato dalla classe ImpId... ed è questo che pareva creare dei problemi.

Ora, con questi nuovi DAO più semplici, in un caso come quello sopra, id_imp è rappresentato nella classe Chiamata dal field chiamato idImp rappresentato da una semplice stringa... posso quindi assegnarli una semplice stringa... che rappresenta poi l'id chiave esterna di imp (imp ha il campo id come stringa)
In pratica come dovrei fare se usassi SQL.

Così sono riuscito a memorizzare senza problemi una chiamata assegnando tramite gli strumenti di HIBernate tutti i valori che mi interessano.
In realtà qualcosa di diverso c'è... tutti i campi sono memorizzati come dati primitivi, String, char, int, date... cioè come la definzione del campo nel database... tranne alcuni (pochissimi) come per esempio HdTipoContab, che è memorizzato ancora come classe... questo perchè (credo) proprio al tabella hd_tipo_contab non ha id composto (come invece il 99% delle altre tabelle) e quindi è comuinque molto semplice usarlo... anche se ho dovuto aggiungere un costruttore alla classe così come era stata creata automaticamente.

Questi DAO semplificati li ho ottenuti usando sempre Hibernate Tool, plugin di Eclipse, selezionando perà nel file di configurazione hibernate.revenge.xml solo el tabelle vere e proprie, ignorando le varie view presenti nel database, e selezionando, all'atto della creazione del codice, nessuna opzione particolare (quindi non selezionando"generate basyc typed composite id", nè "Decect optimistic lock columns", nè "detect many-to-many tables"), e nella schermata exporters, non ho selezionato nessuna delle opzioni in alto (quindi non ho selezionato nè "Use java 5 sintax" nè "Generate EJB3 annotations") e come exporters ho selezionato "domain code" (che genera le classi java che poi utilizzo), "Hibernate xml mapping .hbm.xml" (che genera i file xml per specificare ogni singola classe), "DAO Code" (che a quanto pare genera delle classi java chiamata come le precedenti+home... non so esattamente a cosa siano necessari, pare sempre ad hibernate), sia "Hibernate Xml Configuration, cfg.xml" che genera un nuovo hibernate.cfg.xml con i riferimenti ai file .hbm.xml che vengono generati.

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

giovedì 12 giugno 2008

Alcuni Errori - bug risolti, da ricordare

Durante lo sviluppo della nostra interfaccia sono emersi dei comportamenti strani di Gwt o GET-EXT o grails...
Forse dipendenti dalla mia scarsa esperienza, o forse da veri e propri buchi... non so

Elenco qui quelli che ho risolto, così da ricordarmi in futuro


  • Normalmente grails si accorge di ogni modifica effettuata ai file sorgenti e tenta la ricompilazione dei file necessari. Non ne sappiamo bene i motivi, ma così come è impostato Grails e il plugin di gwt, se si modificano dei file lato client di gwt, non sempre parte la compilazione. O meglio, sembra partire sempre, ma sembra che non vengano cancellati i file che gwt ha creato la compilazione precedente. Di fatto quindi le modifiche non vengono prese.

    Ho perso ore prima di capire questo problema... e pensavo che none ro capace di programmare, non mi capacitavo del perchè non vedevo le modifiche che avevo progettato... ed ecco poi risolto l'arcano... il problema, almeno questa volta, non ero io.

    RISOLTO:
    Francesco ha trovato al soluzione... obbligare lo script del plugin di gwt a ricompilare tutto.
    Questo lo si ottiene aggiungendo alla riga 76 del filke _Internal.groovy del plugin il seguente codice
    gwtForceCompile = true
    Avendo quindi questo codice:modules.each { moduleName ->
     gwtForceCompile = true
    if (!gwtForceCompile &&
    GU.environment != GA.ENV_PRODUCTION &&
      neW File("${outputPath}/${moduleName}/${moduleName}.nocache.js").exists()) {
     // We can skip this module.
             return
     }

    In più, per evitare il problema derivante dal "Java Heap Speace" abbiamo modificato il comando
     java(classname: 'com.google.gwt.dev.GWTCompiler', fork: 'true')
    sempre nello stesso file con
     java(classname: 'com.google.gwt.dev.GWTCompiler', fork: 'true',maxmemory: "512m")

    Se si hanno poi dei problemi con il jar di getext (non ho capito bene in quali casi avviene il problema, ma in alcune circostanze è così) va aggiunta anche questa riga di codice
    pathElement(location: "${basedir}/lib/gwtext.jar")
    nella zona della dichiarazioni del classhpath

  • Mai modificare un file di servizio lato server quando l'applicazione è up, perchè se per caso si aggiorna la pagina dell'aplicazione, grails si accorge che qualcosa è stato modificato, tenta la ricompilazione ma CANCELLA deliberatamente i file del servizio lato client. Non so bene perchè succeda, forse è provocato dalla modifica al plug in che abbiamo fatto (che forza la compilazione del modulo gwt, per risolvere il baco di cui sopra). Bisogna prima fermare l'appplicazione, o distruggere il server, poi ricompilare.

  • Attenzione con la gestione degli ascoltatori dei pulsanti, se poi li si vogliono abilitare e disabilitare. Non so se è un paco gi GWT o GWT-EXT (i button e i listener che utilizzo sono di GWT-EXT) o se è un normale comportamento... sta di fatto che se si aggiunge un ascoltatore ad un pulsante con una ascoltatore anonimo (come quasi sempre si fa), se ad un certo punto lo si disabilita, pare scomparire l'ascoltatore associato, quindi anche se lo si riabilita, non ascolta più nulla.
    Nel mio codice infatti avevo questo

    salvaButton.addListener(new ButtonListenerAdapter(){
    public void onClick(Button button, EventObject e) {
    [...]
    }
    });

    Trasformato poi in

    salvaButton.addListener(new AscoltatoreBottoneSalva());

    private class AscoltatoreBottoneSalva extends ButtonListenerAdapter{
    public void onClick(Button button, EventObject e) {
    [...]
    }
    }

martedì 10 giugno 2008

Giornata di aggiornamenti

Stamane è giornata di aggiornamenti.
Novità non indifferente, in ufficio abbiamo aperto la nuova macchina del caffè, una gaggia, tutta automatica, con il caffè in grani... scusate se è poco... la macchinetta di prima quasi ci esplodeva in mano ogni volta che l'accendevamo...
Bella bella...
vabbhe, torniamo alle cose importanti (perchè, il caffè non lo è? sì, è essenziale nella vita di un programmatore, ma forse non ai fini di questo blog)

Stamattina ho aggiornato sia Grails che Groovy
Di Groovy è stata rilasciata la versione 1.5.6, avevo installato sino ad ora la 1.5.5... d fatto nulla di che, solo qualche bugfix e migliorie varie...
Di Grails è stata recentemente rilasciata la 1.0.3, con alcuni bug fix, il supporto al tipo Enum, e un nuovo modo operativo, l'interactive mode, e migliore supporto all'internalizzazione i18n.
Oltre aver installato grails ho anche aggiornato il nostro progetto con la nuova release, dopotutto è semplice... basta il comando grails update nella directory del progetto.

AGGIORNAMENTO di Giovedì 12
Oggi ho aggiornato anche gwt-ext, di cui hanno rilasciato al versione 2.0.4
Alcune importanti migliorie sono presenti in questo aggiornamento, come il supporto a firefox3, che con la nuova verisone, dava alcuni problemi (firefox3 è+ molto più compatibile agli standard w3c di firefox2 e ie7... questo per assurdo creava problemi).
In questo aggiornamento c'è anche una nuova componente, pagebus... non so esattamente cosa sia, non ho capito, si orienta su nuove tecnologie presenti in opera, firefox, e safari. Comunque proprio questo aggiornamento ha iniziato a crearmi dei problemi.
Non in fase di compilazione, tutto filava liscio, ma Intellij IDEA ha iniziatoa darmi strani errori... in tutti i file mi continuava a dire
class blabla is defined in module 'com.gwtext.Pagebus' witch is not inherited in in module 'nome dle modulo del mio progetto'
ho aggiunto questa riga

al file nomemodulo.gwt.xml

venerdì 6 giugno 2008

Rieccomi

Dopo quasi un mese senza aggiornamenti, rieccomi.
Ho avuto poco tempo da dedicare al blog, nonostante le mie intenzione, a causa del sovraccarico di lavoro che la consegna di un progetto che mi coinvolgeva in prima persona ha portato.
Infatti, proprio durante lo studio sulla possibile accoppiata Grails+GWT (e GWT-Ext) si è conretizzata la possibilità di utilizzare proprio queste tecnologie per lo sviluppo di uno dei lavori richiesti da un cliente della fdl servizi.
Bisognava infatti rivedere un "HelpDesk" (una sorta di gestioen di ticket-aiuto) per la gestione di una centrale termica di un noto ospedale milanese.
Il tutto ovviamente deve essere usufruibile via web, e quale migliore occasione per testare queste tecnologie?

Il risultato? decisamente buono
A parte molti problemi nati qua e là durante il percorso, e quasi integralmente risolti, abbiamo ottenuto un interfaccia fresca, veloce e razionale, molto più performante di quello che pensavamo. Le rpc di GWT, integrate lato server con servizi groovy-grails, hanno una risposta davvero buono e lo scambio di dati tra client e server, anche attraverso "internet" e non solo uan rete intranet, è davvero buona, anche quando si tratta di compilare una griglia con migliaia di stringhe al suo interno.

Presto metterò online alcuni stralci di codice, in particolare per quanto riguarda la realizzazione delle RPC (come mi è stato richiesto da altri ragazzi che mi hanno scritto via mail per problemi riguardati lo sviluppo di RPC in GWT).
Parlerò inoltre della delegazione, da preferire in alcuni casi all'ereditarietà... anche se questo pattern, risolvendomi alcuni problemi e semplificandomi alcuni dettagli, mi ha portato a problematiche che non ho ancora risolto, che spero di annullare ocn lo studio di alcuni pattern proposti dai libri che ho già nominato in passato sul blog.

mercoledì 7 maggio 2008

Grails + GWT

Ho finito di leggere ed applicare l'esempio del libro "Getting started with Grails"... sembra proprio essere un gran bel framwork, ottimo per lo sviluppo di service web, praticamente fa tutto lui e grazie alla convention over configuration il codice da scrivere è davvero poco. Eseguendo la logica del MVC sembra non sia difficile integrare, come parte "view" del client dei moduli GWT.

Quindi ora passo allo studio proprio dell'integrazione di Grails con GWT.
Esiste un plugin per grails, già esistente nella normale installazione di Grails: http://grails.org/GWT+Plugin

Purtroppo sul sito di codehouse il plugin, o meglio, l'integrazione effettiva di gwt e grails non sono ben documentate, anzi...
Per prima cosa l'installazione del plugin nei singoli progetti grails non è veloce, almeno la prima volta: eseguito nel progetto racetack, quello che ho creato studiando GSWG, il comando
grails install-plugin gwt
ho dovuto aspettare quasi 10 minuti prima che si "ripulisse la cache"... di cosa non so bene.

Ho quindi creato il modulo gwt, chiamandolo Racetrack, seguendo la classica regola dei nomi del package, quindi con il comando
grails create-gwt-module com.fdlservizi.Racetrack

Seguendo la logica che quella che voglio fare è una view al progetto già esistente e funzionante, ho creato la pagina hosting del modulo gwt pensandola proprio come una view al controller "race".
La convenzione del plug in di gwt vuole che con il comando grails create-gwt-page , dove è la pagina hosting con il relativo path, se la pagina è indicata come una gsp e come indirizzo ha una singola cartella, questa viene interpretata come un controller view, altrimenti viene trattata come una normale pagina web.
Quindi ho eseguito il comando
grails create-gwt-page race/gwt.gsp com.fdlservizi.Racetrack

Tutto ciò dovrebbe avermi creato i file di configurazione necessari per il funzionamento del modulo gwt, e la pagina hosting dove mettere poi i widget gwt.
Con il comando
grails run-gwt-client
parte l'esecuzione dell'hosted mode di gwt, quindi parte anche il "browser" interno di gwt... va però precisato, e io me ne sono accorto dopo un po' di imprecazione, che deve essere attivo il server web... quindi da IDEA prima faccio partire il progetto racetrack e poi eseguo il comando run-gwt-client (ovviamente questo e i comandi precedenti vanno fatti all'interno del path del progetto racetrack)


Edit 8 Maggio
Iniziano i primi problemi... in pratica, come indicato nella pagina del plugin di gwt, per poter usare map e list in gwt, bisogna specificare nel servizio di grails il tipo di ritorno di queste liste o mappe. In teoria questo lo si dovrebbe fare con @CollectionTypeArg e @MapTypeArg ma qui casca l'asino. Il mio ambiente non vede le classi per queste annotation... viene questo errore
"unable to find class for annotation"
Ho chiesto aiuto nella mailing list di Nabble Grails User
Vediamo se ottengo aiuto.

Edit
Pausa pranzo finita e per ora nessun aiuto. Ho cercato di arrangiarmi e qualcosa, forse, ho risolto. Smattendo a destra e a manca no trovato dove risiede la classe delle annotazioni del plug in di gwt... in pratica nella directory del porgetto, sotto plugin in, vi è la directory in cui vi sono due jar. Uno di questi è grails-gwt-util.jar
Ho importato questa libreria da IDEA, andando in Setting -> Project Setting -> Library -> Attach Classes -> e ho selezionato il jar di cui sopra.
Poi nel file in cui voglio usare le annotazioni ho importato il riferimento alle annotazioni con
import org.codehaus.groovy.grails.plugins.gwt.annotation.CollectionTypeArg
Non so se è il metodo corretto, nè perchè abbia dovuto farlo (presupponevo che il plugin di GWT almeno importasse le jar necessarie al suo funzionamento), ma sembra funzionare.