Archive for Uncategorized

Gestire file di testo di grandi dimensioni

Può capitare di dover analizzare con un programma Uniface file di testo di grandi dimensioni, ad esempio file di log. In genere, specialmente se i files non sono molto grandi li si carica in memoria con una fileload e si analizzano le righe che costituiscono il file con un loop quasi sempre così costituito:
1 – cerco il terminatore di riga
2 – se non lo trovo termino perché sono arrivato al fondo del file
3 – isolo il contenuto della linea identificata
4 – lavoro la riga e accumulo su una DUMMY il contenuto che interessa
5 – ricomincio
6 – scarico il contenuto della DUMMY su un nuovo file di testo

Vi siete mai chiesti:
A) le performance che si ottengono con questo loop sono valide per files di testo di qualunque dimensione?
B) lo scarico finale su nuovo file di testo può essere migliorato?

Bene, vi racconto la mia ultima esperienza pratica, che da una significativa risposta a queste due domande!
Dovevo analizzare file di LOG di Uniface generati su una installazione multiaziendale con circa 200 utenti; la dimensione minima di questi files era di circa 50MB pari a circa 1.000.000 di righe ma alcuni erano costituiti da milioni di righe, il più grande poco più di 8 milioni. Avevo già scritto il programma di analisi ed era stato testato su file di dimensioni decisamente minori; provando a riutilizzarlo su file di queste dimensioni diventava praticamente inutilizzabile!

A) Il criterio di stacco della riga usando la ricerca del terminatore è valido solo per file di piccole dimensioni quando il file diventa più corposo, ossia maggiore di qualche decina di migliaia di righe conviene decisamente utilizzare il driver TXT. La ricerca del terminatore di riga e lo stacco della riga durava decimi di secondo mentre con il driver TXT dura millesimi di secondo. Ho predisposto nell’Application Model che utilizzo da molto tempo per questo tipo di funzioni una entità:
TXTFILE:
myPK C(1)
myRec C*

in modo da poter eventualmente gestire file di testo organizzati a tipi di record in funzione del valore del primo carattere.
La sequenza di istruzioni:
vEOL = $scan(vBuffer[vIni], cCR)
if (vEOL = 0) break
vLinea = vBuffer[vIni, vIni+vEOL-1]
… lavoro la riga …
vIni = vIni + vEOL

è diventata:
setocc “TXTFILE”, $curocc(TXTfile)+1
if ($status < 0) break
vLinea = $concat(myPK, myRec)
… lavoro la riga …

L’occupazione di memoria del processo nei due casi è simile, ma una considerazione collaterale va fatta: il caricamento del file di testo in memoria con la fileload dura circa 10 secondi, mentre la retrieve iniziale con il driver TXT dura quasi il doppio (18 sec) ma è ampiamente ripagata dalla miglioria nello scorrimento sulle occorrenze.
I tempi sono migliorati di circa 800 volte!

B) La funzione suddetta accumula in una entità DUMMY costituita da un singolo campo DUMMYLINE C* i contenuti necessari. Ad esempio, al termine dell’elaborazione di 2.000.000 di record mi sono ritrovato circa 250.000 record nell’entità DUMMY. In origine la mia applicazione era costituita da questo loop:
filedump cNul, vFilename
forentity “DUMMY”
filedump/append $concat(dummyline.dummy, cCR), vFilename
endfor

Anche in questo caso i tempi si allungavano in modo importante al crescere del numero di occorrenze gestite. Ho sostituito la creazione progressiva del file con la creazione in memoria di una lista, sostituendo successivamente con una istruzione utilizzante la $replace() i separatori di lista con i terminatori di riga; il nuovo loop è:
filedump cNul, vFilename
vContent = cNul
putlistitems vContent, dummyline.dummy
vContent = $replace(vContent, 1, cSep, cCR, -1)
vContent = $concat(vContent, cCR)
filedump/append vContent, vFilename

Anche in questo caso i tempi sono migliorati notevolmente di circa 300 volte!

Con le due azioni elencate sono riuscito su una macchina virtuale W10 dotata di 4GB di RAM a leggere ed analizzare un LOG di 2.023.616 righe, ordinare le circa 250.000 righe costituenti il risultato ottenuto e scaricarlo su un nuovo file di testo in poco più di 4 minuti, per la precisione 4 minuti e 24″.

Non male…non male!!!

Chi volesse maggiori informazioni può contattarmi a gianni.sandigliano@unifacesolutions.com

Uniface ed i Web Services, 2.parte – SOAP

Nella prima parte abbiamo affrontato una descrizione generale dei “Web Services”, vediamo ora come si implementa in ambiente Uniface sia la generazione (call-in) che la fruizione (call-out) di servizi applicativi di questo tipo, partendo dalla modalità SOAP.

Per fruire di WebServices in formato SOAP è necessario avere a disposizione il driver SOAP, attualmente in versione 2.0; è disponibile anche un driver versione U1.0 che ancora oggi si affianca e NON è sostituito dal nuovo driver. Questo perché il driver SOAP U1.0 è costruito sulla base delle specifiche SOAP 1.0 e supporta solamente il binding style “RPC/Encoded”, utilizzando il Microsoft SOAP Toolkit reso disponibile solo in ambiente Microsoft dal 2000; il più aggiornato driver SOAP U2.0 supporta i “binding styles” definiti nel WS-I Basic Profile 1.1 ed è disponibile su diverse piattaforme.

Il driver SOAP svolge le seguenti funzioni:
– Riconosce/Genera i file di tipo WSDL (Web Services Description Language)
– Esegue la conversione tra i tipi di dato caratteristici dei WebServices e quelli comunemente disponibili in ambiente Uniface
– Integra funzioni di callback per permettere al programmatore di complementare la struttura tecnologica al fine di poter rispondere a caratteristiche funzionali specifiche.
– Si integra con il resto dell’architettura Uniface per ricevere o inviare richieste di Web Services via http
– Mappa le condizioni di errore tipiche di SOAP sulle condizioni di errore standard di Uniface

Per prima cosa va varificata la disponibilità del driver SOAP nella licenza e va configurato l’utilizzo del driver SOAP nei nostri file di configurazione (ASN) con una definizione simile alla seguente:
[DRIVER_SETTINGS]
SOP    U2.0
USYS$SOP_PARAMS callback = SOAP_GEN_CALLBCK

Vediamo quale debba essere il flusso operativo per fruire (call-out) di un Web Services reso disponibile via Internet:
1) Importare il WSDL che descrive le funzioni messe a disposizione dal web service utilizzando l’ambiente di sviluppo con la sua opzione “/sti /mwr=ws”. Esempi:
[Path]idf /sti /mwr=ws http://www.serverWSDL.org/percorso/nomeServizio?wsdl
[Path]idf /sti /mwr=ws .\wsdl\nomeServizio.wsdl
A seguito di questo import Uniface genera una signature avente come nome quello descritto nel tag <wsdl:service name>. Un WSDL può comprendere sia dati semplici che dati complessi (occorrenze, entità secondo la terminologia Uniface); per fare in modo che il driver SOAP tenti di riconoscere e mappare automaticamente le strutture complesse utilizzate dal WSDL su un Application Model è necessario utilizzare le opzioni /gen e /mod nel corso dell’importazione del WSDL. In caso di non utilizzo degli switch suddetti le strutture complesse verranno mappate su semplici tipi stringa lasciando al programmatore il compito di costruire le stringhe XML necessarie.
La funzionalità di import mette a disposizione anche altre opzioni (/nosig /verbose /bare) che permettono di verificare il comportamento del driver nel corso dell’importazione al fine di ottenere il risultato desiderato.
2) Una volta importata la signature è sufficiente richiamare il Web Service rispettando la nomenclatura che è stata importata dal wsdl mediante una semplice istruzione di:
activate “nomeServizio”.operation(params)
tenendo in considerazione alcune avvertenze basilari:
– Se è previsto un tipo di dato complesso (occorrenza / entità) la relativa entità va definita nel componente che esegue il richiamo.
– Se la stessa entità è utilizzata sia come parametro di input che di output è bene utilizzare dei sottotipi per evitare il sovrapporsi delle informazioni sulla stessa struttura
– Se è previsto un tipo di dato complesso trattato come stringa è necessario creare da programma una opportuna stringa XML che sia allineata allo schema definito nel WSDL
– Se per accedere al web service sono necessarie funzionalità di sicurezza e/o authenticazione è necessario configurare opportunamente USYS$SOP_PARAMS e/o inserire delle funzionalità di callback in ambito CALLOUT per ottenere quanto necessario.

Quando invece si volesse rendere disponibile (call-in) un servizio costruito con Uniface come Web Service si deve:
1) Esportare su WSDL l’interfaccia del servizio  utilizzando l’ambiente di sviluppo con la sua opzione “/sto /mwr=ws”. Esempio:
[Path]idf /sto /mwr=ws nomeServizio
Viene generato un file avente nome nomeServiziodlw.wsdl; nel caso in cui nelle operations del servizio siano presenti parametri complessi di tipo occorrenza o entità viene generato anche un file di tipo .xsd contenente la definizione delle entità coinvolte.
2) Pubblicare il servizio applicatvo in ambiente Uniface Server. Si tratta di pubblicare sia i wsdl/xsd appena generati che il componente Uniface e la sua signature in ambiente Uniface Server. Nell’ambiente Tomcat standard la directory in cui wsdl/xsd vanno pubblicati è la directory wsdl sottostante la directory di lavoro del contesto Tomcat, normalmente denominata WEB-INF. Il wsdl può essere recuperato, insieme all’eventuale xsd, all’indirizzo:
http://serverName[:portNum]/<contestoDiProgetto>/services/nomeServizio?wsdl
mentre il servizio Uniface può essere raggiunto all’indirizzo
http://serverName[:portNum]/<contestoDiProgetto>/services/nomeServizio
Nel contesto dell’utilizzo di un WebService sviluppato con Uniface da parte di un client esterno agisce ovviamente l’intera struttura dei server Uniface. I seguenti eventi vengono gestiti:
– Il client esterno costruisce il richiamo ad un web service e lo invia al web server, che per semplicità, assumiamo essere Tomcat.
– Il web server ridirige la richiesta alla servlet configurata, nella configurazione di default quella denominata SRD.
– La servlet SRD estrae la richiesta e attiva attraverso la rete il dovuto servizio Uniface. In particolare:
– Il connettore SOAP in un contesto di call-in identifica la richiesta come SOAP e la spacchetta.
– Il connettore SOAP sempre nel contesto di call-in mappa i tipi di dato SOAP ai tipi di dato Uniface.
– Il connettore SOAP sempre nel contesto di call-in attiva l’operazione del servizio Uniface ricevuta nella richiesta SOAP insieme ai parametri ricevuti.
– Il servizio Uniface gestisce l’operazione richiesta.
Se la comunicazione ha caratteristica asincrona non ci sono ulteriori attività.
In una comunicazione sincrona, una risposta deve essere restituita, nella seguente modalità:
– Il servizio Uniface restituisce i parametri in OUTPUT:
– Il connettore SOAP sempre nel contesto di call-in mappa i tipi di dato Uniface ai tipi di dato SOAP e costruisce la risposta SOAP contenente i dati da restituire.
– Il contenuto della risposta SOAP viene restituito attraverso la rete alla servlet SRD.
– La servlet SRD riceve la risposta, estrae il contenuto della risposta SOAP, lo imbusta e lo restituisce al web server.
– Il web server lo restituisce al client esterno.

Termina qui la breve descrizione del supporto web services in modalità SOAP da parte di Uniface; maggiori informazioni sulla configurazione del driver SOAP e sull’utilizzo sia in call-in che in call-out della modalità SOAP possono essere recuperate nella Uniface Library aggiornata e sui siti internet di riferimento.

Nella prossima parte di questa serie di articoli ci concentreremo sui servizi di tipo REST.