Microsoft Access, COBOL e XML
per realizzare uno spooler di stampa
di Gionata Aladino Canova e Paolo Rivieri (VBJ 78)
Realizzare tanto, con poco sforzo, grazie alla potenza degli strumenti che abbiamo
oggi a disposizione
Introduzione
L'articolo nasce descrivendo la realizzazione di una soluzione software creata
a 4 mani. Il dott. Paolo Rivieri è stato titolare per 25 anni della TDE Center srl,
azienda che ha fondato il suo business sullo sviluppo del software, la quasi totalità
del quale è stato creato in COBOL, nei suoi diversi dialetti. Svariate procedure
sono utilizzate ancora oggi con piena soddisfazione degli utenti. Un problema che
si fa sempre più pressante è però quello delle stampe. Nell’ultimo decennio,
le tecnologie di stampa sono cambiate drasticamente e le stampanti ad aghi sono
quasi scomparse. L'uso del modulo continuo ha senso ormai, soltanto dove si
abbia bisogno di più copie, magari su moduli prestampati o bollati. In tutti gli
altri casi, una stampante laser risolve egregiamente qualsiasi necessità di stampa.
Proprio per questo, presso alcuni clienti, è nata l'esigenza di aggiornare il
sistema di stampa senza però dover convertire l'intero software sviluppato.
Presso alcuni clienti era stata implementata anche una soluzione che consisteva
nel disegnare un modulo in PCL grazie a programmi appositi. Delle macro fondevano
poi il modulo e l'output del programma COBOL, inviando il tutto alla coda di
stampa. A parte le notevoli difficoltà legate alla modifica delle stampe, il tutto
richiedeva comunque una stampante PCL. Recentemente, abbiamo sviluppato una soluzione
software, tutto sommato molto semplice, che risolve tutti i problemi illustrati,
consentendo di toccare il meno possibile i sorgenti COBOL, di utilizzare qualsiasi
stampante a disposizione e di modificare facilmente i report.
L'importanza dell'analisi e della progettazione, ieri ed oggi
L'analisi e la progettazione del software sono sempre stati fondamentali. Ma,
nel passato ed oggi, lo sono per motivi diversi. Quando in passato si utilizzavano
linguaggi poco espressivi, una buona analisi ed un'accurata progettazione erano
essenziali per arrivare in fondo al lavoro. Procedure anche banali potevano produrre
migliaia o decine di migliaia di righe di codice. Oggi abbiamo a disposizione strumenti
molto potenti e linguaggi altamente espressivi. Chi ha programmato in qualsiasi
linguaggio procedurale, senza fare uso particolare di librerie per la gestione dati,
ed ha provato a gestire un archivio, ha un'idea della differenza che passa tra
un'istruzione SQL e decine o centinaia di istruzioni di un linguaggio come il
C, per eseguire lo stesso compito. Tuttavia oggi, l'analisi e la progettazione
sono importanti per scegliere la soluzione più efficace. Adottare un linguaggio,
uno strumento o un protocollo al posto di un altro, può portare ad aumentare enormemente
i tempi di sviluppo o di debug. Quindi, a differenza di una volta, è più facile
comunque arrivare a completare il lavoro, ma perché sprecare tempo, fatica e soldi?
Vediamo quali sono state le scelte vincenti per la nostra soluzione.
XML come collante
Nel nostro caso non aveva senso pensare di leggere direttamente i file dati COBOL,
cosa per altro fattibile per alcuni dialetti con appositi driver. Infatti, i programmi
che stampano bolle e fatture fanno spesso elaborazioni anche complesse. Quindi,
la nostra attenzione si è accentrata su di essi. Catturare semplicemente il loro
output avrebbe reso semplice la modifica dei sorgenti COBOL, ma avrebbe complicato
il recupero dei dati. In COBOL, la soluzione naturale per l’esportazione dati
è fare un file ASCII delimitato. Il problema di questa soluzione è che il recupero
dei dati si presenta ancora una volta complesso. Infatti, una fattura è composta,
almeno, da una intestazione, dalle righe di dettaglio e da un pié di pagina. Quindi
si sarebbe reso necessario un parsing del file per estrapolarne i diversi elementi.
Inoltre, cambiando qualsiasi cosa nell'output del programma COBOL, si sarebbe
reso necessario il contemporaneo aggiornamento del programma che legge i dati.
Qui entra in gioco l'XML. I punti di forza sfruttati dell'XML sono:
- Il file contiene sia i dati che i metadati
- Il file contiene più entità; ad esempio, l'intestazione e le righe di
dettaglio
Nel nostro caso, abbiamo realizzato una sezione in cui memorizziamo le informazioni
relative al report da stampare, alla stampante da utilizzare e al numero di copie;
una sezione relativa ai dati del documento ed una relativa alle righe. Vedremo tra
poco i dettagli.
Scelta dell'applicazione per realizzare lo spooler
Il sistema è stato realizzato in Microsoft Access XP. La scelta è stata dettata
dalla facilità con cui, in Microsoft Access si possono gestire i report. Il VBA
consente di gestire agevolmente l'importazione dei file XML e qualsiasi elaborazione
eventualmente richiesta sui dati. Qualsiasi strumento di alto livello con un generatore
di report, come Visual Studio, sarebbe comunque stato adatto.
Realizzazione del programma COBOL
Dal lato COBOL, la parte del sistema che va realizzata è sostanzialmente una conversione
delle routine di stampa già esistenti. In pratica, si prendono i programmi di stampa
e si aggiungono le istruzioni che servono per realizzare la struttura del file XML
e per scrivere il tutto su un file di testo, anziché su una stampante. Una facilitazione
sta nel fatto che non servono rotture di codice per la gestione del salto
pagina. Eventuali elaborazioni possono essere spostate sul report realizzato in
Access. Sarebbe, anzi, dannoso, se ci fossero. Infatti cambiando semplicemente margini
al report, potrebbe essere che le righe contenute in una pagina cambino, invalidando
tutti i conti fatti in COBOL per il salto pagina.
Una complicazione sta nel fatto di dover aprire e chiudere i tag relativi all'intero
file, alle sezioni ed ai singoli campi. Poiché l’apertura e la chiusura di
un tag possono essere distanti all’interno del codice sorgente, è necessario
tenerne conto in fase di scrittura del programma, prevedendo magari subroutine che
aprano e chiudano i tag.
Per fare mente locale, una stampa di 3 copie, con una riga di intestazione ed una
riga di dettaglio, come da esempio:
TDE Informatica srl
0001 Realizzazione articolo per VBJ € 10
va tradotta in XML in
<Radice>
<XMLImpostazioniStampa>
<NumeroCopie>3</NumeroCopie>
</XMLImpostazioniStampa>
<XMLDocumento>
<Campo001>TDE Informatica srl
</Campo001>
</XMLDocumento>
<XMLRigaDocumento>
<Campo0>001</Campo0>
<Campo1>Realizzazione articolo per VBJ
</Campo1>
<Campo2>€ 10</Campo2>
</XMLRigaDocumento>
</Radice>
Un esempio completo, aperto in un editor XML è visibile in
Figura1
Figura 1
I caratteri speciali
L'XML è un formato molto rigido. Nella parte che contiene dati, sono vietati
molti caratteri, tra cui, ad esempio, l'apostrofo. Ovviamente, la nostra terza
stampa di prova conteneva un apostrofo nel nome della ditta... Le soluzioni sono
essenzialmente due. La prima consiste nell’alterare il programma COBOL aggiungendovi
una routine che converte i caratteri speciali con la corrispondente stringa valida.
Ad esempio, l'apostrofo diventa '. Le specifiche di XML prevedono esplicitamente
anche la possibilità di utilizzare la codifica Unicode per rappresentare i caratteri
non latini, come ad esempio i caratteri greci, cirillici, gli ideogrammi cinesi
e giapponesi. Il problema principale di queste soluzioni è che,
ad un carattere, corrisponde una stringa di più caratteri e quindi, avendo il COBOL
dei campi a spaziatura fissa, va gestito l'eventuale overflow che si potrebbe
avere nella conversione.
Una soluzione molto più efficiente, per cui ringraziamo la dottoressa Renata Bandelloni,
è affidarsi ad una caratteristica dell'XML (1) che consente di specificare qualsiasi
carattere all'interno di un blocco ben delimitato.
Nel codice seguente
<codice>
<![CDATA[
<libro>
<capitolo>Questo capitolo contiene € e ‘
</capitolo>
</libro>
]]>
</codice>
tutto quello che è incluso nel tag ![CDATA[ viene ignorato, dal punto di
vista del controllo sintattico. Ovviamente, in COBOL, la via più veloce per risolvere
tutti i problemi di caratteri particolari è proprio questa. Si scrive nel file il
tag di apertura, si lascia invariato il codice che scrive dati e si chiude il tag.
Realizzazione dello Spooler
La struttura dello spooler è semplice. Un timer provvede, ad intervalli regolari,
a fare una scansione di una cartella, specificata in un parametro. Se vengono trovati
nuovi file, vengono importati in una lista. La lista viene ordinata e poi i file
vengono aggiunti ad una listbox. L’interfaccia dell’applicazione è visibile
in Figura 2.
Figura 2
L'ordinamento si è reso necessario quando vi sono stampe consecutive, come nel
caso della fatturazione riepilogativa. È necessario preservare l'ordine di uscita
dei file da COBOL. Per fare questo, il nome dei file COBOL contiene la data e l'ora
in formato yyyymmgghhssxx più un numero progressivo che consente di non sovrascrivere
file stampati nello stesso secondo.
La scelta del timer è dettata da ragioni di semplicità. Si potrebbe pensare di utilizzare
le API con delle funzioni di callback per avere notifica dal sistema operativo della
presenza di nuovi file, ma il beneficio ottenuto sarebbe veramente minimo a fronte
di una notevole complicazione del codice e di un più difficile debug.
Il comando Application.ImportXML importa un file xml. La cosa interessante
è che esso crea una tabella per ogni entità di primo livello. Quindi, scegliendo
con criterio il nome degli oggetti di primo livello, ci risparmiamo un po' di
lavoro. Ogni nostro oggetto inizia con il prefisso "XML". La scelta è
dovuta al fatto di poter cancellare le tabelle importate senza averne un elenco
preventivo. La routine seguente
For Each tbl In CurrentData.AllTables
If Left(tbl.Name, 3) = "XML" Then
CurrentProject.Connection.Execute "DROP TABLE " & tbl.Name
End If
Next
provvede all'eliminazione delle tabelle importate. Visto che il programma deve
essere il più robusto possibile, se un'importazione fallisce a metà o se il
file contiene informazioni errate, ci garantiamo, entro certi limiti, di ripulire
sempre bene il nostro database, prima di una nuova importazione.
La prima tabella creata, XMLImpostazioniStampa, contiene le informazioni
sulla stampante da utilizzare, il numero di copie da stampare e il report da utilizzare.
Il nome della stampante è associato, tramite la tabella tblStampanti alla
reale stampante Windows da utilizzare. A questo punto, è sufficiente accordarsi
su un nome di comodo per indicare una certa stampante e selezionarla poi effettivamente
da Windows, come illustrato in
Figura 3.
Figura 3
Nel caso in cui la stampante venga cambiata, il programma COBOL non viene toccato.
Il nome del report consente al programmatore COBOL di selezionare il report che
verrà utilizzato per la stampa. Quindi, per aggiungere la stampa di una DDT, si
crea un nuovo report in Access ed il programmatore COBOL dovrà soltanto richiamarlo.
La seconda tabella creata, XMLDocumento, contiene i dati
di testa e di fondo pagina del documento. Normalmente, conterrà un solo record,
mentre la terza tabella, XMLRigaDocumento, contiene tanti
record per quante sono le righe di dettaglio del documento. In entrambe le tabelle,
si è scelto di dare un nome sequenziale ai campi (campo001, campo 002...).
Installazione del sistema
L'installazione del sistema si divide in due fasi, la parte relativa al COBOL
e la parte relativa ad Access. Nel nostro caso, le procedure COBOL giravano su un
server UNIX, che è il caso più complesso. Dopo aver messo su i programmi modificati,
si deve fare in modo che i file generati siano visti da Windows. O si condivide
una cartella sul server, dopo aver installato un programma tipo Samba per renderla
visibile da Windows, o si crea una procedura che copia i file su una cartella condivisa
di un pc Windows, che è la strada da noi seguita.
Sul pc Windows si installa il programma scritto in Access, con l'eventuale runtime
se necessario. Per chi ancora non lo sapesse, il runtime di Access 2007 è diventato
gratuito (2). Poi, dal programma stesso, si impostano le stampanti e la cartella
da cui leggere i file. Infine si spera bene... J
Il risultato finale che deriva dal file di prova è quello visibile in
Figura 4.
Figura 4
L'applicazione completa è scaricabile
qui.
Conclusioni
Oggi più che mai è necessario, di fronte ad un problema, staccare le mani dal pc
e fermarsi a riflettere su quali strade sono percorribili. Sono utilissimi anche
confronti con colleghi che hanno specializzazioni diverse dalle nostre, proprio
perché potrebbero suggerire approcci alla soluzione del problema radicalmente diversi
dai nostri. Sempre più problemi possono essere risolti senza scrivere righe di codice
o scrivendone pochissime. Ma bisogna aver imboccato la strada giusta all’inizio…
Un ringraziamento va a Paolo Rivieri che, pur essendo in pensione e pensando alla
sua barca a vela, ha trovato il tempo per scrivere un programma in COBOL ridotto
al massimo, da utilizzare come esempio per VBJ.
Riferimenti
1) http://xml.html.it/guide/lezione/1843/documenti-ben-formati/
2)
http://office.microsoft.com/it-it/access/HA102188641040.aspx