Sono passati molti mesi da quando é stato scritto l’ultimo articolo pubblicato su questo blog. Inutile dire che in questo tempo il JUG di Brescia si é pressoché dissolto, non tanto nella sostanza (i fondatori del gruppo godono tutti di ottima salute) ma soprattutto negli intenti. Dopo vari anni non c’é più nessuno interessato a mantenere in vita questo gruppo e non ci sono nuovi membri che possono iniettare nuova linfa nel gruppo. Come possessore del dominio, vorrei pertanto annunciare la fine del JUG di Brescia e ringraziare tutti quelli che hanno contribuito a questo blog.
Estendere il sistema di configurazione di Spring
Recentemente ho fatto alcuni esperimenti in Spring. Volendo snellire la parte di configurazione ho cercato di approfondire la mia conoscenza del supporto a custom schema nei file di configurazione di Spring.
In pratica Spring consente di aggiungere un proprio xsd ai file di configurazione in modo da creare dei comportamenti custom al momento della creazione del relativo contesto. I passi da seguire sono questi:
- Creare un proprio xsd che rispecchi la struttura degli elementi che vogliamo andare a creare
- Implemmentare una classe che implementi (appunto)NamespaceHandler in cui registreremo un apposito BeanDefinitionParser per ogni custom tag definito nell’xsd.
- Creare un file META-INF/spring.handler per abbinare l’handler con il file schema
- Creare un file META-INF/spring.schemas per abbinare il namespace all’uri dell’xsd
Partendo dalla struttura delle beans che vogliamo creare è necessario definire xsd. Se per esempio abbiamo una struttura del genere
<bean class=”org.jugbrescia.audit.DBAudit”>
<property name=”level” value=”INFO” />
<property name=”datasource”>
<ref id=”dsx”/>
</property>
</bean>
l’xsd equivalente potrebbe essere questo
<xs:schema attributeFormDefault=”unqualified” elementFormDefault=”qualified” xmlns:xs=”http://www.w3.org/2001/XMLSchema”>
<xs:element name=”audit” type=”auditType”/>
<xs:complexType name=”auditType”>
<xs:simpleContent>
<xs:extension base=”xs:string”>
<xs:attribute type=”xs:string” name=”level”/>
<xs:attribute type=”xs:string” name=”datasource”/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:schema>
Il NameSpaceHandler deve semplicemente inirizzare il parser per un determinato tag verso l’implememntazione corretta
public class PojoRegistryNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser(”audit”, new PojoRegistryBeanDefinitionParser());
}
}
Il Bean definitionParser esegue effettivamente il lavoro.
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionBuilder pre = BeanDefinitionBuilder.rootBeanDefinition(org.jugbrescia.audit.DBAudit.class);
….
final BeanDefinitionRegistry definitionRegistry = parserContext.getRegistry();
definitionRegistry.registerBeanDefinition(”DBAudit”, definition);
return definition;
}
Da notare che le bean non vengono istanziate direttamente: tale compito viene delegato al container di Spring. Le property semplici possono essere assegnate leggendo i valori dall’xml di configurazione metre per i riferimenti è necessario rifarsi alla struttura di metadati di sping tramite il metodo addPropertyReference.
E’ possibile anche popolare proprietà complesse creando bean on the fly e anche proprietà di tipo Map o List creando le corrispondenti strutture ManagedMap e ManagedList e usando il metodo addPropertyReference di BeanDefinitionBuilder.
strictfp
Non so a quanti dei nostri due lettori possa nella pratica interessare, ma da un punto di vista meramente teorico penso sia interessante scoprire, nel linguaggio di programmazione che si usa ogni giorno da anni, una keyword della quale si ignoravano non solo lo scopo, bensì anche l’esistenza.
La keyword in questione, per quanto riguarda me, ma forse non solo, è strictfp.
Essa può essere utilizzata per controllare in modo esplicito alcuni aspetti della semantica delle operazioni floating point, ed è questo suo riguardare aspetti decisamente di basso livello della programmazione a renderla (probabilmente) una delle “keyword dimenticate” di Java (nel gruppetto possiamo includere a buon diritto anche goto e const, per le quali vale però un discorso diverso: sono parole riservate non utilizzate dal linguaggio).
In poche parole, l’utilizzo di strictfp permette di marcare un insieme di espressioni come FP-strict. Ogni sotto-espressione di ogni espressione di tipo double marcata come FP-strict deve essere un valore double ammesso (compreso cioè tra Double.MIN_VALUE e Double.MAX_VALUE): non sono ammesse cioè estensioni del range di validità di double per i valori parziali di un’espressione.
Sia ad esempio double result = (a + b) * 0.1 un’espressione, con a = b = Double.MAX_VALUE * 2/3. E’ evidente come a+b > Double.MAX_VALUE, mentre result = Double.MAX_VALUE * 4/30 < Double.MAX_VALUE. Se l’espressione è marcata FP-Strict, essa verrà valutata come “Positive Infinity“, poiché il risultato parziale di una sua sotto-espressione risulta maggiore di Double.MAX_VALUE. Se l’espressione non è marcata FP-strict, la specifica lascia all’implementazione la scelta tra il valutare l’intera espressione come “Positive Infinity” o come (4/30) * Double.MAX_VALUE, mediante l’utilizzo – per i risultati parziali – di un’estensione del range di valori ammessi per double.
Avendo la possibilità di ottenere come risultati valori ammissibili benché alcuni risultati intermedi non lo siano, perché si dovrebbe voler marcare un’espressione come FP-strict, rinunciando alla possibilità di un’estensione del range? Per essere certi che il comportamento della propria applicazione non cambi al variare dell’implementazione Java utilizzata. L’espressione dell’esempio precedente è valutata “Positive Infinity” (pur senza l’utilizzo della keyword strictfp) quando la si fa girare su JDK 1.6.0_13 di SUN. Volendo avere la certezza di ottenere lo stesso comportamento anche su altre implementazioni Java, si può utilizzare la keyword in oggetto per disabilitare eventuali estensioni al range double supportato per risultati parziali.
Una presentazione completa di come e dove utilizzare la keyword strictfp è disponibile qui.
Oracle!
Eccoci qui, dopo qualche settimana passata a commentare voci e smentite, iniziative e rinunce circa un possibile acquisto di SUN da parte di IBM, di fronte alla notizia, questa volta reale e definitiva, dell’acquisto di SUN da parte di Oracle.
La notizia è sulla Home Page di entrambe le aziende: Sun Microsystems (NASDAQ: JAVA) and Oracle Corporation (NASDAQ: ORCL) announced today they have entered into a definitive agreement under which Oracle will acquire Sun common stock for $9.50 per share in cash. The transaction is valued at approximately $7.4 billion, or $5.6 billion net of Sun’s cash and debt.
Vorrei avviare una discussione come quella di qualche settimana fa.
Per quanto mi riguarda, sono curioso di vedere che cosa accadrà a MySQL, DBMS open acquistato non molto tempo fa da SUN, che si troverà ora a far parte della stessa famiglia del più noto e diffuso DBMS commerciale. Non è detto che il suo destino sia segnato: non escluderei anzi che MySQL possa diventare una sorta di community edition di Oracle DBMS.
A proposito di Java, e di ciò che attorno a Java ruota, sarà interessante vedere come si risolveranno alcuni dualismi che l’acquisizione annunciata oggi crea nell’ambito della stessa realtà aziendale. Penso a quello tra GlassFish e WebLogic, ma anche a quello tra NetBeans e JDeveloper. Se nel primo caso ho il timore che la sorte dell’attuale reference implementation di JavaEE non sarà delle migliori, nel secondo caso non penso che l’esito del confronto sia scontato, vista la diffusione di utilizzo dell’IDE “ufficiale” di SUN.
In ogni caso, penso che il mondo Java sia abbastanza vasto, ricco e variegato da non subire conseguenze negative dall’acquisizione di SUN da parte di Oracle. Una vasta comunità open che vive attorno al linguaggio di SUN è a mio parere la garanzia migliore che il “nuovo proprietario” del linguaggio più diffuso del mondo non potrà permettersi grandi dietrofront rispetto alla svolta open che SUN ha intrapreso negli ultimi anni: in fondo, se Oracle decidesse per una svolta di chiusura di JDK e per un completo abbandono di NetBeans e GlassFish, e non credo lo farà, vi sarebbe senza dubbio una comunità pronta a raccogliere la sfida di proseguire lo sviluppo delle attuali versioni free software.
Non penso infine ci sia da preoccuparsi per quanto riguarda la diffusione di Java. Oracle, come IBM, da tempo punta sul linguaggio di SUN (non per niente ha un proprio IDE, aveva un proprio Application Server e ne ha acquisito un altro). In questo senso, non penso ci sia molta differenza tra l’ipotizzato acquisto da parte di IBM e quello realizzato da parte di Oracle.
Chi esce sconfitto da questa acquisizione è forse solo Big Blue, anche se non ci metterei la mano sul fuoco. IBM in ogni caso punta molto su Java e sul mondo open, e sarà in qualche modo il primo guardiano delle mosse di Oracle, avendo tutto l’interesse a continuare a collaborare, in ottica open appunto, all’evoluzione della piattaforma e della comunità Java.
Per concludere con una battuta… tiro un sospiro di sollievo a pensare che WebSphere non diverrà la reference implementation di JavaEE. Forse WebLogic, chissà…
Facoltà di Ingegneria
Siamo stati citati e linkati nel forum della Facoltà di Ingegneria dell’Università degli Studi di Brescia, nella sezione “Ingegneria dell’Informazione”.
Il thread in questione è questo: un grazie ad Alberto per la “pubblicità”.
Singleton: pattern o anti-pattern?
Il Singleton è un design pattern creazionale che ha lo scopo di garantire che di una determinata classe venga creata una e una sola istanza, e di fornire un punto di accesso globale a tale istanza.
[it.wikipedia.org]
Questa è la definizione che Wikipedia dà di Singleton, pattern tanto semplice quanto di utilizzo diffuso, i cui abusi di frequente portano a parlare di anti-pattern.
Il problema non sta tanto nel pattern in sé (garantire l’unicità dell’istanza di una determinata classe è senza dubbio utile in particolari contesti, si pensi ad un gestore di connessioni a DB, ad un generatore di ID univoci o ad un orologio di sistema di riferimento), quanto piuttosto nel modo in cui, in un’applicazione, si sfrutta la possibilità di accedere in modo diretto (ciò che Wikipedia definisce “punto di accesso globale“) all’istanza del singleton. Vediamo ad esempio il caso di un’applicazione che gestisca fatture, ad ognuna delle quali venga assegnato in automatico un ID univoco.
public class Fattura
{
private final String uid;
private final String cliente;
private final Double importo;
// altri attributi
public Fattura(final String cliente, final String importo)
{
this.uid = UIDCreator.getInstance().nextUID();
this.cliente = cliente;
this.importo = importo;
}
}
public class UIDGenerator
{
private static final UIDGeneratorINSTANCE = new UIDGenerator();
public static UIDGeneratorgetInstance()
{
return INSTANCE;
}
public synchronized String nextUID()
{
return ... ;
}
}
Nell’esempio proposto, UIDGenerator è un singleton al quale la classe Fattura fa accesso direttamente, al fine di ottenere un ID univoco in fase di creazione di una nuova istanza. Una soluzione di questo genere lega in modo molto forte la classe Fattura alla classe UIDGenerator, creando un accoppiamento che rende difficile, ad esempio, effettuare test di unità sulla classe Fattura. Si supponga ad esempio che la logica di generazione dell’ID univoco implementata da UIDGenerator richieda l’accesso ad una tabella di un DB: con il forte accoppiamento in oggetto non è possibile testare la classe Fattura se non è disponibile la connessione al DB in questione. Il problema principale consiste ovviamente nel fatto che il costruttore di Fattura recupera direttamente l’istanza singleton e la utilizza, rendendo impossibile costruire istanze della classe Fattura qualora la logica di generazione dell’UID non sia disponibile. Un test di unità dovrebbe poter isolare il più possibile la classe oggetto di test, cosa resa impossibile dall’invocazione nel costruttore del metodo statico UIDGenerator.getInstance().
Un passo avanti, in questo senso, può essere il seguente:
public class Fattura
{
private final String uid;
private final String cliente;
private final Double importo;
// altri attributi
public Fattura(final String uid, final String cliente, final String importo)
{
this.uid = uid;
this.cliente = cliente;
this.importo = importo;
}
}
public class ManagerFatture
{
public void creazioneNuovaFattura()
{
Double importo = ... ;
String cliente = ...;
...
Fattura f = new Fattura(UIDGenerator.getInstance().nextUID(), cliente, importo);
}
}
La versione “rivista” della classe Fattura risolve il problema dell’accoppiamento tra la classe stessa ed il generatore di UID, ma lo fa spostando semplicemente il problema dell’accoppiamento della classe ManagerFatture. Anche in questo caso, il problema non sta nell’unicità dell’istanza di UIDGenerator (in questo senso Singleton è senza dubbio un pattern), ma piuttosto nell’utilizzo diretto di tale istanza in più punti dell’applicazione, tutti quelli in generale che abbiano necessità di ottenere ID univoci (in questo senso, Singleton è senza dubbio un anti-pattern). Per disaccoppiare ulteriormente la logica di business dalla logica di generazione degli ID, si dovrebbe adottare uno schema di implementazione come quello che segue:
public class ManagerFatture
{
public void creazioneNuovaFattura(UIDGenerator uidGenerator)
{
Double importo = ... ;
String cliente = ...;
...
Fattura f = new Fattura(uidGenerator.nextUID(), cliente, importo);
}
}
In questo modo anche il servizio di gestione della fatture è stato svincolato dall’accesso diretto al generatore di UID, che viene ora fornito come parametro ai metodi di business.
Una scelta alternativa ed ugualmente valida potrebbe essere quella di iniettare UIDGenerator nel costruttore del servizio, rendendolo così disponibile a tutti i metodi di business:
public class ManagerFatture
{
private UIDGenerator uidGenerator;
public ManagerFatture(UIDGenerator uidGen)
{
this.uidGenerator = uidGen;
}
public void creazioneNuovaFattura()
{
Double importo = ... ;
String cliente = ...;
...
Fattura f = new Fattura(uidGenerator.nextUID(), cliente, importo);
}
public void copiaFattura(Fattura fOld)
{
Fattura fNew = new Fattura(uidGenerator.nextUID(),
fOld.getCLiente(), fOld.getImporto());
}
}
Adottando una soluzione come quella proposta, è possibile rendere la logica di business e gli oggetti di modello testabili indipendentemente dalla disponibilità della logica di generazione di UID (logica che, secondo l’idea dello unit testing, verrà testata a sua volta in isolamento dal resto).
Astraendo il generatore di UID mediante un’interfaccia, si può arrivare ad uno schema come quello che segue:
public interface UIDGenerator
{
public abstract String nextUID();
}
public class RealWorldUIDGenerator implements UIDGenerator
{
private static final RealWorldUIDGenerator INSTANCE = new RealWorldUIDGenerator();
private RealWorldUIDGenerator()
{
...
}
public static RealWorldUIDGenerator getInstance()
{
return INSTANCE;
}
public synchronized String nextUID()
{
return ...;
}
}
public class UIDGeneratorMock implements UIDGenerator
{
public String nextUID()
{
return ...;
}
}
A questo punto, in fase di unit testing è possibile iniettare nella logica di business un’istanza opportuna di UIDGeneratorMock, demandando all’integration testing l’utilizzo dell’implementazione RealWorldUIDGenerator, poi utilizzata a regime dall’applicazione.
Concludendo, vorrei sottolineare come, al solito, il problema non stia tanto in una scelta architetturale o tecnologica (in questo caso nel pattern Singleton utilizzato), quanto nel volerla utilizzare al di fuori delle sue finalità. Il pattern Singleton è pensato infatti per garantire unicità di istanziazione di una classe: utilizzarlo come escamotage per disporre di qualcosa di simile ad una variabile globale porta ai problemi di accoppiamento che abbiamo visto, problemi legati in effetti all’idea di avere qualcosa di globale. L’accoppiamento e la dipendenza in fase di test legati all’unicità di una risorsa, infatti, si possono facilmente risolvere creando un’astrazione come quella proposta rendendo UIDGenerator un’interfaccia.
La cache di JAXB
JAXB (Java Architecture for XML Binding) è uno strumento per la gestione del mapping da documenti XML ad oggetti Java e viceversa.
Generalmente, utilizzare JAXB significa dare in pasto un XSD al compilatore JAXB (denominato xjc), ottenendo un insieme di classi Java che rispecchiano le definizioni di tag contenute nellXSD.
Utilizzando in modo opportuno Marshaller ed Unmarshaller JAXB è possibile, a questo punto, passare in modo semplice da istanze delle suddette classi a documenti XML, e viceversa.
Un esempio standard di utilizzo di JAXB per leggere un file XML ottenendo una struttura dati è il seguente:
JAXBContext jc = JAXBContext.newInstance("org.jugbrescia.jaxbtest.generated");
Unmarshaller unmarshaller = unmarshaller = jc.createUnmarshaller();
unmarshaller.setSchema(
SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(
new URL(Constants.INSTANCES_XSD_URL)
)
);
JAXBElement
In questo modo si inizializza un contesto di JAXB facendolo “puntare” al package generato da xjc, si ottiene da tale contesto un’istanza di Unmarshaller, si richiede la validazione secondo un XSD e si procede poi all’operazione di unmarshalling vera e propria.
Tutto questo nasconde una piccola insidia, nella quale mi sono ovviamente imbattuto non appena ho cercato di ottimizzare un modulo di lettura XML che fa utilizzo di JAXB. Nella mia ottimizzazione, infatti, avevo portato le prime tre operazioni nel costruttore della classe di lettura, lasciando solo la quarta nel metodo invocato per la lettura vera e propria.
Tutto ciò portava però ad un comportamento piuttosto strano: di tanto in tanto, ed inizialmente senza alcuna apparente regolarità, nella struttura dati root apparivano valori contenuti in XML di input processati da precedenti chiamate del metodo di lettura.
Ad un esame più approfondito (sia lodato l’inventore dei debugger…), la situazione si è chiarita (ed i problemi, fortunatamente, si sono dimostrati sistematici, e per nulla casuali): le istanze ottenute da root navigando la gerarchia di oggetti corrispondente alla gerarchia dei tag XML erano corrette, mentre le istanze ottenute navigando eventuali riferimenti contenuti in altre istanze (corrispondenti ad IDREF XML) erano istanze vecchie. Raggiungendo cioè una certa istanza navigando la struttura dati dalla root senza passare per IDREF, si trovavano i valori corretti dell’ultimo XML fornito in input, raggiungendo invece un’istanza con lo stesso ID navigando attraverso IDREF si trovavano i valori presenti in input precedenti. Le istanze ottenibili nei due modi, dunque, pur avendo lo stesso ID (XML) non risultavano essere la stessa istanza (Java). Tutto ciò si spiega facilmente col fatto che l’Unmarshaller JAXB risolve la costruzione di riferimenti (IDREF) utilizzando una cache indicizzata in base agli ID definiti nell’XSD. Tale cache viene creata in fase di inizializzazione dell’Unmarshaller: l’invocazione Unmarshaller unmarshaller = unmarshaller = jc.createUnmarshaller(); non può pertanto essere effettuata una volta sola, ma deve essere ripetuta ad ogni lettura (a meno di non avere la certezza che gli ID presenti nell’XML cambiano ad ogni richiesta di unmasrhalling), in modo da svuotare la cache prima di ogni lettura.
Nota finale sul mapping XML-Object: JAXB definisce uno standard architetturale ed una reference implementation.
Esso non è l’unico strumento disponibile in quest’ottica: mi vengono in mente ad esempio Castor ed XMLBeans, la cui logica di funzionamento è sostanzialmente la stessa. Esso è tuttavia una sorta di standard di fatto, poiché è sviluppato da SUN e (soprattutto) poiché è compreso in JFC da JDK 6 in poi.
Utilizzare un bus eventi per migliorare il design di una GUI
Dopo anni di sviluppo più o meno selvaggio con Swing, vorrei fare un po’ d’ordine e utilizzare in modo più sistematico alcuni accorgimenti maturati con l’esperienza. Uno di questi accorgimenti è quello di adottare un bus eventi per far comunicare le diverse parti della GUI. Cos’è un bus eventi?
Per bus eventi intendo un “servizio” di notifica messaggi, che mi permetta di registrare dei listener, interessati a ricevere una particolare categoria di “eventi”, e distribuire dei messaggi, che notifichino quando si è verificato un certo evento.
Dal punto di vista dell’API un bus eventi può essere paragonato a un bus JMS, la differenza sta nel fatto che un bus eventi dovrebbe essere qualcosa di molto leggero e ottimizzato per essere utilizzato embedded nell’applicazione.
I vantaggi che derivano dall’utilizzo di un bus eventi sono molteplici: basso accoppiamento tra i componenti della gui, robustezza, semplicità di tracciamento delle operazioni eseguite, facilità di debug e di test automatico, possibilità di remotizzazione delle operazioni.
Se quanto sto scrivendo vi incuriosisce, allora potete leggere il codice allegato, che contiene un’implementazione molto semplificata di un bus eventi per Swing, e utilizzarlo come punto di partenza per ulteriori esperimenti. Buon divertimento!
http://www.jugbrescia.org/wp-content/uploads/2009/03/test.java
Post “collettivo”
Trascrivo in questo post e nei suoi commenti una discussione in mailing list della settimana scorsa, cui ho dato origine con l’esplicito intento di riportarne qui lo svolgimento.
Ipotesi di acquisizione di SUN da parte di IBM: come pensate cambierebbe (pensate cambierebbe?) il mondo Java?
(la spaventosa idea che WebSphere divenga reference implementation per JavaEE me la son già giocata io, astenersi imitatori…)
100 di questi Post
Centesimo post… grazie a tutti quelli che con grande competenza e costanza hanno contribuito in questi mesi al risorgere, almeno online, del jug di Brescia.
