Mettere sotto Journal cartelle IFS

Eseguire automaticamente un programma al caricamento di un file IFS
D.: vorrei monitorare una specifica cartella IFS, e quando compare un nuovo file, vorrei eseguire un
programma per elaborarlo. Come posso fare
?
D.: C'è un modo per far eseguire automaticamente un programma quando un nuovo file viene caricato
nell'IFS ?

R.: in questo articolo vedremo come collegare un giornale ad una directory IFS, in modo che quando viene
aggiunto un nuovo file venga richiamato un apposito programma per elaborarlo.
Il primo passo consiste nel designare una directory di destinazione per i file. In questo esempio useremo la
directory IFS chiamata /cust/upload. Naturalmente è possibile usare qualsiasi directory, a patto che sia
sotto il file system Root o QOpenSys dell'IFS.
MKDIR DIR('/cust/upload') +
DTAAUT(*RWX) +
OBJAUT(*NONE)
Supponiamo che un cliente utilizzi l'FTP per caricare dei file qui. Altri potrebbero usare SFTP o un altro
strumento SSH, oppure fare un semplice drag-and-drop di un file dal loro PC direttamente nella cartella IFS.
Indipendentemente dal metodo di trasferimento utilizzato, si vuole che la nostra routine prenda il file e lo
elabori. Come si può fare ?
Mettere sotto journal una directory IFS
Un caratteristica interessante dell'IFS è che è possibile collegare un journal ai suoi files. Questo giornale
registra gli eventi che riguardano quei file, ma in questo caso non si vuole controllare un file specifico, ma si
vuole semplicemente sapere quando viene aggiunto ad una certa directory. Oppure, in altre parole, occorre
porre sotto giornale la directory stessa, in modo da poter scoprire quando viene aggiunto qualcosa.
Per attivare il journal per una directory si può utilizzare questo CL:
PGM
DCL VAR(&LIB) TYPE(*CHAR) LEN(10)
RTVJOBA CURLIB(&LIB)
CRTJRNRCV JRNRCV(&LIB/UPLOAD)
CRTJRN JRN(&LIB/UPLOAD) JRNRCV(&LIB/UPLOAD) +
MNGRCV(*SYSTEM) DLTRCV(*YES)
STRJRN OBJ(('/cust/upload' *INCLUDE)) +
SUBTREE(*ALL) +
JRN('/QSYS.LIB/' *CAT &LIB *TCAT '.LIB/UPLOAD.JRN')
ENDPGM

Si può pensare ad una directory IFS come se fosse un file perché, in un certo senso, lo è proprio. Si tratta di
un file che tiene traccia dei nomi di file presenti nella directory. L'aggiunta di nuovo file in una directory è
molto simile all'aggiunta di un record ad un file. Allo stesso modo, la cancellazione di un file è simile alla
cancellazione di un record.
La modifica di un file esistente non ha alcun effetto sulla directory. Finché il nome del file è lo stesso, la
modifica del suo contenuto non modifica la directory.
Perciò:
• ogni volta che si crea un nuovo file, viene aggiunta una voce di giornale che registra questo evento.
• Ogni volta che si cancella un file, una voce di giornale registra la cancellazione.
• Quando viene caricato un file, si crea una voce di giornale con codice B, tipo B1. Essa identifica che è
stato creato un nuovo file e fornisce il nome del file.
• I file vengono creati prima che in essi vengano scritti dei dati, per cui quando il journal mostra il
nuovo file, si tratta in quel momento di un file vuoto. Nessuna voce di giornale appare quando
vengono scritti dati nel file, perché questo non ha effetto sulla directory.
Lettura delle voci di giornale
IBM fornisce il comando RCVJRNE (Receive Journal Entries) che riceve le voci di giornale. È quindi
possibile scrivere un programma che giri in background in attesa che nuove voci appaiano nel giornale.
Quando compare una voce nuova, viene richiamato il programma con il quale è possibile estrarre il percorso
dell'oggetto IFS e cercare di ottenerne l'uso esclusivo. Se il vincolo riesce, vuol dire che chi ha caricato
l'oggetto ha terminato l'operazione e si può quindi procedere a caricare il file nel proprio database, o eseguire
qualsiasi altra elaborazione.
Il programma che richiama il comando RCVJRNE può essere come il seguente:
PGM
DCL VAR(&SEQ) TYPE(*CHAR) LEN(10)
RTVDTAARA DTAARA(QTEMP/JRNLASTSEQ) RTNVAR(&SEQ)
MONMSG MSGID(CPF1015) EXEC(DO)
CRTDTAARA DTAARA(QTEMP/JRNLASTSEQ) +
TYPE(*CHAR) +
LEN(10) +
VALUE(*FIRST)
CHGVAR VAR(&SEQ) VALUE(*FIRST)
ENDDO
RCVJRNE JRN(UPLOAD) +
EXITPGM(JRNHANDLER) +
JRNCDE((B)) +
ENTFMT(*TYPE2) +
DELAY(*NEXTENT 600) +
FROMENTLRG(&SEQ)
ENDPGM
L'area dati viene utilizzata per tenere traccia dell'ultima voce di giornale elaborata dal programma. In questo
modo, non si elaborerà due volte lo stesso file. Il programma CL reperisce il numero di sequenza dalla data
area e richiama il comando RTVJRNE. Ogni volta che una nuova voce viene scritta nel giornale, il comando
RTVJRNE richiamerà un programma di uscita chiamato JRNHANDLER. Quest'ultimo è il programma RPG
che estrae il percorso del file IFS appena creato e resta in attesa finché non si ottiene un vincolo esclusivo su
quel file, in modo che lo si possa elaborare.
Il precedente programma CL è inteso come parte di un programma batch in esecuzione continua, che resti in
attesa tutto il giorno di nuove voci di giornale. Non appena viene creato un nuovo file IFS nella directory
prescelta, viene richiamato JRNHANDLER, e dopo la sua chiusura, si resta in attesa di una nuova voce di
giornale. In pratica, JRNHANDLER funziona come un trigger sul giornale.
Elaborazione delle voci di giornale

Di seguito un estratto dal programma JRNHANDLER:
* this is equivalent to *ENTRY PLIST:
D JRNHANDLER PI
D Entry likeds(Entry_t)
D Info likeds(Info_t)
/free
// See if there's a new entry added:
if info.flag1'1' and info.flag1'2';
return;
endif;
// If the entry type=B1, it means a new IFS file created
// in my directory!
if (entry.code='B' and entry.type='B1');
p_B1 = %addr(Entry.entSpec);
p_ObjName = %addr(Entry.entSpec) + B1.nameOff;
p_PathName = %addr(Entry.entSpec) + B1.pathOff;
if ObjName.ccsid=1200 and PathName.ccsid=1200;
plen = %div( PathName.len : 2 );
olen = %div( ObjName.len : 2 );
IfsObj = %char( %subst( PathName.Name: 1: plen )
+ %ucs2('/')
+ %subst( ObjName.Name: 1: olen ) );
waitFor(IfsObj);
process(IfsObj);
endif;
endif;
// Update the last sequence used so that I won't
// process the same journal entry again.
in *lock LastSeq;
LastSeq.seq = entry.seq + 1;
out LastSeq;
La routine waitFor() tenta di acquisire un vincolo esclusivo sull'oggetto appena creato. Questo è il modo
migliore per determinare quando il caricamento dell'oggetto nell'IFS è completato. Se l'upload è ancora in
corso, non sarà possibile ottenere il vincolo esclusivo.
Infine, la routine process() prende il file e lo elabora. Per esempio, se il file fosse un CSV, sarebbe possibile
usare CPYFRMSTMF per convertire il CSV in record di file di database. Se fosse un XML, lo si potrebbe
gestire tramite i codici operativi XML dell'RPG.
I sorgenti completi dei programmi descritti in questo articolo si possono prelevare nell'area download categoria Utility/articoliPDF nel file Eseguire file su IFS esercizi