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.