Visualizzazione post con etichetta java. Mostra tutti i post
Visualizzazione post con etichetta java. Mostra tutti i post

venerdì 7 luglio 2017

Eclipse Oxygen

Dal mese scorso è disponibile Eclipse Oxygen, la nuova versione del popolare IDE tuttofare.
Questa versione ha diverse migliorie sia alla parte di sviluppo Java che alla parte di gestione git. Una cosa che noto è che sempre di piu' gli sviluppatori pongono l'attenzione verso scorciatoie da tastiera per aiutare e velocizzare l'interazione con l'ambiente, come ad esempio CTRL-e per ciclare fra i tab degli editor o ALT-c per entrare nello staging git.

domenica 25 giugno 2017

Riflessioni su pl/java

Bruce Momjian ha pubblicato un breve articolo circa l'adozione di pl/Java.
I linguaggi pl/ sono una serie di bindings per utilizzare un linguaggio di programmazione differente dal "plain" SQL all'interno di una istanza PostgreSQL, ovvero lato server.
Pertanto pl/java altro non è che un binding per poter utilizzare il linguaggio Java direttamente all'interno del server PostgreSQL, ad esempio per la costruzione di trigger o stored procedures. Io stesso ho utilizzato in passato pl/java in produzione, e ho anche tenuto dei mini corsi e dei seminari sul suo utilizzo (si veda ad esempio qui), nonché ho provato a contribuire a minime porzioni di codice.


Pl/Java risulta abbastanza ostico rispetto ad altri linguaggi pl/, e ciò è dovuto alla natura di Java (non certo ai suoi limiti), in particolare alla fase di compilazione che richiede sempre:

  • un deploy di una forza compilata del codice;
  • un pezzo di codice collante SQL che possa "iniettare" le funzionalità Java dentro al server PostgreSQL.

Pl/Java si basa per scelta progettuale su Java Native Interface (JNI), scelta abbastanza efficiente se si considera che il codice deployato risulta locale al server cluster e quindi non è necessario utilizzare chiamate remote (es. RMI). In modo coerente con le scelte architetturali di PostgreSQL, pl/java utilizza una virtual machine backend per ogni processo attivo, richiedendo quindi dei tempi di startup piuttosto lunghi (o diciamo piu' lunghi rispetto ad altri pl/).


L'implementazione di pl/java è elegante e interessante, permette la sincronizzazione dei thread su un mutex singolo e consente di interagire con gli oggetti di backend arrivando perfino a implementare una sorta di SQL MED del poveraccio.


Eppure pl/java non sfonda, come nota appunto Bruce nel suo articolo.


Avendolo usato in produzione posso affermare che pl/java è fortemente condizionato dalla competizione con altri linguaggi pl, in particolare quelli di scripting. Effettivamente io stesso, ad un certo punto, ho modificato porzioni di codice abbastanza sostanziose per passare da pl/java a pl/perl. Ovviamente ciò è stato possibile perché potevo scrivere codice in entrambi i linguaggi, competenza non sempre presente, e l'esigenza principale è stata quella di dover garantire una modifica on-the-fly al codice sorgente anche quando non fosse disponibile un ambiente di sviluppo completo. Detto in due parole: per usare pl/java occorre impostare un progetto (Eclipse o similare), compilare, deployare e "iniettare" il codice nel backend, in pl/perl basta un editor di testo per modificare il codice e la fase di deploy si riduce ad iniettare il codice nel backend.


Personalmente ritengo che pl/java sia uno strumento interessante e importante, e che la sua adozione in contesti fortemente Java-based (per competenze, librerie, stack) sia opportuna, ma solo da un punto di vista degli sviluppatori. Difficilmente un DBA utilizzerà pl/java per implementare le proprie logiche di controlle server-side.

venerdì 3 marzo 2017

Fluent Interfaces e method-chaining

La prima volta che ho visto una "fluent" interface è stato quando tanti anni fa ho studiato Java NIO.
Oggi la fluent interface è una buona prassi di molti linguaggi di programmazione, e in modo un po' ridicolo qualcosa di non accettato in Java stesso.
L'idea di una fluent interface è abbastanza semplice: i mutators dovrebbero ritornare un riferimento all'oggetto che modificano, così da abilitare una method chain, in sostanza:

MyObject foo = ...;
foo.setTitle( 'My Object' ).setVisible( true ).setDescription( 'Here the description' );

che sicuramente risulta piu' compatto rispetto al classico

MyObject foo = ...;
foo.setTitle( 'My Object' );
foo.setVisible( true );
foo.setDescription( 'Here the description' );

Ora, che si tratti di una versione piu' leggibile o meno è una questione di gusti, sicuramente avere linee troppo lunghe non aumenta la leggibilità, ma considerando che molti linguaggi ammettono la gestione flessibile degli spazi, una via di mezzo fra la method-chain e l'approccio classico diventa

MyObject foo = ...;
foo.setTitle( 'My Object' )
   .setVisible( true )
   .setDescription( 'Here the description' );

abbstanza compatto e leggibile, grazie ad una buona indentazione.

Ma il vero vantaggio della fluent interface si nota, a mio avviso, nell'uso combinato dei conditionals. Si consideri il seguente esempio:


MyObject foo = ...;
if ( init ) {
  foo.setTitle( 'My Object' );
  foo.setVisible( true );
  foo.setDescription( 'Here the description' );
}

che può agilmente essere riscritto eliminando la necessità di piu' istruzioni e le parentesi graffe:

MyObject foo = ...;
if ( init )
  foo.setTitle( 'My Object' );
     .setVisible( true );
     .setDescription( 'Here the description' );


E questo a me personalmente piace molto!

Ahimé i Java Beans, stupidamente, si rifiutano di accettare la fluent interface e abilitare la method-chain. Infatti la specifica impone che i mutators siano di tipo "void", ossia non ritornino nulla. E i framework aderiscono spesso severamente a questa opzione, non riconoscendo quindi i mutators che possono ritornare un riferimento all'istanza stessa.

sabato 14 gennaio 2017

Eclipse Neon, Tomcat 7 e il nome del server non editabile...

Sono inciampato in un piccolo bug di Eclipse Neon 1: ero impossibilitato a creare qualunque server Tomcat 7 perchè non mi era possibile inserire il nome del server, come invece avveniva correttamente per le altre versioni di Tomcat (sia inferiori che maggiori).
La soluzione, nel mio caso, è stata quella di rimuovere i due file

org.eclipse.jst.server.tomcat.core.prefs
org.eclipse.wst.server.core.prefs

dalla directory .metadata/.plugins/org.eclipse.core.runtime/.settings dentro al workspace.
Ovviamente tutto fatto ad Eclipse spento (e con backup di tali file).

martedì 3 gennaio 2017

java: No such file or directory (!)

Ci sono applicazioni che richiedono un JDK per eseguire correttamente, così l'unica cosa che si può fare è quella di scaricare dal sito Oracle una qualche versione, installarla, configurare JAVA_HOME o qualcosa di simile (a seconda dell'applicazione) e lanciare il programma desiderato.
Abbastanza semplice, no?
Eppure oggi ho dovuto combattere con una installazione della JDK 1.8 (111) e un errore abbastanza subdolo riportato dalla shell:

/opt/jdk1.8.0_111/bin/java: No such file or directory

quando ovviamente il file era al suo posto, eseguibile e pronto a servire il sistema.
Inizialmente ero protato a pensare ad un classico problema di permessi, ma come ho già scritto il file era correttamente eseguibile. Forse un problema di exec sul filesystem? No, nemmeno questo.
Dopo un po' di ricerca ho scoperto che il mio pc mancava di una libreria, e quindi ben presto si è fatto un passo avanti:

sudo apt-get install libc6-i386

ma questo ancora non era sufficiente a far funzionare tutto l'ambiente, specialmente quello grafico, e così ho dovuto installare anche

sudo apt-get install libxrender1:i386 libxtst6:i386 libxi6:i386

e finalmente ho un jdk funzionante perfettamente.
Certo che il primo errore, quello riportato dalla shell, è piuttosto fuorviante!

mercoledì 13 aprile 2016

My story about using Eclipse

I started using Eclipse as my first Java IDE back in 2004.
I had always written code in Java, since version 1, but back in those days I was using some very simple editing (even the Microsoft Edit, it was even before I started using Emacs!) or some less expensive (in term of resources) IDEs.
My switch to a "real" IDE was during the fourth year of University, when I started using Borland. I remember the IDE was around 80 MB in size and required almost a minute to start on my Pentium IV. Nevertheless it was simple and to the point: it allowed me to develop in Java having syntax highlight, Javadoc inspection, project management and some other feature.
Ok, I have to say it was much more than I needed, since I was working on one or two projects at the same time back in those days. Morever, I was totally unaware of code versioning, and in fact I had developed my own way of doing incremental backups, but that's another story.

While approaching my master thesis, that of course had to be based on Java (not because of choice of mine), I started evaluating some other Java IDE. At that time the Forte For Java was a really famous piece of code, but it was a little hungry on resources and required almost two minutes to start up on my Celeron 700 MHz with 192 MB of RAM. Moreover, while the start up time was not such a big deal for me, having to work for a lot of hours without turning off the computer, the program itself was pretty much unusable with menus half-empty, rendering delays and freezes.
I tried then the Emacs extension for Java, but at that time I was a young Emacs users and the whole JDEE was too much complex for either my fingers and brain.
Having spent a couple of weeks searching for a product, I switched back to the Borland Java IDE, something I already know quite well and that allowed me to be productive from day one.

In the following years I experimented a lot with other two emerging products: IBM Eclipse and Netbeans IDE. However I was not working as a Java developer, or better, Java was not my main language after the university, and therefore I did not spent a lot of time using them for real projects.
In 2004 there was a shift: at the Principle and Practice of Programming Java (PPPJ) conference in Kilkenny I met Vivek Sarkar, an IBM fellow that shown us the magic power of Eclipse (and of Jikes RVM for what it matters). At that time there was Eclipse 2 (if my memory serves me well 2.2) and the main feature that astonished us was the "refactoring" capabilities of Eclipse.
Once I was back home I installed Eclipse and switched definitely to such environment, letting Emacs for quick-and-dirt editing.

Several year later, at the time of version 3, I got back to a Java-based job and developed an application with the Rich Client Platform (RCP), so I used Eclipse to develop another Eclipse application (a kind of recursion that computer scientist seems to appreciate very well).

Then I had to manage a PHP codebase, and did it with Eclipse too even if, I have to admit, the support to such language was not mature enough and the editor had some lag.

Today I use Eclipse on a regular day-by-day basis mainly for web development, and mainly for Java because I use Emacs for all the rest.

martedì 7 gennaio 2014

La Nebula che avanza

E' passato molto tempo da quando iusavo Nebula, un framework di widget SWT estesi complessi atti a creare interfacce grafiche ricche e complete. Vedo che il progetto è andato avanti con determinazione, creando nuovi widget di transizione e tabelle che, a prima vista, sono ideali per programmi di interfacciamento con la borsa o mercati dinamici. Ahimé alcuni dei widget che avrei sempre voluto usare, come il CollapsibleButton sono ancora in fase di incubazione.
Sono convinto che il progetto Nebula sia molto importante, anche se probabilmente ha risentito dell'avvento delle interfacce web che hanno, in parte, semplificato e ridotto le richieste ai programmi desktop.

domenica 19 maggio 2013

Eclipse Day Florence 2013

Ho partecipato alla seconda edizione dell'Eclipse Day Firenze (EDF 2013), organizzata da RCP-Vision presso il centro congressi di Firenze una settimana fa. Devo dire che questa edizione mi è piaciuta anche piu' della precedente: in particolare molte presentazioni hanno incluso delle demo live from scratch, che hanno permesso al pubblico di meglio comprendere come la tecnologia Eclipse e l'intero ecosistema si sia evoluto.

La conferenza ha avuto un tenore molto serio, forse fin troppo per i miei guisti, ma nulla da eccepire sull'organizzazione e sul catering, sempre di alto livello, nonché sul centro congressi che è sempre bello (sarà perché comunque adoro la Toscana in generale e quindi mi appare sempre tutto molto bello).
Devo comunque notare che mi appare sempre piu' evidente come l'ecosistema Eclipse si sia evoluto per sopperire alle carenze proprie del linguaggio Java, e ora sia possibile fare instrumenting con pseudo linguaggi che prendono caratteristiche di altri linguaggi, come l'operator overloading che gli ingegneri Java hanno tanto disgustato...

venerdì 8 febbraio 2013

My story about using IBM Eclipse

For one reason or the other, my jobs always required the development of Java applications. I was used to work with Borland JBuilder, and the main reasons were it was simple but powerful and did not waste  megabytes or RAM making me wait more than 30 seconds to start and be fully functional. Nevertheless, it had some limitations, and so I was thinking to switch to a more complex and heavy environment, in particular NetBeans.
Then, at the 2002 PPPJ in Kilkenny, I attended the keynote speech of Mr. Vivek Sarkar that was at that time employed at IBM and therefore presented Eclipse 2. After the speech, while having a coffee, I asked what were the differences between Eclipse and NetBeans and why use one instead of the other. The main reason I got was the code refactoring, so once at home I installed Eclipse and tried it on my code base. At glance I did not like it: it was too heavy and complex, with too many functions and windows and tabs and other annoying stuff (I'm a terminal guy...). But the code refactoring was really impressive and I found it really useful.

So I started using Eclipse for my day-to-day development. 

Today it is still my Java default IDE, and I have noticed that a lot of people use it as a kind of fancy editor with autocompletion, without using some of the more interesting features like run configurations and Mylin.
In one of my previous job I had to create a client-server Java application that should have been really fast to run on Microsoft Windows and Apple machines. So I thought it was time for giving a try to SWT, the IBM Java toolkit Eclipse is based on. 

From SWT to Eclipse RCP the jump was fast and great, and therefore I started developing applications using Eclipse RCP v3.

venerdì 6 luglio 2012

AspectJ 1.7 & invokedynamic

E' stato appena rilasciato AspectJ 1.7 che allinea il compilatore (weaver) alla versione 3.7 di Eclipse e presto raggiungerà la versione di Juno (4.x). E' interessante notare che il numero di versione, .7, corrisponde alla major release di Java, la 7 appunto, che il nuovo compilatore supporta. In particolare si sta lavorando molto per supportare la nuova istruzione invokedynamic di Java 7, anche se ancora non ci sono dei join points definiti.
Questa istruzione permette a Java di invocare un metodo su un tipo di oggetto non specificato, aprendo quindi la strada all'integrazione con i linguaggi di scripting. Infatti Kava è sempre stato un linguaggio staticamente tipizzato, e tutte le versione di invokeXXX (come invokevirtual) hanno sempre compreso un tipo sul quale chiamare il metodo oltre alle informazioni dei tipi parametrici e di ritorno. Con la nuova istruzione invece i tipi non sono presenti, di fatto si usa un trucco simile ai puntatori void* di C: nel constant pool si inserisce un MethodHandle che viene linkato alla prima invocazione, lasciando così il bytecode "libero" da tipizzazioni preventive.

venerdì 29 giugno 2012

Autonomous Role Discovery for Collaborating Agents


I'm proud to announce that a paper I wrote with my colleague and friend prof. Haibin Zhu has been published on-line on Software Practice and Experience (SPE). The paper, titled, Autonomous Role Discovery for Collaborating Agents , can be found here.
 This is the result of the last year efforts on the WhiteCat framework.

JFK and Roles....

With the help of a friend of mine a work on roles applied to the JFK framework is ongoing. It is still early to present some results, and it is quite difficult to even imagine the final result, that came into my mind from Object Teams. We'll see!

sabato 26 maggio 2012

Do not merge the singleton pattern with the factory one!

Do you remember the programming school and what the teacher was repeating any lesson? "Using global state is a bad programming technique!" Singletons are nothing more than doing global state, even if in a more elegant way.

With the advent of many Java framework the singleton pattern was made so popular that pretty much every developer now know it. For those that still have doubts, a singleton is an object that exists in a "single" instance along the whole application/framework. As an example, many GUI libraries provide a "Display" class, which is a singleton (the library needs a single display to draw onto); other examples could be a shared resource or service. The idea is simple: does the program require access to always the same object? If yes is the answer, chances are a singleton will be used.
How is implemented a singleton? Often it is implemented using a factory pattern like in the following snippet:

public class Display{
       // make constructor private
       // so that nobody can build
       // an instance autonomously
       private Display(){ ... }


       // a static reference to the
       // ONLY available instance
       private Display mySelf = new Display();


       // use a factory method to get
       // an instance of the Display
       public final static Display getInstance(){
              return mySelf;
       }
      
}

The above example is kept simple just to show what and how a singleton is implemented; it is possible to make very complex and elegant implementations as well as to port the above code in any other programming language.
What is wrong with the above piece of code? That two patterns are involved: the singleton one and the factory one.
Now, singleton is bad, factory is good.
To explain the latter statement, allow me to introduce a piece of client code that is going to use the above Display class:

void showPopupWindow(){
     Display.getInstance().showPopupDialog( "Hello World" );
}

What is strange with the above code? The method has no parameters, and is not accessing any private instance of the class. Let's see from another perspective: the client code knows where to find and how to get the Display instance, and therefore it is going to obtain it. This is a very strong coupling, and make code hard to read and test. Why? Let's start from the coupling first: the client code is going to access directly the Display class, and therefore there is no way to "inject" another Display instance to use. The two classes are therefore strictly coupling. This leads to the hard-test problem: how are you supposed to test the showPopupWindow() method if you cannot inject a mock-up Display object? The only way is to uninstall the real Display and use a fake one. It is not so simple. Finally, the hard-to-read problem: imagine you have access to the Javadoc documentation, that is something like:

     /**
       * Shows a popup window on the user display.
       */
     void showPopupWindow();

You don't see what the code does in its implementation, so you have to guess that it is going to access the Display class to get an instance. Please note the use of the word "guess".
How is it possible to solve such problems? Let's start from the client code: adopt dependincy injection as follows

void showPopupWindow( Display display ){
     display.showPopupDialog( "Hello World" );
}

or use an internal variable to keep an injected instance of Display and access it. Then refactor the Display code to use a separate factory:

public class Display(){
       public Display(){ ... }
}


public class DisplayFactory{
       private Display instance = new Display();


       public static final Display getDisplayInstance(){
              return instance;               
       }
}


Note that the instance is now cached into the factory, and that the Display class is pretty normal now. You can use some fancy protection mechanism, like making the Display's constructor package-visible and not public, but you get the idea. Now, no matter how you implement the Display class, the factory pattern is implemented the right way and the coupling among classes is strongly reduced. Moreover, you can also distribute the Display in binary form letting the clients to reimplement the factory pattern, while in the former case you could not. This problem lies in the fact that the first implementation of the Display class was playing a double role: the singleton and the factory for itself. This in particular is very common in many programs (and I did also!) but it is wrong. Note I'm not saying "it could be" wrong. A class must be thought as a unit with as less aims as possible, in particular with a single aim. So the right way to go is using a class as a singleton and one as a factory. This requires much more code and a few effort for initial setup but will pay in the long term.

sabato 28 gennaio 2012

La mia opinione sulle asserzioni

Ad una conferenza uno sviluppatore certificato ha fatto un commento che mi ha lasciato perplesso:
"...sarebbe bello che le asserzioni funzionassero anche a run-time..."
Tale affermazione potrebbe essere estremamente fuorviante, ed è per questo che voglio esprimere la mia opinione sulle asserzioni e il loro uso. Una asserzione è un test su una condizione booleana che interrompe il flusso del programma qualora la condizione sia verificata. Ad esempio:

assert( ! username.empty() );

verifica che lo username non sia vuoto, nel qual caso interrompe (abort) l'esecuzione del programma.
Concettualmente una asserzione potrebbe essere considerata simile al seguente blocco di psudo-codice:

if( username.empty() )
   throw new Error();

Anche se concettualmente il pezzo di codice qui sopra è corretto, semanticamente è errato poiché non verifica che l'applicazione non sia in release mode, e quindi andrebbe riscritto come:

#ifdef DEBUG_MODE

if( username.empty() )
   throw new Error();
#endif

o senza le macro di compilazione come:


if( debugMode &&  username.empty() )
   throw new Error();



Per capire perché è così importante il test sulla modalità di debug occorre capire il vero scopo delle asserzioni. Le asserzioni non sono controlli di validità degli argomenti, sono controlli che condizioni impossibili non si possano mai verificare. Consideriamo un sistema di autenticazione:


private bool doAuthentication( String& username, String& password ){
    assert( ! username.empty() );
    assert( ! password.empty() );


    // do authentication stuff
}




public bool auth( String& usnermae, String& password ){
   if( username.empty() )
      // alert the user
   else if( password.empty() )
     // alert the user
   else
    return doAuthentication( username, password );
}

L'idea è quella di avere un metodo di autenticazione, auth, pubblico e richiamabile dall'esterno, e una sua implementazione doAuthentication privata. Si noti che le asserzioni sono solo nell'implementazione, mentre il metodo pubblico effettua analoghi controlli ma senza asserzioni. In effetti quello che si vuole evitare è che il sistema di autenticazione sia richiamato con dati nulli, ossia che doAuthentication sia invocato con parametri vuoti. Questa è una condizione impossibile, non si deve mai verificare, e il compito delle asserzioni è di garantire che durante lo sviluppo non si introducano bug che coinvolgano una chiamata a doAuthentication con parametri vuoti. Ma dal lato utente bloccare l'esecuzione del programma perché i dati sono incompleti non è la scelta corretta: si devono comunque controllare i dati ed eventualmente informare l'utente (con un messaggio di errore) circa la completezza dei dati. In altre parole, si vuole evitare un abort pur tenendo i controlli a run-time. Dall'esempio di cui sopra si potrebbe riassumere che:

  • le asserzioni vengono usate per le implementazioni (metodi non pubblici) per controllare che non ci siano bug "vistosi";
  • le asserzioni non corrispondono a controlli di validazione;
  • i controlli dei parametri devono sempre essere presenti nei metodi pubblici, ossia si deve evitare di lavorare con dati taint.

venerdì 16 dicembre 2011

Eclipse e il renaming dei getter/setter

Una delle feature che ha maggiormente contraddistinto Eclipse rispetto ad altri IDE è stato il refactoring, introdotto con la versione 2. Nel refactoring, una delle caratteristiche che uso maggiormante nel mio lavoro quotidiano è il rename che consente di modificare il nome di una variabile o metodo andando ad aggiustare automaticamente tutte le occorrenze nel progetto.
Dalla versione 3 di Eclipse il rename non viene piu' fatto tramite una dialog, ma tramite digitazione diretta del nuovo nome in overwrite. Questo ha fatto si che mi scordassi, con il tempo, che Eclipse ha anche la possibilità di aggiustare i nomi dei getter e setter di una variabile. Un collega mi ha ricordato che visualizzando la finestra di dialogo di rename all'atto della rinomina è possibile spuntare i checkbox per rinominare anche i metodi di accesso alla proprietà.


sabato 12 novembre 2011

10 anni di eclissi

Il progetto Eclipse compie 10 anni!



Ricordo ancora qualndo venni a conoscenza di questo IDE, alla conferenza Principle and Practice of Programming Java nel 2003. All'epoca quello che faceva spiccare Eclipse rispetto agli altri prodotti era il refactoring; la versione era ancora la 2, e quindi lo schema dei plugin stava prendendo forma ma senza il concetto di OSGi che ha prodotto il vero salto di qualità della piattaforma (almeno a mio avviso).
Io sono diventato utente Eclipse dal 2003, anche se l'uso intensivo dell'IDE l'ho iniziato ad avere nel 2004.


Eclipse & FreeBSD

Ho avuto recentemente dei problemi nell'installazione di Eclipse dall'albero dei ports su un FreeBSD 9 rc-1. Chiedendo aiuto nei forum ufficiali, ho scoperto che Eclipse non è un gran IDE secondo la community FreeBSD. In effetti se ci si pensa, anche la piattaforma RCP fornisce runner nativi per Windows, Linux e OSX, ma non per altri Unix generalizzati.

mercoledì 10 agosto 2011

FLAP is now available as Open Source

A few days ago I created a repository on GitHub to host a few bits of source code: Ferrari Luca's Agent Platform (FLAP). This tiny project is a Java agent platform inspired by Aglets that I used during a course I did at the University of Modena and Reggio Emilia in late 2006. My idea was to present students with a simple agent platform, easy to understand and to debug, in order to make they understand the main concept behind concurrency, thread management and agent messaging a platform must accomplish. The platform represents also a good starting point to do experiments against agent algorithms and theories, and it is for this reason I'm releasing this as Open Source. You are free to experiment and improve the platform as much as you want, but please take into account that the platform is intended to be a didactic workbench, not a professional or complex product. The code compiles with Apache Maven, and if you run test you will actually prompted with a micro interactive shell.

The code is released under the BSD license, so you are really free to do whatever you want with it. But if you find it useful in your courses, studies, free time, please let me know.


venerdì 8 luglio 2011

Template, Generics e quello che non sempre viene spiegato

Anni fa, in occasione dell'uscita della versione 5 del linguaggio Java (release Tiger) tenni un seminario di dipartimento presso l'Universita' degli Studi di Modena e Reggio Emilia nel quale presentavo tutte le novita' introdotte nel linguaggio (qualche copia del seminario si trova ancora in giro, anche se non garantisco l'autenticita' - chi vuole averne una copia autentica mi contatti).
Una delle novita' di Java 5 erano i "Java Generics". In occasione della presentazione di generics, una eminenza grigia, professorone con anni di esperienza nella OOP e nella programmazione C++, con tono sprezzante e quasi schifato disse:

    hanno copiato i templates C++ e li hanno applicati a Java, nulla di nuovo!

All'epoca ero troppo educato per rispondere male! Con questo articolo pero' voglio mettere in chiaro le differenze fra i templates del C++ e i generics di Java, sottolineando come il pensiero sopra espresso dal professore universitario sia ben lontano dalla realta'.

Si consideri Java Generics, di cosa si tratta? Generics e' un modo per evitare errori grossolani di conversione di tipi facendo fare del lavoro sporco al compilatore. Il compilatore Java e' sempre stato fortemente tipizzato, ma solo per quello che riguarda i tipi primitivi. Nel caso degli oggetti, grazie al principio di sostituzione e alla base comune, Object, ogni oggetto potrebbe essere convertito in uno di un altro tipo passando per un Object. Questo genera una serie di errori di coerenza come il seguente:

   Persona madre = new Persona();
   Persona padre = new Persona();
   Animale cane  = new Animale();
   List famigliari = new LinkedList();
   famigliari.add( madre );
   famigliari.add( padre );
   famigliari.add( cane );    // disastro!

A meno che non si abbia una forte considerazione del proprio cane, esso non andrebbe inserito nella lista dei famigliari (il fatto che il proprio cane debba essere inserito o meno nella lista dei famigliari è un argomento che esula da generics!). Il problema nasce a run-time, quando si cerchera' di manipolare gli oggetti eseguendo dei cast che non saranno verificati.
Generics permette di intercettare tali errori permettendo ad una classe di definire dei "segnaposto" generici il cui tipo verra' specificato al momento della creazione dell'istanza. In altre parole il codice di cui sopra diviene:

   Persona madre = new Persona();
   Persona padre = new Persona();
   Animale cane  = new Animale();
   List famigliari = new LinkedList();
   famigliari.add( madre );
   famigliari.add( padre );
   famigliari.add( cane );    // errore di compilazione

In questo caso il compilatore sa che la lista deve accettare solo istanze valide di Persona, e quindi rifiuta di compilare un programma con un simile errore di logica.
Si noti che il controllo di coerenza avviene solo a compile-time: a run-time l'informazione sui tipi viene totalmente rimossa (type erasure) e tutti i tipi generici sono sostituiti con Object. Questo e' dovuto al fatto che un programma compilato con Generics deve poter eseguire anche su una virtual machine che di generics non sa nulla.

I template C++ sono differenti. Anzitutto i template consentono due funzionalita': generics e meta-programming. La prima funzionalita' e' analoga a quella di Java: si definisce un marcaposto per un tipo e tale tipo verra' specificato al momento della istanziazione (nel codice). Il programma di cui sopra diventa:

  class Persona{};
  class Animale{};

  template< class L >
  class List{
  private:
   L elements[3];

   public:
   inline void add( L newElement ){
          // add code
   }
  };


  // quando occorre usare la lista
  List famigliari
< Persona >;
  famigliari.add( padre );
  famigliari.add( madre );
  famigliari.add( cane ); // errore di compilazione


Il sistema riporta un errore di compilazione simile al seguente:

   error: no matching function for call to ‘List::add(Animale&)’
   note: candidates are: void List::add(L) [with L = Persona]

Potrebbe sembrare che almeno nella implementazione di Generics Java e C++ siano identici, in realta' C++ permette la definizione di classi compilate in modo differente per ogni tipo specificato. Quindi mentre in Java esiste una sola classe List che accetta degli Object controllati dal compilatore, in C++ possono esistere piu' versioni della lista compilata ciascuna con ogni tipo richiesto.

Ma la differenza fra i templates C++ e Java Generics diventa ancora piu' evidente quando si parla di meta-programming. In sostanza i templates C++ possono essere applicati non solo a delle classi, ma anche a dei metodi:

   template
   T findMax (T a, T b) {
     T result;
     result = (a>b)? a : b;
     return (result);
   }

ovviamente per avere senso i template applicati ai metodi dovranno spesso  sfruttare anche l'overloading degli operatori.

Infine il C++ utilizza i template per "capire", a tempo di compilazione, come parametrizzare il codice che genera. E' quindi possibile forzare non solo il tipo di un parametro, ma anche il valore, in modo da costringere il compilatore a compilare il codice con un parametro inserito in modo hard-coded. Si consideri il seguente esempio:

template< int printCount, int spaces >
class Printer{
public:
  static void print(){
    string printString = "Hello World!";
    for( int i = 0; i < printCount; i++ ){

      // stampo gli spazi
      for( int j = 0; j < spaces; j++ )
        cout << " ";

      // stampo la linea e l'andata a capo
      cout << i << " " << printString << endl;
    }
  }


  void croack(){
    string printString = "Hello World!";
    for( int i = 0; i < printCount; i++ ){

      // stampo gli spazi
      for( int j = 0; j < spaces; j++ )
        cout << " ";

      // stampo la linea e l'andata a capo
      cout << i << " " << printString << endl;
    }
  }
};


  int main( int argc, char** argv ){
    cout << "Programma in esecuzione" << endl;
    Printer< 5, 5 >::print();

    Printer< 2 , 8> myPrinter;
    myPrinter.croack();
    cout << "Programma terminato" << endl;
  }

La classe Printer definisce due metodi identici: print (statico) e croack (di istanza). Tali metodi traggono vantaggio del valore di due parametri specificati come template (spaces e printCount) per stampare un certo numero di volte e con un certo numero di spazi di indentazione la famosa stringa "Hello World". La cosa interessante, e che Java non permette, e' che il valore dei parametri viene specificato direttamente al momento della chiamata della funzione statica o della creazione dell'istanza. In Java la classe Printer avrebbe dovuto avere una implementazione simile alla seguente:

public class Printer{
       PRINTCOUNT printCount;
       SPACES      spaces;

       public Printer( PRINTCOUNT count, SPACES spcs ){
                 printCount = count;
          spaces     = spcs;
       }
      
       // metodi analoghi alla classe C++
}

e la si sarebbe dovuta istanziare cosi':

  Printer printer = new Printer(5, 10);

senza la possibilita' di usare il metodo statico direttamente.
Riassumendo quindi Generics e' solo una branchia del template programming, che si estende ben oltre la semplice "specifica variabile dei tipi" e i "controlli di compilazione". L'idea dietro al templating e' si quella di creare classi generiche, ma anche algoritmi generici (come la funzione findMax di cui sopra) e dare la possibilita' al compilatore di capire cosa succedera' a run-time, generando codice specifico per il programma.

In considerazione di questi semplici esempi didattici, e considerata l'affermazione del professore riportata all'inizio, viene da chiedersi se tale professore sia maggiormente incompetente in Java, in C++ o in generale nella OOP. E la cosa che fa ancora piu' amarezza, e' che tale professore continuera' a formare studenti e programmatori che dovranno apprendere da soli cosa realmente siano i templates.


sabato 18 giugno 2011

Maven settings: repository path & proxy

La configurazione del processo di compilazione di Maven puo' essere modificata opportunamente mediante il file che si trova in $HOME/.m2/settings.xml. Inizialmente potrebbe essere necessario creare tale file, poiche' Maven non lo inizializza di default. Due personalizzazioni molto utili da inserire in tale file sono:
 
  • localRepository: configura il percorso del repository affinche' non sia, in default, in $HOME/.m2. Questo e' utile quando si vuole, ad esempio, tenere i jar scaricati su una partizione differente (magari condivisa).
  • proxy: consente la configurazione di un proxy, con eventuale autenticazione, per il download delle risorse.

Un esempio di file che utilizza entrambe le funzioni di cui sopra e' il seguente:



 <settings>

  <localRepository>/sviluppo/java/jars</localRepository>


  <proxies>
   <proxy>
      <active>true</active>
      <protocol>http</protocol>
      <host>192.168.1.76</host>
      <port>8080</port>
      <username>luca</username>
      <password>password</password>
    </proxy>
  </proxies>

</settings>