Offer XML To a Java Customer Maintenance App
The developers at Krengel Tech have been busy looking for new non-traditional ways for you to put RPG-XML Suite (RXS) to work. Considering the sole existence of RXS is to process incoming and outgoing XML, the possibilities for how you can benefit your business are endless!
In this installment of “Cool Things You Can Do With RPG-XML Suite” you will see how to offer non-verbose XML to a Java Customer Maintenance application running on the desktop. Please note that SOAP was not used because that would have added unnecessary overhead. By eliminating more complex XML we were able to use a very light weight parser/composer on the Java end which can be found here: http://nanoxml.cyberelf.be/. Note that they also have a port for C#.NET if you are looking to accomplish the same thing from the Visual Studio environment.
The basic concept here is that we developed a Java desktop application that draws the UI and based on button clicks makes XML HTTP calls out to the System i5 and receives the response back for displaying to the user. In this case RPG-XML Suite is used to facilitate a handful of validation and file I/O operations, but imagine if you could drive the entire desktop user interface completely from RPG without any need for Java knowledge on your staff to add/change GUI screens!? Stay tuned as we have some more exciting things in store!
Download it now!
Note that you don't have to install the System i5 components if you don't want to. By default the GUI application will connect to Krengel Tech's System i5 over the internet.
File: MYLIB/CUSTMST
**************************************************************************
* File: CUSTMST
* Use: Store customer information.
**************************************************************************
A UNIQUE
A R CUSTMSTR
A CUSTNBR 5P 0 COLHDG('Cust Nbr')
A FNAM 15A COLHDG('First Name')
A LNAM 15A COLHDG('Last Name')
A CMPNY 20A COLHDG('Company')
A ADR1 20A COLHDG('Address 1')
A ADR2 20A COLHDG('Address 2')
A CTY 15A COLHDG('City')
A STT 2A COLHDG('State')
A ZIPCD 5A COLHDG('Zip Code')
A PHN 18A COLHDG('Phone')
A K CUSTNBR
Module: CUSTFN
//------------------------------------------------------------------------
// @Author: Aaron Bartell
// @Created: 2007-06-08
// @Desc: Modular customer RPG service program.
// @Compile: ADDLIBLE MYLIB
// CRTRPGMOD
// MODULE(MYLIB/CUSTFN) SRCFILE(MYLIB/QSOURCE) SRCMBR(CUSTFN)
//
// CRTSRVPGM
// SRVPGM(MYLIB/CUSTSV)
// MODULE(MYLIB/CUSTFN) SRCFILE(MYLIB/QSOURCE)
//------------------------------------------------------------------------
h nomain
fCUSTMST uf a e k disk prefix('CM.')
d CM e ds qualified extname(CUSTMST)
/copy qsource,CustCP
//------------------------------------------------------------------------
// @Author: Aaron Bartell
// @Created: 2007-06-08
// @Desc: Get the next available customer number.
//------------------------------------------------------------------------
p Cust_nextNbr b export
d Cust_nextNbr pi
d pError like(Cust_Error)
d pNbr like(CM.CUSTNBR)
/free
clear pError;
setgt *hival CUSTMST;
readp CUSTMST;
pNbr = CM.CUSTNBR + 1;
clear CUSTMSTR;
CM.CUSTNBR = pNbr;
write CUSTMSTR;
/end-free
p e
//------------------------------------------------------------------------
// @Author: Aaron Bartell
// @Created: 2007-06-08
// @Desc: Retrieve a customer record.
//------------------------------------------------------------------------
p Cust_getRec b export
d Cust_getRec pi
d pError like(Cust_Error)
d pCustRec likeds(CM)
d pCustNbr like(CM.CUSTNBR)
/free
clear pError;
clear pCustRec;
chain(n) pCustNbr CUSTMST;
if not %found;
pError = 'Customer Not Found';
else;
pCustRec = CM;
endif;
/end-free
p e
//------------------------------------------------------------------------
// @Author: Aaron Bartell
// @Created: 2007-06-08
// @Desc: Save a customer record
//------------------------------------------------------------------------
p Cust_save b export
d Cust_save pi
d pError like(Cust_Error)
d pCustRec likeds(CM)
/free
monitor;
clear pError;
if ( pCustRec.CustNbr = 0
or pCustRec.FNam = *blanks
or pCustRec.LNam = *blanks
or pCustRec.Cmpny = *blanks
or pCustRec.Adr1 = *blanks
or pCustRec.Cty = *blanks
or pCustRec.Stt = *blanks
or pCustRec.ZipCd = *zeros
or pCustRec.Phn = *blanks );
pError = 'Not all fields were set!';
return;
endif;
chain pCustRec.CustNbr CUSTMST;
CM = pCustRec;
if %found(CUSTMST);
update CUSTMSTR;
else;
write CUSTMSTR;
endif;
on-error;
pError = 'Error occurred during save of customer!';
endmon;
/end-free
p e
Copy book: CUSTCP
D Cust_nextNbr pr
D pError like(Cust_Error)
D pNbr like(CM.CUSTNBR)
D Cust_getRec pr
D pError like(Cust_Error)
D pCustRec likeds(CM)
D pCustNbr like(CM.CUSTNBR)
D Cust_save pr
D pError like(Cust_Error)
D pCustRec likeds(CM)
D Cust_Error s 50A
Binder Language (used to create *SRVPGM):
strpgmexp
export symbol(Cust_nextNbr)
export symbol(Cust_getRec)
export symbol(Cust_save)
endpgmexp
RPG-XML Suite web service program:
//*******************************************************************************************
// @Author: Aaron Bartell
// @Created: 2005-08-23
// @Desc:
// @Compile: CRTBNDRPG PGM(MYRXS/WSCUST) SRCFILE(MYLIB/QSOURCE) SRCMBR(WSCUST)
//*******************************************************************************************
H dftactgrp(*no) bnddir('RXSBND': 'SINBND')
/copy rxs,RXSCp
/copy qsource,CustCp
D allHandler pr
D pType value like(RXS_Type)
D pXPath value like(RXS_XPath)
D pData value like(RXS_XmlData)
D pDataLen value like(RXS_Length)
D errHandler pr
D pCurLine 10i 0 value
D pCurCol 10i 0 value
D pErrStr 1024a value varying
D errRsp pr
D gAction s 25a varying
D CM e ds qualified extname(CUSTMST)
D gError ds likeds(RXS_Error) inz
D gXml s 65535a varying
D gCustErr s like(Cust_Error)
D gCustNbr s like(CM.CUSTNBR)
/free
monitor;
clear gError;
clear gAction;
if RXS_libLEExists('MYLIB') <= 0;
RXS_addLibLE('MYLIB');
endif;
gXml = RXS_readStdIn();
RXS_allElemContentHandler(%paddr(allHandler));
RXS_addHandler('/ENV@ACTION': %paddr(allHandler));
RXS_parse(gXml: RXS_VAR: %paddr(errHandler));
if gError.code <> *blanks;
RXS_stdOutError('error': gError: *on);
*inlr = *on;
return;
endif;
exsr compose;
on-error;
RXS_stdOutError('error': RXS_catchError(): *on);
endmon;
*inlr = *on;
//--------------------------------------------------------------------------------------------
// @Author: Aaron Bartell
// @Created: 2005-08-03
// @Desc: Using the template engine compose the response and send it to standard out (i.e.
// RXS_STDOUT).
//--------------------------------------------------------------------------------------------
begsr compose;
// Compose xml
RXS_initTplEng(RXS_STDOUT: *omit: *omit: *omit: *omit: *off);
RXS_loadTpl('wscust.tpl': '::': '': '.:': ':.');
RXS_wrtSection('HTTP_HEAD');
RXS_wrtSection('env_beg');
select;
when gAction = 'CUST_NEXTNBR';
Cust_nextNbr(gCustErr: gCustNbr);
if gCustErr <> *blanks;
RXS_updVar('ERROR': gCustErr);
RXS_wrtSection('ERROR');
else;
RXS_updVar('CUSTNBR': %char(gCustNbr));
RXS_wrtSection('CUSTNBR');
endif;
when gAction = 'CUST_SAVE';
Cust_save(gCustErr: CM);
if gCustErr <> *blanks;
RXS_updVar('ERROR': gCustErr);
RXS_wrtSection('ERROR');
endif;
when gAction = 'CUST_GETREC';
Cust_getRec(gCustErr: CM: gCustNbr);
if gCustErr <> *blanks;
RXS_updVar('ERROR': gCustErr);
RXS_wrtSection('ERROR');
else;
RXS_updVar('CUSTNBR': %char(CM.CUSTNBR));
RXS_updVar('FNAM': CM.FNAM);
RXS_updVar('LNAM': CM.LNAM);
RXS_updVar('CMPNY': CM.CMPNY);
RXS_updVar('ADR1': CM.ADR1);
RXS_updVar('ADR2': CM.ADR2);
RXS_updVar('CTY': CM.CTY);
RXS_updVar('STT': CM.STT);
RXS_updVar('ZIPCD': CM.ZIPCD);
RXS_updVar('PHN': CM.PHN);
RXS_wrtSection('CUSTMST');
endif;
other;
RXS_updVar('ERROR': 'No action specified');
endsl;
RXS_wrtSection('env_end': *on);
endsr;
/end-free
//--------------------------------------------------------------------------------------------
// @Author: Aaron Bartell
// @Created: 2005-08-03
// @Desc: This local sub procedure will be called for each element content and attribute event
// that occurs during the parsing of the env document. Based on the event this sub
// procedure is being notified of it will place the value in the appropriate global
// variable.
//--------------------------------------------------------------------------------------------
P allHandler b
D allHandler pi
D pType value like(RXS_Type)
D pXPath value like(RXS_XPath)
D pData value like(RXS_XmlData)
D pDataLen value like(RXS_Length)
/free
select;
when pXPath = '/ENV@ACTION';
gAction = pData;
when pXPath = '/ENV/CUSTMST/CUSTNBR/';
CM.CUSTNBR = RXS_charToNbr(pData: 0);
when pXPath = '/ENV/CUSTMST/FNAM/';
CM.FNAM = pData;
when pXPath = '/ENV/CUSTMST/LNAM/';
CM.LNAM = pData;
when pXPath = '/ENV/CUSTMST/CMPNY/';
CM.CMPNY = pData;
when pXPath = '/ENV/CUSTMST/ADR1/';
CM.ADR1 = pData;
when pXPath = '/ENV/CUSTMST/ADR2/';
CM.ADR2 = pData;
when pXPath = '/ENV/CUSTMST/CTY/';
CM.CTY = pData;
when pXPath = '/ENV/CUSTMST/STT/';
CM.STT = pData;
when pXPath = '/ENV/CUSTMST/ZIPCD/';
CM.ZIPCD = pData;
when pXPath = '/ENV/CUSTMST/PHN/';
CM.PHN = pData;
when pXPath = '/ENV/CUSTNBR/';
gCustNbr = RXS_charToNbr(pData: 0);
endsl;
/end-free
P e
//--------------------------------------------------------------------------------------------
// @Author: Aaron Bartell
// @Created: 2005-08-03
// @Desc: If an error occurs this local sub procedure will be called by the parser.
//--------------------------------------------------------------------------------------------
P errHandler B
D errHandler PI
D pCurLine 10i 0 value
D pCurCol 10i 0 value
D pErrStr 1024a value varying
/free
gError.code = 'RXS3.1';
gError.severity = 100;
gError.pgm = 'RXS3.errHandler';
gError.text =
'Line:' + %char(pCurLine) +
' Column:' + %char(pCurCol) +
' ' + pErrStr;
/end-free
P E
RPG-XML Suite template file: /www/myrxs/templates/wscust.tpl
::HTTP_HEAD
Content-type: text/xml
::env_beg
<env>
::CUSTMST
<CUSTMST>
<CUSTNBR>.:CUSTNBR:.</CUSTNBR>
<FNAM>.:FNAM:.</FNAM>
<LNAM>.:LNAM:.</LNAM>
<CMPNY>.:CMPNY:.</CMPNY>
<ADR1>.:ADR1:.</ADR1>
<ADR2>.:ADR2:.</ADR2>
<CTY>.:CTY:.</CTY>
<STT>.:STT:.</STT>
<ZIPCD>.:ZIPCD:.</ZIPCD>
<PHN>.:PHN:.</PHN>
</CUSTMST>
::CUSTNBR
<CUSTNBR>.:CUSTNBR:.</CUSTNBR>
::ERROR
<ERROR>.:ERROR:.</ERROR>
::env_end
</env>