lunedì 28 dicembre 2009

OpenSolaris e scheda di rete Atheros AR8121

La prima installazione di OpenSolaris 2009.11 sul mio portatile mi ha portato ad una situazione dove solo la scheda di rete Wireless era funzionante, mentre quella wired (Atheros AR8121) risultata dispersa. Nemmeno l'utility dei driver era in grado di fornirmi il driver per la suddetta scheda. Dopo qualche ricerca ho trovato che il pacchetto che fornire i driver per la mia scheda è SUNWatge, dove ovviamente at sta per Atheros mentre ge per gigabit ethernet. Il problema è che OpenSolairs 2009.11 è al ramo di sviluppo snv 111, mentre il pacchetto di cui sopra richiede il 130.
E' stato quindi necessario installare l'aggiornamento dal repository di sviluppo di OpenSolaris, facendo avanzare il sistema fino a snv 130, per poi poter installare il driver della scheda di rete. Per fare questo ho anzitutto aggiunto il repository di sviluppo, l'ho impostato come repository di default, ed ho ordinato un image-update del sistema.

mercoledì 16 dicembre 2009

Un ComboBox per selezionare le viste di una applicazione RCP

In una applicazione RCP che sto sviluppando mi è capitato di avere molte viste, ciascuna che deve essere associabile ad una serie di permessi. Per permettere ad un utente amministratore di selezionare a quali viste concedere quali permessi, ho creato un semplice wrapper attorno ad un combo che contiene l'array delle viste disponibili e la loro descrizione. Il codice è abbastanza banale, ma potrebbe tornare utile in altre applicazioni.

public class ViewsCombo {

/**
* The combo that will be displayed.
*/
private Combo combo = null;


/**
* The available views in the system.
*/
private IViewDescriptor[] availableViews = null;

/**
* Initializes the combo and the map to contain the views.
* @param parent
*/
public ViewsCombo( Composite parent, String viewIDToSelect ){
super();
// create the combo
this.combo = new Combo( parent, SWT.READ_ONLY );

// fill the combo with the data
this.fillComboAndMap();

// select a view if specified
if( viewIDToSelect != null )
for( int i = 0; i < availableviews =" (IViewDescriptor[])">

Database Independent

La nostra soluzione è indipendente dal database, quindi possiamo usarla con qualunque DBMS preferiate.


Quante volte ho sentito questa frase pronunciata da un qualche vendor di soluzioni informatiche!
Quello che si nasconde dietro questa frase è l'illusione della portabilità, ovvero il poter riusare la soluzione informatica in contesti disparati e con DBMS differenti.
Certo, la portabilità è buona, ma va saputa usare nel modo piu' opportuno, e farsi vanto di essere indipendenti dal database non sempre è una buona cosa, anzi per me è un errore grosso e anche grossolano.

E' un errore grosso perché generalmente al cliente finale non interessa l'interoperabilità del sistema proposto con altri DBMS. Lo scopo del cliente è quello di avere una soluzione informatica funzionante e performante (quest'ultimo punto è spesso molto importante), mentre l'indipendenza dal database è un fattore di interesse generalmente per gli sviluppatori. E' pur vero che il cliente potrebbe avere già un suo DBMS installato (o preferito) sul quale vuole far girare la applicazione in questione, e quindi avere un modo per "convertire" l'applicazione ad un altro DBMS è una buona cosa. Ma la parola chiave, spesso ignorata, è appunto "conversione": un conto è avere una soluzione informatica portabile, ovvero convertibile facilmente ad un'altra architettura (ossia ad un altro DBMS) e un conto è avere un sistema che è svincolato dal tipo di DBMS e quindi è già pronta per qualunque architettura. Quest'ultima soluzione, a mio avviso, è la peggiore. I piu' staranno saltando sulla sedia vista questa mia affermazione. Per meglio capire il mio pensiero occorre valutare anche la seconda parte dell'errore circa l'indipendenza dal database: la grossolanità.
Reputo l'indipendenza dal database un errore grossolano poiché automaticamente significa escludere tutte le funzioni evolute che il DBMS puo' offrire, e che nella maggior parte dei casi sono vendor-specific. Il rifiuto di ogni cosa non assomiglia ad una query standard porta a due problemi principali:
  1. si reinventa la ruota;
  2. non si sfrutta al massimo il DBMS.
Il primo problema è facile da capire: il DBMS ha come solo scopo la gestione dei dati, e lo fa al meglio delle proprie capacità. Se non ci si fida del DBMS e si vuole riprogettare tutto quello che potrebbe essere fatto con estensioni del DBMS stesso (ad esempio query complesse via stored procedure) allora si sta scrivendo codice per replicare le funzionalità del DBMS. Questo risulta in uno spreco di risorse, tempo e generalmente in performance cattive. Unitamente a questo si ha che non si sfrutta il database al 100%, e considerando che spesso ci si trova a pagare licenze (dal costo elevato) per il solo utilizzo del DBMS, ciò corrisponde ad una perdita economica non sempre trascurabile. In altre parole, spesso il cliente si trova a pagare un costo di licenza per uno strumento, il DBMS, che viene usato solo in parte, e paga contemporaneamente i costi della realizzazione del software che implementa quelle funzionalità che non vengono sfruttate nel DBMS stesso!
A questo punto, dovrebbe iniziare ad essere chiaro che l'indipendenza dal database non sempre è una buona cosa, almeno per il fatto che implica costi aggiuntivi sul cliente (e sul fornitore prima).
Quindi non si deve progettare il sistema in modo che sia portabile? Ovviamente no, occorre fare una scelta implementativa mirata (ovvero stabilire quale DBMS puo' fornire il miglior rapporto qualità/prezzo per il sistema) e fornire implementazioni di supporto per i DBMS che non offrono le funzionalità richieste. In sostanza, occorre legarsi ad un DBMS e cercare di fornire supporto per gli altri, ma occorre che lo sviluppo sia orientato ad un DBMS specifico! Dopotutto scelte del genere nel processo di sviluppo sono già state fatte: la scelta del linguaggio, la scelta dell'IDE, la scelta del framework MVC, ecc., allora perché il database non dovrebbe sottostare a scelte analoghe?
L'indipendenza da un componente importante come il DBMS è per me utopia; è come se si comprasse un paio di scarpe ma non si usassero i lacci, perché dopotutto non tutte le scarpe hanno i lacci e quindi non si vogliono prendere abitudini non portabili. Ma così non si riuscirà mai a correre bene! E lo stesso avviene con i DBMS, che se non vengono sfruttati non possono fornire prestazioni elevate.
Quindi occorre fare una scelta, spesso coraggiosa: legarsi ad un DBMS che offra le migliori feature per il progetto che si sta facendo. E se il database è libero, allora questa scelta non imporrà costi addizionali sul cliente e potrà essere accettata facilmente.

giovedì 10 dicembre 2009

I 7 peccati di Microsoft Windows 7

Non è passato molto tempo da una mia riflessione sul mondo dell'OpenSource, e ieri ho letto questo articolo molto interessante sulla mentalità (se così la si vuole chiamare) di Microsoft Windows 7.
E ancora c'è gente che mi vuole vendere la storia del se si rompe so a chi chiedere aiuto?

lunedì 7 dicembre 2009

PGDay 2009: un altro evento di successo!

Sono molto soddisfatto del PGDay 2009 che si è tenuto a Pisa, presso l'Università.
Inizialmente ero un po' scettico, devo ammetterlo. Il fatto è che questo è stato un anno pesante, sia per i singoli soci (come me) dell'associazione promotrice (ITPug) che per l'associazione stessa. Eppure ancora una volta, i nostri sforzi hanno dato ottimi risultati.
L'atmosfera che si respirava fin dai primi momenti dell'evento era di festa, di voglia di scambiare idee e opinioni, di crescere condividendo esperienze.
L'affluenza è stata molto buona: circa 60 membri suddivisi fra aziende, privati, università ed enti pubblici. Si poteva fare di meglio, ma va anche considerato che il clima non ci ha certo aiutato (diversi sono rimasti intrappolati nel traffico e nella neve!).
Il Dipartimento di Informatica dell'Università di Pisa è bellissimo, uno dei piu' belli che abbia mai visto (e di facoltà ne ho girate diverse) e la gente è cordiale e disponibile. Le sale a nostra disposizione sono spaziose, luminose, ordinate e ben attrezzate.
Purtroppo si parte con un po' di ritardo, dovuto appunto a problemi climatici, ma poi tutta la giornata si articola in una unica discussione continua su PostgreSQL. L'interesse è alto, e piu' che nelle altre edizioni ho l'impressione che si stia realmente colpendo il pubblico. In un certo senso, era come se il pubblico fosse digiuno di PostgreSQL e rimanesse allibito vedendo con quale semplicità e velocità si potevano fare le cose di piu' alto livello.
Notevole la sezione dei lightning talks, che ha coinvolto praticamente tutti, e ha permesso di verificare che PostgreSQL non è poi un prodotto di nicchia, ma un prodotto molto utilizzato e molto apprezzato.
Penso che ancora una volta ITPug abbia mostrato, sul campo, che è capace di muovere eventi degni di nota. Certo questo non basta, si vuole crescere ancora ed essere in grado di promuovere eventi ancora piu' spettacolari o con maggiore frequenza, e per questo occorre il supporto di tutti i soci. Ma penso che, concretamente, l'associazione si stia comportando davvero bene.
Al prossimo PGDay!

Trenitalia e il warp temporale

A velocità diverse il tempo scorre in modo diverso (teoria della relatività).


Alla Trenitalia questa cosa la devono aver presa molto seriamente quando hanno compilato gli orari dei treni. Capita allora che, alla soglia del 2010, quando su tutte le emittenti ci sono pubblicità a raffiche sull'efficienza e l'eleganza dei nuovi servizi ad alta velocità, 15 minuti di ritardo su un treno si tramutino in circa 2 ore di ritardo sull'arrivo finale.
Mi è capitato infatti di dover andare a Pisa, per il PGDay 2009, e di aver viaggiato in ritardo di 15 minuti con il primo treno diretto a Fidenza; ritardo fatale perché mi ha impedito di prendere la coincidenza. Ho quindi deviato su un altro percorso suggeritomi dal capostazione. Ma anche il secondo treno, partito puntuale, è arrivato con circa 30 minuti di ritardo (avevo 37 minuti per prendere la terza coincidenza). Poco male, ho preso in tempo l'ultimo treno che è partito puntuale alla volta di Pisa...arrivando con 20 minuti di ritardo.
E io complessivamente sono arrivato invece che alle 21.30 (come indicato sui biglietti) alle 23.50!
Beh, se voglio viaggiare bene e puntuale posso spendere di piu' e usare la Freccia Rossa...ma perché devo pagare per avere un disservizio e arrivare con un simile ritardo?

venerdì 27 novembre 2009

SWT e class inheritance

I widget SWT non consentono l'eredità: al loro interno esiste un metodo (checkSubclass()) che effettua dei controlli per garantire che una sottoclasse non venga utilizzata in una applicazione.
Ovviamente questa è una restrizione a run-time, di fatto i componenti possono essere derivati da altri anche se poi non risulteranno utilizzabili a run-time.
Le motivazioni di questa scelta progettuale risiedono nella preferenza della composizione rispetto all'ereditarietà, e mi fa molto piacere vedere che esistano dei framework che si spingono in questa direzione.
Tuttavia ogni tanto è piu' comodo derivare widget da altri piuttosto che usare la composizione (che costringe l'utilizzo di interfacce e la costruzione di API wrapper), ma questo dovrebbe avvenire solo per widget per cui sia garantita la portabilità. Se non si vuole poi implementare la propria logica di controllo nel metodo checkSubclass() è necessario inserire i widgets in un package org.eclipse.swt.widgets.

Spring: RowMapper e avanzamento del ResultSet

I ResultSet passati ad un RowMapper di Spring sono già posizionati sulla prima riga utile, quindi non è necessario fare il classico ciclo alla JDBC:

while( rs.next() ){
...
}

Qualora si debba iterare fra piu' righe del ResultSet occorre convertire il ciclo in un do-while:

do{
...
}while( rs.next() );

SWT: Combo box con modello integrato

Il widget Combo della libreria SWT non consente l'inserimento nel drop down menu' di oggetti qualunque, ma solo di String. Questa è ovviamente una limitazione perché richiede che il combobox sia affiancato da un modello dati che consenta la correlazione fra indice selezionato e oggetto a cui ci si riferisce.
Creare un wrapper per il Combo che contenga un semplice modello dati lineare (monodimensionale) è un'operazione quasi banale. Nell'esempio sottostante si suppone che BusinessObject sia il capostipite degli oggetti che si vogliono selezionare tramite il Combo; questo wrapper rende il comportamento di Combo piu' simile al suo duale Swing JComboBox.

public class BusinessObjectCombo extends Combo {

public BusinessObjectCombo(Composite parent, int style) {
super(parent, style);
}

/**
* An internal list to keep track of all elements this combo is displaying.
*/
private List model = new LinkedList();


/**
* Provides the business object corresponding to the selected element in the combo.
* @return the business object from the combo
*/
public synchronized final BusinessObject getSelectedBusinessObject(){
// get the selected index
int index = this.getSelectionIndex();
// return the element from the model
return this.model.get(index);
}

/**
* Adds a new business object to the combo and to the internal model.
* @param newElement
*/
public synchronized final void add(BusinessObject newElement){
// check arguments
if( newElement == null )
return;
else{
// add the element to the list and to the combo
this.model.add(newElement);
this.add( newElement.toString() );
}
}

/**
* Removes the specified element from the combo.
* @param element
*/
public synchronized final void remove(BusinessObject element){
// get the index from the model
int index = this.model.indexOf(element);
if( index >= 0 ){
this.model.remove(index);
this.remove(index);
}

}


/**
* Clears all the elements from the combo and the internal model.
*/
public synchronized final void clear(){
this.model = new LinkedList();
this.removeAll();
}


/**
* Adds all the elements from the specified collection.
* @param businessObjects
*/
public synchronized final void addAll(Collection businessObjects ){
for( BusinessObject bo : businessObjects )
this.add( bo );
}

}

mercoledì 25 novembre 2009

if( opensource instanceof NonHaValore )

Questo non vuole essere un post-flame sull'OpenSource, e non ha nemmeno la pretesa di essere un post di evangelizzazione. Voglio solo enfatizzare un modello comune di pensiero, quasi un paradigma, che si è diffuso e che è difficile da sradicare in molti sviluppatori, designers, software house, ecc. E ovviamente è un paradigma che limita l'adozione dell'OpenSource.

Quando si parla di soluzioni OpenSource spesso si viene bloccati con affermazioni del tipo "ma se poi mi si rompe, o non va, io (il mio cliente, il cliente del mio cliente, l'amico del cliente del cliente, ...) a chi mi rivolgo?". Detta in altre parole: "vorrei tanto usare l'OpenSource, ma non ho le garanzie di una ditta commerciale alle spalle che mi possa offrire un servizio di assistenza".

E' vero!
Ma se parliamo di piccoli progetti OpenSource. Se parliamo di soluzioni di classi enterprise (qualche esempio: PostgreSQL, MySQL, Eclipse, OpenSolaris) allora le aziende alle spalle ci sono, i contratti di assistenza ci sono, i bugfix ci sono. Quello che manca, nella controparte, è la mentalità.
Le affermazioni della controparte possono essere riscritte con altre parole: "pago perché voglio la garanzia di un'assistenza futura".
Scusa ma cosa paghi?
Paghi il prodotto. Allora perché pagando il prodotto hai diritto all'assistenza futura? Perché paghi anche un contratto di assistenza, ma allora paghi il prodotto e l'assistenza.
E se l'azienda fallisce? Chi ti fornirà assistenza?
Ma le grosse aziende non falliscono! Molte volte questo è vero, ma molte volte le grosse aziende vengono inghiottite da aziende ancora piu' grosse (per fare un esempio si pensi al caso Oracle-MySQL). E in quei casi l'azienda potrebbe cambiare direzione, potrebbe smettere un prodotto considerato obsoleto o un doppione di un prodotto presente in entrambe le parti che partecipano alla fusione.
E l'interoperabilità? Beh, quella solitamente va a braccetto con l'OpenSource, perché se le applicazioni sono OpenSource allora gestire i dati e i flussi di dati risulta concretamente fattibile. Se le applicazioni sono closed-source allora tutto si complica, e spesso ci si deve affidare a programmi di import/export creati dall'azienda stessa o un suo partner. E questi non sempre hanno in mente le vostre esigenze, quindi siete spesso voi a dovervi piegare alle loro. Ma come, pagate il prodotto, pagate l'assistenza, e non ricevete nemmeno il classico servizio che anche un ristoratore vi offre (il cliente ha sempre ragione)?
Ma si sta scendendo nella polemica.

Quello che realmente secondo me deve essere enfatizzato è che la mentalità attuale porta a dire che se una cosa è OpenSource (o peggio ancora free) allora non ha valore. Se lo sviluppatore stesso non si fa pagare significa che non sa quanto valga, e comunque deve valere poco.
Io preferisco pensare che abbia talmente tanto valore da non essere misurabile, e che il vero business dietro all'OpenSource sia il servizio.

E metto in guardia i fanatici: non tutto deve rispettare le quattro libertà della licenza GNU GPL. Il Web 2.0 ha portato sistemi che non possono materialmente rispettare la freedom 0 (capacità di eseguire il programma). Ma questo non significa che si debba cedere sull'OpenSource!

Compile time constraints and runtime constraints: WhiteCat merges both!

Due to the reviews on a WhiteCat paper I received, and the claims from a colleague of mine, I decided to write a little post about my way of thinking OOP and how I conceive it with WhiteCat.

I started developing serious applications using Java version 1. If you remember, at that time there were no fancy features, so the only thing you could have to do to decouple your code was to have a good OOP design. What is a good OOP design? It is a strictly typed system.
Am I a strictly-typed kind of developer? Yes.

In my early stage of software designer I was used to create classes and interfaces as it was raining cats and dogs! Then I read THE book about Perl (Programming Perl) and a statement by Larry Wall really impressed me: Larry wrote that there must a balance between programming-in-a-rush and programming-by-presumption. What does it mean? It means (to me) that if you are a pure OOP designer/developer you will start thinking that you are not developing only a piece of software to solve an assigned problem, but you are creating a creature that everybody could/would use. This leads to a class/interface storm, since you are taking into account every possible use of your piece of software. I was such a developer!
Now I've changed my mind, and I've found a good balance between rush and presumption.

How does my OOP mindset connect to the WhiteCat approach? Well, of course WhiteCat is a creature of mine, and therefore it has been developed following my ideas and design/developing habits. But this of course not the answer you want to read.

WhiteCat is a very dynamic system, and usually dynamism is at the opposite of strong typing (think to Python for instance). Since I am a strictly-typed designer/developer, how can I accept a very dynamic system like WhiteCat?

Short answer: Liskov substitution principle is guarantee to work, so the type system is ensured to work.

Do you need a more detailed answer?
When you compile an application you accept the compiler rules. A strict compiler leads to a typed system (secure), while a weak compiler leads to an untyped system (more dynamic, less secure).
WhiteCat agents, roles, and framework itself is compiled thru a standard Java compiler (well, to be onest thru an AspectJ one), so WhiteCat components obey the same compiler rule.
But WhiteCat allows developers to dynamically change the class definition at runtime; does it means that compile rules are overtaken? No, since WhiteCat guarantees the Liskov substitution principle to work. But this is not all folks: WhiteCat imposes compile-time constraints at runtime! This is the very soul of WhiteCat: being able to start from a strongly-typed system (your agents/role compiled thru a Java compiler) and to modify them at runtime as they were compiled in a different way (i.e., starting from modified sources).
So WhiteCat starts from a typed system and leads to a typed system, but in the middle it is able to change the types in order to improve your agents/roles.

Was this the same as RoleX (a.k.a. BlackCat) did? No, because BlackCat was changing dynamically the agent class making the Liskov substitution principle not applicable. In other words, BlackCat was very dynamic, but started from a typed system to get to an untyped (and unmanageable) system. This is the real difference between WhiteCat and BlackCat.

venerdì 6 novembre 2009

PGDay 2009

Ebbene anche quest'anno ci siamo: ITPug sta organizzando (assieme a tutti i volontari e gli appassionati di PostgreSQL) l'edizione 2009 del PGDay (PostgreSQL Day).
Questa edizione, a differenza di quelle passate, sarà a solo carattere nazionale (il PGDay Europeo si sta svolgendo in questi giorni in Francia). Questo però non significa che la qualità tecnica del meeting sarà inferiore a quella degli altri anni, anzi...basta dare un'occhiata al programma (ancora in costruzione) per accorgersi che non manca davvero nulla per iniziare ad usare o approfondire la propria conoscenza di questo RDBMS.

Forza ragazzi, diamoci da fare!



PGDay 2009 - Scopri il più avanzato database open-source

mercoledì 14 ottobre 2009

Fixed a bug in the array class loading

Thanks to the effort of a friend of mine, Fernando, involved in the Aglets development, a bug about the class loading of an array of objects have been fixed. Today I commited the new code to the git repository.
The bug caused an agent, using an array of objects, to be not loaded well after a migration; the problem was that the Aglets class loader was unable to load the class of the array and instantiate, consequently, the array itself. This happened with a recent JVM (e.g., 1.6) and not with older ones, due to some minor changes in the new JVM array handling.

Installare KDE 4 su OpenSolaris

OpenSolaris è un sistema che mi sta affascinando ogni giorno di piu', ma da buon utente KDE mi trovo un po' spaesato con il desktop GNOME (il default su OpenSolaris), così ho cercato le istruzioni per installare il mio desktop preferito.
Inizialmente mi sono imbattuto nella pagina ufficiale di KDE su OpenSolaris, ma ahimé senza successo. Il problema, mi verrà spiegato poi, è che il repository IPS indicato non e' piu' disponibile, e quindi ho dovuto ripiegare su una versione di un altro repository.
I comandi da lanciare nel terminale di un OpenSolaris 2009.06 sono i seguenti:

pfexec pkg set-authority -O http://solaris.bionicmutton.org:10000 bionicmutton
pfexec pkg refresh
pfexec pkg install qt
pfexec pkg install KDEBase-apps KDEgdm-integration

venerdì 25 settembre 2009

Che maleducazione!

L'altra sera il treno che mi doveva portare a casa si è fermato per quasi un'ora in stazione a causa di un black-out elettrico. Noi passeggeri siamo tutti scesi e ci siamo messi ad aspettare sotto le pensiline, così che il treno è rimasto vuoto.
Ad un certo punto ho sentito uno dei controllori gridare e ho assistito ad una scena realmente raccapricciante: un extracomunitario era risalito su uno dei vagoni per urinare (all'interno del vagone, fra i sedili!).
Essendo stato cappellato l'uomo è sceso dal treno e come nulla fosse si è messo in un angolo attendendo che il treno ripartisse, incurante delle urla del capotreno. Ed è ripartito regolarmente con il treno appena è stato possibile!

Ora mi domando: erano forse i bagni della stazione troppo lontani per lui? Era forse quel cespuglio che si trova accanto ai binari troppo scomodo per lui?
E non era forse il caso di chiamare anche una qualche guardia per meglio fargli comprendere la gravità del suo gesto?

E poi facciamo spot televisi e trasmissioni proclamando l'importanza dell'igiene e del lavarsi spesso le mani...

mercoledì 29 luglio 2009

New GIT repository for Aglets

After a quite long suffering with the CVS repository provided by SourceForge, I moved the Aglets code base to a GIT repository, still hosted at SourceForge (see the official news).
I haven't moved the official CVS module aglets, but instead I've integrated my own branch which includes the new threading scheme, the new GUI (based on Swing), the new message queueing and the full localization of all messages.
I hope this change will ease the development and, in the meantime, attract new developers to work on the code base. Quite surprisingly the number of project members is growing, even if the commits are not, but of course there must be time for new members to learn the code before they can work on it!
At the moment the documentation is still on the CVS repository, but I guess it will be migrated sooner or later. The CVS repository is still active, in order to allow GIT-newbies to take a look at the code without having to use GIT, but CVS will be disabled soon so I recommend using the GIT repository instead.

giovedì 23 luglio 2009

Happy Birthday JPUG!

Il 23 Luglio è una data importante per il Japan PostgreSQL Users' Group (JPUG): dieci anni fa nasceva infatti questa associazione per la promozione e la divulgazione di PostgreSQL in Giappone.
E' dietro la spinta di Mr. Ohtani e Mr. Tado, che la comunità di utenti di una mailing list tecnica (pgsql-jp) si aggrega fino a diventare una associazione per la divulgazione di PostgreSQL. E in questi dieci anni JPUG ha contribuito notevolmente alla crescita degli utenti PostgreSQL, basti pensare che questo è il database maggiormente utilizzato in Giappone (e anche il primo ad essere supportato da Ruby!).
Per celebrare il decennale di JPUG dal 20 al 21 Novembre 2009 si terrà a Tokyo una conferenza interamente dedicata a PostgreSQL.

Avendo conosciuto due membri del JPUG, Hiroshi Saito e Ishikawa Toshiyuki, posso confermare il profondo interesse e la grande serietà delle persone che lavorano per JPUG.

martedì 30 giugno 2009

Setter, Getter o entrambi?

I Java Beans hanno introdotto diverse convenzioni, fra le quali quelle circa l'utilizzo dei metodi getter e setter: i primi sono metodi che ritornano il valore di un attributo d'istanza, i secondi sono mutator che impostano il valore di tale attributo. La convenzione vuole che il getter abbia un nome che inizia con get, seguito dal nome della proprietà (variabile di istanza) con la prima lettera maiuscola e non abbia alcun parametro, ma ritorni un tipo pari a quello della proprietà. Il setter segue regole simili, e ha il suo nome composto da set e il nome della proprietà (prima lettera maiuscola) e riceva esattamente un argomento del tipo della proprietà senza ritornare al chiamante nulla.
Da quanto sopra si ha che per ogni proprietà servono almeno due metodi distinti, un getter e un setter, abbastanza semplici e rapidi da scrivere (addirittura gli IDE moderni consentono di creare tali metodi con un click). E' però possibile prendere in prestito da Perl il comportamento dei metodi e scrivere un metodo unico che svolga entrambe le funzioni.
Si consideri la seguente semplice classe:

public class Person {

private String name = null;
private String surname = null;
...
}



e si supponga di voler scrivere un unico metodo name che possa funzionare sia da getter che da setter a seconda del contesto in cui viene chiamato. In altre parole il seguente main deve funzionare:

public static void main(String[] args) {
Person p = new Person();
p.name( "Luca" );
System.out.println("Persona " + p.name() );

}

Come si può notare il metodo name viene chiamato in due contesti diversi: se gli viene passato un parametro si deve comportare come setter, altrimenti come getter.
Come si puo' ottenere un simile comportamento? Usando gli argomenti opzionali introdotti da Java 5 è abbastanza semplice:

 public String name(String...newName){
// controllo se e' stato passato
// un parametro o meno
if( newName != null && newName.length == 1 )
// passato un parametro => metodo setter
this.name = newName[0];

// in ogni caso ritorno il valore
// corrente come metodo getter
return this.name;
}

Come si può notare il metodo si comporta come getter sempre, ovvero restituisce sempre il valore della proprietà name. Il metodo poi indovina il contesto di chiamata analizzando gli eventuali parametri: se ne sono stati passati al massimo uno allora il metodo si deve comportare come setter. E' possibile rilassare il test sul numero di parametri considerando valida una lista di parametri qualsivoglia lunga, e usando solo il primo come setter, ma questa scelta dipende dall'interfaccia che si vuole dare al metodo.
Il fatto che il metodo restituisca sempre, anche se chiamato in un contesto di setter, il valore della proprietà non è una limitazione, anzi consente di effettuare un method-chain invocando piu' metodi in cascata.

mercoledì 24 giugno 2009

Creare un modulo per il kernel di Linux

Questo breve articolo mostra come creare e caricare un semplice modulo per il kernel di Linux, in modo da dare accesso alle funzionalità del kernel.
Un modulo deve essere scritto in linguaggio C, compilato in un modo un po' particolare (ma tutto sommato semplice) e installato e rimosso mediante i comandi insmod, depmod e similari.

Vediamo subito il codice di un modulo che viene caricato in memoria e appena attivato stampa la classica frase Hello World:

/* Declare what kind of code we want from the header files */
#define __KERNEL__ /* We're part of the kernel */
#define MODULE /* Not a permanent part, though. */

#include

/* Initialize the LKM */
int init_module()
{
printk("Modulo in fase di inizializzazione...\n");


printk("\nHELLO WORLD!\n");

/* If we return a non zero value, it means that
* init_module failed and the LKM can't be loaded
*/
return 0;
}

/* Cleanup − undo whatever init_module did */
void cleanup_module(){
printk("Modulo in fase di uscita\n");
printk("\nBYE BYE WORLD");
}

La parte relativa agli header, come pure i prototipi delle funzioni, sono standard. In particolare init_module viene richiamata al momento dell'installazione del modulo, mentre cleanup_module al momento della sua rimozione.
La compilazione del modulo avviene tramite il seguente Makefile:

obj-m := HelloWorld.o

KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

Una volta compilato il modulo, sarà disponibile un file .ko (Kernel Object) che può essere caricato nel sistema. Siccome stiamo parlando di un semplice modulo di prova, anziché installare il modulo direttamente nella directory modules possiamo installare e rimuoverlo manualmente.
Per installarlo basta usare il comando insmod:


 insmod ./HelloWorld.ko

Una volta installato il modulo (è possibile controllarne l'installazione con lsmod) si possono vedere i primi messaggi del kernel. Un dmesg mostra infatti la stampa della funzione di inizializzazione.
Rimuovendo poi il modulo con:

 rmmod HelloWorld
si avrà lo scaricamento dalla memoria del modulo e la scrittura fra i messaggi del kernel che la funzione di chiusura del modulo è stata richiamata.

lunedì 22 giugno 2009

Programmazione a oggetti in Perl

Perl è un linguaggio molto elegante e potente, che viene spesso insegnato per evidenziare concetti un po' più evoluti riguardo lo scripting.
Pur supportando la programmazione ad oggetti, Perl non fornisce alcun costrutto particolare per la realizzazione di classi e tipi di dato astratto. Si può paragonare Perl a un C che viene programmato ad-hoc per implementare un comportamento ad oggetti.
Imparare a programmare Perl a oggetti è molto interessante, soprattutto perché permette di vedere effettivamente come molti compilatori a oggetti si comportano. Inoltre gran parte della libreria Perl/CPAN è orientata agli oggetti, e quindi è indispensabile avere nozioni di OOP. Non è comunque facile trovare una guida sulla programmazione OOP in Perl, perciò ho deciso di scrivere questo articolo introduttivo. Da notare che farò riferimento a versioni di Perl inferiori alla 6, dove la scrittura di classi e oggetti cambierà grazie alla presenza di apposite parole chiave.

Il seguente elenco mostra i punti per me fondamentali da tenere a mente quando si vuole implementare una classe e di conseguenza usare degli oggetti.

Gli oggetti possono essere acceduti solo per riferimento.

Ogni istanza di oggetto viene mantenuta in un riferimento, in maniera simile a come avviene in Java.

Un riferimento ad un oggetto è un normale riferimento scalare
.
Come i riferimenti alle strutture dati native (hash, array) sono contenuti in variabili scalari, così pure i riferimenti agli oggetti sono mantenuti in variabili scalari.

Una classe corrisponde ad uno spazio dei nomi, e quindi si implementa tramite un package.

In Perl una classe è una entità sul quale sono richiamati dei metodi. Per la precisione, un metodo di classe è un metodo al quale viene passato un parametro che indica lo spazio dei nomi nel quale si agisce. Ecco quindi che assegnare un metodo ad una classe corrisponde ad inserire un metodo in un determinato package: il metodo potrà essere invocato solo nell'ambito di quello spazio dei nomi (classe/package).

Se la struttura dati dell'oggetto è complessa, allora essa deve essere realizzata tramite un array o un hash, e il riferimento all'oggetto diventa il riferimento all'array/hash.
Questo punto va chiarito bene: in Perl esistono solo tre tipi di variabili (scalari, array, hash) e una classe sarà presumibilmente composta da diverse variabili di tale tipo. Dovendo però usare un riferimento ordinario (si vedano i punti precedenti) per l'oggetto, si ha che tale riferimento deve essere il punto di ingresso di una struttura più complessa. Di conseguenza, il riferimento all'oggetto deve essere un riferimento ad un array/hash che contenga tutte le altre variabili di istanza. Per meglio comprendere, se in Java una definizione di classe Person potrebbe essere così strutturata:
public class Person{
private String name = null;
private String surname = null;
}

In Perl non potrebbe avere la definizione duale, ossia:

package Person;

$name = undef();
$surname = undef();
poiché, così facendo, si avrebbe che lo spazio dei nomi della persona (Person) contiene due variabili scalari, ed è impossibile ritornare un singolo riferimento che fornisca accesso ad entrambe le variabili. E' quindi necessario incapsulare le variabili di classe all'interno di un unico contenitore e fornire accesso attraverso tale contenitore:

package Person;
...
$this = { name => undef(),
surname => undef(),
};


In questo modo la variabile $this consente accesso ai valori di name e surname, ed è possibile ritornare all'esterno un singolo riferimento a $this.
Da notare che la seguente traduzione non funziona come ci si aspetta:

package Person;

$name = undef();
$surname = undef();

$this = { name => \$name,
surname => \$surname,
};

Infatti così facendo si ha l'esistenza di uniche variabili $name e $surname e di conseguenza i riferimenti di ogni oggetto creato puntano alle stesse variabili globali (all'interno del package) e quindi agli stessi valori. La soluzione di cui sopra è invece appropriata per variabili statiche (ossia condivise all'interno di ogni classe).

Un metodo di classe o di oggetto riceve sempre in automatico un primo argomento che specifica la classe o l'istanza
.
Questa cosa non dovrebbe stupire particolarmente, poiché è già quello che avviene nei normali compilatori ad oggetti: il primo parametro di un metodo di istanza è il puntatore/riferimento all'istanza stessa. E' in questo modo che i compilatori predispongono la variabile this. In altre parole, il metodo Java seguente:
public class MyClass{
public void instanceMethod(String arg1, int arg2){
this.stringVariable = arg1;
this.integerVariable = arg2;
}
}

viene mutato dal compilatore silenziosamente nel seguente:

public class MyClass{
public void instanceMethod(MyClass this, String arg1, int arg2){
this.stringVariable = arg1;
this.integerVariable = arg2;
}
}

Da notare che il primo parametro, di nome simbolico this, è un riferimento all'oggetto stesso. In altre parole i metodi di istanza ottengon come parametro la zona di memoria (o meglio il riferimento a tale zona) dove risiede l'oggetto sul quale si agisce. Perl segue lo stesso percorso: il primo argomento di un metodo di istanza contiene il riferimento all'oggetto stesso, di modo che il metodo possa essere scritto come:

sub instanceMethod{
my ($this, $arg1, $arg2) = @_;
...
}
La differenza è che qui è compito del programmatore ricordarsi che il primo argomento è il riferimento all'istanza; il compilatore Perl infatti fa solo la prima parte del lavoro (passare al metodo il riferimento come primo argomento).
Quanto sopra per quello che riguarda gli oggetti, ma per le classi?
In un linguaggio OOP come Java/C++ i metodi di classe (es. static) non necessitano di nessun argomento particolare o di modifiche da parte del compilatore. Infatti quello che avviene è che viene tenuta in memoria la tabella di dispatching dei metodi e l'invocazione di un metodo corrisponde al salto all'indirizzo fornito da questa tabella. Ma il metodo non ha bisogno di sapere se appartiene ad una classe oppure no, come non ha bisogno di sapere a che classe appartiene poiché per forza di cose, se non è di istanza, deve essere di classe.
In Perl questo non è vero: essendo una classe implementata tramite un package, i metodi all'interno del package potrebbero essere di istanza, oppure di classe, oppure nessuno dei due (normali metodi globali). Perl allora fornisce un modo per informare un metodo che è stato invocato in un contesto di classe (ossia come se fosse un metodo di classe): questo metodo riceverà infatti come primo argomento il nome (stringa) della classe/package al quale appartiene.
Riassumendo quindi, tutti i metodi di classe o di oggetto in Perl ricevono come primo argomento una variabile che indica il nome della classe (metodo di classe) o il riferimento all'oggetto stesso (metodo di oggetto) e non ricevono nulla se sono chiamati in un normale contesto di package.

In Perl non esiste il concetto di costruttore (e di distruttore)
.
Siccome Perl non fornisce nessuna parole chiave per definire una classe, i metodi costruttori (e distruttori) non esistono. Questo non significa che non si possano creare dei metodi appositi per l'inizializzazione delle variabili di un oggetto. Tali metodi dovranno ovviamente restituire al chiamante il riferimento alla struttura dati dell'oggetto.

Un metodo usato come costruttore non deve avere lo stesso nome della classe
.
Siccome questi metodi non sono dei veri e propri costruttori, non devono seguire le regole standard dei costruttori Java e C++: essi possono (o meglio devono) avere un tipo di ritorno (solitamente il riferimento alla struttura dati dell'oggetto) e possono avere un nome non vincolante (solitamente new).

In Perl non esiste l'operatore new
.
Quando si incontra qualcosa che assomiglia all'operatore new per l'allocazione di un oggetto, si sta in effetti osservando la chiamata di un metodo di nome new (si veda il punto precedente).
Il compilatore passa automaticamente ad un metodo il primo argomento (nome della classe o riferimento ai dati) quando si usa l'operatore freccia.
Quando la chiamata ad un metodo avviene attraverso l'operatore freccia, il compilatore passa come primo argomento del metodo l'oggetto lvalue su cui viene applicato l'operatore freccia. In altre parole:
LVALUE->method(arg1, arg2, arg3);

corrisponde a:
method(LVALUE, arg1, arg2, arg3);
Questo significa quindi che usando l'operatore freccia su i metodi di un package si ha automaticamente una chiamata a metodo di classe, mentre usando l'operatore freccia su dei riferimenti ad oggetti si ha una chiamata ad un metodo di istanza. Quindi ad esempio, per creare un nuovo oggetto, si può invocare il metodo new nei due modi equivalenti:
$objectReference = Person->new('Luca', 'Ferrari'); # sintassi di classe
$objectReference = new( Person, 'Luca', 'Ferrari'); # sintassi di package


e analogamente, una invocazione di metodo di oggetto può essere scritta come:

$objectReference->setName('Giovanni'); # sintassi di oggetto
Person::setName( $objectReference, 'Giovanni'); # sintassi di package

Perl deve sapere quali riferimenti puntano ad un oggetto e quali no.
Il costruttore di un oggetto non deve solo inizializzare l'oggetto e il suo stato, ma deve anche informare Perl che il riferimento non è un normale riferimento ma un collegamento ad un oggetto. Questo avviene tramite l'operatore bless, che accetta il riferimento ad una struttura dati e la classe/package a cui la struttura dati appartiene. L'operatore memorizza in una tabella il collegamento fra il riferimento e il nome del package/classe a cui è associato, in modo da poter individuare a quale classe il riferimento appartiene. In particolare, in seguito al blessing, l'operatore ref applicato ad un riferimento di oggetto restituisce il nome della classe per la quale è stato fatto il blessing.
Sulla base di questa considerazione, il template generale per un costruttore di oggetto è il seguente:

sub new
{
my ($class, @args) = @_;
# inizializza i valori dell'oggetto
$this = { arg1 => $args[0],
arg2 => $args[1],
...
};

bless( $this, $class );
return $this;
}





Sulla base di tutti i punti di cui sopra, è possibile paragonare la creazione di una classe/oggetto fra un linguaggio OOP (es. Java) e Perl. Supponiamo di avere la seguente classe Java:

public class Person{
public static int personCounter = 0;

private String nome = null;
private String cognome = null;

public Person(String nome, String cognome){
this.nome = nome;
this.cognome = cognome;
Person.personCounter++;
}


public String getNome(){
return this.nome;
}

public String getCognome(){
return this.cognome;
}
}

e di volerla tradurre in Perl. La sua definizione diviene la seguente:

#!/usr/bin/perl

package Person;

# variabile statica (la stessa
# condivisa fra tutte le istanze)
$personCounter = 0;


sub new
{
my ($class, $name, $surname) = @_;

# creazione nuovo riferimento ad hash
# con i dati (stato) dell'oggetto
$this = { name => $name,
surname => $surname,
};



# associo il riferimento alla classe
bless( $this, $class );

# incremento il contatore di istanze create
$personCounter++;

print "\nCreazione di un nuovo oggetto di tipo $class (numero $personCounter)\n";

# restituisco il riferimento appena creato
return $this;
}




sub getNome
{
my ($this) = @_;

# restituisco il dato richiesto
return $this->{name};
}


sub getCognome
{
my ($this) = @_;

# restituisco il dato richiesto
return $this->{surname};
}




che può essere testato con il seguente main:

package main;

$person1 = Person->new('Luca','Ferrari');
$person2 = Person->new('Paperon','De Paperoni');

print "\nPersona con nome ", $person1->getNome();
print "\nAltra persona con nome ", $person2->getNome(), " ", $person2->getCognome();
print "\n\n";


che produce il seguente output:

Creazione di un nuovo oggetto di tipo Person (numero 1)

Creazione di un nuovo oggetto di tipo Person (numero 2)

Persona con nome Luca
Altra persona con nome Paperon De Paperoni


Ereditarietà e Polimorfismo
L'ereditarietà in Perl viene implementata in modo molto semplice: è disponibile un array globale all'interno dello spazio dei nomi di un package, denominato ISA, che contiene le classi/package dalle quali si vuole ereditare. L'idea quindi è semplice: ogni package definisce una lista di ulteriori package in cui cercare dati e/o metodi. Quando Perl non trova un attributo nel package specificato inizia ad esplorare (sequenzialmente) l'array ISA al fine di trovare in ogni classe specificata il simbolo, e quindi lo utilizza.
Per meglio comprendere questo concetto, si consideri di estendere la classe Person con una classe Male che implementa una persona di sesso maschile:


package Male;

@ISA=(Person);

sub new
{
my ($class, $name, $surname) = @_;

# creazione nuovo riferimento ad hash
# con i dati (stato) dell'oggetto
$this = { name => $name,
surname => $surname,
};



# associo il riferimento alla classe
bless( $this, $class );

# incremento il contatore di istanze create
$personCounter++;

print "\nCreazione di un nuovo oggetto di tipo $class (numero $personCounter)\n";

# restituisco il riferimento appena creato
return $this;
}


Come si può notare la definizione del costruttore è praticamente la stessa della classe Person, addirittura si referenzia un simbolo ($personCounter) che qui non è stato ancora definito. Ora se un programma crea un nuovo oggetto di tipo Male:

$maschio = Male->new( $person1->getNome(), $person1->getCognome() );
print "\nPersona maschio con nome ", $maschio->getNome(), " ", $maschio->getCognome();


l'output di esecuzione è del tutto simile a quello del programma precedente, e addirittura la variabile $personCounter inizia il suo conteggio come fosse separata dalla classe Person (ovvero come fosse stata definita singolarmente di classe). Tutta la magia viene fatta nella ricerca dei simboli, che anziché passare per una vtable passa per l'array ISA del package corrente (ogni package può ridefinire il proprio @ISA).

La ridefinizione dei metodi è automatica, quindi se si ridefinisce il seguente metodo nella classe Male si ha che la chiamata a getNome() verrà risolta da tale metodo:

package Male;

@ISA=(Person);

sub new
{
my ($class, $name, $surname) = @_;

# creazione nuovo riferimento ad hash
# con i dati (stato) dell'oggetto
$this = { name => $name,
surname => $surname,
};



# associo il riferimento alla classe
bless( $this, $class );

# incremento il contatore di istanze create
$personCounter++;

print "\nCreazione di un nuovo oggetto di tipo $class (numero $personCounter)\n";

# restituisco il riferimento appena creato
return $this;
}



sub getNome
{
my ($this) = @_;

return $this->{name} ." (maschio) ";
}


E' inoltre possibile riferirsi alle proprietà della classe base mediante l'uso di SUPER. Si presti attenzione poiché qui super è un operatore che si preoccupa di salire di un livello nella scala dei riferimenti, ma il nome della classe deve essere sempre specificato come primo argomento: SUPER deve quindi essere usato in combinazione con l'operatore freccia.

package Male;

@ISA=(Person);

sub new
{
my ($class, $name, $surname) = @_;

$this->SUPER::new( $name, $surname );


# associo il riferimento alla classe
bless( $this, $class );

# incremento il contatore di istanze create
$personCounter++;

print "\nCreazione di un nuovo oggetto di tipo $class (numero $personCounter)\n";

# restituisco il riferimento appena creato
return $this;
}



sub getNome
{
my ($this) = @_;

return $this->SUPER::getNome() ." (maschio) ";
}


Si noti che quando si usa SUPER la parte a destra viene sempre invocata con la sintassi di package (::) mentre quella di sinistra con la sintassi ad oggetto (->).


Per concludere
Riassumendo quindi si ha che in Perl la programmazione ad oggetti viene attualmente implementata tenendo separati i metodi e i dati, in modo piuttosto esplicito. Il riferimento ai dati rappresenta il riferimento all'oggetto (e quindi al suo stato), mentre l'invocazione dei metodi avviene grazie all'operatore freccia, che impone al compilatore di passare l'entità di sinistra (il riferimento o il nome della classe) come primo argomento. Grazie poi ad una tabella dei simboli gestita tramite l'operatore bless si ha l'associazione fra riferimento ai dati e classe, e questo consente, in combinazione all'array @ISA di sfruttare anche l'ereditarietà e il polimorfismo.

mercoledì 17 giugno 2009

Linus può...

Ispirandosi alle scenette di Chuck Norris dello Zoo di 105, ecco le abilità segrete di Linus Torvalds...

domenica 7 giugno 2009

Il ruolo delle Materialized-Views

Una vista materializzata (Materialized-View) non è altro che una tabella (solitamente temporanea) che memorizza il risultato di una particolare query su dati di dimensioni evelate. L'idea è quella di anticipare le richieste di risorse di una query estraendo i dati prima che il client effettui la query.
Supponendo di avere una tabella molto vasta (qualche milione di righe), e sapendo in anticipo su quali proiezioni di dati i client vogliono lavorare, è possibile estrarre questi dati in una elaborazione batch, e far si che i client accedano a questo sotto-insieme dei dati già estratti.
Quali sono i vantaggi?
Anzitutto sulla tabella così costruita è possibile costruire indici ad-hoc, e siccome questa tabella non deve essere aggiornata (poiché rappresenta una porzione dei dati sorgenti, e andranno aggiornati solo questi ultimi), anche gli indici non devono essere aggiornati. Ne consegue che in questo caso gli indici rappresentano un caso di puro catalizzatore di accesso.
In secondo luogo la tabella così costruita può contenere il risultato di diverse computazioni che altrimenti andrebbero eseguite al volo, con conseguente richiesta di tempo e risorse.
Ma le viste materializzate hanno anche degli svantaggi: il primo fra tutti è che si opera su dei dati non aggiornati, e questo richiede infatti una buona progettazione per stabilire ogni quanto la vista deve essere annullata e ricreata (o quantomeno aggiornata). Il secondo svantaggio è la richiesta di spazio. ai dati originali si aggiunge la copia costruita ad-hoc.
Non esiste una regola fissa per l'uso delle viste materializzate: occorre analizzare attentamente il carico di lavoro sul database, le query effettuate dal client e le eventuali richieste OLAP (assieme alla loro frequenza). Solo così si sarà in grado di realizzare un sistema con viste materializzate che funzioni.

Patella con aggancio triplo

Osservando alcune foto di arcieri americani mi è saltata all'occhio la patella usata da Brady Ellison. Cos'ha di particolare? Ebbene la patella in questione differisce da quelle tradizionali perché dotata di un sistema di aggancio triplo: anziché avere l'elastico di aggancio attorno al solo dito medio, è dotata di tre elastici che avvolgono indice, medio e anulare.


Dopo qualche indagine in rete
ho scoperto che la suddetta patella viene costruita appostiamente presso il centro JOAD dall'attuale coach K. Lee e che è disponibile in commercio una versione con il piatto metallico già predisposto per un simile aggancio a tre dita.
Non sono particolarmente fiducioso sul fatto che un tab con wrapper per le tre dita sia realmente indispensabile, ma sicuramente è un'idea interessante. Il problema secondo me è che, mentre con un tab classico quest'ultimo ha una cera liberta di movimento e si può automaticamente posizionare sulla corda secondo la pressione delle dita, quello con triplo wrapper rischia di posizionarsi male sulla mano e di rimanere fissato in quella posizione per tutti i tiri, con conseguente problemi sull'uscita della corda. In sostanza, a mio parere, il triplo wrapper può risultare molto comodo in situazioni climatiche avverse, ovvero quando la mano è comunque umida (sudore o pioggia), ma il suo posizionamento deve essere ancora più accurato e controllato al momento della salita dell'arciere in pedana. Sono curioso comunque di fare una prova modificando una delle mie patelle per provare, anche se rimango dell'opinione che il singolo wrap non stretto sia il migliore. Chi si ricorda dei vecchi wrap metallici delle Cavalier e ha presente le rotazioni che Justin Huish faceva compiere al tab prima di ogni tiro capisce di cosa parlo...

mercoledì 3 giugno 2009

Scream

Niente da dire, Chris Cornell non si smentisce mai.
Ho iniziato ad apprezzarlo fin dai tempi dei Soundgarden, che rimpiango moltissimo.
Il progetto degli Audioslave è stato molto buono, e sono perfino adnato a vedere il loro concerto al Flippaut di Bologna.
E ora questo bellissimo album da solista, a mio avviso migliore di Euphoria Morning.
Continua così! (ma per via di una reunion con i Soundgarden?)

martedì 2 giugno 2009

Qualità della rivista federale

Arcieri è la rivista ufficiale della FITARCO, e viene mandata a tutti i tesserati che ne facciano richiesta al momento dell'iscrizione in federazione. Essendo iscritto alla federazione da parecchi anni (inizia nel 1991) ho visto l'evoluzione della rivista fino a questi giorni, e devo dire che sono molto deluso da come sia diventata.
Infatti, mentre una volta la rivista includeva articoli tecnici, spunti di riflessioni, e foto di grandi campioni al tiro, oggi include solo resoconti di eventi arcieristici, pochissimi (se non nessuno) articolo tecnico degno di nota, e solo foto di podi e premazioni.
Perché è così importante la qualità tecnica della rivista?
Anzitutto perché la rivista rappresenta il primo contatto che l'atleta ha con la sua federazione, e di conseguenza una rivista accattivante e che invoglia alla lettura implica fiducia e partecipazione nella federazione (non è automaticamente vero il contrario però). In secondo luogo la rivista rappresenta una forma di informazioni autorevole e ufficiale, e quindi è una sorta di guida ufficialmente approvata per chi vuole praticare la disciplina in Italia.
Per meglio comprendere cosa manchi, a mio avviso, alla rivista oggi, è sufficiente fare qualche confronto con i numeri delle precedenti edizioni. Citerò qualche esempio, quelli che mi vengono in mente, ma ovviamente si potrebbe analizzare ogni singolo numero per trovarne altri.
Una volta veniva pubblicata una serie di articoli specifici sull'Hunter&Field, nei quali venivano presentate le principali tecniche di valutazioni delle distanze, gli errori di posizionamento ecc.; oggi di questa tipologia di articoli non esiste traccia.
Una volta venivano pubblicate interviste a grandi campioni internazionali, mentre oggi non vedo nessuna intervista.
Una volta venivano pubblicati articoli tecnici sulle metodologie di messa a punto dell'arco, sulla selezione delle freccie, e su esercizi di allenamento. Ora sono rimaste solo alcune timide rubriche sull'allenamento fisico, che però lasciano realmente il tempo che trovano. Qualche volta si è trovato un estratto tecnico di un qualche libro, molto interessante a mio giudizio, ma la serie di articoli non ha avuto seguito.
Oltre a queste considerazioni qualitative, ne va fatta anche una quantitiva: la lunghezza della rivista è rimasta pressoché la stessa negli anni, ma ora la rivista include anche il Notiziario Federale, che veniva invece mandato a parte. Essendo ancora presenti le classifiche degli eventi arcieristici, ed essendo presente (giustamente) piu' pubblicità che un tempo, cosa è stato rimosso per far spazio al notiziario federale? Forse sono state tagliate anche le foto? Eppure quelle ci sono, ma sono foto di scarso valore tecnico, poiché quasi tutte ritraggono gruppi di atleti e podi. Certo è molto importante dare gratificazione ai nostri atleti, ma una volta questa veniva data pubblicando una foto dell'atleta al tiro, perché ora non viene fatto? Forse è piu' importante la sua divisa (nazionale) che la sua attrezzatura? Eh si, perché una volta dalle foto si potevano anche spiare le attrezzature dei campioni, vedere il loro setup per poi ragionarcici sopra. Lo stesso discorso valeva per le loro tecniche. Insomma, anche da una foto poteva nascere uno spunto di riflessione!
Poi c'è il resoconto degli eventi. Si, perché questo non manca, ma viene fatto in maniera asettica, senza nessun risvolto tecnico. Non si parla delle tecniche, tecnologie e stranezze di vincitori e vinti. Ci si limita a dare un secco bollettino di come è andato l'evento. Ma questo lo si puo' avere quasi in tempo reale con altri mezzi di informazione, ed ecco quindi che se la rivista mi deve riproporre, con un ritardo di circa due mesi (quando va bene) informazioni vecchie, francamente la voglia di leggerla si tramuta in voglia di sfogliarla!
Ora, ho provato a sondare il terreno e vedere se qualcun altro avesse questa mia impressione, ma apparentemente la gente oggi è talmente bombardata da informazioni che se non trova quello che cerca sulla rivista lo va a cercare in Internet o da un'altra parte. Ma questa non è una giustificazione perché la rivista abbia perso spessore tecnico. E si badi che io non penso alla rivista come la bibbia dalla quale copiare (anche se dopotutto c'è gente che copia da libri ben peggiori!), ma come un possibile spunto di riflessione. E se questi vengono a mancare, allora perché leggerla?

E giusto per fare un esempio, si consideri il numero 2 del 2009 (l'ultimo al momento in cui scrivo). Ebbene in questo numero i campionati del mondo indoor sono liquidati in appena due pagine, alle quali si sommano altre due pagine di rapide considerazioni sul settore giovanile. Non viene citato in nessun punto alcuna curiosità tecnica o tecnologica sui vincitori, addirittura non si sa chi componga la squadra USA (vincitrice dell'oro maschile), visto che il terzo componente non compare nemmeno nella classifica a fine rivista. Ancora: viene presentato un articolo sulle contratture muscolari, molto interessante. Però l'articolo (anch'esso di due pagine - come quello sui mondiali - quasi avessero lo stesso spessore) presenta due foto di un'atleta olimpionico che effettua esercizi con un elastico alla spalliera. Cosa c'è di strano? Beh, non si citano le figure e/o gli esercizi in tutto l'articolo. Allora le foto a cosa servono? A riempire spazio? Vengono inserite per alimentare l'immaginazione del lettore sul loro scopo? E infine l'articolo della rubrica agonismo sul ritorno della Freccia D'Argento in versione giovanile; nessun commento tecnico.
Possibile che non si possa pubblicare qualcosa di meglio?

lunedì 1 giugno 2009

Non usate Facebook

Un articolo veramente interessante sull'uso di Facebook, del quale questa frase mi sembra riassuma abbastanza bene il contenuto:

Futhermore, have you Facebook users ever actually read the privacy policy? It tells you that you don't have much privacy.

giovedì 28 maggio 2009

Log Collector

PostgreSQL permettere di effettuare il log delle attività del database principalmente in due modi: il primo è tramite il classico syslog, il secondo è tramite un log collector. Quest'ultimo è un processo specializzato con il quale tutti i backend comunicano, e il cui unico scopo è quello di inserire su log le informazioni ricevute. L'uso del log collector consente di avere un log molto piu' pulito e completo rispetto a quello ottenuto con syslog, anche perché il primo puo' facilmente loggare informazioni circa il mancato caricamento di un modulo (ad esempio di linguaggio) o altro.
Il log collector viene abilitato agendo sul parametro di configurazione di postgresql.conf:

logging_collector = on
(richiede il riavvio del cluster)

Da questo momento il log collector è attivo e inizierà a memorizzare i log secondo le impostazioni fornite nel file postgresql.conf. E' possibile notare la differenza nel run-time nel caso di abilitazione o meno del log collector: con il log collector attivo la lista dei processi postgresql aumenta di una unità (il collector appunto).
Qualora poi le informazioni diventino troppe, o si voglia forzare una rotazione dei file di log, è possibile da un terminare invocare la funzione pg_rotate_logfile():

select pg_rotate_logfile();

e ottenere la creazione di un nuovo file di log.

lunedì 25 maggio 2009

Amarok 2.0.2: caricare file sull'ipod

Nelle versioni 1.x di Amarok, caricare file sull'ipod (o su un lettore MP3 collegato) era banale. Bastava infatti trascinare i file dalla playlist all'elenco dei brani del lettore MP3 per accodarli, dopodiché si doveva semplicemente premere il pulsante Transfer per passarli definitivamente al lettore.
Nella versione 2.x di Amarok questa procedura non esiste piu', e occorre usare un metodo piu' semplice e immediato, ma meno intuitivo e documentato. Anzitutto occorre notare che in Amarok 2.x l'ipod (o il lettore MP3) compare come una Collection a sé. Per trasferire i file è quindi necessario andare nella sezione Files, individuare i file da trasferire, selezionarli, e fare click con il tasto destro su di essi. Si aprirà un menu' contestuale, dal quale è possibile selezionare la voce Copy To => iPod Collection. E il gioco è fatto: i file saranno immediatamente trasferiti sull'iPod.

lunedì 18 maggio 2009

Drop table e cancellazione del file su disco

Quando si esegue un drop table, PostgreSQL azzera il file fisico su disco corrispondente alla tabella specificata (pg_class.relfilenode) ma non cancella il file da disco. La ragione di ciò è nell'evitare di avere diverse possibili corse critiche, come ad esempio un riassegnamento di quel relfilenode ad un'altra tabella appena creata. In sostanza PostgreSQL tiene da parte una lista di file node disponibili per essere riciclati, e nel caso una drop table sia seguita da una create table, riutilizza il file node già allocato. In questo modo non si deve chiedere al sistema operativo di ricreare il file su disco, risparmiando tempo e risorse.
Ovviamente però potrebbe capitare che il file non sia mai piu' riciclato, ed è per questo che PostgreSQL quando giunge al prossimo checkpoint verifica la lista dei relfilenode riutilizzabili, e rimuove fisicamente da disco quelli che non stati riassegnati.

venerdì 15 maggio 2009

Vacuum

Una cosa che molto spesso lascia perplessi gli utenti che si avvicinano a PostgreSQL è l'uso di vacuum per l'analisi di una tabella e la sua "pulizia" da tuple morte. La necessità dell'utilizzo di vacuum deriva dal sistema di storage non-overwriting che PostgreSQL ha adottato fin dalle prime versioni, e che poi oggi è parte integrante del sistema MVCC. Senza dilungarsi troppo sull'argomento, l'idea è che ogni tupla modificata (ossia una tupla sottoposta a scrittura come UPDATE o DELETE) non viene aggiornata in loco, ma viene appesa in fondo alla tabella come nuova. La vecchia tupla viene poi invalidata (coerentemente con le transazioni in corso) e solo la nuova rimane (se correttamente committata). Mediante questa tecnica il database riesce ad implementare un buon isolamento fra le transazioni senza richiedere troppi lock.
Questo però richiede che ogni tanto venga reclamato lo spazio occupato da tuple morte, e questo è uno dei compiti di vacuum. Oltre a questo, vacuum si occupa di aggiornare le statistiche di sistema per poter fornire dati precisi all'ottimizzatore.
La buona notizia è che vacuum è solitamente attivo in modalità automatica in tutte le installazioni recenti, quindi non dovrete eseguirlo a mano.

Per mostrare l'uso di vacuum noi dovremo prima di tutto disabilitare l'aggiornamento automatico delle statistiche, agendo sui parametri track_count e track_activities e impostandoli ad off.
Successivamente possiamo creare una tabella di prova con cui giocare:

pgdaydb=# create table test_table(id serial, descrizione varchar);
NOTICE: CREATE TABLE will create implicit sequence "test_table_id_seq"
for serial column "test_table.id"
CREATE TABLE
pgdaydb=# insert into test_table(descrizione) values('Prima riga');
INSERT 0 1
pgdaydb=# insert into test_table(descrizione) values('Seconda riga');
INSERT 0 1
pgdaydb=# insert into test_table(descrizione) values('Terza riga');
INSERT 0 1
pgdaydb=# insert into test_table(descrizione) values('Quarta riga');
INSERT 0 1

Se ora chiediamo le statistiche sulla tabella otteniamo che la tabella risulta essere ancora vuota (ossia per il sistema non ci sono tuple inserite):

pgdaydb=# select relname, reltype, relfilenode, relpages, reltuples
from pg_class where relname = 'test_table';
relname | reltype | relfilenode | relpages | reltuples
------------+---------+-------------+----------+-----------
test_table | 26147 | 26145 | 0 | 0

Se guardiamo su disco la dimensione del file corrispondente alla tabella, troviamo che esso occupa 8 KB, ossia lo spazio di esattamente una pagina (le quattro tuple infatti sono abbastanza piccole per ricadere in una pagina dati). Ricordiamoci infatti che le tabelle sono organizzate a pagine di 8KB, e quindi il sistema alloca sempre multipli di 8KB:

ls -lh 26145
-rw------- 1 postgres postgres 8.0K 2009-05-15 08:08 26145
Ora possiamo popolare la nostra tabella forzando delle insert fino ad arrivare a circa 2 milioni di record; è sufficiente ripetere qualche volta la query seguente:

INSERT INTO test_table(descrizione) SELECT descrizione FROM test_table;
A questo punto lo spazio disco occupato dalla tabella è notevolmente cresciuto:

ls -lh 26145
-rw------- 1 postgres postgres 93M 2009-05-15 08:12 26145
Siccome le statistiche non vengono aggiornate in automatico, il sistema è ancora convinto che non vi siano tuple nella tabella, e quindi una eventuale query porterebbe a percorsi sbagliati (ossia uno scan sequenziale quando magari un indice sarebbe piu' opportuno):

select relname, reltype, relfilenode, relpages, reltuples
from pg_class where relname = 'test_table';
relname | reltype | relfilenode | relpages | reltuples
------------+---------+-------------+----------+-----------
test_table | 26147 | 26145 | 0 | 0


Cosa succede se ora facciamo un update di tutte le tuple? Vengono generate 2 milioni di nuove tuple che invalidano le precedenti, e infatti lo spazio disco occupato dalla tabella cresce di circa il doppio:

pgdaydb=# update test_table set descrizione='Nuova descrizione';
UPDATE 2097152

ls -lh 26145
-rw------- 1 postgres postgres 197M 2009-05-15 08:14 26145

Chiediamo a vacuum di aggiornare le statistiche di sistema:

pgdaydb=# vacuum analyze test_table;
VACUUM
pgdaydb=# select relname, reltype, relfilenode, relpages, reltuples
from pg_class where relname = 'test_table';
relname | reltype | relfilenode | relpages | reltuples
------------+---------+-------------+----------+-------------
test_table | 26147 | 26145 | 25206 | 2.09715e+06


ls -lh 26145
-rw------- 1 postgres postgres 197M 2009-05-15 08:14 26145
Quello che succede è che vacuum scorre la tabella per vedere quante pagine dati sono presenti, quante tuple sono valide nelle pagine dati e aggiorna le statistiche di sistema. Notare che le tuple valide sono effettivamente 2 milioni e che le pagine sono 25206, che forniscono uno spazio disco di circa 200 MB, come infatti risulta guardando lo spazio occupato su disco.

Siccome sappiamo che la tabella contiene 4 milioni di tuple, di cui solo 2 milioni sono valide, possiamo richiedere il vacuum della tabella e ottenere la riduzione di spazio.


pgdaydb=# vacuum full verbose test_table;
INFO: vacuuming "public.test_table"
INFO: "test_table": found 0 removable, 2097152 nonremovable row versions in 25206 pages
DETAIL: 0 dead row versions cannot be removed yet.
Nonremovable row versions range from 46 to 46 bytes long.
There were 2097152 unused item pointers.
Total free space (including removable row versions) is 88442096 bytes.
11848 pages are or will become empty, including 0 at the end of the table.
11850 pages containing 88388672 free bytes are potential move destinations.
CPU 0.06s/0.22u sec elapsed 0.28 sec.
INFO: "test_table": moved 1836489 row versions, truncated 25206 to 13509 pages
DETAIL: CPU 0.95s/4.95u sec elapsed 18.32 sec.
INFO: vacuuming "pg_toast.pg_toast_26145"
INFO: "pg_toast_26145": found 0 removable, 0 nonremovable row versions in 0 pages
DETAIL: 0 dead row versions cannot be removed yet.
Nonremovable row versions range from 0 to 0 bytes long.
There were 0 unused item pointers.
Total free space (including removable row versions) is 0 bytes.
0 pages are or will become empty, including 0 at the end of the table.
0 pages containing 0 free bytes are potential move destinations.
CPU 0.00s/0.00u sec elapsed 0.00 sec.
INFO: index "pg_toast_26145_index" now contains 0 row versions in 1 pages
DETAIL: 0 index pages have been deleted, 0 are currently reusable.
CPU 0.00s/0.00u sec elapsed 0.00 sec.
VACUUM
Notate che il sistema analizza la tabella, decide di troncare le pagine dati muovendo le tuple e compattando lo spazio (truncated 25206 to 13509 page) e infatti, osservando lo spazio disco si ha che questo si è quasi dimezzato:

ls -lh 26145
-rw------- 1 postgres postgres 106M 2009-05-15 08:16 26145

Cosa si conclude da tutto ciò?
Vacuum è uno strumento indispensabile, consente sia di aggiornare le statistiche di sistema (indispensabili per l'ottimizzatore) che di ricompattare lo spazio non usato. Comprendere la sua necessità, dovuta anche all'esistenza del sistema di storage non overwriting e a MVCC è la base per ogni DBA PostgreSQL.
Ma come è già stato scritto, il sistema attiva l'autovacuum per default e anche le statistiche vengono solitamente aggiornate automaticamente, quindi ci sono buone probabilità che non dobbiate mai intervenire manualmente!

mercoledì 13 maggio 2009

Gestione dei tipi nel driver JDBC

Immagiamo di avere una tabella con un campo data, uno intero e uno stringa, e di scrivere e usare il seguente semplice programma JDBC:

public class PGTest {
public static void main(String[] args) throws Exception{
Class driverClass = Class.forName("org.postgresql.Driver");
driverClass.newInstance();

Connection connection = DriverManager.getConnection("jdbc:postgresql://localhost/pgdaydb","luca", "fluca");

Statement st = connection.createStatement();
String sql = "SELECT date_field, int_field, string_field FROM mytable";
ResultSet rs = st.executeQuery(sql);

while(rs.next()){
String stringForDate = rs.getString("date_field");
String stringForInt = rs.getString("int_field");
int intForString = rs.getInt("string_field");

System.out.println("Obtained: " + stringForDate + " - " + stringForInt + " - " + intForString);
}

}
}

Quello che succede è che i campi sono ottenuti dal database, ma i loro valori sono scambiati: il tipo data e intero sono inseriti in stringhe e quello stringa in un intero.
Se siete fortunati, questo programma girerà senza nessun problema!
Come mai avviene cio?
La ragione è che il driver JDBC di PostgreSQL gestisce la comunicazione con il backend attraverso stringhe di caratteri, e quindi ogni valore di tupla inviato indietro dal database è restituito come "stringa". A questo punto, quando su una colonna invocate un getInt() il driver effettua (si veda AbstractJdbc2ResultSet):

 public int getInt(int columnIndex) throws SQLException
{
checkResultSet(columnIndex);
if (wasNullFlag)
return 0; // SQL NULL

Encoding encoding = connection.getEncoding();
if (encoding.hasAsciiNumbers()) {
try {
return getFastInt(columnIndex);
} catch (NumberFormatException ex) {
}
}
return toInt( getFixedString(columnIndex) );
}



public static int toInt(String s) throws SQLException
{
if (s != null)
{
try
{
s = s.trim();
return Integer.parseInt(s);
}
catch (NumberFormatException e)
{
try
{
BigDecimal n = new BigDecimal(s);
BigInteger i = n.toBigInteger();

int gt = i.compareTo(INTMAX);
int lt = i.compareTo(INTMIN);

if (gt > 0 || lt < 0)
{
throw new PSQLException(GT.tr("Bad value for type {0} : {1}", new Object[]{"int",s}),
PSQLState.NUMERIC_VALUE_OUT_OF_RANGE);
}
return i.intValue();

}
catch ( NumberFormatException ne )
{
throw new PSQLException(GT.tr("Bad value for type {0} : {1}", new Object[]{"int",s}),
PSQLState.NUMERIC_VALUE_OUT_OF_RANGE);
}
}
}
return 0; // SQL NULL
}


Quindi il driver effettua una semplice conversione a Integer della stringa con il valore della colonna. In sostanza il driver non effettua nessun controllo sul tipo del dato che si sta chiedendo al ResultSet, e tenta di convertirlo da stringa al valore desiderato.
Si noti che comunque il driver e' ingrado di stabilire il tipo di una determinata colonna, e in effetti la generica getObject() viene implementata per mezzo di getInternalObject():

 protected Object internalGetObject(int columnIndex, Field field) throws SQLException
{
switch (getSQLType(columnIndex))
{
case Types.BIT:
// Also Types.BOOLEAN in JDBC3
return getBoolean(columnIndex) ? Boolean.TRUE : Boolean.FALSE;
case Types.TINYINT:
case Types.SMALLINT:
case Types.INTEGER:
return new Integer(getInt(columnIndex));
case Types.BIGINT:
return new Long(getLong(columnIndex));
case Types.NUMERIC:
case Types.DECIMAL:
return getBigDecimal
(columnIndex, (field.getMod() == -1) ? -1 : ((field.getMod() - 4) & 0xffff));
case Types.REAL:
return new Float(getFloat(columnIndex));
case Types.FLOAT:
case Types.DOUBLE:
return new Double(getDouble(columnIndex));
case Types.CHAR:
case Types.VARCHAR:
case Types.LONGVARCHAR:
return getString(columnIndex);
case Types.DATE:
return getDate(columnIndex);
case Types.TIME:
return getTime(columnIndex);
case Types.TIMESTAMP:
return getTimestamp(columnIndex, null);
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
return getBytes(columnIndex);
case Types.ARRAY:
return getArray(columnIndex);
case Types.CLOB:
return getClob(columnIndex);
case Types.BLOB:
return getBlob(columnIndex);
...
}
e quindi viene cercato il tipo della colonna per poter fare un cast automatico opportuno.
Nelle specifiche JDBC esiste una tabella che illustra le conversioni che un driver deve supportare fra i vari tipi. Ad esempio la tabella illustra come una data possa anche essere ottenuta come byte[]. Si presti attenzione al fatto che tali conversioni sono le minime indispensabili che un driver deve fornire, quindi il fatto che il driver JDBC di PostgreSQL consenta anche la conversione di esempio da data a stringa non rappresenta affatto una limitazione del driver stesso.

martedì 12 maggio 2009

Costruire una corda

In questo articolo illustro il mio modo di realizzare corde per archi. E' possibile che siate in disaccordo con alcune o tutte le cose che scriverò di seguito, ma questo è il mio modo di realizzare corde, dovuto ad un insieme di tecniche che io e mio padre abbiamo appreso e messo a punto negli anni. I risultati sono piu' che soddisfacenti, e infatti non ho mai avuto problemi tecnici alle corde così realizzate (se non la normale usura dei serving).
La motivazione dietro questo articolo è anche quella di fornire un aiuto a chi si appresta a cominciare la costruzione delle corde, visto che in rete non sempre si trovano buone descrizioni del processo realizzativo.
Da notare che le foto mostrate qui si riferiscono ad una corda di "test", quindi realizzata bene ma senza una cura maniacale; di conseguenza non fissatevi troppo sul risultato finale che vedrete ma sulla descrizione del procedimento, che puo' portarvi a risultati eccellenti.

Cose indispensabili
  • il telaio per corde: senza questo è impossibile procedere. Il telaio deve avere una struttura telescopica, in modo da poter essere regolato alla lunghezza che la corda deve assumere. Inoltre deve essere dotato di due bracci a U capaci di rotare sul centro del telaio stesso. Ciascuna estremità dei bracci deve avere una carrucolina o un sostegno circolare attorno al quale verranno avvolti i fili della corda.
  • i filati: occorre avere il rocchetto del filato principale (quello con il quale sarà realizzata la corda) e almeno un rocchetto di filato per serving. Io generalmente faccio i loop con del fast-flight, perché morbido e flessibile, mentre uso del nylon per il serving centrale (quello dell'incocco) poiché piu' uniforme, liscio e meno soggetto a usura rispetto al fast-flight. Lo svantaggio del nylon è che tende a rompersi facilmente, sia in fase di costruzione della corda che durante il normale utilizzo. Se siete dotati di pazienza e siete disposti a rischiare di dover rifare il serving allora il nylon vi darà maggiori soddisfazioni, altrimenti procedete con il fast-flight che però con il tempo tenderà ad assotigliarsi in particolare nel punto dell'incocco. Per la corda io uso indistintamente fast-flight o dyneema. L'ultimo è il migliore, poiché consente di ottenere corde piu' morbide e robuste, ed inoltre non è cerato come il primo e quindi consente mediamente di usare due fili in piu' per corda. Potete anche usare un singolo filato sia per la corda che per i serving, anche se questa non è una soluzione molto in voga.
  • tempo: fare una corda richiede tempo e pazienza. Non iniziate a fare una corda se avete poco tempo o non siete nella condizione giusta per farlo.
  • cutter: dovrete tagliare velocemente e in modo abbastanza preciso pezzi di filato, quindi meglio usare un cutter che un paio di forbici.
  • accendino o fiammiferi: per bruciare le estremità dei fili.
  • una buona illuminazione: l'ideale è di avere una lampada da ufficio (a pantografo) per poter illuminare la zona in cui state lavorando.
  • una freccia: per testare la presa della cocca sul serving centrale.
Cose opzionali (ma utili)
  • un paio di pinze (sottili): siccome dovrete tendere con forza i fili, rischiate di rovinarvi le mani nell'operazione, e quindi un paio di pinze possono risolvervi il problema.
  • un paio di morsetti: necessari per fissare il telaio al tavolo, così da poter tenderlo senza rischiare che si muova.
  • un paio di piastine sottili metalliche: da usare per impedire alla corda di avvolgersi troppo su se stessa mentre create il serving.
  • un pennarello: utile per marcare sulla corda le posizioni in cui iniziare e finire i serving.
  • un righello: utile per misurare la lunghezza dei serving in realizzazione.

Concetti preliminari

La corda è dotata di due anelle che si infilano nei tip dei flettenti, una piccola (inferiore) e una piu' grande (superiore). Le anelle possono essere fatte principalmente in due modi:
  1. con un unico filo. In questo modo il filo che realizza il loop è lo stesso che realizza il serving che appoggerà sul flettente. Questa soluzione è semplice ed è quella che si trova nelle corde in commercio, tuttavia ha lo svantaggio di rendere il sistema loop-serving meno flessibile e meno robusto (tutto dipende da un filo solo).
  2. con due fili. In questo modo si realizza il loop in modo separato dal serving che appoggerà sul flettente. Questa è la soluzione che preferisco poiché il sistema loop-serving divente molto flessibile (e quindi si adatta meglio alla conformazione del flettente) e inoltre è piu' resistente (essendoci due fili la rottura di uno vi permetterà di usare la corda per finire la grara!).
Circa il metodo 2, che è quello che descriverò, vi sono poi diverse scuole di pensiero. Alcuni realizzano il loop con un serving fatto a mano, a maglie grosse, molto morbido e flessibile, altri invece con un serving fatto a macchina (io ad esempio), sicuramente piu' rigido ma anche piu' resistente all'usura e alle intemperie. Ricordatevi infatti che sotto al loop c'è la corda, che va comunque protetta, e un loop a maglie grosse farà penetrare nel filato della corda gli agenti atmosferici. Inoltre c'è chi sostiene che il loop e il serving normale debbano sovrapporsi e chi dice che debbano solo coincidere nella congiunzione. Personalmente realizzo corde con entrambi i metodi: se voglio una corda molto robusta (tipicamente da allenamento) sovrappongo i serving, mentre per le corde da gara uso la continuità dei due serving. Mentre nel primo caso si ha un nodo piu' grosso e rozzo, nel secondo caso si ha una corda molto piatta.

Costruzione della corda

Vediamo ora i passi necessari per la realizzazione della corda.

  • Fissaggio del telaio al tavolo.

  • Apertura del telaio alla lunghezza opportuna. Occorre aprire il telaio con i due bracci rotanti paralleli alla struttura telescopica e misurare l'apertura massima (cioè la distanza fra le due parti esterne dei bracci) che corrisponderà alla lunghezza della corda finale. Solitamente si parte, come indicazione, da una corda usata in precedenza, aprendo il telaio pari alla lunghezza di tale corda (o in piu' o in meno a seconda della lunghezza finale che si vuole ottenere). Tenete presente che i fattori che incidono sulla lunghezza finale della corda sono: tipo di filato e sua elasticità, numero di fili della corda (piu' fili ha una corda e piu' si accorcierà dandogli dei "giri"), tensione dei fili sul telaio (se i fili sono ben tesi la corda avrà esattamente quella lunghezza, senza troppe sorprese).
  • Apertura dei bracci rotanti. Occorre posizionare i due bracci ad angolo retto rispetto alla struttura principale del telaio. Verificate che la struttura telescopica del telaio sia ben fissata, in modo che il telaio non possa accorciarsi o comunque variare la sua lunghezza durante la costruzione della corda. Infine verificate che i bracci rotanti siano ben fissati in posizione trasversale al telaio e siano il piu' possibile paralleli fra loro (il telaio dovrebbe essere un rettangolo, o quantomeno un parallelogramma per non falsare la lunghezza della corda).
  • Fissate un capo del filo ad uno dei due bracci (nel mio telaio ho delle rondelle di fissaggio studiate appositamente allo scopo) e iniziate a posizionare i fili attorno ai quattro vertici dei bracci del telaio. Fate attenzione a che i fili non si sovrappongano fra loro ma restino il piu' possibile paralleli fra loro. Se i fili si accavallano la tensione fra gli stessi e la loro lunghezza potrebbe non essere uniforme. Abbiate cura di tirare i fili mentre li srotolate, in modo da assicurarvi che siano a tensione uniforme. Non tendeteli all'esasperazione, il telaio non deve diventare un violino, ma i fili non devono nemmeno essere poco tesi. Tenete presente che il numero di fili totali della corda corrisponderà al doppio dei fili visibili su ciascun lato lungo del telaio. Una volta srotolati tutti i fili chiudete il capo finale sul braccio opposto a quello da cui siete partiti (questo significa che sul lato corto orizzontale di quel braccio ci saranno lo stesso numero di fili che sugli altri lati).
  • Sbloccate i bracci rotanti e ruotateli, assicuratevi che tutti i fili abbiano la stessa tensione, e se necessario regolate la lunghezza del telaio. Rifissate poi il telaio e i bracci nella posizione di prima.
  • Si parte ora a fare il loop dell'anello piccolo (inferiore). Siccome l'anello piccolo è fisso sui flettenti (ovvero non scorre sul flettente per permettere l'allogiamento della corda), risulta anche quello meno sollecitato ad aperture e quindi intrinsicamente piu' resistente. E' quindi una buona idea costruire l'anello piccolo attorno ai bracci di apertura/chiusura della corda, così che esso "schermi" i due capi del filato. L'anello piccolo risulta quello piu' complesso da fare, proprio perché deve "chiudere" la corda e i due lembi fissati al braccio rotante (che potrebbero avere tensione diversa rispetto agli altri, come si vede dalle foto). Per prima cosa occorre stabilire la dimensione del loop; io solitamente utilizzo 8 cm di lunghezza (quindi un'anella di 4 cm di raggio). Marcate con un pennarello il centro della corda fra i due bracci (lato orizzontale corto) e da questi segnate il raggio dell'anella. Tenete presente che questo, essendo il primo anello, non richiede particolare simmetria nella sua realizzazione.
  • Inserite delle lamelle di metalle agli angoli retti della corda, in modo da evitare che i fili, durante la costruzione dei serving, si attorciglino fra di loro mutando anche la lunghezza della corda finale.
  • Per realizzare l'anello occorre anzitutto fissare il serving all'estremità di un braccio (ad esempio ad una carrucolina o una vite in esso), tirarlo in direzione parallela a quella dei fili della corda (in modo che faccia parte dei fili sul lato corto, e fare un anello (magari attorno ad un cacciavite o un punteruolo) e ripercorre in parallelo nel verso opposto. A questo punto fate un giro attorno al punto di partenza del serving e iniziate ad avvolgere il serving a mano. Il risultato dovrebbe essere di avere il serving che si avvolge attorno ai fili della corda e quello fissato al braccio mobile.
  • Dopo aver fatto un numero sufficiente di giri a mano (5-6 almeno) , continuate ad avvolgere il serving usando la macchinetta. Regolate la tensione in modo che il serving sia ben stretto (questo punto contiene i capi morti della corda e non si deve aprire). Dopo un numero di giri di circa 15-20 siete pronti per fissare il primo "nodo" del serving. Non esagerate troppo con il numero dei giri, altrimenti non riuscirete piu' a "tirare" i fili attraverso il serving. Tenendo il punteruolo attorno al quale avete fatto l'anello del serving in una mano, tirate con l'altra (eventualmente con le pinze) il capo parallelo del serving. Mettete il capo in tensione e rilasciate di colpo il punteruolo, in modo che si crei un nodo naturale alla prima estramità del serving. Tirate ancora un po' per sicurezza il capo parallelo del serving, ma non forzate troppo o rischiate che i giri del serving si sovrappongano per la troppa pressione.


  • Continuate ad avvolgere il serving con la macchinetta. Regolate la macchinetta in modo che il serving sia ben stretto ma non troppo. L'idea è che la macchinetta, lasciata libera, non dovrebbe allentare il filo (troppo morbida) né roteare in senso opposto (troppo rigida).
  • Arrivati vicini all'estremità finale del loop allentate la macchinetta, srotolate del filo e fate una grossa U attorno alla corda. L'importante è che il filo del serving e quello di incrocio della U siano opposti (se uno è sopra la corda l'altro deve essere sotto e viceversa). Fate una serie di giri a mano (non importa che siano chiusi fra loro e siano tesi) in senso opposto (cioè verso il serving) passando con la macchinetta all'interno della U. Anche qui occorrono almeno 15 giri per la chiusura. Al termine annodate la macchinetta in modo che il filo sia parallelo alla corda e in direzione del serving.
  • Svolgete i fili a mano attorno al serving. Fate ruotare la U costruendo altri giri del serving e rimovuendo quelli fatti a mano al passo precedente. All'ultimo giro avrete un solo anello che si potrà chiudere tirando il filo fissato prima all'altra estremità (verso l'inizio del loop). Potete nuovamente aiutarvi con un cacciavite sottile o un punteruolo.
  • A questo punto il loop è finito, potete tagliare il filo del serving (se intendete procedere con lo stesso filo per il serving dei flettenti allora lasciatelo intatto) e bruciare leggermente le estremità che sbucano dal serving. Non esagerate con la fiamma o la lama per non danneggiare i giri del serving.
  • Liberate le estremità della corda ancorate alle estremità del braccio. Non tagliatele ancora, possiamo infilarle sotto al serving dei flettenti per avere maggiore resistenza della corda.
  • Ruotate il braccio in modo da renderlo parallelo al telaio. Muovete la corda fino a che il serving del loop è simmetrico attorno alla carrucolina del braccio.

  • A questo punto si puo' iniziare a costruire il serving per il flettente. Si hanno due scelte possibili, come già anticipato prima: sovrapporre il serving del flettente a quello del loop o farlo immediatamente adiacente. La prima soluzione da maggiore robustezza alla corda, mentre la seconda produce una corda piu' flessibile e che meglio si adatta alla conformazione della punta del flettente. In entrambi i casi occorre anzitutto fissare un capo del filo del serving al telaio, in direzione opposta a quella del loop appena fatto. Successivamente si fa un anello che sovrasta il loop appena completato, lo si fissa con un punteruolo e si riprocede in direzione opposta al loop. Infine si inizia ad arrotolare il serving attorno alla corda nella posizione di inizio desiderata. Come prima, iniziate con dei giri a mano ben tesi, in modo da unire le due estremità della corda. Dopo qualche giro a mano proseguite con la macchinetta per una quindicina di giri circa.

  • Una volta finiti i giri di "blocco" della parte iniziale del serving possiamo procedere a tirare l'anello creato prima. Ancora una volta iniziate a tendere con decisione l'estremità del filo lasciato libero prima (quello in direzione opposta al loop) e rimuovete il punteruolo che teneva il loop velocemente . Il risultato sarà quello di chiudere la parte iniziale del serving.

  • Si procede a fare il serving con la macchinetta, avendo cura che il filo sia ben teso senza però esagerare (pena la rottura del filo stesso). Misurate il serving con un righello per assicurarvi che sia della misura desiderata. Quando siete vicini alla fine del serving procedete alla costruzione di un anello inverso come spiegato per la fase finale del primo loop. Ricordate che è importante che i giri inversi inizino dal lato verticale opposto rispetto a dove si ferma il filo del serving. Fate circa 15 giri inversi, fissate la macchinetta del serving (e il filo) in direzione del primo loop, svolgete i giri inversi e chiudete il serving tirando il capo di filo libero e usando un punteruolo per gestire l'anello di chiusura.

  • E' ora il momento di creare il loop dell'altra estremità, quello piu' grande. In questo caso è importante la geometria, ovvero il loop deve essere il piu' centrato possibile rispetto alla corda, poiché non si potrà "aggiustare" la posizione del loop come si è fatto per il precedente. Ancora una volta si parte fissando delle linguette metalliche agli angoli della corda, si marca con un pennarello la dimensione del serving, si fissa un capo del serving al telaio, si crea un anello in direzione opposta e si inizia ad avvolgere a mano il serving.

  • La costruzione del serving prosegue usando la macchinetta, e come per il loop precedente il serving viene chiuso con i giri inversi. A questo punto è possibile tagliare il filo del serving, bruciarne le estremità, girare il braccio rotante del telaio e prepararsi per la realizzazione del serving per i flettenti.

  • Il serving del flettente si realizza nello stesso modo del caso precedente. Una volta terminato è possibile unire i due tronconi della corda.

  • E' ora il momento di realizzare il serving centrale. Per prima cosa occorre marcare sulla corda l'inizio e la fine del serving stesso, per questo meglio usare una vecchia corda come riferimento. Vi consiglio di partire a fare il serving dalla parte inferiore, poiché in essa è facile fare un nodo di chiusura grosso che, se portato nella parte superiore, potrebbe graffiarvi il mento al rilascio. Per il serving è meglio usare del filo di nylon, ma fate attenzione a non tendere troppo la macchinetta o lo romperete. Al solito l'inizio del serving si ha con la costruzione di un anello: fissate un capo del filo verso l'anello superiore della corda, create l'anello e iniziate ad avvolgere a mano verso l'anello superiore. Dopo circa 15 giri tirate l'anello creato e chiudete così il serving. Procedete poi con la macchinetta per la costruzione del resto del serving. In prossimità del punto di incocco testate con una freccia la tenuta del serving. La cocca deve incastrarsi bene nel serving, e deve teoricamente cadere dalla corda se questa viene picchiettata dolcemente. Tenete presente che il nylon non si assottiglia con l'usura, quindi non fate un serving troppo grosso pensando che poi si adatti. La parte finale del serving viene chiusa con i soliti giri inversi. Una volta ultimato il serving tagliate le estremità del filo e bruciatele per farle "assorbire" nella corda.

Possibili varianti
  • L'anello piccolo (il primo realizzato) può essere ottenuto piu' facilmente se i capi della corda sono fissati in modo orizzontale (come mostrato in figura). In questo modo si spreca piu' corda ma si ha meno tensione nei fili al momento della chiusura del serving del loop.
  • E' possibile realizzare anelli "invisibili" usando del filo dello stesso colore della corda. Io ad esempio li realizzo con del fast-flight non cerato, molto resistente e allo stesso tempo morbido.

Aggiustamento della lunghezza della corda

La corda appena creata subirà un aggiustamento della sua lunghezza man mano che la usate. Tipicamente dopo una sessione di tiro la corda si è aggiustata e puo' essere considerata definitiva. La ragione di questo aggiustamento è che il telaio, per quanto teso possa essere, non solleciterà mai la corda come l'arco. I fili e i nodi verranno infatti tesi e sollecitati ad ogni tiro, fino a quando troveranno la loro collocazione stabile.
L'aggiustamento della corda sarà tanto maggiore quanto maggiore è il libraggio dell'arco e inferiore il numero di fili della corda. Ovviamente la tensione del telaio e dei fili puo' aiutare ad ottenere un setup iniziale piu' stabile.
Un metodo per ottenere una corda stabile prima di una sessione di tiro consiste in:
  1. armare l'arco con la corda appena costruita;
  2. scaldare la corda sfregandola velocemente con un panno o con un cordino avvolto ad essa;
  3. fare qualche rilascio a vuoto (ovviamente con allunghi corti);
  4. ripetere un paio di volte il procedimento.
In questo modo la corda viene sollecitata e dovrebbe adattarsi (come lunghezza) a quella definitiva.