Spring Web Flow tutto d’un fiato ! [Part 1/2]

spring-webflowSpring Web Flow è un framework basato su Spring MVC che permette lo sviluppo di applicazioni flow-based. In questo articolo vedremo come aggiungere Spring Web Flow ad applicazioni Spring-based e definire alcuni flussi tra le applicazioni e i loro utenti.
Iniziamo col dire che tutti i flussi (da ora in avanti li chiameremo con loro vero nome, flow) sono costituiti da 3 elementi base, ovvero:

  • States
  • Transitions
  • Flow data

States: indica ciò che accade all’interno di un flow. Spring Web Flow definisce 5 tipi di States differenti: View, Active, End, Subflow, Decision. Vedremo più avanti come questi tipi di stato partecipano alla creazione di un flow.
Transitions: sono di fatto il mezzo di collegamento tra i vari States. Una View state, può contenere al suo interno un certo numero di transizioni ciascuna delle quali collega altri stati.
Flow data: all’interno di un flow vengono collezionati alcuni dati che dipendono dagli stati in cui il flow si trova o si trovava. La visibilità dei dati di ciascun flow dipende dallo scope con cui questo viene definito. Per la precisione esistono 5 tipi di scope differenti in SWF che sono:
 

  • Flow scope : viene creato all’inizio del flow e distrutto quando il flow termina. I dati all’interno del Flow scope sono accessibili e disponibili per tutta la durata del flow
  • Conversation scope : viene creato all’inizio di un top-level flow e distrutto quanto il flow termina. Il conversation scope è simile al flow scope, eccetto per l’accesso ai dati. Infatti mentre il flow scope è accessibile solo dal flow che l’ha creato, il conversation flow può essere acceduto sia dal top-level flow che da tutti i suoi subflow.
  • Request scope : viene creato all’inizio di una richiesta HTTP e distrutto al termine di tale richiesta. I dati all’interno del request scope sono disponibili a tutti gli stati del flow per l’intera durata della richiesta.
  • Flash scope : viene creato quando il flow ha inizio, ripulito quando si entra all’interno di una view e distrutto alla fine del flow. I dati all’interno del flash scope sono disponibili a tutti gli states per l’intera durata del flow.
  • View scope : viene creato quando il flow entra all’interno di una view e distrutto quando la view viene renderizzata. E’ il flow che ha più breve durata, per questo motivo i dati all’interno di questo scope sono disponibili solo alla view che li ha creati.

Ciò detto vediamo come fare ad installare ed utilizzare SFW in pochi semplice passi. Naturalmente per poter iniziare a lavorare con SWF è necessario scaricare i jar che lo contengono, questa pagina costituisce il punto di partenza da cui potete trovare info dettagliate sul framework e dal link download potete scaricare le librerie. Le librerie necessario da aggiungere al classpath della propria applicazione sono :

  • org.springframework.WebFlow-2.0.8.RELEASE.jar
  • org.springframework.binding-2.0.8.RELEASE.jar
  • org.springframework.faces-2.0.8.RELEASE.jar
  • org.springframework.js-2.0.8.RELEASE.jar

in realtà WebFlow e Binding sono le librerie principali, mentre faces e js mettono a disposizione il supporto a JSF e Javascript/Ajax. In aggiunta a queste librerie è necessario aggiungere il supporto al linguaggio che aiuta a definire i Flow.
SWF usa OGNL.

SWF come detto si basa su Spring MVC, quindi così come per tutte le applicazioni spring-based, le richieste passano attraverso il DispatcherServlet. Per configurare SWF basta aggiungere le seguenti righe di codice al file servlet.xml :

<servlet>
    <servlet-name>DevMeApp</servlet-name>   
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

Bene, a questo punto non rimane che indicare quale richiesta debba essere "catturata" da spring e quindi girata a SWF. Per fare questo, ci basterà configurare il nostro DispatcherServlet con un handle per tutte le richieste il cui URL fa match con la stringa "devme"…tradotto:

<servlet-mapping>
  <servlet-name>DevMeApp</servlet-name>
  <url-pattern>/devme/*</url-pattern>
</servlet-mapping>

Ora che abbiamo configurato Spring passiamo alla configurazione di SWF. La configurazione avviene tramite compilazione di file XML di Spring aggiungendo a tali file il namespace di SWF, in modo tale da poter utilizzare i tag messi a disposizione dal framework. Quindi partendo da un file di spring aggiungiamo i seguenti namespace:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:webflow="http://www.springframework.org/schema/webflow-config"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/webflow-config
           http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd">
</beans>

quindi aggiungiamo la nostra configurazione all’interno del tag beans.
La prima cosa da fare è aggiungere un riferimento al flow executor, ovvero all’elemento che gestisce l’esecuzione del flow. Aggiungiamo quindi il tag:

<webflow:flow-executor id="flowExecutor"/>

Il secondo step è quello di indicare a SWF dove trovare il file xml della definizione del flow. Questo viene fatto attraverso il tag flow registry che permette di indicare la posizione su file system in cui risiedono i file XML dei vari flow. Per esperienza, è molto comodo creare i file di flusso separati all’interno di ogni singolo file.
Aggiungiamo quindi il flow registry:

<webflow:flow-registry id="flowRegistry" base-path="/WEB-INF/flows-defs">
	<webflow:flow-location id="devme" path="/WEB-INF/flows/devme-flow.xml" />
</webflow:flow-registry>

l’attributo id del tag flow location permette di assegnare un id al flow di modo che possa essere riferito all’occorrenza. In assenza dell’attributi id, l’id del flow corrisponderà al nome del file del flow.

SWF fornisce un handler adapter (spring based) che permette che fa da bridge tra il DispatcherServlet e il flow executor, gestendo la richiesta e manipolando il flow. E’ necessario configurare l’handler all’interno del file di configurazione affinché possa essere invocato dal framework. Aggiungiamo quindi il seguente tag bean:

<bean class="org.springframework.WebFlow.mvc.servlet.FlowHandlerAdapter">
  <property name="flowExecutor" ref="flowExecutor" />
</bean>
<bean class="org.springframework.WebFlow.mvc.servlet.FlowHandlerMapping">
  <property name="flowRegistry" ref="flowRegistry" />
</bean>

in questo modo il DispatcherServlet è in grado di stabilire a chi girare la richesta consultando uno o più handler. Nel codice di sopra abbiamo aggiunto un riferimento al FlowHandlerMapping il quale mantiene un riferimento al FlowRegistry in modo tale da girare la richiesta al flow corretto. Un’applicazione basata su SWF tipicamente contiene più di un flow, per questo il collegamento tra FlowHandlerMapping e flow registry consente di selezionare il flow corretto sulla base della richiesta ricevuta.

Dopo aver configurato la nostra applicazione Spring fornendo il supporto a SWF vediamo come definire un file di flow. Un file di flow è un file XML diverso da quello visti finora, nel senso che ha un nodo radice diverso il quale appartiene ad un namespace differente.
 

<flow xmlns="http://www.springframework.org/schema/webflow"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.springframework.org/schema/webflow
      http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
	<-- Add your definition here -->
</flow>

Vediamo quali sono gli elementi che contribuiscono alla definizione di un flow. Di seguito viene mostrato un elenco di tali elementi con per ciascuno una breve descrizione.

  • <action-state>, definisce una o più azioni, al termine delle quali si può transire verso uno stato successivo all’interno del flow.
  • <attribute>, consente di definire un attributo da memorizzare all’interno del flow. Viene utilizzato assieme al tag <value>.
  • <bean-import>, importa un elemento bean definito dall’utente.
  • <decision-state>, permette di valutare un’espressione sulla base della quale decidere verso quale stato transire.
  • <end-state>, indica lo stato finale del flow. La transizione su questo stato comporta il termine del flow.
  • <exception-handler>, indica un bean che può gestire eccezioni per questo flow.
  • <global-transition>, definisce una o più transizioni che sono disponibili da tutti gli stati.
  • <input>, definisce un input per questo flow.
  • <on-end>, evento che definisce l’azione che viene richiamata quando il flow termina.
  • <on-start>, evento che definisce l’azione che viene richiamata quando il flow inizia.
  • <output>, definisce un output per questo flow.
  • <persistence-context>, crea e alloca un contesto quando il flow ha inizio. Viene usato assieme al transaction manager.
  • <secured>, viene usato per restringere l’accesso ad un determinato stato.
  • <subflow-state>, invoca un nuovo flow come sotto-flow di quello corrente.
  • <var>, definisce una variabile con flow scope.
  • <view-state>, indica uno stato che si presenta all’utente tipicamente caricando un certo output e richiedendo l’interazione da parte dell’utente.

Per evitare di dlungarmi più di quanto abbia già fatto rimando la continuazione di questo articolo al post successivo. Continuo a scrivere quandi non passerà molto dalla pubblicazione seguente…prometto :P  !
Stay tuned.

Il backup di linux

backupIn questo ultimo periodo mi sto occupando di trovare un metodo di backup da implementare per il server che amministro. Ho dato un occhiata a diversi sistemi che consentono di effettuare l’operazione di backup dei dati, ma al momento non ho ancora scelto quello che fa per me. In più ho postato anche sul forum del sito debianizzati.org un’ottima risorsa per la comunità Debian con gente in gamba disponibile ad aiutare altri in difficoltà. Quindi essendo un’altro in difficoltà, ho deciso di scrivere sul forum chiedendo loro un parere, al fine di risolvere il problema e di creare un guida più o meno completa del problema del backup su linux.
Vi siete mai chiesti quale sia lo strumento di backup utilizzato da wikipedia ? Sinceramente sì, ma non so come possa essere implementato…mi vengono in mente cluster di server che fanno solo quello con tera e tera disponibili utilizzati per salvare i dati.
E riferiamoci proprio a Wikipedia per la definizione di backup: "Il backup, copia di sicurezza o copia di riserva in informatica indicano un’operazione atta a prevenire la perdita totale dei dati archiviati nella memoria di massa dei computer siano essi stazione di lavoro o server. L’attività di backup è un aspetto fondamentale della gestione di un computer: in caso di guasti, manomissioni, furti, ecc., ci si assicura che esista una copia dei dati, pertanto l’esecuzione del backup è quasi sempre automatica e svolta normalmente con una periodicità stabilita (per esempio una volta al giorno o alla settimana)."
Su Linux esistono diversi strumenti per effettuare il backup dei dati…uno di questi è dd, un tool che si usa da riga di comando mooolto potente. Nei prossimi articoli riguardanti questo argomento vedremo altri tool.

Il comando dd

Il comando dd permette di copiare i dati in blocchi, effettuando delle conversioni all’occorrenza. E’ molto facile da utilizzare, in sostanza è necessario indicare il valore di 2 parametri.

devme:~$ dd if=/dev/sda1 of=/media/backup.img

Nell’esempio di sopra è richiesto a dd di effettuare la copia della prima partizione dell’unità sda all’interno dell’immagine backup.img. Al termine, il file di immagine corrisponderà alla partizione sorgente e sarà possibile montarlo grazie al comando mount. Vedremo più avanti come.

devme:~$ dd if=/dev/sda of=/media/sdb

In questo caso invece viene effettuata il clone del disco sda all’interno del disco sdb. Si noti che la copia in questo caso prevede la copia eventuale anche del MBR , master boot record e dell’eventuale partizione di swap.
La sintassi base del comando dd è la seguente:

devme:~$ dd if=<source></source> of=<target> bs=<byte size=""> skip= seek= conv=<conversion> </conversion></byte></target>

Per effettuare l’operazione simmetrica, ovvero il ripristino dei dati da un backup, è sufficiente invertire il valore dei parametri if e of. Vediamo ora una serie di esempi di utilizzo del comando dd.

Effettua la duplicazione di una partizione in un’altra partizione, copiando i dati in blocchi da 4kb, effettuando le conversioni: notrunc=non troncare il file di output,noerror=non fermarti in caso di errore nella lettura di un blocco di dato.

devme:^$ dd if=/dev/sda2 of=/dev/sdb2 bs=4096 conv=notrunc,noerror

Crea un’immagine ISO di un cd, leggendo in blocchi da 2kb, effettuando le conversioni: sync fa padding dei blocchi letti con bye nulli, noerror non fermarti in caso di errore nella lettura di un blocco di dato.

devme:^$ dd if=/dev/hdb of=/home/devme/cdrom.iso bs=2048 conv=sync,notrunc

Crea un’immagine ISO di un cd, leggendo in blocchi da 2kb, effettuando le conversioni: sync fa padding dei blocchi letti con bye nulli, noerror non fermarti in caso di errore nella lettura di un blocco di dato. Successivamente sarà possibile montare l’immagine creata usando il comando mount.

devme:^$ dd if=/dev/hdb of=/home/devme/cdrom.iso bs=2048 conv=sync,notrunc
devme:^$ mount -o loop /home/devme/cdrom.iso /mnt/iso_images

Effettuare una cancellazione sicura a prova di bomba, ovvero senza la possibilità di poter risalire ai dati scritti (secure delete).

1
2
#!/bin/bash 
for n in `seq 7`; do dd if=/dev/urandom of=/dev/sda bs=8b conv=notrunc; done

Modo semplice e veloce per effettuare un backup del proprio portatile. Segue comando per effettuare il ripristino.

devme:^$ dd if=/dev/hda of=/dev/sda bs=64k conv=notrunc,noerror
devme:^$ dd if=/dev/sda of=/dev/hda bs=64k conv=notrunc,noerror

Crea il backup di un hard disk e memorizzalo all’interno di una serie di DVD. Seguono comandi per il ripristino.

devme:^$ if=/dev/sda3 of=/home/devme/dvd_set_1.img bs=1M count=4430
devme:^$ dd if=/dev/sda3 skip=4430 of=/home/devme/dvd_set_2.img bs=1M count=4430
devme:^$ dd if=/dev/sda3 skip=4430 of=/home/devme/dvd_set_3.img bs=1M count=4430
devme:^$
devme:^$ dd if=/media/devme/devme_set_1.img of=/dev/sda3 bs=1M conv=sync,noerror
devme:^$ dd if=/media/devme/devme_set_2.img of=/dev/sda3 seek=4430 bs=1M conv=sync,noerror
devme:^$ dd if=/media/devme/devme_set_3.img of=/dev/sda3 seek=4430 bs=1M conv=sync,noerror

Copia il master boot record, ma non la tabella delle partizioni.

devme:^$ dd if=/dev/sda of=/home/devme/devme_mbr.img bs=446 count=1

Vediamo ora un caso particolare, ovvero fare il backup completo di un disco, quindi con MBR, tabella delle partizioni e tutte le partizioni del disco, e vediamo come fare a selezionare solo la partizione da montare con il comando mount. Quindi supponiamo di avere:

devme:^$ dd if=/dev/sda of=my_disk_image.img

con il quale effettuiamo il backup creando il nostro file immagine my_disk_image.img che contiene al suo interno l’intero disco. Per montare solo una certa partizione dovremmo sapere l’offset in cui inizia la partizione e quindi montarla con il comando mount sul dispositivo di loop. Per determinare l’offset dei vari elementi costituenti il disco utilizziamo un’altro comando di linux che consente di far questo, parted. Lanciando parted sul file appena creato otteniamo:

devme:^$ parted my_disk_image.img
GNU Parted 1.7.1
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) unit
Unit?  [compact]? B
(parted) print
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Number  Start        End           Size         Type     File system  Flags
1      32256B       106928639B    106896384B   primary  ext3         boot
2      106928640B   1184440319B   1077511680B  primary  linux-swap
3      1184440320B  10256924159B  9072483840B  primary  ext3
(parted) quit

a questo punto possiamo montare il file system che ci interessa indicando l’offset specifico, ovvero:

devme:^$ mount -o loop,ro,offset=32256 my_disk_image.img /mnt/devme

E qui mi fermo, anche perché potrei continuare per delle ore con degli esempi su dd, alcuni dei quali dimostrano quanto veramente potente è questo comando. Nei prossimi articoli vedremo altri strumenti per realizzare il backup.
Stay tuned !

Generative Programming: Domain Engineering

genSecondo appuntamento con la programmazione generativa già discussa in precedenza in questo articolo sempre sulle pagine di DevMe. Ci eravamo lasciati parlando delle opportunità e delle comodità di cui gode la programmazione generativa, ovvero la creazione di programmi detti generatori capaci di creare, software specifici sulla base di una configurazione data in input. Il generatore è un programma in grado di generare una famiglia di altri programmi; la scelta del componente della famiglia è a carico della configurazione, ovvero del mezzo che discrimina un determinato componente da un altro. La configurazione può essere fornita in diversi modi: un file XML, un insieme di tabelle di un database, etc.
Quello di cui volevo parlare ora riguarda il Domain Engineering. Cos’è ? Partiamo dalla definizione: 
il Domain Engineering è l’attività di recuperare, organizzare e memorizzare l’esperienza maturata durante la costruzione di sistemi software o parti di sistemi di un particolare dominio, in modo tale che possa essere definita una forma di prodotto riutilizzabile, oltre a fornire un mezzo adeguato che descriva come fare a riutilizzare questi prodotti durante la costruzione di nuovi sistemi.
Ecco non spaventatevi….sebbene questa definizione possa sembrare incomprensibile, vedrete che analizzandola ci accorgeremo che è più semplice di quanto sembri.
I sistemi software che conosciamo possono essere classificati secondo la loro area di business, commercio elettronico, sistemi medicali, sistemi di prenotazione aerea, e così via. Chiamiamo queste aree Domini Verticali. Allo stesso modo possiamo classificare le parti di questi software sulla base della loro funzionalità, ad esempio i database, i sistemi di workflow, le librerie, etc. Chiamiamo queste parti di sistemi Domini Orizzontali.
Naturalmente questi sistemi o componenti all’interno di uno stesso dominio, condividono molte caratteristiche tra loro, e quando un’azienda che ha già lavorato ad applicazioni appartenenti ad uno stesso dominio, si trova a dover sviluppare di nuovo parti o sistemi dello stesso dominio, può trarre vantaggio da quanto già fatto riutilizzando il lavoro svolto. Qual è la parte difficile ? Ovviamente, cercare di trarre dalla conoscenza acquisita dal lavoro svolto, una forma che possa rendere quanto prodotto riutilizzabile, così da rimettere in campo componenti già sviluppati, e quindi già pronti. In questo modo l’azienda potrà produrre nuovi prodotti in minor tempo, a basso costo e di altà qualità.
Il Domain Engineering comprende:

  • Domain Analysis
  • Domain Design
  • Domain Implementation

Il Domain Analysis consiste nel definire il focus del dominio, analizzarne le caratteristiche annotando quelle comuni, quelle variabili e le dipendenze all’interno della famiglia dei sistemi.
Il Domani Design consiste nel progettare il design di un’architettura comune costituita da un insieme di componenti elementari e altro, e del loro meccanismo automatico che permette di assemblarli.
Infine il Domain Implementation permette di implementare i componenti, definire la specifica e quindi il generatore.
Introduciamo ora quello che sarà l’esempio del prossimo articolo, ovvero un piccolo progettino che dimostra ancora una volta le potenzialità della programmazione generativa. Nell’ambito del mio lavoro, mi trovo spesso a sviluppare applicazione web in Java, tutti basata su Spring, ormai famoso framework di sviluppo, con il supporto di Spring Web Flow sottoprogetto di Spring utilizzato per lo sviluppo di Internet Rich Applications.
Prima osservazione: la struttura di un progetto Spring, almeno per quanto mi riguarda oramai è nota, o meglio detto in termini di Domain Engiinering, ha molte parti che accomunano i diversi progetti. Seconda osservazione: molti dei task implementati nell’ambito di applicazioni web, hanno in comune alcune funzionalità. Ad esempio, nell’ambito dello sviluppo di un RIA, classica di gestione clienti e fornitori, per ciascuna delle due entità dovremo fornire sempre le funzionaltià di inserimento, modifica e cancellazione. Bene, sulla base di queste osservazioni costruiremo una versione alpha del nostro generatore, che andremo via via migliorando. Al momento esiste già una versione alpha di questo generatore, non sono ancora riuscito a portarla a termine visto che il tempo che ho disposizione non è molto. Vedremo infine come la specifica di configurazione viene fornita mediante database, per la quale in futuro potrebbe essere sviluppata una piccola applicazione ad-hoc che permette di definirla in modo semplice.
Stay tuned !

Top 10 Wordpress tips 2009

wordpress1Siamo alla fine del 2009 e come si fa in questi casi è tempo di bilanci. Volevo stilare una classifica dei migliori 7 tips (trucchi) per wordpress che ho usato e trovato in rete. Wordpress si sta affermando sempre più come piattaforma di blogging e CMS quindi spero risulti utile a chiunque visiti le pagine del mio sito.
 
 

1. Pubblicare un post su Wordpress dall’esterno di Wordpress

Questo tip è quello che mi è piaciuto più di tutti…non l’ho ancora mai provato ma sicuramente lo farò in futuro. Il codice che segue permette di pubblicare un post su wordpress dall’esterno di wordpress, utilizzando cURL ovvero delle librerie php che permettono di simulare un web browser…più precisamente permettono di utilizzare tra gli altri il protocollo HTTP facendo submit dei dati di un form ad esempio.
Per utilizzare questa funzionalità è necessario attivare XMLRPC all’interno del file php.ini presente nell’installazione di php. Se non la si attiva non sarà possibile inserire il contenuto dei post all’interno del database.
Vediamo il codice:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function add_post_to_wordpress($title, $body, $rpcurl, $username, $password, $category, $keywords='', $encoding='UTF-8') {
    $title = htmlentities($title,ENT_NOQUOTES,$encoding);
    $keywords = htmlentities($keywords,ENT_NOQUOTES,$encoding);
 
    $content = array(
        'title'=>$title,
        'description'=>$body,
        'mt_allow_comments'=>0,  // 1 to allow comments
        'mt_allow_pings'=>0,  // 1 to allow trackbacks
        'post_type'=>'post',
        'mt_keywords'=>$keywords,
        'categories'=>array($category)
    );
    $params = array(0,$username,$password,$content,true);
    $request = xmlrpc_encode_request('metaWeblog.newPost',$params);
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
    curl_setopt($ch, CURLOPT_URL, $rpcurl);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT, 1);
    $results = curl_exec($ch);
    curl_close($ch);
    return $results;
}

2. Visualizzare il numero di parole contenute in un post

E’ possibile contare il numero di parole che compongono un post per visualizzarlo, ad esempio, alla fine del post stesso. Basta aggiungere il seguente pezzo di codice all’interno del file function.php

1
2
3
4
5
6
7
8
9
10
function post_word_count(){
    /* Attiva l'output buffering */
    ob_start();
    /* Ottieni il contenuto del post */
    the_content();
    /* Preleva e ripulisci il contenuto del buffer */
    $content = ob_get_clean();
    /* Calcola il numero delle parole*/
    return sizeof(explode(" ", $content));
}

Al termine per richiamare la funzione è sufficiente effettuare la chiamata:

<?=post_word_count()?>

3. Ottenere la prima immagine di un post e visualizzarla

Questo tip è stato uno dei più quotati del 2009 dal sito WpRecipes. Dall’intero del contenuto di un post è possibile prelevare l’immagine contenuta al suo interno e visualizzarla.

1
2
3
4
5
6
7
8
9
10
11
12
13
function get_first_post_image() {
  global $post, $posts;
  $first_img = '';
  ob_start();
  ob_end_clean();
  $output = preg_match_all(\'//i\', $post->post_content, $matches);
  $first_img = $matches [1] [0];
 
  if(empty($first_img)){ //Defines a default image
    $first_img = "/images/default.jpg";
  }
  return $first_img;
}

Il codice di sopra va inserito all’interno del file function.php in modo che sia disponibile ai file del tema corrente. Al termine è possibile richiamarlo nel seguente modo:

1
<?=get_first_post_image()?>

4. Usare SSL per l’accesso al pannello di amministrazione

Per aumentare la sicurezza durante gli accessi al pannello di amministrazione di wordpress è possibile abilitare il protocollo SSL. SSL è un protocollo crittografico che permette una comunicazione sicura e integrità dei dati trasmessi su reti che utilizzano il protocollo TCP-IP, come internet. Per abilitarlo è sufficiente aggiungere la seguente riga di codice all’interno del file wp-admin.php:

1
define('FORCE_SSL_ADMIN', true);

5. Potenziare la funzionalità di ricerca

E’ possibile aumentare la potenzialità della ricerca integrata all’interno di wordpress evidenziando ad esempio i risultati trovati.

1
2
3
4
5
6
 $title = get_the_title();
 $keys= explode(" ",$s);
 $title = preg_replace('/('.implode('|', $keys) .')/iu',
	'<strong class="search-excerpt">\0</strong>',
	$title);
<br />

Il codice di sopra va inserito all’interno del file search.php del vostro tema corrente. Al termine aggiungete all’interno del vostro foglio di stile il codice per evidenziare i risultati della ricerca:

1
strong.search-excerpt { background: yellow; }

6. Override del nome dell’autore utilizzando un custom field

Se dal vostro blog permette la pubblicazione di post ad utenti guest, allora questo tip può esservi utile. In sostanza si tratta della creazione di un custom field che memorizza il nome dell’autore del post. Quindi in fase di visualizzazione del post se presente il custom field, si preleva il contenuto; altrimenti utilizzando la funziona predefinita di wordpress, si ottiene il nome dell’autore.
Vediamo come:

1
2
3
4
5
6
$author = get_post_meta($post->ID, "guest-author", true);
if ($author != "") {
    echo $author;
} else {
    the_author();
}

7. Riutilizzare i vecchi post

Questo tip permette di riutilizzare i vecchi post cercando di sfruttarli anche se sono stati pubblicare molto tempo fa. Per monetizzare i vecchi post è possibile ad esempio far comparire banner pubblicitari, ad esempio, solo se il post è più vecchio di 15 giorni.
Il codice che segue riconosce se il post è più vecchio di 15 giorni e in caso positivo aggiunge un banner pubblicitario.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function is_old_post($post_id=null){
   $days = 15;
   global $wp_query;
   if(is_single() || is_page()) {
      if(!$post_id) {
         $post_id = $wp_query->post->ID;
      }
      $current_date = time();
      $offset = $days *60*60*24;
      $post_id = get_post($post_id);
      $post_date = mysql2date('U',$post_id->post_date);
      $cunning_math = $post_date + $offset;
      $test = $current_date - $cunning_math;
      if($test > 0){
         $return = true;
      }else{
         $return = false;
      }
   }else{
      $return = false;
   }
   return $return;
}

Il codice di sopra va inserito all’interno del file function.php in modo che sia disponibile ai file del tema corrente. Al termine è possibile richiamarlo nel seguente modo dall’interno del file singel.php:

1
2
3
<? if(is_old_post()){ ?>
INSERIRE IL CODICE QUI
<? } ?>

Stay tuned!

Generative Programming: yes !!

gen

Oggi volevo parlare di un argomento che mi appassiona molto, ovvero della Generative Programming (Programmazione Generativa).
Mi piacerebbe molto dedicarmi a tempo pieno, ma ahimé il tempo è quello che è ! Cerchiamo di capire di cosa stiamo parlando, o meglio proviamoci.

La Programmazione Generativa è un paradigma di programmazione basato sulla modellazione di famiglie di sistemi software tali che, data una particolare specifica dei requisiti, è possibile generare su richiesta (on-demand) una versione definitiva del software personalizzata in modo automatico.
La programmazione generativa si concentra sulle famiglie di sistemi software piuttosto che su un tipo specifico di sistema. I singoli software appartenenti a famiglie di sistemi, possono essere generati a partire da un modello di dominio generativo (generative domain model), ovvero un modello di una famiglia di sistemi che ha 3 elementi:

  1. un mezzo per la specifica dei singoli software
  2. un insieme di implementations components capaci di costruire, assemblandoli, i singoli software
  3. una specifica di configurazione, che mappa la specifica di un membro con la sua implementazione finale

Un’analogia la si ha quando si ordina un’automobile. C’è un sistema per ordinare l’automobile, ci sono i componenti con i quali si provvede ad assemblare (costruire) l’automobile ed infine c’è una specifica configurazione che indica come assemblare l’automobile secondo l’ordinazione richiesta.
Riepilogando, l’idea è generare in modo automatico singoli sistemi a partire da modelli di famiglie di sistemi. I componenti e la specifica di configurazione vengono riutilizzati per ogni membro generato, riducendo così il costo dello sviluppo per ogni singolo membro.
Esiste realmente un potenziale di riduzione dei costi di realizzazione di singoli software, perchè piuttosto che dover sviluppare da zero l’intero sistema, spesso basta aggiungere solo qualche funzionalità al modello generativo esistente. In sintesi è possibile praticare economie di scala utilizzando la programmazione generativa, riducendo sia i tempi che i costi dello sviluppo di un singolo software.
Non voglio annoiare con troppa teoria, quindi passiamo a vedere un esempio pratico che illustra le potenzialità della generazione automatica del codice, non prima di aver precisato che, prima di avventurarsi alla scrittura di un generatore è necessario effettuare una attenta analisi per determinare quali sono le caratteristiche comuni della famiglia dei software con cui si vuole operare. Questa analisi ha un nome preciso e si chiama: Domain Engineering.
Nell’ultimo post pubblicato su devme, abbiamo visto come rendere più chiara l’organizzazione dei custom fields di wordpress raggruppandoli semanticamente all’interno di un riquadro. L’articolo illustra il codice da scrivere per realizzare il raggruppamento, in particolare analizzando il codice generato si nota come alcune parti di esso sono statiche, o meglio non cambiano nelle varie implementazione; altre invece sono dinamiche, cioè cambiano sulla base di ciò che si sta creando. Vediamo più nel dettaglio. 
La prima parte di codice costituisce la definizione della struttura dati che mappa i custom fields che si vogliono raggruppare con il riquadro. Si definisce il nome del custom fields ed il titolo che comparirà in corrispondenza del campo all’interno del riquadro. La seconda parte consiste nella creazione di funzioni che riferiscono quanto dichiarato nella prima parte e creano il riquadro che verrà visualizzato, in termini di codice HTML, la logica che permette il salvataggio dei dati in fase di inserimento e modifica di un post. Infine si procede con la registrazione all’interno del framework del codice scritto.
Non mi soffermerò molto sul codice dal momento che l’articolo è esaustivo, piuttosto rendo disponibile il codice che realizza il generatore qui ampiamente commentato e la versione demo qui.
Nelle prossime puntate vedremo cosa vuol dire e come si realizza il Domain Engineering passaggio chiave per la realizzazione di software generatori.
Stay tuned.
 

 

Personalizzare il pannello di admin dei custom fields in Wordpress

wordpress1Come avrei potuto non avere a che fare con Wordpress !? Provate un pò a dirmelo….il mitico CMS semplice da usare, gestire e facile da installare. Non ho mai parlato sul blog di wordpress, ne di quanto lo usi a lavoro..Tutti i giorni il caro inkiostro mi riempie sempre di nuovi e mirabolanti problemi da risolvere in wordpress, quindi mi sono fatto una discreta cultura.
Quello di cui volevo parlare è di come fare a rendere più chiara l’organizzazione dei custom field all’interno del pannello di amministrazione di wordpress, raggruppandoli semanticamente all’interno di un riquadro. Per intenderci dalla situazione in figura :

campi-personalizzati-wp2

passare a questa:

Pannello dei custom field 

Vediamo il codice php per realizzare quanto mostrato in figura:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$box_dati_devme =
	array(
	    "titolo" => array(
	    "name" => "titolo",
	    "std" => "",
	    "title" => "Titolo",
	    "description" => ""),
 
	"url" => array(
	    "name" => "url",
	    "std" => "",
	    "title" => "URL",
	    "description" => ""),
 
	"argomento" => array(
	    "name" => "argomento",
	    "std" => "",
	    "title" => "Argomento",
	    "description" => ""));

Primo step: definire una variabile di tipo array in cui il primo elemento ha come chiave il nome del campo custom e come valore, un array che definisce il nome alcuni attributi del campo custom, nome, std, title e description.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function box_dati_devme_boxes() {
	global $post, $box_dati_devme;
	echo '<table style="width:99%">';
	foreach($box_dati_devme as $meta_box) {
		echo '<tr>';
		echo '<td class="left" style="width:25%;">';
		$meta_box_value = get_post_meta($post->ID, $meta_box['name'], true);
		if($meta_box_value == "")
			$meta_box_value = $meta_box['std'];
		echo '<input type="hidden" name="'.$meta_box['name'].'_noncename" id="'.$meta_box['name'].'_noncename" value="'.wp_create_nonce( plugin_basename(__FILE__) ).'" />';
		echo $meta_box['title'];
		echo '<br />';
		echo '<label class="desc" style="font-size:10px;" for="'.$meta_box['name'].'_value">'.$meta_box['description'].'</label>';
		echo '</td>';
		echo '<td>';
		echo '<textarea style="width:99%" rows="2" name="'.$meta_box['name'].'_value">'.$meta_box_value.'</textarea>';
		echo '</td>';
		echo '</tr>';
	}
	echo '</table>';
}

Secondo step: definire una funziona in cui l’unica cosa da fare è aggiungere come variabile globale dopo $post il nome della variabile definita allo step uno e la stessa variabile come iteratore del foreach.

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
27
28
29
30
31
32
33
34
function create_meta_box() {
    global $theme_name;
 
    if ( function_exists('box_dati_devme_boxes') ) {
        add_meta_box('box_dati_devme', 'Boxe DevMe', 'box_dati_devme', 'post', 'normal', 'high');
    }
}
 
function save_postdata( $post_id ) {
	global $post, $box_dati_devme;
 
	foreach($box_dati_devme as $meta_box) {
		if ( !wp_verify_nonce( $_POST[$meta_box['name'].'_noncename'], plugin_basename(__FILE__) )) {
			return $post_id;
		}
		if ( 'page' == $_POST['post_type'] ) {
			if ( !current_user_can( 'edit_page', $post_id ))
				return $post_id;
		} else {
			if ( !current_user_can( 'edit_post', $post_id ))
				return $post_id;
		}
		$data = $_POST[$meta_box['name'].'_value'];
		if(get_post_meta($post_id, $meta_box['name']) == "")
			add_post_meta($post_id, $meta_box['name'], $data, true);
		elseif($data != get_post_meta($post_id, $meta_box['name'], true))
			update_post_meta($post_id, $meta_box['name'], $data);
		elseif($data == "")
			delete_post_meta($post_id, $meta_box['name'], get_post_meta($post_id, $meta_box['name'], true));
	}
}
 
add_action('admin_menu', 'create_meta_box');
add_action('save_post', 'save_postdata');

Terzo step: nell’ultimo step ho racchiuso un pò di cose tutte assieme. Si definiscono 2 funzioni : la create_meta_box e la save_postdata. La prima viene richiamata dal framework di wordpress per creare il riquadro, contestualmente si definiscono il nome del riquadro che comparirà poi in pagine e si aggiunge il riferimento alla variabile definita nello step uno. La funzione save_postdata viene usata da wordpress per effettuare il salvataggio dei custom field inseriti al momento dell’inserimento e della modifica di un post. Infine si registrano le 2 funzioni php con le add_action di wordpress.
E’ chiaro che in presenza di pochi custom field si può procedere anche senza personalizzare il pannello; al contrario, con un centinaio di campi il tutto diviene più chiaro. Consiglio nel caso usiate questa soluzione, di definire la personalizzare dei campi all’interno di un file .php che terrete all’interno del tema che state usando, quindi importarlo all’interno del file function.php.
Stay tuned.

NFC: qualcosa si muove (part-two)

NFC2Seconda parte dell’articolo su come scrivere programma che legge da un tag RFC e scrive su un tag RFC. Qui potete trovare quanto scritto sulla lettura da un tag NFC.
La parte che andremo ad analizzare prevede la scrittura di alcune informazioni su un tag NFC, attraverso l’interfaccia della nostra applicazione. Nello specifico andremo a scrivere i dati di un nuovo contatto della rubrica sul tag e successivamente potremo leggere quanto scritto con la funzionalità vista nell’articolo precedente.

Anche in questo caso la logica che sta dietro alla funzionalità è basata sugli eventi, in particolare, per la classe NFCWriter viene registrato un evento, il Tag Detection, che quanto catturato, esegue il codice contenuto al suo interno. Dall’interfaccia viene richiesto di inserire il testo da scrivere sul tag, si preme su OK e si avvicina il cellulare in prossimità del tag. A quel punto viene gestito l’evento che si preoccupa di stabilire una connessione con il tag NFC ed inviare.

 

write-on-nfc-1     write-on-nfc-3

 

Vediamo il codice che realizza quanto detto:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class NDEFWriter extends NDEFHandler {
    Form previousScreen = null;
    String fName = "No name";
 
    public NDEFWriter(Form form) {
	previousScreen = form;
    }	
 
    public void targetDetectionMade(TargetProperties[] tProp) {
	previousScreen.append("Target detected.\n");
	if(tProp == null || tProp.length == 0) {
	    previousScreen.append("No TargetProperties found!\n");
	}
	NDEFTagConnection ndefTagConnection = null;
	try {				
	    previousScreen.append("Target mapping:"+tProp[0].getMapping()+"\n");				
	    ndefTagConnection =NDEFUtil.getNDEFTAGConnection(tProp,previousScreen);
	    if(ndefTagConnection == null) {
		previousScreen.append("Could not establish connection to card.\n");
		return;
	    }
	    NDEFUtil.writeNDEFMessage(ndefTagConnection,previousScreen);
	    ndefTagConnection.close();
	} catch (Exception e) {
	    if(ndefTagConnection != null) {
		try {
		    ndefTagConnection.close();
		} catch (IOException e1) {
		    previousScreen.append("Exception in closing connection:"+e1.toString());
		}
	    }
	    previousScreen.append("Exception:\n"+e.toString()+"\n");
	    e.printStackTrace();
	}
 
    }
 
    public String getFormattedName() {
	return fName;
    }
 
    public void setFormattedName(String fName) {
	this.fName = fName;	
    }
}

Verifica dei dati scritti sul tag

Come potete vedere non è molto difficile realizzare un programma Java che sfrutti le potenzialità fornite dalla tecnologia NFC, tutta sta a conoscere un pò le API messe a disposizione dalla Nokia. Tutto il resto è Java, gestione degli eventi, ereditarietà, interfacce, etc etc. Un vero (buon) programmatore Java non troverà alcuna difficoltà a realizzare la maggior parte delle funzionalità.
Questo è tutto.
Il codice completo per far girare l’esempio lo trovato nelle SDK del Nokia 6131 NFC è necessario la registrazione sul sito per poter effettuare il download. Dopo di che cercate nella cartella examples.
Stay tuned. yo.
 

Symbian – Autostart di un applicazione su serie 9

symbian_logoTempo fa in questo post ho scritto come realizzare un programma starter per Symbian serie 8, cioè un launcher di un’applicazione al boot del cellulare. Oggi invece volevo farvi vedere l’analogo su serie 9, la nuova serie di Symbian installata su molti dei cellulari Nokia attualmente in commercio. Premesso che al momento del post precedente la documentazione sull’argomento non era molta, oggi invece esistono molti siti dove è possibile trovare informazioni sull’argomenti, a partire dallo wiki della nokia dove c’è praticamente tutto, per finire sulla pagine di google ;) .
Ma siccome le risorse in italiano non sono mai abbastanza allora volevo illustrare comunque la soluzione

A differenza della precedente versione con Symbian 9 è possibile realizzare l’autostart di un’applicazione usando il meccanismo dello "Startup Management List API" consultabile qui. L’idea di base di questa gestione consiste nel definire un file di risorse attribuendogli come nome l’UID del package che contiene l’applicazione. Tale file conterrà al suo interno alcuni dati di configurazione al fine di poter eseguire l’applicazione indicata all’avvio del programma. Vediamo in cosa consistono questi dati di configurazione:
 

1
2
3
4
5
6
#include <startupitem.rh> 
RESOURCE STARTUP_ITEM_INFO devme 
{ 
    executable_name = "!:\\sys\\bin\\devme.exe"; 
    recovery = EStartupItemExPolicyNone; 
}

Bene. Subito una piccola precisazione. Come vedete il valore di executable_name coincide con un path, quello di installazione dell’applicazione. Ha all’inizio un punto esclamativo che sta ad indicare la destinazione intesa come unità di installazione. Chi ha installato un’applicazione per symbian sa che ad un certo punto viene chiesto di indicare la destinazione su cui installare il programma, se la memoria del telefono o eventuali memorie esterne. Ecco il ! riferisce quella destinazione. Se si vuole, ad esempio, imporre l’installazione sulla memoria del telefono il ! verrà sostituito con C. 
Come vedete i dati di configurazione sono pochi e il commento viene da se.

Prossimo step consiste nell’aggiungere il riferimento del file appena creato all’interno del file di configurazione .mmp.

1
2
START RESOURCE ..\DATA\APP_UID.rss
END

Ed infine aggiungere le informazioni di configurazione nel file .pkg.

1
"$(EPOCROOT)Epoc32\data\z\private\101f875a\import\apps\APP_UID.rsc" -"c:\private\101f875a\import\[APP_UID].rsc"

Dove la prima parte della riga di configurazione indica il percorso delle SDK che si stanno utilizzando per la build corrente. Nel mio caso siccome sto usando Carbide come IDE posso utilizzare le variabili d’ambiente che mette a disposizione. Da notare che, mentre per un qualunque altro programma posso usare il ! per indicare la destinazione, per quanto riguarda l’autostart su serie 9 devo indicare C come destinazione…non solo, in aggiunta l’intero percorso dove vengono memorizzati questi tipi di file "C:\private\101f875a\import\".
Stay tuned, a presto.

NFC: qualcosa si muove (part-one)

NFC2Oggi ho visto in tv un servizio che parlava su come usare il proprio cellulare per effettuare piccoli pagamenti, il cosidetto telefonino/bancomat. Il funzionamento si basa sulla tecnologia Near Field Communication, ovvero un’evoluzione dell’ RFID la tecnologia che permette di far comunicare due dispositivi portanto in prossimità l’uno dell’altro. In questo articolo pubblicato sulle pagine di devme poco tempo fa, reperibile qui si parla di questa tecnologia, in particolare è possibile utilizzare il telefonino come fosse bancomato, nella modalità touch-to-pay: "avvicinando il cellulare sarà possibile utilizzarlo come una carta di credito. Le informazioni sensibili della carta sono memorizzate al suo interno in un elemento chiamato secure element. In sostanza funziona come una carta di credito senza contatto.E’ possibile visualizzare le informazioni informative classiche delle proprie transazioni".
Nello specifico in linea di principio basta scrivere i dati del proprio bancomat all’interno del cosidetto secure element e a quel punto sarà possibile, avvicinando il cellulare in prossimità di un dispositivo capace di leggere tag NFC, effettuare un’addebito sul proprio conto corrente. Pensateci, quale migliore comodità !!
Bene, analizziamo ora più da vicino, in un esempio molto semplice, come scrivere un programma che legge da un tag NFC e scrive su un tag NFC. Il programma è scritto in Java, l’IDE che utilizziamo per fare il test è Eclipse sul quale installeremo il plugin eclipseme che permette di sviluppare applicazione utilizzando le librerie J2ME.

Partiamo quindi lo scrivere la nostra midlet, DevMeNFCMidlet:

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
public class DevMeNFCMidlet extends MIDlet {
    Display display;
    MainList list;
    String[] actions = new String[]{"Read","Write","Show contacts"};
    Image[] images = null;
 
    protected void startApp() throws MIDletStateChangeException {
	display = Display.getDisplay(this);
	list = new MainList("Select action to do:", List.EXCLUSIVE,actions,images,this,null);
	display.setCurrent(list);
    }
 
    protected void pauseApp() {}
 
    public void destroyApp(boolean arg0) throws MIDletStateChangeException {
	notifyDestroyed();
    }
 
    /**
     * Helper method to change the current displayable
     * 
    */
    public void setToDisplay(Displayable dspl) {
	display.setCurrent(dspl);
    }
}

Nel caso vi foste persi qualcosa sulle Midlet Java, in breve, è un’applicazione che gira su sistemi su cui è presente la J2ME Virtual Machine. Per creare una Midlet è sufficente estendere la propria classe con la classe Midlet, messa a disposizione dal Wireless Toolkit. L’estensione impone l’implementazione di alcuni metodi dal momento che la classe Midlet è una classe astratta. Il metodo StartApp() viene richiamato al momento del lancio dell’applicazione e nel nostro caso crea una lista con 3 opzioni: Read, Write e Show Contacts. La nostra applicazione legge da un tag NFC virtuale, emulato per intenderci e scrive sullo stesso tag. L’emulazione viene messa a disposizione dalle librerie opzionali, Contactless Communication API scaricabili dal sito della nokia, che sarà dunque necessario rendere disponibili all’interno del progetto in eclipse. Lanciando l’applicazione avremo:

midlet-on-nfc

Ciò che si vede è l’interfaccia dell’emulatore con la midlet caricata e pronta per essere eseguita. L’emulatore sostanzialmente è un cellulare, nello specifico il nokia 6131 che possiede al suo interno un tag NFC con le relative librerie. Per avviare l’applicazione è sufficente cliccare il bottone (sul cellulare) in corrispondenza della voce Select.

midlet3-on-nfcL’applicazione come detto mostra il menù con 3 voce selezionabili, in particolare se selezioniamo l’opzione Read saremo in grado di leggere da un tag NFC un certo contenuto. Nella nostra simulazione, i tag NFC sono virtuali e vengono mostrati sulla destra nell’interfaccia dell’emulatore. Alcuni di questi sono attivi, connessi altri disconnessi. Per effettuare una simulazione di lettura trasciniamo il tag NFC attivo all’interno del cellulare come mostrato in figura. A quel punto il contenuto del tag viene mostrato all’interno del telefono.

Ma diamo un occhiata al codice che realizza la funzionalità. L’idea che sta dietro alla logica è molto semplice. Si registra la classe del Reader per un determinato evento, ovvero per l’evento Tag Detection. Rilevato il tag viene richiamato un metodo della classe che stabilisce una connessione con il tag e legge il messaggio inviato dal tag. Il contenuto del messagio viene visualizzato sul display del cellulare. Di seguito incollo il codice della classe Reader.

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class NDEFReader extends NDEFHandler {
    Form previousScreen = null;
    public NDEFReader(Form form) {
        super();
        previousScreen = form;
    }
 
    public void targetDetectionMade(TargetProperties[] tProp) {
	previousScreen.append("Target detected.\n");
	if(tProp == null || tProp.length == 0) {
	    previousScreen.append("No TargetProperties found!\n");
	}
	NDEFTagConnection ndefConnection = null;
	try {			
	    previousScreen.append("Target mapping:"+tProp[0].getMapping()+"\n");				
	    ndefConnection =NDEFUtil.getNDEFTAGConnection(tProp,previousScreen);
	    if(ndefConnection == null) {
		previousScreen.append("Could not establish connection to card.\n");
		return;
	    }
	    NDEFUtil.readNDEFMessage(ndefConnection,previousScreen);
	    ndefConnection.close();
	} catch (Exception e) {
	    if(ndefConnection != null) {
	        try {
		    ndefConnection.close();
	        } catch (IOException e1) {
		    previousScreen.append("Exception in closing connection:"+e1.toString());
	        }
	    }
	    previousScreen.append("Exception:\n"+e.toString()+"\n");
	    e.printStackTrace();
        }
    }
 
    public String getFormattedName() {
        return null;
    }
 
    public void setFormattedName(String fName) {}
 
}

Il metodo targetDetectionMade realizza tutto quello che è stato detto nel paragrafo precedente. Per quanto riguarda il resto del codice rimando ai prossimi articoli. Stay tuned! yo

Crittare l’url di un immagine in jsp con Spring

DataObfuscationIn questo articolo voglio far vedere come nascondere l’href di un immagine in una pagine JSP, pur visualizzando l’immagine come elemento HTML in modo standard.
Supponiamo di dover sviluppare un’applicazione web in cui ciascun utente mantiene una libreria multimediale, costituita per semplicità da soli elementi immagine. Ogni immagine viene memorizzata all’interno di un database assieme alla relative informazioni dell’utente a cui appartiene.
Affinché l’immagine non sia accessibile semplicemente attraverso l’indirizzo indicato nell’HREF, è necessario offuscare il suo contenuto attraverso una codifica il cui funzionamento è noto al sistema. Lo stesso sistema sarà in grado così di decodificare l’HREF in modo da poter restituire la risorsa e quindi visulalizzarla all’interno della pagina.
Vediamo il seguente esempio:

1
2
3
4
5
   <!-- Immagine in chiaro -->
   <img src="http://host/images/img.jpg" title="Immgine in chiaro" alt="" />
 
   <!-- Immagine offuscata -->
   <img src="http://host/image.htm?a=QVMyJCU0c2Rm%3D&b=QVMyJCU0c2RmMzI0M3NkdzI%3D" title="Immgine in chiaro" alt="" />

Nell’esempio di sopra viene mostrata un’immagine in cui l’href è crittata secondo un algoritmo che ora vedremo. E’ chiaro che nel secondo caso, affinché l’immagine possa essere visualizzata è necessaria la presenza di un elemento capace di interpretarne il contenuto, ottenere e restituire la risorsa.
Nell’ambito di Spring tutto questo può essere tradotto nel modo seguente: è necessario un Controller che venga richiamato ogni volta in cui nella pagina è presente un’immagine.
Il Controller dovrà prelevare il contenuto dell’HREF decodificarlo e restituire l’immagine, se corretto. Procediamo quindi per step.
Primo Step configuriamo il Controller (Servlet) all’interno di Spring…quindi editiamo il file di configurazione di spring ed aggiungiamo le seguenti righe di codice:

1
2
3
4
5
6
7
8
    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" id="urlMapping"> 
        <property name="mappings">
	<value>/image.htm=mediaResolver</value>
        <property value="true" name="alwaysUseFullPath"></property>
    </bean> 
    <bean class="it.devme.obfuscator.media.MediaResolver" id="mediaResolver">
        <property name="dbManager"><ref bean="dbManager"></ref></property>     
    </bean>

L’invocazione della risorsa virtuale index.htm provoca l’invocazione del Controller MediaResolver. Vediamo la sua struttura di seguito:

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
27
28
29
30
31
32
33
34
35
36
37
public class MediaResolver extends BaseCommandController {
 
    private DBManager dbManager;
    public void setDbManager (DBManager dbManager) {
	this.dbManager = dbManager;
    }
 
    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	Object mediaid = req.getParameter("a");
	Object uid = req.getParameter("b");
 
	if (mediaid==null || uid==null) 
	    return new ModelAndView(new MediaView("image/jpeg", "no-image.jpg", rs.getString("basePath"), MediaView.USE_DEFAULT_IMAGE));
 
	Encrypter dec = Encrypter.getInstance();
	int idaccount = Integer.parseInt(URLDecoder.decode(dec.decrypt(uid.toString()), "UTF-8"));
	int idelement = Integer.parseInt(URLDecoder.decode(dec.decrypt(mediaid.toString()), "UTF-8"));
 
	MediaBean media = null;
	File f = null;
	String mediaPath = null;
 
	/* Check if media item exists */
	media = dbManager.getImage(idelement, idaccount);
	if (media==null) return new ModelAndView();
	mediaPath = media.getPath();
	f = new File(UPLOAD_PATH + File.separator + mediaPath);
 
	MimetypesFileTypeMap mime = new MimetypesFileTypeMap();
 
	resp.setHeader("Cache-Control", "private,no-cache,no-store");
	resp.setContentType(mime.getContentType(f));
	return new ModelAndView(new MediaView(mime.getContentType(f), mediaPath, rs.getString("basePath"), !MediaView.USE_DEFAULT_IMAGE));
    }
 
}

La classe di sopra estende BaseCommandController e quando invocata viene richiamato l’unico metodo di cui è stato fatto l’override che si occupa di decodificare e restituire la risorsa. Vediamo come.
Sostanzialmente recupera i parametri dalla GET (nel nostro caso sono a e b). Li decodifica utilizzando un algoritmo di crittografia a noi noto (si può usare qualunque algoritmo a nostro piacimento basato su una chiave provata) e quindi ottiene per il parametro a, l’id dell’utente a cui appartiene la risorsa e per il parametro b, l’id della risorsa a cui si vuole accedere. Questi dati vengono passati in input ad un metodo che effettua una query su db (getImage(idelement, idaccount)) e verifica se effettivamente la risorsa richiesta appartiene all’utente corrente.
In caso contrario viene restituita una View vuota, altrimenti si determina il mime-type della risorsa e si restituisce una view creata a partire dalla classe MediaView.

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
27
28
29
30
31
32
33
34
35
public class MediaView extends AbstractView {
 
    private String media;
    private String basePath;
    private boolean noImage;
 
    public MediaView(String mime, String media, String basePath, boolean noImage) {
	this.media = media; 
	setContentType(mime);
	this.basePath = basePath;
    }
 
    @Override
    protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
	String rpath = 	null;
 
	rpath = this.basePath + request.getContextPath().substring(1) +
		MediaResolver.UPLOAD_PATH + File.separator + this.media;
 
 
	File f = new File(rpath);
	byte[] bytes = FileUtils.readFileToByteArray(f);
 
 
        // Write content type and also length (determined via byte array).
        response.setContentType(getContentType());
        response.setContentLength(bytes.length);
 
        // Flush byte array to servlet output stream.
        ServletOutputStream out = response.getOutputStream();
        out.write(bytes);
        out.flush();
    }
 
}

La classe MediaView non fa altro che leggere fisicamente i byte dell’immagine e restituirla nella response utilizzando un OutputStream. Il risultato è che l’immagine scritta nella response verrà visualizzata in pagina come un elemento HTML standard, mantenendo perà l’HREF dell’immagine crittato.
Alla prossima.

 

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