I programmi di servizio ILE

Una presentazione dei programmi di servizio dell’ILE

Facciamo conoscenza con le caratteristiche dei programmi di servizio, compresi gli export, il linguaggio binder, le firme e gli indirizzari di bind

 


I programmi di servizio ILE (Integrated Language Environment) sono la versione AS/400 delle librerie di subroutine. I programmi di servizio consentono di raggruppare le subroutine più comunemente usate facilitandone la gestione e stimolando il riutilizzo di codice RPG IV, o di altro codice ILE, già esistente. Si potrebbe, per esempio, creare un programma di servizio per gestire tutte le routine di calcolo degli interessi per un’applicazione finanziaria. Anche i programmi OPM (Original Program Model) possono utilizzare programmi di servizio chiamando un programma ILE che a sua volta richiama un programma di servizio.

I programmi di servizio offrono inoltre un vantaggio importante per quanto riguarda le prestazioni. L’overhead di chiamata associato ai programmi di servizio è molto inferiore a quello imposto dalle chiamate dinamiche e più o meno lo stesso di quello con bind per copia (per maggiori dettagli sulle prestazioni relative ai vari tipi di chiamata, si vedano gli articoli " Le prestazioni delle chiamate ILE" e "Tutti i retroscena sulle chiamate RPG IV").

 

La creazione di un programma di servizio

I programmi di servizio consistono di uno o più moduli in bind ("legati") in un programma. Il codice eseguibile all’interno di un modulo consiste di una o più procedure. Per creare un programma di servizio innanzitutto bisogna creare i singoli moduli che si vuole che il programma includa usando i comandi CrtxxxMod (Create xxx Module = Creazione modulo xxx) o l’opzione 15 nel PDM. Una volta creati tutti i moduli, si deve usare il comando CrtSrvPgm (Create Service Program = Creazione programma di servizio) per creare il programma di servizio. Per esempio, il comando:

 

CrtSrvPgm SrvPgm( MyLib/MySrvPgm ) +
Module( Mod1 Mod2 Mod3 ) +
Export( *All )

 

crea il programma di servizio MySrvPgm, che consiste dei moduli Mod1, Mod2 e Mod3, nella libreria MyLib. Il parametro Export (*All) specifica che tutti i moduli pubblici e le voci di dati interni al programma di servizio sono anche a disposizione dell’utente del programma di servizio. Vedremo più avanti altri particolari su questo aspetto dei programmi di servizio.

Supponiamo d’avere un modulo di programma di servizio chiamato ClcDueDate e che all’interno di ClcDueDate sia stata codificata un’operazione CallB ad un altro modulo, GetArInfo. A questo punto si hanno due possibili scelte: o si include il modulo GetArInfo nel programma di servizio, oppure si utilizza il parametro BndSrvPgm del comando CrtSrvPgm per specificare il nome di un altro programma di servizio comprendente GetArInfo. Si deve infatti tener sempre presente che tutti i riferimenti alle procedure esterne e ai dati importati devono essere risolti quando viene creato il programma di servizio (per maggiori informazioni su come si possono creare programmi di servizio, si veda "Come creare programmi di servizio ILE" NEWS3X/400, settembre 1996).

 

L’utilizzo di un programma di servizio

Chiamare delle procedure di un programma di servizio non è diverso dal chiamare quelle presenti nei normali programmi ILE. Per esempio, supponiamo di avere un programma di servizio RPG ILE chiamato ClcInt (per calcolare gli interessi) che contenga un modulo chiamato ClcSmpInt (che calcola gli interessi semplici). Si potrebbe chiamare questo modulo come viene mostrato in figura 1.

Se si ha un sorgente di programma che fa riferimento ad una procedura del programma di servizio ClcInt, bisogna prima compilare il sorgente usando il comando CrtxxxMod (o l’opzione 15 del PDM). Poi, per far in modo che il sistema possa trovare il programma di servizio che contiene la procedura, si immette un comando CrtPgm (Create Program = Creazione programma) come segue per "legare" al programma un riferimento al programma di servizio:

 

CrtPgm Pgm( LibName/MyPgm ) +
BndSrvPgm( LibName/ClcInt )

 

L’Export

Un programma di servizio rende disponibile ad un programma ILE chiamante una certa serie di procedure e di dati detti collettivamente gli "export" del programma. Quando si crea un programma di servizio, si ha un controllo completo su quello che esso esporta o non esporta. Per facilitare la comprensione del concetto di export, facciamo un esempio.

Quando ero sistemista presso l’IBM, la società svolgeva un’indagine di opinione annuale per ottenere un feedback dai suoi dipendenti a proposito di diversi aspetti del loro lavoro. Una tipica domanda di questa indagine era: "Vi è stata data ogni occasione per raggiungere i vostri obiettivi a breve termine nel vostro lavoro" e noi dovevamo indicare se eravamo d’accordo, totalmente d’accordo, non molto d’accordo o completamente in disaccordo con tale affermazione. L’IBM manteneva strettamente segrete le risposte dell’indagine, ma talvolta annunciava i risultati globali.

Ora, immaginiamo che l’IBM metta i risultati delle risposte in un file fisico AS/400 chiamato Survey (indagine), che io sia stato incaricato di realizzare un sicuro mezzo di accesso alle informazioni e che voglia impedire alla maggior parte dei programmatori di accedere alle singole risposte ma consentire loro di testare i propri programmi di query e di report a fronte di dati veri, per assicurarsi che il codice operi correttamente.

I programmi di servizio forniscono un modo per raggiungere questi obiettivi. Potrei scrivere un programma di servizio (chiamiamolo programma OpSrvyPgm) che contenga moduli che ricerchino vari tipi di informazioni dell’indagine. Penserei di rendere pubbliche le interfacce ai dati e alle procedure non riservate restituendo soltanto delle informazioni riassuntive al chiamante. Ho in mente tre moduli da includere nell’OpSrvyPgm: SmrzByMgr (summarize by manager = riassunto per manager), SmrzByOfc (summarize by office = riassunto per ufficio) e SmrzByRgn (summarize by region = riassunto per regione), ma c’è una difficoltà.

Il programma di servizio deve includere una procedura, GtSrvyInfo, che reperisce un’informazione di un’indagine individuale. Gli altri moduli in OpSrvyPgm hanno bisogno di avere accesso a GtSrvyInfo, perciò devo rendere tale modulo esportabile specificando la parola chiave Export nell’istruzione di inizio procedura:

 

P GtSrvyInfo B Export

 

Ora ho un dilemma: voglio rendere il programma di servizio OpSrvyPgm disponibile all’utilizzo dei programmi esterni, ma voglio limitare l’accesso dei programmi esterni alla procedura GtSrvyInfo. Se specifico Export(*All) quando creo il programma di servizio, ogni programma che abbia autorità di *Use sul programma di servizio può richiedere delle risposte personali all’indagine chiamando GtSrvyInfo. Come posso dare accesso a GtSrvyInfo alle procedure all’interno del programma di servizio e contemporaneamente impedire che i programmi esterni lo usino? La risposta viene dal linguaggio binder.

 

Il binder sblocca la situazione

Con il linguaggio binder, si può scegliere quali procedure e dati esportabili di un programma di servizio rendere disponibili al mondo esterno. Per usare il linguaggio binder, si specifica Export(*SrcFile) sul comando CrtSrvPgm quando si crea il programma di servizio. Questo valore è in realtà il valore di default. Si utilizzano i parametri SrcFile e SrcMbr del comando per specificare un membro e un file sorgente che conterranno il linguaggio binder. Per default, il comando CrtSrvPgm assume che il membro contenente le specifiche di binder sia posto in un file chiamato QSRVSRC nella lista delle librerie in un membro con lo stesso nome del programma di servizio. In ogni caso, si può specificare qualunque membro di un file fisico sorgente.

Il linguaggio binder è abbastanza semplice, e consiste di tre comandi:

• StrPgmExp (Start Program Export = Avvio elenco esportazione programma)

• Export (Esportazione di un simbolo pgm)

• EndPgmExp (End Program Export = Fine elenco esportazione programma)

 

Si usa il parametro Symbol (Nome simbolo esportato) del comando Export per specificare i moduli che si vogliono esportare. Vediamo come potrei utilizzare il linguaggio binder per risolvere il problema che ho descritto a proposito del programma di servizio OpSrvyPgm:

 

StrPgmExp
Export Symbol( SmrzByMgr )
Export Symbol( SmrzByOfc )
Export Symbol( SmrzByRgn )
EndPgmExp

 

Il sistema per rendere la procedura GtSrvyInfo non disponibile al programma di servizio esterno è semplicemente quello di non specificarla nell’elenco di export.

 

La firma

Quando si crea un programma di servizio, il sistema assegna una firma alla serie di export. La firma export viene memorizzata in ogni programma che usa il programma di servizio e lavora esattamente come il controllo di livello per i file. Infatti, il comando binder StrPgmExp ha un parametro chiamato LvlChk che ha come default *Yes (per superare questo controllo di livello, si specifica LvlChk(*No)). Quando viene attivato un programma che fa riferimento ad un programma di servizio, il sistema confronta la firma memorizzata nel programma con quella memorizzata nel programma di servizio. Se la firma è diversa, il sistema manda un messaggio "MCH4431 Violazione della firma del programma" e blocca l’esecuzione.

Il sistema genera una firma basata sui nomi e l’ordine degli export. Per avere maggiori informazioni sulle firme in un programma di servizio, si veda "AS/400 ILE Concepts" (SC41-4606).

Per vedere le firme in azione, portiamo ora il nostro esempio più avanti nel tempo di due anni. Ora circa 150 programmi ILE utilizzano intensamente il programma di servizio OpSrvyPgm. Un utente richiede una nuova funzione: la possibilità di riassumere le risposte dell’indagine per ubicazione. Se volessi aggiungere una nuova procedura, SmrzByLoc (summarize by location), all’OpSrvyPgm e volessi rendere la procedura disponibile ai programmi ILE esterni al programma di servizio, dovrei aggiungere la nuova procedura alla lista di export specificata nel mio membro di linguaggio binder:

 

StrPgmExp
Export Symbol( SmrzByMgr )
Export Symbol( SmrzByOfc )
Export Symbol( SmrzByRgn )
Export Symbol( SmrzByLoc )
<----
EndPgmExp

 

Tuttavia, quando si usa questo sorgente di linguaggio binder per ricostruire il programma di servizio, il sistema crea una nuova firma associata con questi export. Ogni programma che usa le tre procedure originali ora si bloccherà a causa della violazione della firma. Per consentire ai miei programmi di lavorare con la nuova firma, dovrei rifare il bind di tutti i 150 programmi (usando il comando CrtPgm). Come si può immaginare, pensare a quali programmi bisogna rifare il bind e poi realizzarlo è un lavoro fastidioso e soggetto ad errori.

Un modo di risolvere il problema è quello di assumersi la responsabilità di specificare LvlChk(*No) nel comando StrPgmExp, ma, togliendo il controllo di livello, ci si espone ad altri problemi che potrebbero essere anche più difficili da risolvere. Un’altra soluzione è di codificare in modo hard la firma, specificandola nel parametro Signature (Firma) del comando StrPgmExp, ma questo equivale a specificare LvlChk(*No).

Fortunatamente esiste una terza soluzione. Il linguaggio binder fornisce un modo di assegnare firme multiple ad un programma di servizio. Usando il parametro PgmLvl dello StrPgmExp, si è in grado di generare una seconda firma. Il seguente linguaggio binder genera due firme:

 

StrPgmExp PgmLvl( *Current )
Export Symbol( SmrzByMgr )
Export Symbol( SmrzByOfc )
Export Symbol( SmrzByRgn )
Export Symbol( SmrzByLoc )
EndPgmExp

StrPgmExp PgmLvl( *Prv )
Export Symbol( SmrzByMgr )
Export Symbol( SmrzByOfc )
Export Symbol( SmrzByRgn )
EndPgmExp

 

La prima serie di export genera una firma per l’elenco di export correnti, compresa la più recente procedura SmrzByLoc, mentre il secondo elenco di export specifica la stessa serie di export nello stesso ordine dell’originale, e così genera la stessa firma del programma di servizio originale. Ora non è più necessario rifare il bind dei programmi che sono stati creati per usare il programma di servizio originale.

Bisogna sempre specificare esattamente una coppia di StrPgmExp/EndPgmExp con PgmLvl(*Current). Si può specificare un numero qualunque di altre serie di export con PgmLvl(*Prv). Se si decidesse più tardi di aggiungere un altro export (diciamo SmrzByJcd, riassunto per codice di lavoro), si dovrebbero creare tre elenchi di export, come segue:

 

StrPgmExp PgmLvl( *Current )
Export Symbol( SmrzByMgr )
Export Symbol( SmrzByOfc )
Export Symbol( SmrzByRgn )
Export Symbol( SmrzByLoc )
Export Symbol( SmrzByJcd )
EndPgmExp

StrPgmExp PgmLvl( *Prv )
Export Symbol( SmrzByMgr )
Export Symbol( SmrzByOfc )
Export Symbol( SmrzByRgn )
Export Symbol( SmrzByLoc )
EndPgmExp

StrPgmExp PgmLvl( *Prv )
Export Symbol( SmrzByMgr )
Export Symbol( SmrzByOfc )
Export Symbol( SmrzByRgn )
EndPgmExp

 

Si noti che, nell’elenco export *Current, l’export più recente appare alla fine. Il sistema usa l’elenco *Current per costruire una schiera interna con gli export e la loro posizione all’interno del programma di servizio. Ogni programma ILE che chiami o usi delle procedure esportate o delle voci di dati di un programma di servizio ha un record con la voce di schiera associata (che si chiama il numero di voci export). Se si cambia l’ordine di questi export, si rischia di scegliere la voce esportata sbagliata al momento dell’esecuzione. Per questo motivo bisogna sempre aggiungere i nuovi export alla fine dell’elenco degli export *Current.

Desidero sottolineare che non è sempre necessario usare un linguaggio binder; esso è richiesto soltanto quando si vuole controllare cosa viene esportato da un programma di servizio. In molti casi probabilmente non è necessario - e nemmeno desiderabile - limitare l’interfaccia pubblica ai programmi di servizio e in quel caso non serve a nulla preoccuparsi del linguaggio binder. Basta specificare Export(*All) nel comando CrtSrvPgm per evitare del tutto il linguaggio binder. Come abbiamo appena visto, però, si renderebbe necessario il linguaggio binder se si decidesse in seguito di aggiungere degli export ai programmi di servizio senza rifare il bind dei programmi ILE che lo usano. Comunque sia, anche in questo caso, si può ritardare l’utilizzo del linguaggio binder fino a quando non vengano effettivamente aggiunti nuovi export.

 

È troppo bello?

Non esistono delle precise indicazioni su quanti moduli debba contenere un programma di servizio. La risposta naturalmente dipende delle dimensioni e dalla complessità dei singoli moduli. L’IBM raccomanda, come regola di massima, di limitare il numero di moduli in un programma di servizio a 10 o 20.

Per comprendere il motivo di questa raccomandazione si osservi la figura 2. Il programma di servizio SrvPgmX contiene due moduli, il Modulo1 e il Modulo2. Il Modulo1 ha una variabile statica, VarStat1. Una variabile statica è una variabile che persiste durante tutta l’attivazione del programma. Ogni variabile definita in una routine principale RPG (o un programma principale del modulo Cobol) è statica.

Ipotizziamo di avere un programma che usi solo il Modulo1. Quando questo programma viene attivato, il sistema deve allocare la memoria ed inizializzare tutte le variabili statiche. Così, nell’esempio, il sistema alloca la memoria per VarStat1, VarStat2 e VarStat3 anche se il programma chiamante usa soltanto VarStat1 dal Modulo1. Se aumentiamo notevolmente il numero di moduli di questo esempio, si comincia a capire perché è uno spreco memorizzare troppi moduli in un programma di servizio. Ciò non solo produce una lenta attivazione del programma, ma può anche influire sulle prestazioni in esecuzione perché ogni modulo aumenta il numero delle variabili statiche occupando memoria nel PAG (Process Access Group).

Ora qualcuno potrebbe pensare: "Se devo limitare i miei programmi di servizio ad un adeguato numero di moduli, cosa dovrei fare se volessi creare una serie più grande di moduli correlati?". Bella domanda. Supponiamo che si voglia creare un programma di servizio con 100 moduli e che si decida di organizzarli in sei o sette programmi di servizio. Potrebbe non esistere un modo naturale per suddividere i moduli in programmi di servizio separati. Forse la cosa migliore da fare è organizzarli in ordine alfabetico, includendo circa 15 moduli in ogni programma di servizio. Per ogni programma che utilizza uno di questi programmi di servizio correlati, bisogna citare tutti dei programmi di servizio nel parametro BndSrvPgm del comando CrtPgm specificando, per esempio, BndSrvPgm( S1 S2 S3 S4 S5 S6 S7 ). Questo è l’unico modo per assicurarsi che il sistema includa tutti i programmi di servizio di cui ha bisogno. Se poi si deciderà in un secondo tempo di aggiungere un altro programma di servizio a questo gruppo, bisogna ricordare di inserirlo nel comando CrtPgm da quel momento in poi. Questo comporta una certa mole di lavoro amministrativo ma, fortunatamente, esiste un modo più semplice.

 

Gli indirizzari di bind

Per risolvere il problema di gestione dei programmi di servizio, si può usare un indirizzario di bind, che elenca tutti i programmi di servizio ed i moduli di cui un’applicazione può aver bisogno. Quando si usano degli indirizzari di bind per elencare i nomi dei moduli e dei programmi di servizio, non serve specificare i singoli componenti al momento della creazione del programma.

Per impostare un indirizzario di bind, si usa il comando CrtBndDir (Create Binding Directory = Creazione indirizzario di bind) per creare un indirizzario di bind vuoto:

 

CrtBndDir LibName/SrvyDir

 

Poi si devono inserire nell’indirizzario di bind i nomi dei programmi di servizio correlati, usando il comando AddBndDirE (Add Binding Directory Entry = Aggiunta voce all’indirizzario di bind) come segue:

 

AddBndDirE BndDir( LibName/SrvyDir ) +
Obj( ( S1 ) ( S2 ) . . . ( S7 ) )

 

Ora non c’è più bisogno di tenere traccia dei nomi di questi programmi di servizio correlati. Invece, ogni qualvolta si voglia usare uno di essi, bisogna semplicemente specificare l’indirizzario di bind quando si crea il programma:

 

CrtPgm Pgm( LibName/MyIlePgm ) +
BndDir( LibName/SrvyDir )

 

Il sistema cercherà tutti i programmi di servizio cui si fa riferimento nell’indirizzario di bind ed includerà soltanto quelli cui viene fatto riferimento nel modulo MyIlePgm. Il problema è così risolto!

 

Si comincia!

Con i programmi di servizio, i programmatori AS/400 dispongono finalmente di un mezzo equivalente alle librerie di subroutine o alle librerie a collegamento dinamico a disposizione di altre piattaforme. I programmi di servizio sono degli strumenti potenti che aiutano ad organizzare e riutilizzare il codice ILE, nonché ad aumentare le prestazioni. Come la maggior parte dei programmi, essi vanno utilizzati con cognizione di causa dopo un adeguato tirocinio. Questo articolo dovrebbe aver fornito un buon punto di partenza per progettare e sviluppare il proprio set di programmi di servizio ILE.