Java lato server: utilizzo di XML e JSP insieme

Ai fini di questo articolo presumo che tu sappia cosa sono JavaServer Pages (JSP) e Extensible Markup Language (XML), ma potresti non essere chiaro su come utilizzarli. L'uso di JSP è abbastanza facile da difendere. Ti consente di progettare un sito Web costruito da file che assomigliano e si comportano in modo molto simile a HTML. L'unica differenza è che i JSP agiscono anche dinamicamente, ad esempio, possono elaborare moduli o leggere database, utilizzando Java come linguaggio di scripting lato server. L'uso di XML è più difficile da giustificare. Anche se sembra che ogni nuovo prodotto lo supporti, ognuno sembra utilizzare XML per uno scopo diverso.

In questo articolo imparerai a progettare un sistema usando XML in modo abbastanza modesto. Molti siti Web hanno vaste raccolte di dati che vengono visualizzati in modo più o meno standard. Progetterò un sistema che utilizza file XML per memorizzare i dati su un server Web e file JSP per visualizzare tali dati.

XML contro database relazionali

"Ma aspetta," potresti chiedere, "stai usando XML per memorizzare i dati? Perché non usi un database?" Buona domanda. La risposta è che per molti scopi un database è eccessivo. Per utilizzare un database, è necessario installare e supportare un processo server separato, che spesso richiede anche l'installazione e il supporto di un amministratore del database. È necessario apprendere SQL e scrivere query SQL che convertono i dati da una struttura relazionale a una struttura di oggetti e viceversa. Se archivi i dati come file XML, perdi il sovraccarico di un server aggiuntivo. Ottieni anche un modo semplice per modificare i tuoi dati: usa semplicemente un editor di testo, piuttosto che un complicato strumento di database. È anche più facile eseguire il backup dei file XML, condividerli con i tuoi amici o scaricarli sui tuoi clienti. Puoi anche caricare facilmente nuovi dati sul tuo sito, utilizzando FTP.

Un vantaggio più astratto dell'XML è che, essendo un formato gerarchico piuttosto che relazionale, può essere utilizzato in modo molto più diretto per progettare strutture di dati adatte alle proprie esigenze. Non è necessario utilizzare un editor di relazioni di entità né normalizzare lo schema. Se hai un elemento che contiene un altro elemento, puoi rappresentarlo direttamente nel formato, anziché utilizzare una tabella di join.

Nota che per molte applicazioni, un filesystem non sarà sufficiente. Se si dispone di un volume elevato di aggiornamenti, un filesystem può essere confuso o danneggiato da scritture simultanee; i database di solito supportano le transazioni, che consentono la concorrenza senza danni. Inoltre, un database è uno strumento eccellente se è necessario eseguire query complicate, soprattutto se variano di volta in volta. I database creano indici e sono ottimizzati per mantenere gli indici aggiornati con un set di dati in continua evoluzione. I database relazionali hanno anche molti altri vantaggi, tra cui un ricco linguaggio di query, strumenti di creazione e progettazione di schemi avanzati, scalabilità comprovata, controllo degli accessi granulare e così via.

(Nota: puoi utilizzare un semplice blocco dei file per fornire un server delle transazioni di un uomo povero. E puoi anche implementare uno strumento di indicizzazione e ricerca XML in Java, ma questo è un argomento per un altro articolo.)

In questo caso, come nella maggior parte dei siti Web basati sulla pubblicazione di volumi medio-bassi, si può presumere quanto segue: la maggior parte dell'accesso ai dati è in lettura, non in scrittura; i dati, sebbene potenzialmente di grandi dimensioni, sono relativamente immutati; non dovrai fare ricerche complicate, ma se lo fai, utilizzerai un motore di ricerca separato. I vantaggi dell'utilizzo di un RDBMS maturo svaniscono, mentre vengono in primo piano i vantaggi dell'utilizzo di un modello di dati orientato agli oggetti.

Infine, è del tutto possibile fornire un wrapper per il tuo database che esegue query SQL e le traduce in flussi XML, in modo da poterlo avere in entrambi i modi. XML diventa un frontend più robusto e intuitivo per un database maturo per l'archiviazione e la ricerca. (Il servlet XSQL di Oracle è un esempio di questa tecnica.)

L'applicazione: un album fotografico online

Tutti amano le foto! Le persone adorano mostrare foto di se stesse, dei loro amici, dei loro animali domestici e delle loro vacanze. Il Web è il mezzo migliore per gli appassionati di fotografia autoindulgenti: possono infastidire i loro parenti da migliaia di chilometri di distanza. Mentre un vero e proprio sito di album di foto richiederebbe un complicato modello di oggetti, mi concentrerò sulla definizione di un singolo Pictureoggetto. (Il codice sorgente per questa applicazione è disponibile in Risorse.) L'oggetto che rappresenta un'immagine necessita di campi che ne rappresentino il titolo, la data in cui è stata scattata, una didascalia opzionale e, ovviamente, un puntatore all'origine dell'immagine.

Un'immagine, a sua volta, necessita di alcuni campi propri: la posizione del file sorgente (un GIF o JPEG) e l'altezza e la larghezza in pixel (per assistervi nella creazione di tag). Qui c'è un netto vantaggio nell'usare il filesystem come database: puoi memorizzare i file immagine nella stessa directory dei file di dati.

Infine, estendiamo il record dell'immagine con un elemento che definisce un insieme di immagini in miniatura da utilizzare nel sommario o altrove. Qui utilizzo lo stesso concetto di immagine che ho definito in precedenza.

La rappresentazione XML di un'immagine potrebbe essere simile a questa:

 Alex On The Beach 1999-08-08 Tentando invano di abbronzarsi alex-beach.jpg 340200 alex-beach-sm.jpg 72 72 alex-beach-med.jpg 150 99    

Si noti che utilizzando XML, si mettono tutte le informazioni su una singola immagine in un unico file, invece di spargerle tra tre o quattro tabelle separate. Chiamiamolo .pixfile, quindi il tuo filesystem potrebbe assomigliare a questo:

 summer99 / alex-beach.pix summer99 / alex-beach.jpg summer99 / alex-beach-sm.jpg summer99 / alex-beach-med.jpg summer99 / alex-snorkeling.pix ecc. 

Tecniche

C'è più di un modo per scuoiare un gatto e c'è più di un modo per portare i dati XML nella tua pagina JSP. Ecco un elenco di alcuni di questi modi. (Questo elenco non è esaustivo; molti altri prodotti e framework sarebbero altrettanto utili.)

  • DOM : puoi utilizzare classi che implementano l'interfaccia DOM per analizzare e ispezionare il file XML
  • XMLEntryList : puoi usare il mio codice per caricare l'XML in una java.util.Listcoppia di nome-valore
  • XPath : è possibile utilizzare un processore XPath (come Resin) per individuare gli elementi nel file XML in base al nome del percorso
  • XSL : puoi utilizzare un processore XSL per trasformare l'XML in HTML
  • Cocoon : è possibile utilizzare il framework Cocoon open source
  • Roll il tuo bean : puoi scrivere una classe wrapper che utilizza una delle altre tecniche per caricare i dati in un JavaBean personalizzato

Notare che queste tecniche possono essere applicate altrettanto bene a un flusso XML ricevuto da un'altra origine, come un client o un server delle applicazioni.

JavaServer Pages

La specifica JSP ha avuto molte incarnazioni e diversi prodotti JSP implementano versioni diverse e incompatibili della specifica. Userò Tomcat, per i seguenti motivi:

  • Supporta le versioni più aggiornate delle specifiche JSP e servlet
  • È approvato da Sun e Apache
  • È possibile eseguirlo autonomamente senza configurare un server Web separato
  • È open source

(Per ulteriori informazioni su Tomcat, vedere Risorse.)

Puoi usare qualsiasi motore JSP che ti piace, ma la configurazione dipende da te! Assicurati che il motore supporti almeno la specifica JSP 1.0; ci sono stati molti cambiamenti tra 0.91 e 1.0. Il JSWDK (Java Server Web Development Kit) funzionerà perfettamente.

La struttura JSP

When building a JSP-driven Website (also known as a Webapp), I prefer to put common functions, imports, constants, and variable declarations in a separate file called init.jsp, located in the source code for this article.

I then load that file into each JSP file using . The directive acts like the C language's #include, pulling in the text of the included file (here, init.jsp) and compiling it as if it were part of the including file (here, picture.jsp). By contrast, the tag compiles the file as a separate JSP file and embeds a call to it in the compiled JSP.

Finding the file

When the JSP starts, the first thing it needs to do after initialization is find the XML file you want. How does it know which of the many files you need? The answer is from a CGI parameter. The user will invoke the JSP with the URL picture.jsp?file=summer99/alex-beach.pix (or by passing a file parameter through an HTML form).

However, when the JSP receives the parameter, you're still only halfway there. You still need to know where on the filesystem the root directory lies. For example, on a Unix system, the actual file may be in the directory /home/alex/public_html/pictures/summer99/alex-beach.pix. JSPs do not have a concept of a current directory while executing, so you need to provide an absolute pathname to the java.io package.

The Servlet API provides a method to turn a URL path, relative to the current JSP or Servlet, into an absolute filesystem path. The method ServletContext.getRealPath(String) does the trick. Every JSP has a ServletContext object called application, so the code would be:

String picturefile = application.getRealPath("/" + request.getParameter("file")); 

or

String picturefile = getServletContext().getRealPath("/" + request.getParameter("file")); 

which also works inside a servlet. (You must append a / because the method expects to be passed the results of request.getPathInfo().)

One important note: whenever you access local resources, be very careful to validate the incoming data. A hacker, or a careless user, can send bogus data to hack your site. For instance, consider what would happen if the value file=../../../../etc/passwd were entered. The user could in this way read your server's password file.

The Document Object Model

DOM stands for the Document Object Model. It is a standard API for browsing XML documents, developed by the World Wide Web Consortium (W3C). The interfaces are in package org.w3c.dom and are documented at the W3C site (see Resources).

There are many DOM parser implementations available. I have chosen IBM's XML4J, but you can use any DOM parser. This is because the DOM is a set of interfaces, not classes -- and all DOM parsers must return objects that faithfully implement those interfaces.

Unfortunately, though standard, the DOM has two major flaws:

  1. The API, though object-oriented, is fairly cumbersome.
  2. There is no standard API for a DOM parser, so, while each parser returns a org.w3c.dom.Document object, the means of initializing the parser and loading the file itself is always parser specific.

The simple picture file described above is represented in the DOM by several objects in a tree structure.

Document Node --> Element Node "picture" --> Text Node "\n " (whitespace) --> Element Node "title" --> Text Node "Alex On The Beach" --> Element Node "date" --> ... etc. 

To acquire the value Alex On The Beach you would have to make several method calls, walking the DOM tree. Further, the parser may choose to intersperse any number of whitespace text nodes, through which you would have to loop and either ignore or concatenate (you can correct this by calling the normalize() method). The parser may also include separate nodes for XML entities (like &), CDATA nodes, or other element nodes (for instance, the big bear would turn into at least three nodes, one of which is a b element, containing a text node, containing the text big). There is no method in the DOM to simply say "get me the text value of the title element." In short, walking the DOM is a bit cumbersome. (See the XPath section of this article for an alternative to DOM.)

From a higher perspective, the problem with DOM is that the XML objects are not available directly as Java objects, but they must be accessed piecemeal via the DOM API. See my conclusion for a discussion of Java-XML Data Binding technology, which uses this straight-to-Java approach for accessing XML data.

I have written a small utility class, called DOMUtils, that contains static methods for performing common DOM tasks. For instance, to acquire the text content of the title child element of the root (picture) element, you would write the following code:

Document doc = DOMUtils.xml4jParse(picturefile); Element nodeRoot = doc.getDocumentElement(); Node nodeTitle = DOMUtils.getChild(nodeRoot, "title"); String title = (nodeTitle == null) ? null : DOMUtils.getTextValue(nodeTitle); 

Getting the values for the image subelements is equally straightforward:

Node nodeImage = DOMUtils.getChild(nodeRoot, "image"); Node nodeSrc = DOMUtils.getChild(nodeImage, "src"); String src = DOMUtils.getTextValue(nodeSrc); 

And so on.

Once you have Java variables for each relevant element, all you must do is embed the variables inside your HTML markup, using standard JSP tags.

   

See the full source code for more details. The HTML output produced by the JSP file -- an HTML screenshot, if you will -- is in picture-dom.html.