MokaByte 93 - Febbraio 2005
MokaCMS - Open source per il Web Content Management
III parte:
aggiornamenti sulla CMSApplet
di
Pierluigi Grassi
Sottoposta alla prova dei fatti, l'applet CMSApplet ha richiesto alcuni aggiustamenti. In questo articolo esaminiamo ciò che è cambiato dalla versione originale.

Introduzione
Niente resiste all'uso prolungato. A volte nemmeno all'uso breve e sporadico. L'editor CMSApplet appartiene alla seconda categoria. Nella versione precedente l'applet CMSApplet possedeva la capacità di caricare un testo XML, corrispondente al corpo ed alla bibliografia di un articolo. In seguito è emersa la necessità che l'applet potesse essere direttamente inizializzato usando un testo XML. L'introduzione di questa possibilità ha fatto emergere alcuni presupposti impliciti del parser XML. Superfluo ricordare che, in programmazione, un presupposto implicito sia la radice di ogni bug. Vediamo prima l'introduzione del caricamento in fase di inizializzazione, poi la soluzione dei bug emersi con la nuova caratteristica.

 

Inizializzazione con un documento XML
Un Applet Java può ricevere dei valori di inizializzazione, incapsulati in parametri contenuti nella pagina HTML che inglobi l'Applet. Nell'uso più comune questi parametri sono stringhe di testo, colori, o numeri. Il Content Management System richiede invece che ad un Applet riceva un intero documento XML. Non essendo pensabile l'incapsulamento di un intero documento XML in un parametro html, abbiamo pensato di usare come parametro per l'Applet un URL che puntasse ad una risorsa web in grado di fornire il contenuto dell'articolo, in formato XML. Richiedendo l'Applet non solo il contenuto, ma anche l'identificatore dell'articolo, al parametro URL si è aggiunto un parametro che contenesse, appunto, l'ID dell'articolo. Estrarre un parametro inviato all'Applet dalla pagina HTML che lo contiene è questione di poche istruzioni:

String urlArt = getParameter("article-url");
String artid = getParameter("article-id");

Il contraltare HTML di questi elementi è rappresentato da opportuni TAG "param":

<param name="article-id" value="CMSApplet"></param>
<param name="article-url" value="[url costruito durante l'esecuzione]"></param>

Ottenuti, durante l'inizializzazione e dunque nel metodo istanza "init()", l'URL e l'ID dell'articolo, l'Applet altro non fa che leggere il dati da un flusso, restituito da un oggetto java.net.URL, traducendo i byte in caratteri. Dopo aver ricostruito interamente il testo, è invocato il metodo di istanza "setText(String doc, String id)". Abbiamo discusso di questo metodo nell'articolo precedente, a cui pertanto rinviamo.

 

Content is not allowed in prolog?
Il caricamento di testo dal flusso ricavato da un URL è, in Java, un procedimento assolutamente elementare. Un oggetto java.net.URL è in grado di restituire un java.io.InputStream che consente di leggere, byte per byte, il contenuto della risorsa a cui l'URL punti. L'architettura "pipe" del sistema di Input / Output di Java fa il resto. In particolare, uno dei modi più semplici per leggere dei caratteri da un flusso InputStream consiste nel creare uno StringBuffer ed un BufferedReader. Il BufferedReader legge valori interi dal flusso sottostante, e quegli interi corrispondono ad altrettanti caratteri. Aggiugendo allo StringBuffer i caratteri così letti si ottiene una lettura tutto sommato efficiente, per documenti di media dimensione (~1mb). Durante la prima stesura del codice chi scrive è incappato in una svista. Un BufferedReader legge interi. Prima di essere aggiunti allo StringBuffer, quegli interi devono essere convertiti in valori char. La conversione è stata omessa per errore. Poiché uno StringBuffer accetta indifferentemente qualsiasi tipo di dato, componendo internamente il testo con una conversione di ciò che gli sia passato in testo, il risultato era un stringa di testo contenente i numeri interi letti dal flusso. Qualcosa tipo: 15435097987...eccetera. Inutile dire che questo non rappresenti un "documento XML ben formato". Ricevendo questo testo come corpo dell'articolo, il parser produceva un errore. Mancando nella versione attuale dell'applet un log dei messaggi d'errore, l'applet, pur ricevendo un URL corretto sia dal punto di vista dell'indirizzo che del contenuto del documento puntato, non mostrava alcunché. Esaminando la traccia dell'errore riprodotta sulla console Java, risultava un fantomatico:

content is not allowed in prolog

rilasciato dal parser SAX durante l'esecuzione del metodo pushXML di FilteringOp. Questo messaggio è rilasciato nel caso di una particolare malformazione del documento XML. È il sintomo della presenza di alcuni caratteri in eccesso prima dell'header XML. Lascio alla vostra immaginazione quanto poco intuitivo sia stato risalire da un messaggio che tiri in ballo "prolog" ad un intero non convertito in "char".

 

Da una soluzione a molti problemi
Non tutto è andato liscio. L'invocazione del metodo setText(String, String) da parte del componente CMSApplet richiama il metodo "setArticle(String, String)" di un oggetto ArticleEditor. Ricordiamo che ArticleEditor è un tipo interfaccia di cui, nel sistema attuale, esiste una sola versione concreta, prodotta da un factory. Questa versione concreta, SimpleArticleEditor, si appoggia, per la traduzione di un testo XML in testo stilizzato, ad un terzo componente, FilteringOp. Di FilteringOp, in particolare, è invocato in questa fase un metodo "pushXML" che riceve una corposa stringa di testo ed un JTextPane. Il suo scopo è riempire il pannello di testo del risultato della trasformazione di un testo XML in testo stilizzato. Il parser esegue un'operazione "bovina": legge il documento XML e aggiunge letteralmente linee di testo formattato al JTextPane. JTextPane infatti possiede dei metodi brevi per inserire linee di testo formattate, che rinviano internamente a metodi paralleli di uno StyledDocument. Il metodo più usato durante questo trasferimento di testo è "jtextPane.replaceSelection(...)". Il motivo risiede nella polivalenza di replaceSelection. La sostituzione del testo è sostituzione vera solo se esista una selezione corrente. In caso contrario, il metodo realizza un inserimento, attribuendo al testo inserito il formato di paragrafo e di carattere valido per la posizione in cui si inserisca il testo. L'inserimento avviene nella posizione in cui si trova il carrello del pannello di testo. Per un JTextPane, la posizione del carrello è aggiornata automaticamente sull'ultimo carattere del testo inserito, il che solleva dalla necessità di definire una posizione di inserimento, nel caso di una costruzione progressiva del documento, come avviene nel metodo pushXML. A patto che, e qui sta il presupposto implicito citato nell'introduzione, il pannello sia visibile. Nel caso in cui un JTextComponent non sia visibile, infatti, la posizione del carrello non è aggiornata. Da ciò deriva l'inserimento di ogni linea di testo nella posizione zero del documento. Comicamente, il testo del documento appariva invertito. La soluzione al problema è intuitiva. Visibile o non visibile, è sufficiente aggiornare la posizione del carrello del JTextPane nel punto desiderato, durante la composizione del testo, con il metodo setCaretPosition degli oggetti JTextComponent.

 

Conclusioni
Dalla prima versione dell'applet all'attuale l'editor ha subito una caduta di prestazioni rilevante durante il caricamento del testo. Al momento l'obiettivo principale è scovare ed eliminare questo decadimento. Non è un problema di FilteringOp, la classe che trasforma il documento XML in documento di testo. Il che è curioso, essendo questa l'unica classe che effettivamente compia passi rilevanti, in termini di quantità e complessità delle istruzioni. Tra le molte new entry, la più sospetta è l'introduzione di un UndoManager per l'editor JTextPane. Il passaggio a FilteringOp di un componente di testo per cui sia attivato il meccanismo di undo-redo, unito alla costruzione incrementale del documento, potrebbe in effetti caricare il sistema della produzione di un elevato numero di oggetti, destinati all'UndoManager. Destinazione peraltro superflua, essendo il caricamento di un nuovo documento l'atto distruttivo assoluto per eccellenza. Che la soluzione stia effettivamente in una sospensione temporanea del sistema di annullamento e ripetizione delle azioni, è dimostrabile unicamente attraverso la costruzione di micro benchmark del sistema. Questo appartiene alle prospettive del nostro editor di testo per il Content Management System di MokaByte.

 

Bibliografia
[1] Gosling, Arnold, Holmes - "The Java Programming Language, Third Edition", The Java series, Addison Wesley, 2000

MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it