mercoledì 13 febbraio 2019

Multi-Threading

Come si realizza il multithreading in OpenEdge(*)

Progress OpenEdge gestisce il multithreading? No,  Progress AVM è un processo monothread.

In questo primo post illustro quelle che, invece, sono le utilities OpenEdge multi-threaded ed i processi "abili" al multithreading.

- DBTOOL Utility. Tool di diagnostica e report su record format, index space, SQL width, ecc.
- Binary dump e Phase 2 del processo di idxbuild sono stati migliorati per gestire il multi-threading.
- Il processo broker/server per i client ABL (_mprosrv) non è multi-threaded. Può generare più processi  server, ciascuno in grado di servire più client, configurabili tramite i parametri di intermediazione -Mn-Ma del database).
- Il client Progress (_progres, _prowin32) essendo single-thread non permette di progettare applicazioni ABL multi-thread. Una possibilità, a seconda delle esigenze, può essere quella di usare chiamate ad AppServer in modalità asincrona. L'AppServer Broker è un processo java e genera più thread che il sistema operativo può allocare attraverso diverse CPU.Ogni AppServer Agent, essendo un processo _progress, tuttavia, verrà assegnato dal sistema operativo ad una CPU, ma non attraverso più CPU.

* Il presente post tratta della versione OpenEdge 10. Per le versioni successive https://knowledgebase.progress.com/articles/Article/P73663
giovedì 7 febbraio 2019
venerdì 29 maggio 2015

Transazioni - Transaction processing

Normal Transaction Processing

Forward processing
Le normali attività svolte da una applicazione, mentre sta accedendo e cambiando
il database, sono chiamate forward processing. In genere queste attività comprendono inizio e commit di transazione, ricerca e recupero dati, modifica dati esistenti,  aggiunta di nuovi e cancellazione di vecchi dati. 
Ogni cambiamento logico è eseguito apportando modifiche a uno o più blocchi del database. La modifica a un singolo blocco provoca un cambiamento fisico ed esegue un'operazione come la seguente :
nel blocco 409.824 sostituire i primi 8 byte con "!@#$%^&*"

Durante l'elaborazione roll forward i record di log vengono generati per registrare ogni cambiamento fisico del database. La tipica sequenza di eventi è la seguente:
1. Il blocco del db che contiene i dati da modificare si trova nel database buffer pool o viene letto da disco se non è già presente.
2. Il buffer pool è locked in modalità di update (exclusive lock) per evitare un altro processo di accesso o modifica a tale blocco, fino al completamento della modifica.
3. Un undo-redo log record, che descrive la modifica da effettuare, è creato in memoria.
4. Il record di log è memorizzato nel successivo spazio disponibile del buffer di registro corrente. Se lo spazio non è disponibile, il buffer è accodato per la scrittura dal BI writer e un log buffer libero diviene il buffer corrente. Se non ci sono buffer log liberi, il buffer più vecchio viene scritto su disco.
5. La modifica viene apportata al blocco del database, utilizzando solo i dati nel record di log ed i dati nel blocco.
6. Il lock sul buffer è rilasciato.

Quando un'operazione logica colpisce più di un blocco, i passaggi sopra descritti sono
eseguiti su ogni blocco. Un record di log undo-redo viene generato per ogni
blocco.

Rollback delle transazioni
Quando un'applicazione decide di annullare una transazione piuttosto che committarla, viene avviata una rollback transaction. Ciò si verifica quando l'applicazione esegue un'istruzione UNDO attraverso il codice 4GL, un utente interattivo preme il tasto "stop" sulla tastiera, un evento di lock wait è cancellato da un utente interattivo, o quando si verifica un lock wait time out. Il trattamento richiesto per effettuare un rollback è gestito automaticamente senza la necessità di alcun intervento applicativo.
Per eseguire il rollback di una transazione attiva, il database manager legge, in ordine inverso, tutto i records di log generati dalla transazione, fino al punto in cui è iniziata. Gli effetti di ogni variazione sono invertiti e i valori originali dei dati sono ripristinati. Mentre vengono annullate tutte le modifiche, il database è in stato di modifica, nuovamente. Queste nuove modifiche vengono registrate anche loro. Ciò genera un log record durante l'operazione di rollback proprio come durante un processo di normale modifica del database.
La sequenza di eventi scatenati da un processo di rollback è la stessa descritta in precedenza per l'elaborazione in avanti (forward). I record log identifica quali blocchi devono essere modificati e contiene anche tutti i dati necessari per eseguire l'operazione di annullamento.

Le transazioni possono essere ripristinate (rollback) in modo esplicito su richiesta dell’applicazione o dell'utente, o automaticamente dal database manager quando si verifica un errore e l'applicazione non è più in grado di comunicare con il database.
venerdì 22 maggio 2015

Transazioni - il concetto di logging


IL CONCETTO DI LOGGING

Il database manager di Progress realizza le proprietà di atomicità e durabilità di una transazione, utilizzando una tecnica di logging undo-redo, in combinazione con una write-ahead logging.
Dal momento che le modifiche del database avvengono per mezzo di una o più transazioni, notazioni o descrizioni di tali variazioni sono registrate su disco in un tipo di file di log delle transazioni, conosciuto come undo-redo log file. Quando si verifica un errore durante una transazione, questi record di log sono usati per effettuare il rollback della transazione, eliminando tutti i suoi effetti sul database (ripristino di tutti i dati ai loro valori precedenti). Gli stessi record di log sono usati al verificarsi di un crash recovery, per ripristinare il database in uno stato consistente.

Vi è un secondo log opzionale, chiamato redo log (discusso in seguito), che può essere utilizzato per migliorare l'affidabilità del sistema registrando il log delle transazioni in due fasi.

Il log undo-redo
Il log undo-redo è chiamato il "file di pre-immagine (before-image)" o "file BI". Il BI file contiene un registro di tutte le modifiche al database più recenti. Il database manager scrive uno o più record di log, riguardanti le modifiche, nel BI file prima di effettuare la modifica. Sebbene sia chiamato il before-image file, il log undo-redo file non contiene in realtà una pre-immagine del database. 
Esso contiene dati che vengono utilizzati per:
- Annullare o invertire gli effetti di una transazione che non è stata ancora commitata.
- Ripristinare il database in uno stato consistente dopo un crash, ripetendo (redo) le modifiche fatte in precedenza. Lo stesso insieme di modifiche potrebbe essere ripetuto più di una volta perché un guasto potrebbe verificarsi durante una fase di rcovery.
- Conservare gli effetti delle transazioni committate, anche in caso di fallimento
subito dopo il completamento della transazione.
- Consentire il riutilizzo incrementale di spazio nel file di undo-redo log quando i record non sono più necessari, riducendo al minimo la dimensione del file.
- Limitare la quantità di dati che devono essere elaborati, e il tempo richiesto per
farlo, durante il ripristino da un failure.
A seconda del tipo di operazione in corso sul database , ciò che è necessario per soddisfare tali requisiti potrebbe essere una copia dei dati prima di essere modificati, una copia dei nuovi dati, una combinazione dei due, o forse altre informazioni. Ci sono oltre 50 diversi tipi di record di log e ogni tipo contiene dati diversi.


Il BI file log può essere rappresentato da un singolo file o da più file (BI extents) che fanno parte di una struttura di database multi-file.

Redoing a change


L’informazione registrata nel file di log è tale che si può combinare i dati registrati nel log con i dati nella versione “vecchia” di un database block, per produrre una nuova versione aggiornata. Ad esempio, quando un record viene aggiornato, il record di log
conterrebbe vecchi e nuovi valori di solo quelle parti che hanno subito una modifica. I nuovi valori sarebbero fusi nel vecchio record per rifare (redo) una modifica.

Undoing a change


Lo stesso file log di registrazione, che è stato utilizzato per effettuare la modifica iniziale, può essere utilizzato per annullare un'operazione in combinazione con la nuova versione di un database block, per produrre la vecchia versione del record. Continuando l'esempio precedente, i vecchi valori sarebbero consolidati con il nuovo record per annullare una modifica.
Si noti che quando una modifica è annullata, si genera un nuovo record di log che descrive l'annullamento. Questo è necessario perché può verificarsi un errore prima che la copia, residente su disco, del database sia aggiornata a valle dello scatenarsi dell’evento di undo.

Il redo log
Il redo log è chiamato il "log dopo-immagine (after-image)" o "file AI". Il file AI contiene l’immagine di tutte  le modifiche che si sono verificate dopo un backup completo dell'intero database.
Lo scopo del AI log file è di fornire un meccanismo per il recupero dei dati da un eventuale incidente che distrugge tutto o in parte il database e/o il file di BI. Per recuperare i dati è necessario effettuare un ripristino del database e poi applicare tutte le modifiche che sono state fatte dai tempi del backup.
Il processo di riapplicare le modifiche si chiama roll-forward. Durante questo processo, il database manager legge i record nel file di AI e ripete ogni modifica di database, nello stesso ordine in cui sono state eseguite originariamente. Alla fine del processo di roll-forward, il database sarà nello stesso stato in cui si trovava appena prima l’errore si verificasse.
L’AI file log può essere rappresentato da un singolo file o da più file (AI extents) che fanno parte di una struttura di database multi-file.

Deferred Writes
L'esistenza del log undo-redo consente al database manager di mantenere in memoria le modifiche al database a tempo indeterminato, con una politica di gestione del buffer "steal/no force". I blocchi del database possono essere scritti su disco ogni volta che è conveniente. Le modifiche vengono mantenute in un'area di memoria chiamata database buffer pool e non devono essere scritte su disco, quando vengono apportate,  né quando la transazione che le ha effettuate è commited. È sufficiente che il transaction log record esista sul disco. Questo concetto è noto, in gergo, come "scritture differite (deferred writes)". Il meccanismo consente alte prestazioni ed ottimizzazione delle operazioni di scrittura su disco; i database blocks nel buffer pool possono essere aggiornati più volte prima di essere scritti su disco. Il database manager può scegliere il tempo e l'ordine in cui scrivere i dati, infatti, i blocchi aggiornati possono essere scritti prima che la transazione sia committata, o dopo, a seconda di quale momento è il più adatto. In effetti, il transaction log record consente di trasformare il processo di update sul database da random (più lento) a scrittura sequenziale (più veloce).

Write-ahead logging

Quando si utilizza la tecnica write-ahead, le registrazioni di tutte le modifiche del database sono scritte nel log, prima che siano applicate in memoria e su disco. In questo modo i dati nel undo-redo log (before-image log), possono essere usati per ripetere o rifare tutte le modifiche che possono essere perse prima di essere scritte nel database. Senza il log record, le modifiche non possono essere annullate se l'operazione di roll-back si rende necessaria. Il database non può essere ripristinato in uno stato coerente e sarà inutilizzabile. Affinché la tecnica di registrazione write-ahead funzioni correttamente, tutte le scritture nel log file devono essere effettuate in modo sincrono; ciò significa che quando il database manager scrive un buffer log su disco (utilizzando un chiamata di sistema write(), o qualcosa di simile), i dati devono essere effettivamente scritti su disco e non solo salvati in memoria. Per questo motivo i file di database, BI, AI non devono essere memorizzati su file system remoti montati attraverso le reti. 
venerdì 8 maggio 2015

Transazioni - Parte 1

Questo post è la prima parte di una breve guida, atta a descrivere come il Progress database engine utilizza le transazioni per migliorare affidabilità e prestazioni del database. Conoscere questi argomenti permette di essere meglio attrezzati per sfruttare appieno il Progress RDBMS.

Il concetto di transazione è fondamentale per il funzionamento dei sistemi di basi di dati.
Le transazioni sono un meccanismo di gestione degli errori e un meccanismo di strutturazione dei programmi.
Come meccanismo di gestione dell’errore, permettono di fare una quantità arbitraria di lavoro in una unità di tempo e poi cambiare idea. Quando si lavora all'interno di una transazione si può dire al sistema di ripristinare la situazione al momento iniziale; oppure si può dire di rendere ufficiali le modifiche.
Quando una transazione è in esecuzione si dice essere in stato active. Quando una transazione è completata diciamo che è committed. Se si verifica un errore prima che la transazione sia completa, il sistema annulla automaticamente qualsiasi lavoro che non può essere terminato a causa del fallimento. Il processo di annullare gli effetti di una operazione incompleta si chiama rollback.
Come il meccanismo di strutturazione di un programma, le transazioni hanno confini ben definiti dove incapsulare le operazioni su database eseguite da un’applicazione.
L’incapsulamento in una transazione deve essere accompagnato dall'incapsulamento nell'applicazione.
Le transazioni hanno quattro proprietà di base (proprietà ACID): atomicità,
consistenza, isolamento e durabilità. Queste proprietà sono strettamente correlate tra loro
e sono descritte di seguito.

Atomicità
La proprietà atomicità dice che le transazioni sono “tutto o niente”.
Le transazioni spesso apportano diverse modifiche ad un database. Questi cambiamenti correlati sono una unità logica di lavoro che deve essere eseguita insieme. Ad esempio, per trasferire denaro da un conto bancario ad un altro, i fondi devono essere dedotti dal primo conto e aggiunti al secondo. Queste operazioni devono essere eseguite come un'unità. 

Consistenza
La proprietà consistenza dice che le operazioni trasformano il database da un
stato coerente ad un altro.
La nozione di coerenza si riferisce sia alla coerenza del database fisico (cioè che le strutture interne del database, come gli indici, i records, ecc, sono validi e coerenti) che alla coerenza del database logico (cioè che i dati contenuti nel database sono validi dal punto di vista di un'applicazione).
La consistenza fisica è gestita interamente dal database manger. Si tratta di un prerequisito per la consistenza logica.
La consistenza logica, d'altra parte, è gestita sia dal database manager,
attraverso meccanismi quali regole di convalida, trigger, vincoli di integrità, ecc, che dalle azioni dell'applicazione.
Affinché la trasformazione da uno stato ad un altro sia valida , il database manager e l’applicazione devono eseguire le azioni opportune per garantire la consistenza.
Bisogna ricordare che durante l’esecuzione di una transazione, lo stato del database può essere incoerente. La coerenza deve esistere solo quando una transazione si conclude.

Isolamento
La proprietà di isolamento dà, ad ogni transazione, l'illusione che essa sia l'unica in esecuzione.
Gli effetti delle diverse transazioni, contemporaneamente in esecuzione, non devono essere visibili a ciascuna altra. Tutte le modifiche apportate da una transazione sono considerate provvisorie, mentre è ancora in esecuzione. Diventano "ufficiali" o permanenti solo quando la transazione si conclude con successo (commit  rollback).
Pertanto è importante che una transazione non prenda decisioni basate sulle modifiche apportate da un’altra. 
Il database manager provvede al concetto di isolamento attraverso l'uso di un meccanismo di two-phase locking. Questo argomento esula dallo scopo di questa guida.

Durabilità
La proprietà durabilità dice una volta committato, sempre committato.

Quando una transazione è committata, i suoi effetti sulla base di dati sono permanenti. Non possono essere annullati anche se si verifica un guasto. Gli effetti di una transazione committata possono essere invertiti eseguendo una seconda transazione che annulla gli effetti della prima.
mercoledì 6 maggio 2015

Sviluppare con i Dataset


Prodataset è una della migliori implementazioni realizzate da Progress, da quando l'appserver è stato introdotto nel suo framework di sviluppo.
Il vantaggio principale consiste nella possibilità di incapsulare dati, relazionali o no, e di condividerli attraverso procedure.

Tips & Tricks.

1) Definire le temp-table all'interno di include evitando di usare la specifica LIKE
LIKE permette di ereditare le eventuali modifiche al database; tuttavia ciò impedisce di usare il PDS con un altro database. Creare un PDS indipendente dal database permette di essere usato in modo trasversale da diversi client. Non tutte le modifiche al database, per motivi di business, devono o possono essere inserite in un PDS. Infine, definire un'include per ogni TT, permette il riuso delle TT stesse indipendentemente dal PDS.

2) Limitare il passaggio dati attraverso appserver ed uso di NO-UNDO
Una buona soluzione è quella di usare un secondo PDS su cui copiare le righe modificate, usando il metodo GET-CHANGES, da passare attraverso appserver. L'uso di NO-UNDO permette, in caso di errore, di evitare la roll-back delle TT.

3) Usare PDS permette di ridurre il numero di parametri
Il vantaggio di usare PDS permette di limitare il numero di parametri passati tra procedure. Permette inoltre di aggiungerne o eliminarne in modo semplice, senza creare mismatch di parametri. Per esempio possiamo creare un PDS realizzato su una TT contenente i diversi parametri da passare ed una TT contenente i messaggi applicativi e di sistema.

4) Passare un PDS
Passare, come parametro, un dataset handle permette di ottenere dei vantaggi. Consente di implementare codice dinamico, permetter di passare diversi dataset attraverso un handle.

5) Passare PDS e TT by reference/bind ogni volta possibile
Questo permette di condividere PDS e TT attraverso procedure avendo un'unica istanza all'oggetto. Ciò permette un risparmio di risorse nel passaggio parametri.

6) Considerare attentamente quale modalità adottare con il metodo FILL()
Il metodo FILL() permette di "riempire" i PDS con i dati definiti attraverso le TT, i data-source, le query e le relations definite. Il valore di default è MERGE (un nuovo record viene aggiunto se non esiste sulla base del TT unique primary index). Conoscere il funzionamento del metodo FILL è
essenziale nella progettazione di procedure basate su PDS. Altri possibili valori sono APPEND, EMPTY, NO-FILL, REPLACE.

7) DATA-SOURCE
Definire un data-source e lasciare che il metodo FILL risolva le query.

8) Aggiungere un'include, standard, ad ogni TT includendo il database rowid
Il contenuto dell'include può essere, per esempio, il seguente
/* idbrowid.i */
FIELD dbRowid AS CHARACTER
FIELD lastTime AS INTEGER
INDEX dbRowid dbRowid.

Il campo dbRowid contiente il rowid del data-source record. Il rowid è popolato allo scatenarsi di ogni metodo fill and save. E' usato per il repositioning all'interno della query sul db in occorrenza di un refresh.
Il campo lastTime indica il momento in cui i dati sono ritornati dalla query e può essere associato per eventuali trigger di refresh.
L'indice dbRowid  permette di accedere, se noto, più velocemente ai dati.

9) Usare SELF per identificare il TT buffer in una row callback procedure
Se vengono implementate procedure callback dinamiche, è possibile identificare il TT buffer attraverso l'uso di SELF:HANDLE.

PROCEDURE TTAfterRowFill:
DEFINE INPUT PARAMETER DATASET-HANDLE phDataset.

DEFINE VARIABLE hTTbuffer AS HANDLE NO-UNDO.

hTTbuffer = SELF.

10) Ricordarsi set on e set off TRACKING-CHANGES anche lato server
Un problema in cui si può incappare comunemente è dimenticarsi di settare correttamente l'attributo in questione quando si sta usando una AFTER-IMAGE TT. In questa condizione ricordarsi di settare l'attributo al valore TRUE prima di operare le modifiche alla TT e di settarlo uguale a FALSE dopo.

11) Svuotare PDS quando viene popolato all'interno di procedure persistenti
Il concetto si spiega da solo. Essendo l'oggetto istanziato in memoria è valido fintanto che non viene deallocato. Spetta allo sviluppatore eseguire una EMPTY del PDS.

12) Assicurarsi che dopo l'esecuzione del metodo SAVE-ROW-CHANGES() i dati siano corretti
Il metodo in questione assicura il refreshing del buffer con i valori correnti provenienti dal data-source. Il PDS può contenere, per esempio, dei calculated fields o, come visto precedentemente campi (dbRowid) popolati a livello di FILL time. Affinchè il PDS sia coerente si può
customizzare l'evento AFTER-ROW-FILL.

Questa breve guida è da intendersi come un primo passo verso lo sviluppo con i datasets. Non vuole sostituirsi ai manuali forniti da Progress Software, che devono essere usati per lo studio e la comprensione non solo dei  PDS. Lo scopo è fornire un'esperienza di uso e di regole che adotto quando mi trovo a programmare con i PDS.
mercoledì 18 marzo 2015

Concetti OO per sviluppatori 4GL. Esempi istanza classe

L'esempio di seguito mostra come istanziare una classe ed usufruire dei suo metodi/attributi. L'esempio si propone di creare una connessione ad un appserver progress.

- Scaricare il file ASCLASS.zip e scompattarlo in un punto del propath accessibile dall'appserver.
- Aprire in procedure editor e copiare le righe seguenti
- Eseguire il codice editato

DEFINE VARIABLE clsASsysappserver          AS cls.ASsysappserver.
DEFINE VARIABLE clsASConnectionString  AS cls.ASConnectionString.
DEFINE VARIABLE clsASConnect                 AS cls.ASConnect.
DEFINE VARIABLE clsASSysConnect           AS cls.ASSysConnect.
DEFINE VARIABLE hAppServer                    AS HANDLE.
DEFINE VARIABLE lSuccess                         AS LOGICAL.


clsASConnect = NEW cls.ASConnect(). /* Crea una nuova istanza alla classe ASConnect */
clsASConnectionString = NEW cls.ASConnectionString(). /* Crea una nuova istanza alla classe clsASConnectionString */
clsASConnectionString:SetConnectionParameter("Nome Appserver" , /* Metodo della classe. Setta la stringa di connessione all'appserver */
                                             "Ip/Nome host dove l'appserver è in running",
                                             "Service/port del Name Server ").
 
clsASConnect:SetASConnectionString(clsASConnectionString).
 
hAppServer = clsASConnect:AppServerConnect (OUTPUT lSuccess ).
IF lSuccess = TRUE THEN DO:
    RUN vostro_programma.p ON hAppServer .
    clsASConnect:AppServerDisconnect().
END.
 
DELETE OBJECT clsASConnectionString.
DELETE OBJECT clsASConnect.

martedì 27 gennaio 2015

Concetti OO per sviluppatori 4GL

Cos'è una CLASSE ?

Una classe è l'elemento base della programmazione orientata agli oggetti e rappresenta l'elemento madre di un programma. E' un insieme di metodi e proprietà raggruppati in un singolo file di definizione allo scopo di realizzare un compito.

/* presentation/classes/Class.cls */
CLASS presentation.classes.Class:
/* data definitions */
/* method definitions */
/* constructor, destructor*/
END CLASS.

Cos'è un OGGETTO ?

Un oggetto è l'attualizzazione di una classe, contiene i suoi dati di default  ed ogni azione azione che può essere eseguita dai suoi metodi.

classe = definisce come fare qualche cosa
oggetto = un'istanza della definizione
In altra parole:
• Una classe è per la programmazione ad oggetti ciò che una persistent o super procedure è per le procedure.
• Un oggetto è per la programmazione ad oggetti ciò che una procedure instance è per una persistent e super procedure.

Costruzione di un oggetto
/* presentation/examples/Class.p */
DEFINE VARIABLE oClass AS presentation.classes.Class NO-UNDO.
oClass = NEW presentation.classes.Class().

METODI
Cos'è un metodo?
Per tenere un parallelismo con la programmazione di codice 4GL, un metodo può essere paragonato ad una funzione. E' racchiuso in una classe.

/* presentation/classes/MethodClass.cls */
CLASS presentation.classes.MethodClass:
    METHOD CHARACTER MethodName():
        RETURN("character string").
    END METHOD.
END CLASS.

/* presentation/classes/MethodClass.cls */
CLASS presentation.classes.MethodClass:
    DEFINE VARIABLE ch-MethodVar AS CHARACTER NO-UNDO.
    METHOD VOID SetVariable(ip-methodvar AS CHARACTER):
        ch-MethodVar = ip-methodvar.
    END METHOD.
    METHOD CHARACTER GetVariable():
        RETURN(ch-MethodVar).
    END METHOD.
END CLASS.

/* presentation/examples/Method.p */
DEFINE VARIABLE oMethodClass AS presentation.classes.MethodClass NO-UNDO.
oMethodClass = NEW presentation.classes.MethodClass().
oMethodClass:SetVariable("class value").
MESSAGE oMethodClass:GetVariable() VIEW-AS ALERT-BOX.

PROPRIETA'
Cos'è una proprietà?
Una proprietà, sempre mantenendo un parallelo con la programmazione 4GL, è una variabile racchiusa in una classe che può:

• essere direttamente accessibile da oggetti e programmi
• contenere una logica.

/* presentation/classes/property/Property.cls */
CLASS presentation.classes.property.PropertyClass:
    DEFINE PUBLIC PROPERTY character-property AS CHARACTER NO-UNDO
    GET.
    SET.
END CLASS.

/* presentation/classes/property/PropertySetterClass.cls */
CLASS presentation.classes.property.PropertySetterClass:
    DEFINE PROPERTY ExampleProperty AS CHARACTER NO-UNDO
    GET:
        RETURN(ExampleProperty).
    END GET.
    SET(ip-char AS CHARACTER):
        ASSIGN ExampleProperty = ip-char.
    END SET.
END CLASS.

/* presentation/classes/Property.p */
DEFINE VARIABLE oProperty AS presentation.classes. property.PropertyClass NO-UNDO.
DEFINE VARIABLE oPropertySetter AS presentation.classes. property.PropertySetterClass NO-UNDO.
oProperty = NEW presentation.classes.property.PropertyClass().
oPropertySetter = NEW presentation.classes. property.PropertySetterClass().
oProperty:ExampleProperty = "class property".
oPropertySetter:ExampleProperty = "class property setter".
MESSAGE oProperty:ExampleProperty VIEW-AS ALERT-BOX.
MESSAGE oPropertySetter:ExampleProperty VIEW-AS ALERT-BOX.

/* presentation/examples/Using.p */
DEFINE VARIABLE oMethodClass AS presentation.classes.MethodClass NO-UNDO.
oMethodClass = NEW presentation.classes.MethodClass().

Oppure:

USING presentation.classes.*.
DEFINE VARIABLE oMethodClass AS MethodClass NO-UNDO.
oMethodClass = NEW MethodClass().
oMethodClass:SetCharacter("class value").
MESSAGE oMethodClass:GetCharacter() VIEW-AS ALERT-BOX.

venerdì 28 marzo 2014

Concetti OO per sviluppatori 4GL - ereditarietà

L’ereditarietà costituisce il secondo principio fondamentale della programmazione ad oggetti. E' il modo usato per raggruppare metodi e proprietà di un oggetto ed estenderne il codice. Ciò viene fatto creando collezioni di attributi e comportamenti in una classe, istanziando la medesima in una nuova classe (sottoclasse), questa ne eredita le funzionalità e ne può generare di nuove.
Questo concetto permette ad una sottoclasse di essere usata ovunque una superclasse è usata.


/* Animal.cls */
CLASS Animal:
DEFINE VARIABLE cSpecies-name AS CHARACTER NO-UNDO.
    METHOD VOID SetSpecies(iSpecies-name AS CHARACTER):
        ASSIGN cSpecies-name = iSpecies-name.
    END METHOD.
    METHOD CHARACTER GetSpecies():
        RETURN(cSpecies-name).
    END METHOD.
    METHOD CHARACTER GetInformation():
        RETURN(cSpecies-name).
    END METHOD.
    CONSTRUCTOR Animal():
    END CONSTRUCTOR.
    CONSTRUCTOR Animal(iSpecies AS CHARACTER):
        SetSpecies(iSpecies).

    END CONSTRUCTOR.
END CLASS.

/* Bird.cls */
CLASS Bird
             INHERITS Animal:
DEFINE VARIABLE iSpan AS INTEGER NO-UNDO.
    METHOD VOID SetWingSpan(ipSpan AS INTEGER):
        ASSIGN iSpan = ipSpan.
    END METHOD.
    METHOD INTEGER GetWingSpan():
       RETURN(iSpan).
    END METHOD.
    CONSTRUCTOR Bird(iSpecies AS CHARACTER, ipSpan AS INTEGER):
        SetSpecies(iSpecies).
        SetWingSpan(iSpan).
    END CONSTRUCTOR.
END CLASS.

/*Dog.cls */
CLASS Dog
             INHERITS Animal:
DEFINE VARIABLE cBreed-name AS CHARACTER NO-UNDO.
    METHOD VOID SetBreed(ip-breed-name AS CHARACTER):
        ASSIGN cBreed-name = ip-breed-name.
    END.
    METHOD CHARACTER GetBreedName():
        RETURN(cBreed-name).
    END METHOD.
    CONSTRUCTOR Dog(iSpecies AS CHARACTER, ip-breed AS CHARACTER):
        SetSpecies(iSpecies).
        SetBreed(ip-breed).
    END CONSTRUCTOR.
END CLASS.

/* Inheritance.p */
USING inheritance.*.
DEFINE VARIABLE oBird AS Bird NO-UNDO.
DEFINE VARIABLE oDog AS Dog NO-UNDO.
    oBird = NEW Bird("Cardellino", 5).
    oDog = NEW Dog("Cani da pastore", "Pastore tedesco").
    MESSAGE oBird:GetSpecies() SKIP
        oBird:GetWingSpan()
    VIEW-AS ALERT-BOX.
    MESSAGE oDog:GetSpecies() SKIP
        oDog:GetBreedName()

    VIEW-AS ALERT-BOX.

principio di sostituzione di Liskov (Definizione tratta da WikipediA)

In altre parole, se una sottoclasse può sempre essere sostituita con la sua superclasse, allora
 il disegno è valido

• la sottoclasse può essere usata ovunque la sua superclasse può.


/* InheritanceAndSubstitution.p */
USING inheritance.*.
DEFINE VARIABLE oBird AS Bird NO-UNDO.
DEFINE VARIABLE oDog AS Dog NO-UNDO.
oBird = NEW Bird("Cardellino", 5).
oDog = NEW Dog("Cani da pastore", "Pastore tedesco").

/* Note passing subclass as a superclass parameter */
RUN show-species(oBird).
RUN show-species(oDog).

PROCEDURE show-species:
DEFINE INPUT PARAMETER oAnimal AS Animal NO-UNDO.
    MESSAGE oAnimal:GetSpecies() SKIP
    VIEW-AS ALERT-BOX.
END PROCEDURE.



lunedì 24 marzo 2014

Concetti OO per sviluppatori 4GL - polimorfismo


POLIMORFISMO
"- nel contesto della programmazione orientata agli oggetti, si riferisce al fatto che una espressione il cui tipo sia descritto da una classe A può assumere valori di un qualunque tipo descritto da una classe B sottoclasse di A (polimorfismo per inclusione).
- nel contesto della programmazione generica, si riferisce al fatto che il codice del programma può ricevere un tipo come parametro invece che conoscerlo a priori (polimorfismo parametrico)."

.... tratto da wikipedia.

In altre parole, lo stesso metodo può essere chiamato utilizzando parametri di diverso tipo.

ProductionOrder:CreateUsing(sales-order-number).
ProductionOrder:CreateUsing(oSalesOrder).

/* Polymorphism.cls */
CLASS PolymorphismClass:

DEFINE VARIABLE chVar AS CHARACTER NO-UNDO.

    METHOD VOID SetVariable(ip-numeric-value AS INTEGER):
        SetVariable(STRING(ip-numeric-value)).
    END METHOD.

    METHOD VOID SetVariable(ipValue AS CHARACTER):
        chVar = ipValue.
    END METHOD.

    METHOD CHARACTER GetVariable():
        RETURN(chVar).
    END METHOD.

END CLASS.

/* Polymorphism.p */
USING PolymorphismClass.*.

DEFINE VARIABLE oPolyClass1 AS PolymorphismClass NO-UNDO.
DEFINE VARIABLE oPolyClass2 AS PolymorphismClass NO-UNDO.

oPolyClass1 = NEW PolymorphismClass().
oPolyClass2 = NEW PolymorphismClass().

oPolyClass1:SetVariable("string").
oPolyClass2:SetVariable(1234).

MESSAGE oPolyClass1:GetVariable() SKIP
    oPolyClass2:GetVariable()
VIEW-AS ALERT-BOX.

Attenzione: il polimorfismo si applica solamente ai parametri e non al return type.

Corretto – due metodi nella stessa classe
• stesso method name,
• stesso data return type,
• differenti parameter signatures.
METHOD PUBLIC VOID SetVariable(ch-var as CHARACTER):
METHOD PUBLIC VOID SetVariable(de-var as DECIMAL):

Errato – due metodi nella stessa classe
• stesso method name,
• stessa parameter signature,
• differenti data return types.
METHOD PUBLIC CHARACTER GetVariable():
METHOD PUBLIC DECIMAL GetVariable():
La compilazione ritorna un errore.
mercoledì 3 luglio 2013

Comprendere OpenEdge AppServer. AppServer Status


Per  verificare in quale stato si trova un AS possiamo usare uno dei seguenti tool messi a disposizione da OE.Progress explorer tool, openedge explorer tool, linea di comando.

I possibili valori di stato in cui può trovarsi un AS sono:

STARTING (tutti gli AS operating modes).
Il broker ha "lanciato" il processo agent ed è in attesa di risposta dell'avenuto start. L'agent esegue le
sue startup procedure e/o le eventuali connessioni ai database e "risponde" al broker.

AVAILABLE(tutti gli AS operating modes).
Questo stato indica che il processo agent è disponibile e pronto a ricevere le richieste di connessione.

CONNECTING(State-aware e State-reset).
Il broker ha accettato un richiesta di connessione dal client (passando al client stesso il numero di porta
dell'agent disponibile ) e aspetta dall'agent un messaggio che il client è connesso.

CONNECTED(State-aware e State-reset).
E' lo stato in cui si trova un agent immediatamente dopo l'avvenuta connessione al client.

SENDING(Stateless e State-free).
Indica lo stato dell'agent mentre ritorna i parametri di output al client.

RECEIVING(Stateless e State-free).
Indica lo stato dell'agent mentre riceve i parametri di input dal client.

BUSY(solo per WebSpeed)
Indica lo stato dell'agent in fase di esecuzione la richiesta.

LOCKED(Stateless e State-free)
Indica che l'agent è in stato locked, appunto, per una richiesta di connessione da parte del client.
E' spesso riscontrabile quando l'agent viene legato (bound) al client connesso, settando l'attributo
SESSION:SERVER-CONNECTION-BOUND = TRUE (client), il quale setta, a sua volta, lo stesso attributo sull'agent.
Lo stato locked si verifica anche quando il client crea una procedura remota persistente sull'agent.

Esempio di stato available

martedì 2 luglio 2013

Web Service - deployare ed eseguire una ABL procedure

In questo post esaminiamo come effettuare il deploy di una ABL procedure come web service.
Al nostro scopo editiamo e copiamo il codice seguente e salviamolo con il nome test.p.

DEFINE INPUT  PARAMETER iNum AS INTEGER    NO-UNDO.
DEFINE OUTPUT PARAMETER cReturn AS CHARACTER NO-UNDO.

IF iNum = 1 THEN DO:
    cReturn = "Test OK!".
    RETURN "OK!".
END.
ELSE DO:
    cReturn = "Test Fallito".
    RETURN "Fallito!".
END.

Come possiamo ora invocare la procedura attraverso un servizio web?
Prima di tutto abbiamo bisogno di renderla accessibile attraverso un AppServer. L'AppServer è disegnato in modo da ricevere richieste da clients OpenEdge, occorre quindi adattarlo a ricevere chiamate attrraverso web services inserendo nel mezzo un web server. Abbiamo trattato l'argomento nel post OpenEdge AppServer Web Service & Tomcat (on windows).
Un web server permette di eseguire un Web Service Adapter (WSA), programma fornito con OpenEdge che esegue una Java Servlet.
A questo punto, per creare lo "strato" necessario al deploy della nostra BL, dobbiamo usare un programma fornito da OpenEdge che permetta di generare un kit di deploy.
Lanciare il programma Proxy Generator (proxyGen). ProxyGen genera un Web Service Application Descriptor (WSAD) ed un Web service Descriptor Language (WSDL) da usare in fase di deploy e testing.
A runtime una richiesta di web service client viene "codificata" in forma di richiesta SOAP; la richiesta viene instradata, attraverso l'accoppiata web server - WSA, all'AppServer che a sua volta invoca il servizio richiesto (BL procedure) e ritorna l'output come un altro messaggio SOAP.

Figura 1

Andiamo ora a definire un'applicazione (AppObject). Selezionare File->New

Figura 2

Abbiamo dato il nome al nostro servizio ed impostato la propath accessibile dall'appserver su cui è distribuito il programma test.r. Ora selezioniamo il folder Procedures.

Figura 3

Nel nostro esempio vogliamo deployare una procedura non persistente. Premendo il tasto dx del mouse, nel riquadro relativo le Non-persistent Procedures, selezioniamo la voce Add -> Non-persistent ... poi il programma compilato test.r in modo da ottenere ciò che è mostrato in figura 3.
Procediamo selezionando la procedura e premendo il tasto dx del mouse; dal menu a tendina scegliamo la voce Customize ...
Figura 4
Selezionare il toggle-box Return ABL RETURN-VALUE. Ciò è necessario in quanto non è implicito come in un normale ritorno tra ABL procedures. Così facendo viene generato un parametro di output chiamato result.
Ora non ci resta che generare il proxy per questo nuovo AppObject.
Premere l'icona


Inserire le informazione come mostrato in figura 5
Figura 5
AppService : è l'appserver attraverso cui è raggiungibile la BL test.r.
Output Dir:    è la directory su cui sarà salvato il package da distribuire
Session Model: nel nostro caso scegliamo Free in modo da non mantenere la connessione in binding. Ovviamente deve essere anche il session model dell'appserver.

Ora selezioniamo il Folder Web Service
Figura 6
Inseriamo i valori riportati in figura 6.
URL for WSA : se avete configurato la vostra macchina come indicato nel post OpenEdge AppServer Web Service & Tomcat (on windows).
WSDL Style: Doc/Literal è lo stile che usa una formattazione xml standard per rappresentare i parametri come un singolo elemento xml. Scegliendo Doc/Literal ProxyGen effettua la formattazione necessaria per voi ed il web service consumer sarà informato che questo è lo style che deve aspettarsi.

Premendo OK parte la procedura di generazione del proxy.
A questo punto abbiamo tutto il necessario per deployare la nostra prima BL.
Dando uno sguardo alla directory C:\Temp\MyProxyTest vedrete il risultato dell'operazione appena effettuata (proxygen). I file che ci interessano sono test.wsm (web service mapping)  e test.wsdl.
Per deployare la nostra applicazione usiamo uno dei tool forniti da OpenEdge (Progress Explorer Tool o OpenEdge Explorer).
Per velocità nel nostro caso usiamo Progress Explorer Tool. Nel nostro esempio la macchina su cui andremo a deployare è localhost.
Navighiamo l'albero del Tool raggiungendo il nodo Web Services Adapter ed espandiamolo fino a raggiungere il sotto-nodo wsa1 -> WebServices. Selezioniamo quest'ultimo e premendo il tasto destro del mouse scegliamo la voce Deploy A New Web Service ...
Figura 7
Premendo browse scegliere il file C:\Temp\MyProxyTest\test.wsm e premere Continue. Alla maschera successiva premere OK.
Complimenti, il deploy è andato a buon fine. Potete verificare nella directory del vostro web server (es. C:\<dir install>\webapps\wsa\wsa1) la presenza di 3 nuovi file; test.wsad, test.props, test.wsdl.
Non resta che abilitare il servizio; da Progress Explorer Tool selezionare test, tasto destro, Enable.




mercoledì 8 maggio 2013

OpenEdge Architect - Roundtable TSMS plug-in (2)

Per coloro che utilizzano Roundtable come SCM e sviluppano codice attraverso il plug-in fornito per OpenEdge Architect, segnalo alcune domande che mi sono state poste di recente.

Il principio di funzionamento del plug-in è diverso dal client nativo. In particolare cambia la modalità di organizzazione e presentazione delle informazioni.

Q. E' possibile creare un task report?
A. Si. Posizionandosi sul task desiderato (view RTB Task) potete ottenere le informazioni dalle views Properties RTB Versions.

Q. Come fare l’assign di un oggetto alla versione precedente?
A. Selezionare l'oggetto interessato e posizionarsi, nella vista RTB Versions, sulla versione che si desidera assegnare. Premere tasto dx del mouse e selezionare Assign...

Q. Come si crea un nuovo oggetto?
A. Posizionarsi sul task desiderato (view RTB Task), premere taso dx del mouse, scegliere dal menù contestuale la voce New e poi Object... 

Q. Creazione degli oggetti tramite Appbuilder, con salvataggio/import sotto il task selezionato.
A. Posizionarsi sul task desiderato (vista RTB Tasks), selezionare la cartella in cui si trova l'oggetto da importare. Aprire la vista RTB Imports e premere il bottone Import...