JSF 2 e la gestione delle risorse

Innamorati dei tag RichFaces 3 loadScript e loadStyle? Vi state chiedendo come mai non sono più presenti in RichFaces 4? Non avete idea di quello che sto dicendo? Allora se siete curiosi continuate a leggere!

C’era una volta…

JSF2 ha inglobato il meglio dello stato dell’arte dei framework che ruotavano attorno alla versione precedente, come per esempio Facelets o il supporto nativo ad Ajax simile a quello del progetto Ajax4JSF, da tempo integrato in RichFaces. Stesso discorso vale anche per i tag citati poco fa, molto utili soprattutto quando si creano dei componenti personalizzati tramite Facelets che hanno bisogno di caricare degli script o dei fogli di stile necessari al loro funzionamento. a4j:loadScript e a4j:loadStyle infatti garantiscono che queste risorse vengano caricate una sola volta nella pagina ed inseriti automaticamente nella head.

Chiu’ risorse pi’ tutti!

Nel suo processo di “fagocitazione” del mondo circostante, JSF2 ha razionalizzato l’esperienza pregressa, riproponendo, tra le altre cose, questi due tag con il nome di h:outputScript e h:outputStylesheet, inserendoli in un contesto strutturato di gestione delle risorse. Cosa significa? In pratica, in una applicazione JSF2, ogni risorsa web, come css, javascript o immagini, per sfruttare il nuovo sistema di gestione, dovrebbe stare in un cartella chiamata resources posta sotto una di queste cartelle:

  • /WebContent nei progetti Web (WAR);
  • /META-INF in tutti gli atri (JAR).

“Resources in Action”

Come insegna la serie di manuali “In Action”, è meglio vedere subito come funzionano le cose e poi discuterci sopra. Facciamo quindi un esempio: “traduciamo” in JSF2 la pagina di demo del bellissimo Nivo Slider che, per funzionare, ha bisogno dei suoi script, stili e immagini.

Organizziamo quindi le risorse scaricate come si aspetta JSF:

Struttura Risorse

In pratica basta riportare sotto la cartella resources la tipica struttura che uno farebbe normalmente direttamente sotto /WebContent.

Di seguito il codice della pagina “tradotta”:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"
     xmlns:h="http://java.sun.com/jsf/html"
     xmlns:f="http://java.sun.com/jsf/core"
     xmlns:ui="http://java.sun.com/jsf/facelets"
     xmlns:a4j="http://richfaces.org/a4j"
     xmlns:rich="http://richfaces.org/rich"> 

<h:head>

</h:head> 
<h:body>
<f:view>
  <div id="wrapper">
  <a href="http://dev7studios.com" id="dev7link" title="Go to dev7studios">dev7studios</a>
    <div class="slider-wrapper theme-default">
      <div class="ribbon"></div>
        <div id="slider" class="nivoSlider">
          <h:graphicImage name="toystory.jpg" library="images" alt=""/>
          <a href="http://dev7studios.com">
            <h:graphicImage name="up.jpg" library="images" alt="" title="This is an example of a caption"/>
          </a>
          <h:graphicImage name="walle.jpg" library="images" alt="" data-transition="slideInLeft"/>
          <h:graphicImage name="nemo.jpg" library="images" alt="" title="#htmlcaption"/>
        </div>
        <div id="htmlcaption" class="nivo-html-caption">
          <strong>This</strong> is an example of a <em>HTML</em> caption with <a href="#{resource['images:nemo.jpg']}">a link</a>.
        </div>
      </div>
      <h:outputScript name="jquery.js" target="head"/>
      <h:outputScript name="jquery.nivo.slider.pack.js" library="js" target="head"/>
      <h:outputStylesheet name="default.css" library="themes/default"/>
      <h:outputStylesheet name="pascal.css" library="themes/pascal"/>
      <h:outputStylesheet name="orman.css" library="themes/orman"/>
      <h:outputStylesheet name="nivo-slider.css" library="css"/>
      <h:outputStylesheet name="style.css" library="css"/>

      <script type="text/javascript">
         $(window).load(function() {
            $('#slider').nivoSlider();
         });
      </script>
    </f:view>
  </h:body>
</html>

Le parti evidenziate ci mostrano come referenziare le risorse. La chiave di tutto sta negli attributi name e library dei tag:

  • name: è il nome della risorsa;
  • library: è il percorso sotto la cartella resources.

I tag h:graphicImage, h:outputScript e h:outputStylesheet operano con questa logica. Il tag h:head è stato evidenziato anche se vuoto: è molto importante perché senza di questo h:outputStylesheet per esempio non funzionerebbe quindi occhio a non dimenticarlo! Di default infatti tutti gli stili vengono inseriti nell’head della pagina “compilata” sul client anche se sono dichiarati più volte nella stessa pagina. La stessa logica vale anche per h:outputScript, con la differenza che è possibile indicare il target, ovvero dove vogliamo che gli script vengano inseriti nella pagina HTML. Nell’esempio precedente verranno inseriti nella head. Il caricamento degli stili poteva essere messo direttamente dentro il tag h:head, ma così è più evidente la differenza tra la pagina scritta e quella generata sul client. Inoltre l’importazione di jquery.js viene risolta correttamente perché RichFaces 4 è presente nel classpath.

Da notare infine l’espressione

#{resource['images:nemo.jpg']}

che segue il pattern

library:name

In questo modo otteniamo l’url della risorsa da usare dove preferiamo, anche se risiede in un JAR. Se proviamo a visualizzare questa pagina dal browser ci accorgiamo però che manca qualcosa, ovvero le immagini caricate dai css di NivoSlider non vengono trovate.
Diamo allora una sbirciata a come vengono generati gli url sul client: apriamo il source della pagina dal browser e scopriamo per esempio che l’indirizzo della risorsa images:nemo.jpg sul client diventa

/MyApp/javax.faces.resource/nemo.jpg.jsf?ln=images

Mmmm ok quindi facile da ricreare. E’ formato da:

  • /MyApp: il context path dell’applicazione;
  • /javax.faces.resource: sembra una sorta di servlet che mappa tutte le risorse;
  • /nemo.jpg.jsf: il nome dell’immagine, a cui è stata aggiunta l’estensione jsf (non a caso la Faces Servlet nel web.xml è mappata sull’url *.jsf. Al variare del mapping varierà anche questa condizione);
  • ?ln=images: il nome della libreria.

A questo punto possiamo intervenire nei file CSS (eh si quest’ultima operazione è un po’ sporca, ma ne vale la pena ;)) e modificare manualmente il percorso dei file richiesti in modo opportuno. Per esempio, dentro style.css

url(images/dev7logo.png)

diventa

url(dev7logo.png.jsf?ln=images)

Refreshiamo la pagina ed ecco Nivo Slider nel suo splendore, pronto a diventare un tag facelets con poche modifiche!

Conclusioni

I tag HTML link e script possono essere sempre usati in una pagina JSF, senza dover per forza ricorrere al nuovo sistema di gestione delle risorse. Diventa obbligatorio però quando abbiamo la necessità di usare h:outputScript e h:outputStylesheet perché magari stiamo creando dei componenti Facelets riusabili, oppure quando le nostre risorse stanno in un jar e non sono direttamente accessibili da web: in questo caso basta creare una cartella resources sotto META-INF del jar e non avremo più bisogno di un Facelets Resource Resolver per accedere ai jar come si faceva con JSF 1.x.

Andrea Como

Sono un software engineer focalizzato nella progettazione e sviluppo di applicazioni web in Java. Presso OmniaGroup ricopro il ruolo di Tech Leader sulle tecnologie legate alla piattaforma Java EE 5 (come WebSphere 7.0, EJB3, JPA 1 (EclipseLink), JSF 1.2 (Mojarra) e RichFaces 3) e Java EE 6 con JBoss AS 7, in particolare di CDI, JAX-RS, nonché di EJB 3.1, JPA2, JSF2 e RichFaces 4. Al momento mi occupo di ECM, in particolar modo sulla customizzazione di Alfresco 4 e sulla sua installazione con tecnologie da devops come Vagrant e Chef. In passato ho lavorato con la piattaforma alternativa alla enterprise per lo sviluppo web: Java SE 6, Tomcat 6, Hibernate 3 e Spring 2.5. Nei ritagli di tempo sviluppo siti web in PHP e ASP. Per maggiori informazioni consulta il mio curriculum pubblico. Follow me on Twitter - LinkedIn profile - Google+

  • Pingback: JSF 2 e il versionamento delle risorse | Cose Non Javiste()

  • mauro

    scusate ma non ho capito l’ultima parte :

    url(images/dev7logo.png)

    diventa

    url(dev7logo.png.jsf?ln=images)

    poteti aiutarmi ?

    grazie Mauro

    • cosenonjaviste

      Ciao,

      provo a spiegare meglio. L’ultimo passaggio è necessario a JSF per trovare le risorse richieste dai css di NivoSlider se hai messo le sue immagini nella cartella “resources” del progetto. Come viene spiegato nel post, i file presenti in questa cartella vengono referenziati o con i tag outputScript/outputStylesheet o tramite expression language come #{resource[‘libraryName:fileName’]}.

      Dato che in un file css l’expression language non si può usare, basta modificare l’url delle risorse richieste come se fossero state prodotte dall’expression language stesso, ovvero secondo il pattern fileName.jsf?ln=libraryName. Basta infatti dare un’occhiata alla pagina HTML prodotta dal JSF per capire come vengono tradotti gli url delle risorse incluse nella pagina, come nell’esempio del post.

      Spero di aver chiarito il tuo dubbio
      Andrea