Posts tagged: java

Array & Java Reflection

In questo periodo si lavora abbastanza…si programma come al solito usando Java…il mio linguaggio di programmazione di riferimento. Ho avuto a che fare con la riflessione, approcci comuni quali richiamare dei metodi su oggetti, prelevarne i valori delle variabili membro, etc…il tutto ovviamente fatto a run-time. Ho letto un piccolo hint su come recuperare l’istanza di una classe di un Array usando la riflessione in Java. Ricordiamo come si recupera l’istanza di una classe usando la riflessione:

  • Class.forName("it.devme.MYClass"), per ottenere l’istanza dal nome della classe
  • MYClass.class, per ottenere l’istanza dal tipo
  • Integer.TYPE, per ottenre l’istanza da qualunque tipo primitivo

E nel caso di un array ??
Come suggerito nell’articolo, un array è anch’esso un oggetto e quindi possiede una classe, ma con i metodi suggeriti sopra non è possibile ottenerne l’istanza a runtime. Un modo per fare ciò è il seguente:

  • Class.forName("[C"), per un array di caratteri. Si specifica come nome [C che corrisponde al nome della rappresentazione data dal linguaggio agli array di caratteri. Se vi capita di fare debug con un IDE a caso, ad esempio eclipse, vi accorgerete che la rappresentazione di un oggetto array di caratteri data dal linguaggio Java inizia con [C
  • Class.forName("[Ljava.lang.String;"), per un array di stringhe.

L'articolo si conclude con un suggerimento dato da un guru di Java, Tim Eck, che suggerisce di usare semplicemente l'istruzione:

char[].class

per ottenere l’istanza di una classe di array di caratteri.

Il grosso grasso JAR….

jarSalve…non sono morto, è che sto lavorando abbastanza e quindi trovare il tempo per parlare di cose interessanti, anche se mi piace molto, è difficile !!! ma poi con chi sto parlando visto che siete sempre così pochi ?? [crisi esistenziale]. Ciò detto, volevo rendervi partecipe di questa mia piccola scoperta…che poi scoperta in realtà non è, ma dal momento che a lavorare ci si imbatte nei problemi, questa volta la soluzione volevo condividerla…sai mai che a qualcuno possa tornare utile. Dunque il problema è il seguente: creare un jar contenente al suo interno altri jar, ovvero le librerie, che possa funzionare senza dover specificare nulla nel classpath.
Sostanzialmente, dato il jar devme.jar voglio lanciarlo usando il comando:

1
 java -jar devme.jar

senza preoccuparmi di altro.
Cercando su gooogle ho trovato un articolo della IBM che mi ha illuminato sul fare alcuni esperimenti, e quindi sul risolvere il problema…vediamo assieme.
Il classloader di java, per gli amici sun.misc.Launcher$AppClassLoader, che viene richiamato al lancio del comando java -jar, è a conocenza di 2 cose: 

  • Carica classi/risorse che compaiono nella root del JAR.
  • Carica classi/risorse che compaione nell’attributo Class-Path del file MANIFEST.MF.

Inoltre, ignora qualunque valore della variabile d’ambiente CLASSPATH o argomento fornito da riga di comando -cp, usato per specificare il classpath. Dulcis in fundo, si fa per dire, non sa come caricare classi/risorse all’interno di JAR presenti all’interno del jar da eseguire. Per cominciare creaimo un singolo JAR, che sarà il nostro eseguibile e che quindi chiamiamo main.jar. Supponiamo di avere una classe entry-point it.devme.main.Main e assumiamo che dipenda da 2 classi: it.devme.a.A all’interno del jar a.jar e it.devme.b.B all’interno del jar b.jar.

main.jar | it/devme/main/Main.class | it/devme/a/A.class | it/devme/b/B.class

Questo approccio ha delle limitazioni tali da suggerire l’utilizzo di un altro metodo. una di queste è che l’informazioni sulla provenienza originale della classi A.class e B.class viene persa. Un altra più importante è la seguente:
se a.jar e b.jar contengono una risorsa con lo stesso nome, quale scelgo? Cambiamo strada. Un altro approccio è quello di modificare il MANIFEST.MF a manoni, cercando di comporre quello di main.jar in modo che avesse visibiltà degli altri jar. Ma l’unica cosa che si riesce a fare è quella di porli nel filesystem a fianco di main.jar che è esattamente la cosa che si voleva evitare.

Per tagliare la testa al toro, il suggerimento dato è quello di scrivere un class loader personalizzato, in modo da caricare le classi che servono dall’interno di un JAR. Si tenga presente che scrivere un class loader personalizzato non è un operazione da prendere alla leggera, dal momento che questa ha un impatto molto profondo con il resto dell’applicazione, dal momento che si preoccupa di caricare le classi e di rispondere agli errori quando questi si verificano. Il concetto di class loader va oltre lo scopo di questo post, per cui ulteriori dettagli non verranno trattati. Tenendo presente la struttura del nostro jar:

one-jar.jar | META-INF/MANIFEST.MF | main/main.jar | lib/a.jar | lib/b.jar

proviamo a scrivere il nostro class-loader. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
com/simontuffs/onejar/JarClassLoader.java
protected URL findResource(String $resource) {
    try {
        // resolve($resource) returns the name of a
        // resource in the
        // byteCode Map if it is known to this
        // classloader.
        String resource = resolve($resource);
        if (resource != null) {
            // We know how to handle it.
            return new URL(Handler.PROTOCOL + ":" + resource);
        }
        return null;
    } catch (MalformedURLException mux) {
        WARNING("unable to locate " + $resource);
    }
    return null;
}

Si noti subito che per il recupero di una classe si utilizza un URL con rispettivo protocollo che permette di identificare una risorsa. Il protocollo in questo caso è un protocollo custom, che comincia con il prefisso onejar:. Di seguito abbiamo l’handler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
com/simontuffs/onejar/Handler.java
package com.simontuffs.onejar;
...
public class Handler extends URLStreamHandler {
/**
* This protocol name must match the
* name of the package in which this class
* lives.
*/
   public static String PROTOCOL = "onejar";
   protected int len = PROTOCOL.length()+1;
   protected URLConnection openConnection(URL u) throws IOException {
       final String resource = u.toString().substring(len);
       return new URLConnection(u) {
 
           public void connect() {}
 
           public InputStream getInputStream() {
               // Use the Boot classloader to get the resource.
               // is only one per one-jar.
               JarClassLoader cl = Boot.getClassLoader();
               return cl.getByteStream(resource);
           }
       };
   }
}

Il nostro class loader andrà inserito nel MANIFEST.MF/Main-Class attribute. Verrà creato un nuovo bootstrap della main class, com.simontuffs.onejar.Boot, la quale è specificata come Main-Class attribute. La nuova classe creerà una nuova istanza del JarClassLoader userà il nuovo loader, per caricare it.devme.main.Main.class usando la riflessione per invocare il metodo main(). Finito di leggere l’articolo IBM che è possibile trovare qui, o forse nel mentre della lettura non ricordo, ho notato l’indirizzo di questo meraviglioso, risolvi problemi, plugin per eclipse….che realizza né più né meno la tecnica sopra descritta per produrre un jar eseguibile con l’accesso a librerie al suo interno. Il plugin lo trovate qui…è ancora una pre-release alpha, ma fino ad ora non ho avuto alcun problema nell’utilizzo.

Overloading del costruttore – REVIEWED -

Eh sì, devo proprio dirlo. Ho sbagliato !!! Non che non mi capiti mai, ma questa volta l’ho anche scritto sul mio blog, quindi DEVO riparare e salvare quello che ancora rimane della mia reputazione. L’errore è stato nel post in cui si parlava dell’overloading del costruttore, rimetto di seguito il giochino che avevo pubblicato:

1
2
3
4
5
6
7
8
public class OverloadResolver {
    public OverloadResolver(Object param) {
        System.out.println("Construttore con parametro Object");
    }
    public OverloadResolver(Object[] param) {
        System.out.println("Costruttore con parametro Object[]");
    }
}

La domanda era: "Qualcuno sa cosa succede se faccio questa chiamata:"

1
2
3
4
5
......
public static void main(String[] args) {
    OverloadResolver or = new OverloadResolver(null);
}
......

Ingenuamente mi son fidato. Nel senso che ho considerato buona la risposta che ho trovato nell’articolo che ho letto in internet, (in realtà volevo anche rispondere ora che so la risposta corretta, ma non riesco più a trovarlo, poi vi dico se lo trovo) e quindi non mi sono posto il problema di testarlo. Quando ieri, un caro vecchio amico, mi ha fatto gentilmente notare, e per gentilmente intendo a modo suo, che la risposta da me data non era quella giusta, in quanto diffidando dalla risposta ha testato l’esempio, e il risultato non era quello che avevo dato. Quindi, eseguendo l’esempio, il risultato è che viene richiamata la funzione:

1
2
3
public OverloadResolver(Object[] param) {
    System.out.println("Costruttore con parametro Object[]");
}

Perchè ? Il motivo lo incollo, così come l’ho trovato in internet, ovviamente sul sito della sun:

So the third rule is to choose the most "specific" method. The rule is: if any method still under consideration has parameter types that are assignable to another method that’s also still in play, then the other method is removed from consideration. This process is repeated until no other method can be eliminated. If the result is a single "most specific" method, then that method is called. If there’s more than one method left, the call is ambiguous. Suppose that you have the methods: f(float) f(double) In this case, the parameter types for the first method are assignable to the parameter types of the second method, that is, it’s legal to say: double = float through a widening primitive conversion. By contrast, saying: float = double is not valid without a cast. Based on this third rule, f(double) is removed from the set of possible methods to call, and therefore f(float) is called.

Morale della favola? La prossima volta non fidatevi degli articoli trovati in rete senza prima averli provato. Ciauu

Nested Set + Java + Spring + Postgresql + Pl/pgsql + Yahoo framework

hierarchicE’ da 4-5 giorni che sono alle prese con un problemino, che all’inizio sembrava di facile soluzione, ma alla fine, ha impegnato un pò di tempo e non solo….ha richiesto l’uso di molte tecnologie e alla fine mi ha dato soddisfazione, che ovviamente voglio condividere con voi….voi chi, visto che non posta ancora nessuno? ancora non è chiaro :P Dunque il problema in origine era: come faccio a memorizzare una struttura dati gerarchica all’interno di un database ? Se vi fermate un attimo a pensarci, vi accorgerete che la risposta non è né banale, né immediata.Avevo già utilizzato una soluzione tempo fa, ma mi sono reso conto che non era molto potente dal momento che, la struttura gerarchica per quella particolare specifica era statica, quindi non era possibile aggiungere/eliminare alcun elemento, e per modificarla occorreva un grosso refactoring. Dopo di che mi sono imbattuto nell’algoritmo Preorder traversal tree o Nested set molto potente che consente di memorizzare una struttura gerarchica da albero all’interno di un database relazionale, e mi accorgo che la soluzione da me usata in precedenza ha un nome ben preciso Adjacency List Model che in effetti ha una serie di limitazioni come ho potuto riscontrare sul campo. Non starò qui a descrivere nel dettaglio come funzionano i 2 algoritmi, potete tranquillamente approfondire seguendo i link di sopra che spiegano in modo esaustivo il concetto che sta dietro. Sappiate solo che per risolvere i problemi dell’Adjacency List Model, l’algoritmo Nested Set tende ad aggiungere delle meta-informazioni che permettono di recuperare i dati relativi alla struttura gerarchica al costo di query molto semplici che usano dei self-join. Analizziamo ora lo sviluppo dell’applicazione in blocchi.

  • Business Logic La logica di business include diverse componenti, tra cui Spring il potente framework utilizzato per lo sviluppo di applicazione web J2EE, database Postgresql e il linguaggio procedurale PL/pgsql. La logica di Spring ha richiesto lo sviluppo di un interfaccia che descrive le operazioni possibili sull’albero, una classe che la implementa che definisce la logica delle operazioni e comunica con lo strato del database. Il database contiene la struttura di una tabella per memorizzare le informazioni sull’albero, con le sue meta-informazioni. Ho aggiunto una meta-informazione aggiuntiva che definisce la profondità di ciascun nodo, utile poi in fase di visualizzazione.
     CREATE TABLE nestedset ( idnode serial NOT NULL, nome character varying NOT NULL, descrizione character varying NOT NULL, lft integer NOT NULL, rgt integer NOT NULL, depth integer );

    Tutte le operazioni a livello di database per la gestione dell’albero, sono implementate all’interno del database stesso sotto forma di funzioni procedurali scritte in linguaggio PL/pgsql. Le funzioni realizzate sono:

    • setnodesdepth che aggiorna il campo profondità della tabella subito dopo un operazione di inserimento o cancellazione.
    • insertnode, inserisce un nodo all’interno dell’albero.
    • deletesubtree, elimina un nodo dall’albero. Distinguiamo 2 casi: 1, nodo non ha figli e viene eliminato; 2, nodo ha figli e quindi tutto il sottoalbero viene eliminato.
    • deleteparentnode, elimina un singolo nodo dall’albero. Se il nodo ha figli, questi ultimi vengono spostati verso l’alto di un livello.

    Sono ’slegate’ dall’implementazione Java in modo da poter apportare modifiche senza influire sul codice, ovviamente dipende dalla modica.

  • Presentation logic La logica di presentazione include il framework di Yahoo, in particolare il TreeView Control che mappa l’albero sul componente grafico per visualizzarlo, con in aggiunta un menù contestuale, che permette di realizzare le operazioni di Aggiunta nodo/elimina sottoalbero. Nota:La funzione elimina singolo nodo non è stata mappata ad alcuna voce di menù.

DI seguito trovate alcuni riferimenti sull’argomento e qui il pacchettino con i file sorgenti.

Alla prossima….

Overloading del costruttore

1
2
3
4
5
6
7
8
public class OverloadResolver {
public OverloadResolver(Object param) {
System.out.println("Construttore con parametro Object");
}
public OverloadResolver(Object[] param) {
System.out.println("Costruttore con parametro Object[]");
}
}

Qualcuno sa cosa succede se faccio questa chiamata:

1
2
3
4
5
......
public static void main(String[] args) {
OverloadResolver or = new OverloadResolver(null);
}
......

Cioè se richiamo quel costruttore con parametro null, cosa viene richiamato nella classe di sopra ? Dai dai dai che lo sapete……e ditelo !!!!!! (magari qualcuno commenterà….chi lo sa!!!)

 

You need to log in to vote

The blog owner requires users to be logged in to be able to vote for this post.

Alternatively, if you do not have an account yet you can create one here.

Powered by Vote It Up

WordPress Themes