Archive for Author Gianni Sandigliano

About the Author: Gianni Sandigliano
Ho incontrato Uniface per la prima volta parecchi anni fa, si faceva chiamare FastBuild; qualche settimana più tardi ho conosciuto la sua vera poliedrica identità facendo il corso base ad Amsterdam: era la versione 4.0.q6, ed era completamente a carattere...da allora si sono succedute ben 5 major relases ma le prerogative del prodotto rimangono inalterate...una gran bella idea!

Uniface plugin?

Ho pubblicato sul sito di uniface.info il post che segue in questa pagina.
Qualcuno vuol dire la sua qui in italiano?

========== ========== ========== ========== ========== ==========
Hi Unifacers,

In the current IT world driven from integrations could it be of interest to the Uniface world to have a standardized mode to develop and deploy plugins to Uniface apps? IMHO…YES!

I do not remember this specific subject being discussed, but it could be I was not part of the discussion or I have simply forgotten about it!

Pro…? Cons…? Pitfalls…? Tips…? Suggestions…?

Let’s argue about it!
========== ========== ========== ========== ========== ==========

UNIFACE plugins

Definition: a uniface plugin could be a full application (launcher, monitor, configurator, …) or a part of an application (module, library, …) that can be used together or integrated within a Uniface application. Each plugin is dedicated to a single business or technical object. Base for each plugin should be a microservice architecture, extended with a re-susable presentation layer.  The word “plugin” is used to identify a software that can easily be re-used in any Uniface installation and configuration.

Perimeter (DRAFT) for a “whatever” plugin:
SelfContainedUARname: UP_XXXX_whatever_VVV where:
– XXXX is a unique identifier of plugin provider
– whatever is the concept implemented in the plugin
– VVV is the plugin version
each plugin provider is responsible to maintain plugin info updated
Mission: [short description (max 250 char) defining the plugin interface perimeter]
PluginVersion: x.y[.z]
UnifaceVersionsSupported: Ux.Uy[, Ux1.Uy1][, Ux2.Uy2]
ComponentsNames: XXXX_*
GlobalObjects: Each plugin component is connected to its library named XXXX_*
Objects being part of SYSTEM_LIBRARY should have their names starting with XXXX_*
$variation and $language MUST be saved and restored from ALL plugin interactions
Objects being part of USYS variation should have their names starting with XXXX_*
Security: None | ???TBD???
DBMS: None | XXXX_APPMODEL
UserInterface: None | Char | C/S | Web
LanguagesSupported: “USA” MUST be supported! Further list of international codes from ISO-639-3
ServicesPerimeter: None | Uniface | LAN | Internet
PrintingPerimeter: None | Uniface | OtherToBeSpecified
DataExchangePerimeter: InLine | LocalFolder | MailBox | OtherToBeSpecified
OperationsList: simpleListOfOperationsAvailableWithoutParams but a short description
PresentationComponents: simpleListOfPresentationComponents with attribute: Contained | Indipendent and a short description
OtherPluginPreRequisites: None | ListOfOtherPlugin(s)ThatMUSTbeAvailable
PluginOptionals: None | ListOfOtherOptionalPlugins
OtherTechPreRequisites: None | ListOfOtherTechPreRequisites
DeliveryMode: OpenSources | UARonly | withSourcesIfRequested
TechDocumentation: None | Samples | Included
Contact: eMailAddress

Let’s try with an example
(thanks to Theo Neskeens providing initial GoogleMap Integration sample):
SelfContainedUARname: UP_GSAN_GMAP_100
Mission: Interface to Google Map, Services and Presentations
Version: 1.0.0
UnifaceVersion: 9.7
ComponentsNames: GSAN_*
GlobalObjects: None
Security: None
DBMS: None
UserInterface: C/S
LanguagesSupported: ITA
ServicesPerimeter: Internet
PrintingPerimeter: Uniface, GSAN_UPDF plugin
DataExchangePerimeter: InLine
OperationsList:
GSAN_GMAP_SVC.getDistance() get distance in km between two GeoPoints|Addresses|POIs in an UIL
GSAN_GMAP_SVC.getPathFromTo() get traveling steps between two GeoPoints|Addresses|POIs in an UIL
GSAN_GMAP_SVC.getStaticMap() get static map related to a GeoPoint|Address|POI in an UIL
PresentationComponents:
GSAN_GMAP_MAP Contained form showing either static or dynamic maps
OtherPluginPreRequisites: None
– <strong>PluginOptionals: GSAN_UPDF: to interactively print map to PDF
– OtherTechPreRequisites: Google Developer Account must be setup
DeliveryMode: UARonly
TechDocumentation: Samples
Contact: name.surname@domain.ext

Simpler HowTo:
– Add reference to UP_GSAN_GMAP_100 UAR file to the [RESOURCES] section of your ASN file
– Use in your application activate “GSAN_GMAP_SVC”.getDistance() with documented parameters to get distance between two addresses.
========== ========== ========== ========== ========== ==========

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

Tuning applicativo

Chi ha esperienza nello sviluppo di applicazioni, soprattutto quelle gestionali, sa che spesso, dopo aver raggiunto l’obbiettivo iniziale di coprire gli aspetti funzionali indicati dalla fase di analisi, si rende prima o poi necessaria una fase di tuning applicativo.

Si cercano le possibili risposte alla domanda: come faccio a far fare alla mia applicazione le stesse cose che fa oggi ma più velocemente?
Si identificano i punti maggiormente critici e poi si procede…

In genere la tecnica chiave utilizzata è quella di evitare accessi ricorsivi al database lavorando su aree di memoria di processo opportunamente predisposte; alcuni giorni fa attuando su un caso specifico questa tecnica abbiamo sfruttato a fondo le liste Uniface associative (chiave=valore) con un metodo che potrebbe tornare utile in molte occasioni.

Si dovevano ottimizzare le funzionalità del ciclo attivo in una installazione gestionale che lavora su una anagrafica di circa 100.000 articoli; utilizzando il profiling applicativo, messo a disposizione da Uniface, abbiamo dapprima determinato che il punto critico in tutte le funzionalità del ciclo attivo era la determinazione di prezzo di vendita/sconti quantità, perché era costituita da un algoritmo multi-livello che doveva tenere in considerazione:
– listino standard
– listino tra quelli disponibili applicato al cliente
– campagne tra quelle disponibili applicabili al cliente
– promozioni tra quelle disponibili applicabili al cliente
– prezzi speciali ammessi per il cliente

A fronte di una ricerca articoli la determinazione di prezzo/sconti per quel cliente di ogni articolo al “momento” richiedeva con l’algoritmo multilivello circa 0.8 secondi. Non molto in valore assoluto ma un valore comunque impattante rispetto alla usabilità delle funzionalità del ciclo attivo in una schermata che presenta diversi articoli.

Abbiamo provato a trovare il miglioramento ricercato evitando di andare sul database per ogni articolo alla ricerca del prezzo da applicare ma cercandolo in memoria; la predisposizione della ricerca in memoria è stata fatta in questo modo:
1) Popolando all’ingresso della funzione applicativa una area di lavoro associata alla sessione applicativa (nel nostro caso NodoRete+IDprocesso)
2) Inserendo sull’area di lavoro TUTTI gli articoli che il cliente del momento può acquistare dal listino standard
3) Aggiornando gli articoli nell’area di lavoro per listino cliente, campagne, promozioni e prezzi speciali validi per il cliente del momento
4) Caricando in memoria il risultato finale ottenuto sull’area di lavoro: l’insieme completo degli articoli vendibili a quel cliente in quel momento comprensivi di prezzo e sconti quantità.
Questa parte di funzionalità è stata inserita all’inizio nella gestione offerte e nella gestione ordini clienti, subito dopo aver identificato il cliente, in modo da poter applicare tutte le regole del business necessarie per ogni specifico cliente; avviando le suddette funzioni applicative senza identificazione di un cliente specifico, viene caricato il contesto di vendita per un cliente generico, senza particolari facilitazioni economiche. Il caricamento in memoria richiede complessivamente da 3 ai 6 secondi (in funzione del cliente) all’avviamento della corrente funzione applicativa e utilizza in tutto solamente qualche MB (100.000 x 35bytes = 3,5 MB).

Come conviene costruire la matrice in memoria?
L’ideale sarebbe una lista associativa (chiave=valore) per poter sfruttare a fondo il linguaggio ProcScript disponibile in Uniface; per costruirla velocemente sono necessari due passaggi con il seguente snippet di codice:
– sql/data “select campo/iChiave,’=’,attributo/iNecessari from AreaDiLavoro where IDsessione=%%vSessione%%%”, “DEF”
– $$Matrice = $replace($result,1,”!;=!;”,”=”,-1) ; != è uguale a <GOLD>!<GOLD>;
Con queste due semplici istruzioni ci ritroveremo in memoria una lista Uniface di tipo associativo (chiave=valore) con un costrutto riconducibile al seguente:
ARTICOLO1=PREZZO1;SCONTO1.1;SCONTO1.2;SCONTO1.3
ARTICOLO2=PREZZO2;SCONTO2.1;SCONTO2.2;SCONTO2.3
avendo così la possibilità mediante il semplice utilizzo di quest’altra istruzione del ProcScript:
– attributiArticolo = $item(“ARTICOLO1”, $$Matrice)
di ritrovarci in attributiArticolo una lista posizionale contenente:
PREZZO1;SCONTO1.1;SCONTO1.2;SCONTO1.3
e potendo successivamente recuperare le singole componenti in modo posizionale:
– getitem vPrezzo, attributiArticolo, 1
– getitem vSconto1, attributiArticolo, 2
– getitem vSconto2, attributiArticolo, 3
– getitem vSconto3, attributiArticolo, 4
La risoluzione in memoria degli attributi di 1000 articoli random da una matrice che ne comprende 100.000 con copia del risultato ottenuto sulla MessageFrame di Uniface richiede solamente una ventina di centesimi di secondo: praticamente istantanea!

Il limite di questo approccio è ovviamente il fatto che NON è possibile aggiornare con un’altra sessione applicativa un singolo prezzo per il cliente del momento e vederlo utilizzato immediatamente; è necessario uscire e rientrare nella funzione che utilizza il metodo descritto per vedere aggiornati i prezzi utilizzati…ma è una approssimazione accettata nella quasi totalità delle aziende.

Chi volesse approfondire mi contatti a gianni.sandiglianoATunifacesolutions.com.

Gianni

Gestione di lunghe stringhe in memoria

Durante lo sviluppo di un nuova nuova funzione Uniface mediante l’utilizzo del solo “Proc Script” ho dovuto testarla rispetto ai possibili valori. Mi sono trovato a voler generare un file di log su un doppio loop annidato, qualcosa simile a queste poche righe di codice:

cTab = ” ”
cCR = “%%^”
vLog = “i j k”
for i = -180 to 180 step 1
for j = -181 to 181 step 1
; calcolo k con la mia nuova funzione
k = shiftMap(i, j)
; memorizzo i valori sulla stringona che al termine scaricherò su file
vLog = $concat(vLog, i, cTab, j, $concat(cTab, k, cCR))
endfor
endfor
filedump/nobom vLog, “myLog.txt”

Eseguendo questo codice ho constatato un progressivo rallentamento mano a mano che il ripetersi delle singole concatenazioni ampliava la dimensione della stringa vLog in memoria. Il punto di loop con i = -180 aveva impiegato pochi centesimi di secondo mentre il punto di loop con i = 180 aveva impiegato parecchi secondi (il codice reale è un poco più complesso…); una differenza ed un rallentamento progressivo di questa importanza mi ha spinto a cercare una soluzione alternativa che migliorasse le prestazioni.
Ho provato a concatenare sulla stringa vLog nel loop esterno i pezzettoni di stringa derivanti dal loop interno mediante l’aggiunta di una seconda variabile temporanea; quindi la variabile vTmp gestisce il solo loop interno e all’uscita del medesimo il risultato viene concatenato nella variabile vLog come in precedenza. Quella che segue è la nuova versione del codice utilizzato:

cTab = ” ”
cCR = “%%^”
vLog = “i j k”
for i = -180 to 180 step 1
vTmp = “”
for j = -181 to 181 step 1
; calcolo k con la mia nuova funzione
k = shiftMap(i, j)
; memorizzo i valori sulla stringa temporanea
vTmp = $concat(vTmp, i, cTab, j, $concat(cTab, k, cCR))
endfor
; memorizzo i valori sulla stringona che al termine scaricherò su file
vLog = $concat(vLog, vTmp)
endfor
filedump/nobom vLog, “myLog.txt”

Il risultato è stato entusiasmante! A parità di funzionalità (il file scaricato è identico!) il tempo si è quasi ridotto di 100 volte. Nel primo caso il tempo impiegato è stato di 27 minuti 08 secondi e 20 centesimi ossia 1628.20 secondi mentre nel secondo caso è stato di 16.77 secondi.
Nel mio caso stavo lavorando su una form ma questa tecnica risulta valida anche in un servizio o in un report.
Conclusione:
NON sempre la prima versione di codice funzionante o la più semplice è la migliore!

Uniface ed i Web Services, 3.parte – REST

Nella seconda parte abbiamo affrontato i “Web Services” rispondenti alla specifica SOAP, in questa terza parte affrontiamo quelli rispondenti ai criteri REST.

Un “Web Service” REST è un normale componente web in grado di restituire contenuti in un formato testuale utilizzando http(s) come protocollo di trasporto; questo formato testuale NON è precisato nei criteri REST, può essere HTML o XML o JSON o semplice testo. Nei nostri esempi assumeremo che il formato testuale utilizzato sia JSON, che è il formato più comunemente utilizzato.
JSON è l’acronimo di “JavaScript Object Notation”, ed è un formato adatto per lo scambio dei dati in applicazioni client-server. È basato sul linguaggio JavaScript Standard ECMA-262 3ª edizione dicembre 1999, ma ne è indipendente. Fornisce una alternativa ad XML ed è spesso utilizzato in applicazioni web per la serializzazione e trasmissione di informazioni sulla rete.

In Uniface la fruizione (consumo) di servizi REST passa attraverso l’utilizzo del componente UHTTP; tale componente associa in una singola chiamata una accoppiata request/response via http/https. Le operazioni che rende disponibili permettono di attuare diverse funzionalità:
– SEND: invia una request HTTP utilizzando il metodo specificato, come PUT o POST per file upload e GET per file download.
– SET_FLAGS: setta parametri di esecuzione per la successiva operazione SEND determinando come Uniface gestisce la comunicazione http.
– SET_TIMEOUT: stabilisce l’ammontare di tempo debba essere atteso da una request HTTP prima di considerarla completata.
– WRITE_CONTENT: appende un segmento di dati al buffer UHTTP, che verrà utilizzato dall’operazione SEND. Non può essere utilizzata insieme alle operazioni sui file, come  LOAD_FILE_CONTENTS.
– READ_CONTENT: ottiene informazioni aggiuntive sull’ultima request http. Non può essere utilizzata insieme alle operazioni sui file, come DUMP_FILE_CONTENTS.
– LOAD_FILE_CONTENTS:carica un file, o parte di esso, nel body di una request prima di chiamare l’operazione di SEND per inviare il file.
– DUMP_FILE_CONTENTS:crea un file locale leggendo il body di una response che contiene il file, o un suo spezzone.
– GET_INFO: acquisisce informazioni sul body di un messaggio prima o dopo una operazione SEND. Risulta utili quando il body di una request contiene informazioni relative al file corrente, non essendoci nessun altro modo per conoscere la lunghezza o la zona del file richiesta.
– CLOSE_FILE: chiude il file corrente che è stato utilizzato per l’invio a spezzoni del suo contenuto.

Esempi: (Richiedetemi form di esempio GSX_TEST_UHTTP)
– activate uhttp.send(“https://jsonplaceholder.typicode.com/posts”, “GET”, username, password, headers, content, response)
Restituisce TUTTI i post memorizzati
– activate uhttp.send(“https://jsonplaceholder.typicode.com/posts/1”, “GET”, username, password, headers, content, response)
Restituisce il post memorizzato con ID = 1
– activate uhttp.send(“https://jsonplaceholder.typicode.com/posts?userId=1”, “GET”, username, password, headers, content, response)
Restituisce i post memorizzati dall’utente con ID = 1
– activate uhttp.send(“https://jsonplaceholder.typicode.com/posts/1/comments”, “GET”, username, password, headers, content, response)
Restituisce i commenti associati al post con ID = 1
– activate uhttp.send(“https://jsonplaceholder.typicode.com/posts”, “POST”, username, password, headers, “{%%^title: ‘foo’,%%^body: ‘bar’,%%^userId: 1%%^}”, response)
Aggiunge una occorrenza e restituisce l’ID assegnato
– activate uhttp.send(“https://jsonplaceholder.typicode.com/posts/1”, “PUT”, username, password, headers, “{%%^title: ‘foo’,%%^body: ‘bar’,%%^userId: 1%%^}”, response)
Aggiorna l’occorrenza con ID = 1
– activate uhttp.send(“https://jsonplaceholder.typicode.com/posts/1”, “DELETE”, username, password, headers, content, response)
Cancella l’occorrenza con ID = 1

In Uniface la creazione/gestione di uno stream JSON è estremamente facilitata attraverso istruzioni specifiche:
– JSONtoStruct: definisce una variabile di tipo struct a partire da uno stream JSON
– StructToJSON: costruisce uno stream JSON a partire da una variabile di tipo struct
– webload: carica i dati da uno stream JSON su un componente Uniface
– websave /mod | /one: crea uno stream JSON dai dati in a componente Uniface
– uniface.datastore.put(): salva nel browser un documento (insieme di dati in genere in formato JSON) contraddistinti da un ID
– uniface.datastore.get(): recupera dal browser un documento (insieme di dati in genere in formato JSON) passando l’ID corrispondente
– uniface.datastore.remove(): cancella dal browser un documento (insieme di dati in genere in formato JSON) passando l’ID corrispondente
– uniface.getData()
– uniface.setData()

La creazione un web service al fine di fornire dati secondi i criteri REST è molto semplice e passa attraverso lo sviluppo di una DSP; bastano poche linee di codice per:
– leggere eventuali parametri in ingresso dai canali INPUT o PATHINPUT o dall’header del pacchetto ricevuto
– restituire lo stream JSON nel canale OUTPUT
Esempio di codice da inserire nel trigger di “Execute” della DSP:
public web
variables
string vInput  ; Input parameter from URL
string vOutput ; Output content to be returned
endvariables
; — 1 — get input parameter
getitem/id vInput, $webinfo(“PATHINPUT”), “name”
; — 2 — create output based on input
; you can do anything here
vOutput = “Hello %%vInput%%%, I am the output”
; — 3 — fill the output channel
$webinfo(“output”) = vOutput
; And we are done !
return(0)
end
Questa struttura di base va poi adattata a quanto realmente necessario utilizzando le funzioni creazione di stream JSON elencate in precedenza.

La parte più critica dei Web Services REST rimane la totale assenza di una documentazione standard delle interfacce predisposte, qualcosa assimilabile al WSDL dei servizi SOAP; ci sono iniziative a questo riguardo ma non hanno ancora raggiunto una stabilità e riconoscimenti adeguati. Di conseguenza il criterio con il quale i dati vengono scambiati (parametri, tipi di dato trasferiti, formati degli stessi, …) va documentato adeguatamente rispetto alle esigenze identificate. In alcuni casi si constata l’utilizzo di un metodo HTTP specifico (OPTIONS) che elenchi e documenti le chiamate disponibili.

Nella prossima parte parleremo dei criteri con i quali è possibile definire una API mediante i web services, siano essi SOAP o REST.

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.

Uniface e i Web Services, 1. parte – Generalità

Il W3C definisce un “web service” come “un modulo software disegnato per supportare l’interoperabilità e l’interazione tra macchine collegate in rete”.
I Web Services sono univocamente il fulcro delle possibili integrazioni tra ambienti applicativi eterogenei per piattaforma, sistema, linguaggio ma le tecnologie utilizzate si differenziano; negli anni aziende diverse, leader di mercato, hanno  sfortunatamente avuto idee diverse su quale potesse essere la soluzione ideale per garantire interoperabilità ed interazione in rete. Questo fatto ha generato sostanzialmente due tipi diversi di Web Services:
1 – basati sulla specifica SOAP
2 – in linea con i criteri REST
La specifica SOAP è di poco più anziana dei criteri REST (SOAP 1.0 del 1998, mentre 2000 Tesi di laurea di Roy Fielding per REST) ma mentre SOAP è stato adottato da subito su larga scala, REST ha ricevuto una notevole spinta solo negli ultimi anni, in base al suo ampio utilizzo dovuto alla semplicità dei criteri che lo caratterizzano, in linea con le tecnologie comunemente in uso. Va chiarito che REST NON è una specifica formale ma una serie di criteri oggetto di raccomandazione a supporto di una architettura software per la gestione di dati in ambiente ipertestuale.

Un minimo di approfondimento su entrambi i tipi di Web Services:
SOAP = acronimo per Simple Object Access Protocol: può operare su differenti protocolli di rete, ma HTTP è il più comunemente utilizzato per il trasporto, anzi è l’unico ad essere stato standardizzato dal W3C. La busta SOAP si basa su XML e la sua struttura segue la classica configurazione Head-Body, analoga ad HTML. Il segmento opzionale Header contiene meta-informazioni come quelle che riguardano il routing, la sicurezza, le transazioni e parametri per l’Orchestration. Il segmento obbligatorio Body trasporta il contenuto informativo che talora viene detto carico utile (payload). Questo deve seguire uno schema definito dal linguaggio “XML Schema”. SOAP può essere utilizzato in due modi diversi:
– il client controlla in un Service Registry l’oggetto d’interesse e sviluppa il messaggio secondo i parametri contenuti nel Service Registry.
– il client conosce a priori i parametri e non necessita di consultare il service registry. All’interno del body del messaggio si inseriscono i dati scritti nel formato concordato.

REST = acronimo per REpresentational State Transfer: Web services di tipo REST permettono di richiedere ad un sistema l’accesso a determinate risorse ricevendo in cambio una rappresentazione testuale delle medesime attraverso l’uso di un predefinito ed uniforme insieme di operazioni “stateless”. Le risorse sono identificate attraverso un URI (Uniform Resource Identifier) mentre le operazioni che si utilizzano sono quelle tipiche del protocollo HTTP (HTTP verbs): GET, POST, PUT, DELETE, OPTIONS. La rappresentazione testuale della risorsa ricevuta in risposta può essere strutturata in modi diversi, ad esempio: XML, HTML, JSON o addirittura semplice testo; JSON è il formato che più ha preso piede nel corso del tempo. Un sistema che voglia essere pienamente compatibile con una architettura REST dovrebbe comprendere ed implementare in modo rispettoso le seguenti caratteristiche:
– modello client-server: separazione netta delle competenze, client = interfaccia, server = accesso funzionale alle informazioni.
– dialogo di tipo stateless: ciascuna richiesta da un qualunque client contiene tutte le informazioni necessarie e lo stato della sessione è totalmente gestito lato client. Lo stato della sessione può essere trasferito al server e, se necessario, mantenuto persistente per il tempo necessario a gestire la transizione allo stato successivo.
– risposte cacheable: ogni tipo di dialogo web deve essere definito “cacheable” o “non cacheable”; i servizi REST dovrebbero sempre essere gestibili in cache dai nodi sui quali transitano.
– sistema a livelli: ogni client non deve riconoscere se è collegato al server vero e proprio o ad un suo intermediario; gli intermediari diversi possono essere implementati al fine di poterli  specializzare su funzioni diverse (sicurezza, gestione dei carichi e conseguente scalabilità, ecc)
– “code on demand”: alcuni server possono, anche su base temporanea, estendere, anche in modo specifico, le funzionalità di un client attraverso il trasferimento di codice eseguibile.
– interfaccia uniforme: è fondamentale nella definizione di qualsiasi insieme di servizi REST; dovrebbero SEMPRE essere soddisfatte le seguenti linee guida:
. Identificazione delle risorse: specifiche risorse sono identificate nella richiesta, per esempio utilizzando direttamente URI di riferimento. L’identificazione delle risorse da richiedere è concettualmente separata dalla loro rappresentazione restituita al richiedente. Ad esempio: il server può inviare informazioni dal suo database come HTML, XML o JSON, la cui struttura nulla ha a che vedere con quella interna del server.
.. Manipulazione delle risorse mediante la loro rappresentazione: quando un client riceve la reppresentazione di una risorsa, inclusi eventuali metadati, ottiene sufficienti informazioni per modificare o cancellare la risorsa.
… Messaggi auto-descrittivi: ciascun messaggio include sufficienti informazioni per permetterne la completa gestione del suo contenuto. Ad esempio: quale parser va invocato per riconoscere il contenuto potrebbe essere specificato con un “Internet media type” (conosciuto anche come MIME type).
…. Hypermedia come motore degli stati applicativi (HATEOAS): avendo avuto accesso una applicazione REST inizialmente a un URI, il client REST dovrebbe essere in grado di avere sufficenti informazioni, per essere in grado di utilizzare i (hyper)link ricevuti per accedere a tutte le ulteriori possibili azioni o risorse necessarie. Mano a mano che il dialogo tra server e client prosegue il server restituisce ulteriori (hyper)links che abilitano il client all’esplorazione dell’ecosistema applicativo disponibile sul server.

Entrambi i protocolli si basano su trasporto mediante HTTP ed entrambi sono correntemente supportati da Uniface:
– SOAP con il driver SOP: inizialmente in versione 1.0, ora in versione 2.0.
– REST sin da Uniface7 con la costruzione di componenti Web che restituiscono un contenuto coerente con i criteri elencati.
Nella seconda parte vedremo dei semplici esempi di come si implementano o come si fruisce di “web services” con Uniface con entrambi i protocolli.

Utilizzare al meglio la tastiera italiana del PC Windows

Questo articolo ha direttamente poco a che fare con l’ambiente Uniface, ma la conoscenza è stata indotta da una esigenza relativa ad una applicazione Uniface.

Come utilizzare al meglio la tastiera italiana del PC Windows?

Per prima cosa esistono fondamentalmente due tipi di tastiere: quelle con e quelle senza tastierino numerico. Quelle senza tastierino numerico permettono di attivarlo logicamente mediante il tasto “Block Num” o “Bl Num”; una volta attivo alcuni tasti della tastiera emulano il tastierino numerico. Questo fatto è importante perché le sequenze di tasti da premere per ottenere certi caratteri prevedono l’uso fondamentale del tastierino numerico.

Si possono generare direttamente dalla tastiera italiana senza tastierino numerico, tipica di molti notebook, i seguenti caratteri:

!”#$%&'()*+,-./0123456789:;<=>?@

ABCDEFGHIJKLMNOPQRSTUVWXYZ

[\]^_

abcdefghijklmnopqrstuvwxyz

{|}àèéìòùç°§€£

Per completare il set di caratteri più comunemente utilizzati nella lingua italiana si possono utilizzare le seguenti sequenze utilizzando il tasto Alt alla sinistra della barra spaziatrice ed i numeri sul tastierino numerico:

Alt 0096 =   `   singolo apice inverso
Alt 0126 =   ~   tilde
Alt 0160 =   á   a minuscola con accento acuto
Alt 0205 =    í   i minuscola con accento acuto
Alt 0243 =   ó   o minuscola con accento acuto
Alt 0250 =   ú   u minuscola con accento acuto
Alt 0192 =   À   A maiuscola con accento grave
Alt 0193 =   Á   A maiuscola con accento acuto
Alt 0200 =   È   E maiuscola con accento grave
Alt 0201 =   É   E maiuscola con accento acuto
Alt 0204 =   Ì    I maiuscola con accento grave
Alt 0205 =   Í    I maiuscola con accento acuto
Alt 0210 =   Ò   O maiuscola con accento grave
Alt 0211 =   Ó   O maiuscola con accento acuto
Alt 0217 =   Ù   U maiuscola con accento grave
Alt 0218 =   Ú   U maiuscola con accento acuto

Possono tornare utili anche:
Alt 0169 =   ©   Simbolo di copyright
Alt 0174 =   ®   Simbolo di diritti riservati
Alt 0188 =   ¼   Un quarto
Alt 0189 =   ½   Un mezzo
Alt 0190 =   ¾   Tre quarti

Si diventa “pianisti”…e si completa la conoscenza del PC con cui si lavora!

Lavorando in ambito internazionale potrebbero essere necessari ulteriori caratteri, come quelli con la dieresi (umlaut in tedesco) con l’accento circonflesso (tipici in francese) o i caratteri greci molto usati in matematica; chi avesse esigenze di questo genere esistono su Internet documentazioni più appropriate e complete di questo breve articolo.

Tornando alle applicazioni, non tutti questi caratteri debbono necessariamente essere accettati su un campo di tipo stringa; in Uniface è possibile applicare direttamente su ogni campo stringa un filtro limitando i caratteri che vengono accettati:
– Solo i caratteri ASCII (sono escluse TUTTE le lettere accentate minuscole e maiuscole)
– Solo i caratteri ASCII estesi (sono compresi tutti caratteri sopra riportati tranne il simbolo dell’Euro)
– L’intero insieme di caratteri disponibili (TUTTI quelli riportati sopra e molti altri…)
E’ ovviamente possibile costruire routine specializzate a supporto di casistiche più complesse.
Per avere un adeguato supporto a livello di database si devono definire i campi stringa di tipo Unicode (packing codes W).

Ho costruito una semplice form di esempio sulla base di quanto descritto: chi volesse riceverla mi invii una email.

Buona digitazione!

Migliorare le applicazioni Uniface esistenti

Negli ultimi due mesi sono stato spesso coinvolto in attività di consulenza mirate a migliorare applicazioni Uniface esistenti.
Ci sono a tutt’oggi in produzione, sia a livello internazionale che in Italia, applicazioni Uniface 5, Uniface 6, Uniface 7, Uniface 8 e, ovviamente, Uniface 9.
L’ambiente di sviluppo si è ampiamente evoluto nei suoi 30+ anni di presenza sul mercato ed ancora oggi continua ad evolvere piuttosto rapidamente: la nuova versione Uniface 10 ne è l’immediata controprova.
Le nuove funzionalità, rese disponibili nel corso degli anni, vanno integrate nelle modalità di sviluppo originarie facendo attenzione a mantenere l’integrità funzionale dell’applicazione originale.

Nella prima fase di queste attività si esplorano gli “Standard & Guidelines” adottati nello sviluppo dell’applicazione, che nella terminologia corrente vengono denominati “Framework di sviluppo”.
Analizzando il modo con cui l’applicazione è stata sviluppata si possono tracciare le linee di potenziale miglioramento, che ricadono in 6 possibili passi:

  1. Da “Stacked forms” char a “Stacked forms” GUI
    2. Da “Stacked forms” GUI a “NonModal forms” GUI
    3. Su “NonModal forms” GUI inclusione di una API applicativa
    4. Da “NonModal forms” GUI a “Web 2.0 pages”
    5. Da “Web 2.0 pages” a “Mobile pages”
    6. Da “Mobile pages” a “Fully partitionable application”

Lo sforzo necessario per ammodernare un applicazione è ovviamente funzione del punto di partenza e del punto di arrivo selezionato. Conviene definire tre obbiettivi: uno a breve, uno a medio ed uno a lungo termine; in questo modo risulta in genere possibile miscelare la quotidiana attività di manutenzione evolutiva dell’applicazione con gli obbiettivi di miglioramento aziendali rilasciando progressivamente le novità sviluppate sul proprio mercato di riferimento.

Chi volesse approfondire l’argomento mi contatti: gianni.sandiglianoATunifacesolutions.com

Uniface 10 Enterprise Edition, disponibile!

Il mese di settembre 2016 verrà ricordato come quello in cui è stato rilasciato Uniface 10 Enterprise Edition.

EccoVi la schermata iniziale con cui si presenta il nuovo ambiente di sviluppo:
schermatainizialeu10-2

Sono talmente tante le novità che risulta impossibile elencarle sinteticamente!

Chi vuole avere ulteriori informazioni mi contatti: gianni.sandiglianoATunifacesolutions.com

« Older Entries