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...



giovedì 14 febbraio 2013

OpenEdge AppServer Web Service & Tomcat (on windows)


In questo primo di due post, vediamo come deployare una procedura di business logic attraverso un web service. Per esporre un servizio web, OpenEdge fornisce un Web Services Adapter (WSA), un programma eseguito come una Java Servlet in un Web Server.
Come prima cosa occorre procurarsi un Java Servlet Engine (JSE). Esistono diversi Web Server / JSE Open Source; tra questi Tomcat (JSE del progetto Apache) è sicuramente uno dei più popolari.
Scaricate la vs. versione dal sito http://tomcat.apache.org/ ed installatela/unzippatela a seconda della distribuzione scelta.
Personalmente uso la versione .zip, in modo da evitare di registrala come servizio di windows.
Posizionarsi nella directory di tomcat/bin ed eseguire lo script catalina start.
Veriabili di ambiente settate








Verificare la dicitura Server startup in sec.

























Per verificare che Tomcat sia effettivamente running aprite il vs. browser e digitate l’url
http://<yourWebServer>:8080/ (yourWebServer è il server web dove avete installato Tomcat, 8080 è la porta di default usata).

Ora dovete deployare la WSA di OpenEdge (fornita dall'installazione), ed il suo default web service adapter wsa1, nella webapps directory di tomcat. Copiate la directory %DLC%/servlets/wsa all’interno di %tomcat_home%\webapps.
Restart Tomcat. catalina stop / catalina start.
Testate che il servizio sia runnig digitando da vs. browser http://<yourWebServer>:8080/wsa/wsa1

Nel prossimo post deployeremo una OpenEdge AppServer Business Logic.
giovedì 7 febbraio 2013

Micro Focus compra linee di prodotto di Progress Software

Come già annunciato nella conference dello scorso anno, in un'ottica di rilancio strategico, di cui potete leggere il contenuto ....

la seguente notizia conferma, speriamo, la tendenza ad un maggiore focus sul prodotto Openedge.
Leggi la notizia

Leggi la notizia

martedì 5 febbraio 2013

Comprendere OpenEdge AppServer. Advanced Features

OpenEdge AppServer permette di inserirsi, attraverso procedure di configurazione, in diversi momenti della sessione di un agent, ovvero di programmare delle procedure di startup/shutdown, oppure di connect/disconnect, come di activate/deactivate.



Startup/Shutdown

- Incapsulamento logica di startup/shutdown
- Connessione ai DB, load temp table, start super procedure, ecc.
- Usato per state-aware o stateless.
Startup     eseguita appena un  AppServer agent starts up.
Parametri: DEFINE INPUT PARAMETER startup-data AS CHARACTER NO-UNDO.
Shutdown: eseguita appena prima di agent shut down.
Parametri: nessuno.

Connect/Disconnect

- Incapsulamento logica di connessione.
- Usato per state-aware, state-reset o stateless.

Connect : eseguita non appena AppServer agent stabilisce una richiesta di connessione da un client.
Parametri:
DEFINE INPUT PARAMETER user-id AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER password AS CHAR NO-UNDO.
DEFINE INPUT PARAMETER srv-info AS CHAR NO-UNDO.

utilizzo di metodi/attributi di sessione usati per informazioni di logging (SERVER-CONNECTION-ID e SERVER-CONNECTION-CONTEXT).

Disconnect: eseguita ogni volta che un agent riceve una richiesta di disconnessione, ma prima che essa sia terminata.
Parametri: nessuno.

Activate/Deactivate

- Incapsulamento logica di attivazione/disattivazione agent.
- Usato per stateless appserver.

Activate :  eseguita immediatamente prima dell'esecuzione di una procedura remota.
Parametri: nessuno.


Deactivate :  eseguita immediatamente dopo il termine dell'esecuzione di una procedura remota.
Parametri: nessuno.
venerdì 25 gennaio 2013

Video OpenEdge 11 Tour

Bel video descrittivo dell'ambiente OpenEdge v.11.

Clicca qui

giovedì 27 dicembre 2012

Memory Leak - Debug

Di seguito trovate un breve esempio di debug memoria in uso dalla sessione progress.
Vengono esaminati i principali oggetti che un'applicativo può utilizzare durante il suo ciclo di vita.


DEFINE VARIABLE hProcedure AS HANDLE      NO-UNDO.
DEFINE VARIABLE idataset AS INTEGER    NO-UNDO.
DEFINE VARIABLE iDsource AS INTEGER    NO-UNDO.
DEFINE VARIABLE iBuffer AS INTEGER    NO-UNDO.
DEFINE VARIABLE iQuery AS INTEGER    NO-UNDO.
DEFINE VARIABLE iProc AS INTEGER    NO-UNDO.
DEFINE VARIABLE cFile AS CHARACTER   NO-UNDO.

DEFINE STREAM stmem.


/* ***************************  Main Block  *************************** */
DEFINE VARIABLE iBuffers AS INTEGER    NO-UNDO.
DEFINE VARIABLE hBuff AS HANDLE     NO-UNDO.

cFile = SESSION:TEMP-DIR + "memory.log".
OUTPUT STREAM stmem TO VALUE(cFile).
MAIN:
DO:
    PUT STREAM stmem UNFORMATTED
        "** Memory Debug 1.0 **" SKIP
        "--------------------------" SKIP.

    hProcedure = SESSION:FIRST-PROCEDURE. /* Procedure */
    DO WHILE VALID-HANDLE(hProcedure):
        iProc = iProc + 1.
        PUT STREAM stmem UNFORMATTED "PROCEDURE; "
            " Handle=" hProcedure:HANDLE
            "; Procedure.NAME=" hProcedure:FILE-NAME
            "; Name=" hprocedure:NAME
            "; Transaction=" hProcedure:TRANSACTION:IS-OPEN SKIP.
       
        hProcedure = hProcedure:NEXT-SIBLING.
    END.

    IF VALID-HANDLE(hProcedure) THEN
        DELETE OBJECT hProcedure.

    hProcedure = SESSION:FIRST-QUERY. /* Dynamic Query */
    DO WHILE VALID-HANDLE(hProcedure):
        iQuery = iQuery + 1.
        PUT STREAM stmem UNFORMATTED "Query ; "
            " Handle=" hProcedure:HANDLE
            "; Name=" hProcedure:NAME
            "; Dynamic=" hProcedure:DYNAMIC
            "; is-Open=" hProcedure:IS-OPEN
            "; Num Buffers=" hProcedure:NUM-BUFFERS
            "; Prepare-string=" hProcedure:PREPARE-STRING
            "; INSTANTIATING-PROCEDURE=" hProcedure:INSTANTIATING-PROCEDURE " ".
        IF hProcedure:INSTANTIATING-PROCEDURE <> ? THEN
            PUT STREAM stmem UNFORMATTED hProcedure:INSTANTIATING-PROCEDURE:NAME SKIP.
        ELSE
            PUT STREAM stmem UNFORMATTED "" SKIP.

        hProcedure = hProcedure:NEXT-SIBLING.
    END.

    IF VALID-HANDLE(hProcedure) THEN
        DELETE OBJECT hProcedure.

    hProcedure = SESSION:FIRST-BUFFER. /* Dynamic Buffers / temp-table */
    DO WHILE VALID-HANDLE(hProcedure):
        ibuffer = ibuffer + 1.
        PUT STREAM stmem UNFORMATTED "Buffer ; "
            " Handle=" hProcedure:HANDLE
            "; Name=" hProcedure:NAME
            "; Dynamic=" hProcedure:DYNAMIC
            "; Table=" hProcedure:TABLE
            "; TableHandle=" hProcedure:TABLE-HANDLE
            "; TABLE Record=" (IF VALID-HANDLE(hProcedure:TABLE-HANDLE) THEN STRING(hProcedure:TABLE-HANDLE:HAS-RECORDS) ELSE "")
            "; Dataset=" hProcedure:DATASET
            "; INSTANTIATING-PROCEDURE=" hProcedure:INSTANTIATING-PROCEDURE " ".

        IF hProcedure:INSTANTIATING-PROCEDURE <> ? THEN
            PUT STREAM stmem UNFORMATTED hProcedure:INSTANTIATING-PROCEDURE:NAME SKIP.
        ELSE
            PUT STREAM stmem UNFORMATTED "" SKIP.

        hProcedure = hProcedure:NEXT-SIBLING.
       
    END.
   
    IF VALID-HANDLE(hProcedure) THEN
        DELETE OBJECT hProcedure.
    hProcedure = SESSION:FIRST-DATASET. /* datasets */
    DO WHILE VALID-HANDLE(hProcedure):
        iDataset = iDataset + 1.
        PUT STREAM stmem UNFORMATTED "Dataset ; "
            " Handle=" hProcedure:HANDLE
            "; Name=" hProcedure:NAME
            "; Dynamic= " hProcedure:DYNAMIC
            "; N.Buffers=" hProcedure:NUM-BUFFERS
            "; INSTANTIATING-PROCEDURE=" hProcedure:INSTANTIATING-PROCEDURE.
        IF hProcedure:INSTANTIATING-PROCEDURE <> ? THEN
            PUT STREAM stmem UNFORMATTED hProcedure:INSTANTIATING-PROCEDURE:NAME .
        IF hProcedure:NUM-BUFFERS > 0 THEN DO:
            PUT STREAM stmem UNFORMATTED "; Buffers : ".
            DO iBuffers = 1 TO hProcedure:NUM-BUFFERS:
                hBuff = hProcedure:GET-BUFFER-HANDLE(iBuffers) NO-ERROR.
                IF VALID-HANDLE(hBuff) THEN
                    PUT STREAM stmem UNFORMATTED hBuff:TABLE ", ".
                ELSE
                    PUT STREAM stmem UNFORMATTED "not available" ", ".
            END.
        END.

        PUT STREAM stmem SKIP.

        hProcedure = hProcedure:NEXT-SIBLING.
       
    END.
       
    IF VALID-HANDLE(hProcedure) THEN
        DELETE OBJECT hProcedure.
    hProcedure = SESSION:FIRST-DATA-SOURCE. /* data-source */
    DO WHILE VALID-HANDLE(hProcedure):
        iDsource = idsource + 1.
        PUT STREAM stmem UNFORMATTED "Datasource ; "
            " Handle=" hProcedure:HANDLE
            "; Name=" hProcedure:NAME
            "; Type=" hProcedure:TYPE
            "; INSTANTIATING-PROCEDURE=" hProcedure:INSTANTIATING-PROCEDURE.
        IF hProcedure:INSTANTIATING-PROCEDURE <> ? THEN
            PUT STREAM stmem UNFORMATTED hProcedure:INSTANTIATING-PROCEDURE:NAME SKIP.
        ELSE
            PUT STREAM stmem UNFORMATTED "" SKIP.

        hProcedure = hProcedure:NEXT-SIBLING.
        IF VALID-HANDLE(hProcedure) THEN
            DELETE OBJECT hProcedure.
        hBuff = ?.
    END.
END.



PUT STREAM stmem UNFORMATTED SKIP(1)
    "hProcedure VALID-HANDLE = " VALID-HANDLE(hProcedure) " - " SKIP
    "DATASET = " idataset " datasource = " idsource
    " Buffers = " iBuffer " PROCEDURE = " iProc
    " QUERY = " iQuery SKIP
    "--------------------------".
OUTPUT STREAM stmem CLOSE.


giovedì 22 novembre 2012

OpenEdge Architect - Roundtable TSMS plug-in

Come sapete Roundtable fornisce un event handler procedure per la customizzazione del vostro progetto di software configuration management. Questa procedura è distribuita come source code (<rtb install dir>/rtb_events.p). Ogni volta che un evento supportato viene scatenato, i seguenti parametri vengono "pubblicati" attraverso tale procedura.

Parametro   Descrizione
p_event        Nome evento
p_context     Il contesto in cui è chiamato, come il ROWID del record
p_other        Informazioni addizionali che possono essere necessaria, come il Workspace corrente
p_ok            Settato a False permette l'undo dell'evento "trappato".

Riferimenti : Roundtable TSMS User Guide o i commenti all'inizio del programma rtb_events.p.

Con l'introduzione di OpenEdge Architect, Rountable distribuisce il proprio plug-in per eclipse.
Leggendo il documento si fa riferimento all'esistenza del programma _idertb_events.p nella directory com.openedge.pdt.text. Nel mio caso, OpenEdge 10.2B05, la directory in cui ho dovuto salvarlo è com.openedge.pdt.project_10.2.1.05.
mercoledì 17 ottobre 2012
venerdì 5 ottobre 2012

Treeview Prodataset

Esempio di navigazione dataset con activex treeview.

Scaricare lo zip file.
Estrarre il programma treeview_pds.w, compilarlo(se necessario) e lanciarlo.
Viene allegato un dataset xml di esempio (DsOrderItem.xml).

treeview_pds.zip
DsOrderItem.xml
venerdì 10 agosto 2012

get table Primary Unique index

DEFINE VARIABLE cTable AS CHARACTER  FORMAT "x(15)" NO-UNDO.

REPEAT:
UPDATE cTable.
    OUTPUT TO VALUE("c:\temp\primIdxRpt.txt":U) .
    FOR EACH _file WHERE _file._file-name = cTable NO-LOCK,
       FIRST _index WHERE RECID(_index) = _file._prime-index NO-LOCK:
        DISPLAY _file._file-name _index._index-name LABEL "Primary idx"
            _index._unique.
        FOR EACH _index-field OF _index:
         FIND _field OF _index-field NO-ERROR.
         IF AVAILABLE (_field) THEN
           PUT "  Campi chiave: " _index-seq " "_field-name SKIP.
      END.
    END.
    OUTPUT CLOSE.
END.
martedì 24 luglio 2012

Inserire un Adobe PDF reader ActiveX in una SmartWindow

- Creare una nuova SmartWindow.
- Selezionare dalla Palette OCX Control.
- Scegliere Adobe PDF Reader


A questo punto è sufficiente inserire il seguente codice (es. override procedure initializeObject)


chCtrlFrame:AcroPdf:src = ("%path%\file.pdf") .

lunedì 16 luglio 2012

Comprendere OpenEdge AppServer. Operating Modes

Come visto nel post precedente esistono 4 diversi tipi di modalità operativa che l'AppServer utilizza.

State-reset
  • Un client per application server agent
  • Lo stato della sessione è resettato alla disconnessione
Con la modalità operativa state-reset viene stabilita una relazione 1 a 1  tra client ed AppServer.Il broker riceve la richiesta di connessione dal NameServer e ne stabilisce una diretta tra client ed AppServer. La connessione è attiva fino alla disconnessione del client.

State-aware
  •  Un client per application server agent
  • Lo stato della sessione rimane attivo per le connessioni successive
La modalità state-aware funziona allo stesso modo di quella state-reset, rispetto alla relazione 1 a 1 tra client ed AppServer. La differenza sta nel come è mantenuto e gestito il contesto della connessione; quando un client si disconnette da AppServer, il contesto rimane disponibile per le successive connessioni.Questo tipo di connessione viene utilizzata per pre-caricare oggetti, super procedure, variabili ,ecc.in modo da evitare il load ogni volta che una connect viene stabilita. Ovviamente rimane a carico dello sviluppatore la gestione e la "pulizia" degli oggetti in memoria.

Stateless
  • Le connessioni  sono gestite da AppServer Broker. Molti client per AppServer agent
  • Il contesto è gestito in fase di programmazione
Questa modalità permette di utilizzare un limitato numero di processi per soddisfare più richieste di connessione provenienti da diversi client. Ciò significa che un client può usare una differente connessione ad ogni richiesta di AppServer e quest'ultimo non riconosce il client che lo ha invocato il servizio.
Il contesto, se necessario per l'applicazione, deve essere ristabilito ad ogni connessione; è possibile definire diverse procedure che permettono di gestire il contesto utilizzando gli appositi entry point forniti da AppServer configuration.

State-free
  • Connessioni di tipo logico
  • Connessioni in pool (ABL client su AppServer handle, Web service request su WSA, Open Client request su AppObject)
  • Connessione multiple da client in parallelo
  • Ogni richiesta è indipendente
  • Contesto gestito in fase di programmazione
Il modello state-free tende a scalare in modo efficiente ed è più semplice da programmare.
E' la soluzione ideale per applicazioni dove ogni chiamata è indipendente dalla precedente e dalla successiva.
Il contesto è mantenuto in maniera indipendente dall'applicazione.
Un ABL client mantiene in pool le connessioni, ogni server handle connesso all'AppServer rappresenta un pool di connessioni.
Per i Web Service client, il Web Service Adapter mantiene il pool di connessioni, pressappoco come AppServer Broker fa in modalità stateless.
lunedì 18 giugno 2012

Comprendere OpenEdge AppServer. Session Models & Operating Modes

Connection Management:

La gestione delle connessioni da un ABL client ad un AppServer agent è condivisa tra una parte client (session model) ed una parte server (AppServer operating mode).

Session Managed Model (Client)
  • Il client invia le richieste attraverso connessioni persistenti. Le richieste sono sequenziali.
  • Operating mode (AppServer): Stare-reset / State-aware / Staleless
Un singolo AppServer processa tutte le richieste di connessione da un singolo client ad un application service ed il client gestisce la connessione fisica all'AppServer. Il client invia tutte le proprie richieste e riceve le risposte attraverso una connessione persistente all' Appserver che lo supporta. Tutte le richieste provenienti dallo stesso client vengono processate sequenzialmente dall'Appserver (single thread).

 Session Free Model (Client)
  • Il client invia le richieste attraverso una qualsiasi connessione disponibile. Le richieste sono in parallelo.
  • Operating mode (AppServer): State-free
Uno o più AppServer possono processare tutte le richieste di connessione provenienti da un singolo client ad un application service ed il client non gestisce alcuna connessione fisica all'AppServer. Il client invia tutte le proprie richieste e riceve le risposte da uno o più AppServer che non "tengono" traccia delle connessioni con i loro client. Le richieste sono gestite in parallelo (multi thread).


 N.B. non confondersi tra "session model" e "operating mode". Il primo è un settaggio client, mentre il secondo è un parametro di configurazione Appserver (vedi post).