ステップ3:RDMLファンクション・コード
以下は、これらの注文の処理に使用できるRDMLコードです。コード内の注釈で状況を説明しているため、注釈をよくお読みください。XMLBindFileServiceのRDMLX例もあります。
FUNCTION OPTIONS(*DIRECT)
* Define the fields to be used in this application
* Define the fields used by the JSM Commands
DEFINE FIELD(#JSMSTS) TYPE(*CHAR) LENGTH(020)
DEFINE FIELD(#JSMMSG) TYPE(*CHAR) LENGTH(255)
DEFINE FIELD(#JSMCMD) TYPE(*CHAR) LENGTH(255)
* Define the fields used for the orders.
* These fields will hold values read from and written to
* the XML documents
* Define the Order number and date
DEFINE FIELD(#SONUM) TYPE(*CHAR) LENGTH(010) LABEL('Order #')
DEFINE FIELD(#ORDDTE) TYPE(*CHAR) LENGTH(010) LABEL('Order Date')
* Define the Customer details
DEFINE FIELD(#CUSTNUM) TYPE(*CHAR) LENGTH(010) LABEL('Customer #')
DEFINE FIELD(#CUSTNME) TYPE(*CHAR) LENGTH(050) LABEL('Customer name')
DEFINE FIELD(#STREET) TYPE(*CHAR) LENGTH(050) LABEL('Street')
DEFINE FIELD(#CITY) TYPE(*CHAR) LENGTH(050) LABEL('City')
DEFINE FIELD(#STATE) TYPE(*CHAR) LENGTH(005) LABEL('State')
DEFINE FIELD(#ZIP) TYPE(*CHAR) LENGTH(005) LABEL('Post Code')
* Define the order line details. We will also define a
* list that holds the order line details
DEFINE FIELD(#LINENUM) TYPE(*DEC) LENGTH(003) DECIMALS(0) LABEL('Line #')
DEFINE FIELD(#PARTNUM) TYPE(*DEC) LENGTH(003) DECIMALS(0) LABEL('Part #')
DEFINE FIELD(#PARTDSC) TYPE(*CHAR) LENGTH(020) LABEL('Part Desc.')
DEFINE FIELD(#PARTAMT) TYPE(*DEC) LENGTH(010) DECIMALS(2) LABEL('Amount')
DEFINE FIELD(#PARTQTY) TYPE(*DEC) LENGTH(003) DECIMALS(0) LABEL('Quantity')
DEFINE FIELD(#ORDTOT) TYPE(*DEC) LENGTH(010) DECIMALS(2) LABEL('Grand Total')
DEF_LIST NAME(#ORDLINES) FIELDS(#LINENUM #PARTNUM #PARTDSC #PARTAMT #PARTQTY)
* The following field will hold the file name and path
* for the archived files
DEFINE FIELD(#ARCHIVE) TYPE(*CHAR) LENGTH(30) DESC('Archived Orders Directory') INPUT_ATR(LC) DEFAULT('''archive''')
DEFINE FIELD(#X_POSN) TYPE(*DEC) LENGTH(2) DECIMALS(0) DESC('Working field to find the file name')
* Define the order response details
DEFINE FIELD(#COMMENTS) TYPE(*CHAR) LENGTH(256) LABEL('Comments')
DEFINE FIELD(#LINSTAT) TYPE(*CHAR) LENGTH(20) LABEL('Line Status')
DEFINE FIELD(#RESPONSE) TYPE(*CHAR) LENGTH(30) LABEL('Resp filename')
DEFINE FIELD(#TOGGLE) TYPE(*DEC) LENGTH(1) DECIMALS(0)
DEF_LIST NAME(#LSTCMNT) FIELDS(#COMMENTS) TYPE(*WORKING)
DEF_LIST NAME(#RSPLINES) FIELDS(#LINENUM #PARTNUM #PARTDSC #PARTAMT #PARTQTY #LINSTAT) TYPE(*WORKING)
* A single field working list needs to be defined to
* hold the list of order returned from our LIST command.
* The field needs to be long enough to hold the expected
* length of the canonical file path returned from the
* LIST command.
DEFINE FIELD(#FILENAME) TYPE(*CHAR) LENGTH(100)
DEF_LIST NAME(#ORDERLSTW) FIELDS(#FILENAME) COUNTER(#LISTCOUNT) TYPE(*WORKING)
DEF_LIST NAME(#ORDERLSTB) FIELDS(#FILENAME) COUNTER(#LISTCOUNT)
* We will now start the funcionality by opening the JSM
USE BUILTIN(JSM_OPEN) TO_GET(#JSMSTS #JSMMSG)
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
* Next we will load the JSM service - in this example we
* have selected to have tracing on.
CHANGE FIELD(#JSMCMD) TO('SERVICE_LOAD SERVICE(XMLBINDFILESERVICE) TRACE(*YES) DOMSET(*READER)')
USE BUILTIN(JSM_COMMAND) WITH_ARGS(#JSMCMD) TO_GET(#JSMSTS #JSMMSG)
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
* The first thing we want to do is to get a list of all
* the orders that are in the neworders directory. To do
* this we will use the LIST command. In this scenario,
* the neworders directory is immediately under the JSM
* Instance directory on our server, so we can refer to
* the directory simply as 'neworders'.
* In this example the file extension has been hard coded
* as XML. This means that only files with an extension of
* XML will be returned.
CLR_LIST NAMED(#ORDERLSTW)
CHANGE FIELD(#JSMCMD) TO('''LIST DIR(NEWORDERS) SERVICE_LIST(FILENAME) EXT(XML)''')
USE BUILTIN(JSM_COMMAND) WITH_ARGS(#JSMCMD) TO_GET(#JSMSTS #JSMMSG #ORDERLSTW)
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
* Display the list returned from the LIST command.
CLR_LIST NAMED(#ORDERLSTB)
SELECTLIST NAMED(#ORDERLSTW)
ADD_ENTRY TO_LIST(#ORDERLSTB)
ENDSELECT
DISPLAY FIELDS(#JSMCMD) BROWSELIST(#ORDERLSTB) EXIT_KEY(*YES *NEXT) MENU_KEY(*YES *NEXT)
IF_KEY WAS(*MENU *EXIT)
* Nicely close down JSM if the user has select the Exit
* or Menu key (F3 or F12)
USE BUILTIN(JSM_CLOSE) TO_GET(#JSMSTS #JSMMSG)
ENDIF
* The next step of our application is to run through
* the orders in our list and process the data order by
* order. To do this we will need to use the READ, BIND,
* and GET commands.
SELECTLIST NAMED(#ORDERLSTW)
* Next we will use the READ command. For the READ
* command we need to specify the file that we want
* to access, including the file path. In this example,
* if an order is named order1.xml for example, then the
* FILE keyword would normally be specified as
* FILE(neworder/order1.xml), and it would assume that the
* neworder directory is under the JSM Instance directory
* for the server. We could take this approach, but we
* already have the full directory path and file name
* specified in the list (in the field #FILENAME) so it
* will be much simpler to use this. The actual ARCHIVE
* file name and path will be verified in the ARCHIVE
* sub-routine.
* If we were going to be using a hard coded READ, then
* this is what it might look like:
* . USE BUILTIN( BUILTIN) WITH_ARGS(JSM_COMMAND)
* . WITH_ARGS('READ FILE(neworder/order1.xml)
* . ARCHIVE(archive/arc_order1.xml)')
* . TO_GET(#JSMSTS #JSMMSG)
EXECUTE SUBROUTINE(ARCHIVE)
CHANGE FIELD(#JSMCMD) TO('READ FILE(')
USE BUILTIN(TCONCAT) WITH_ARGS(#JSMCMD #FILENAME ') ARCHIVE(' #ARCHIVE ')') TO_GET(#JSMCMD)
USE BUILTIN(JSM_COMMAND) WITH_ARGS(#JSMCMD) TO_GET(#JSMSTS #JSMMSG)
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
* The next command to run is the BIND. We named the
* service to handle the inbound XML documents as
* 'inboundorder'.
CHANGE FIELD(#JSMCMD) TO('''BIND SERVICE(INBOUNDORDER) TYPE(*INBOUND) BINDTRACE(*YES)''')
USE BUILTIN(JSM_COMMAND) WITH_ARGS(#JSMCMD) TO_GET(#JSMSTS #JSMMSG)
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
* The next step is to retrieve the data using the GET
* command.
USE BUILTIN(JSM_COMMAND) WITH_ARGS('GET FRAGMENT(ORDERS) SERVICE_EXCHANGE(*FIELD)') TO_GET(#JSMSTS #JSMMSG)
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
CHANGE FIELD(#JSMCMD) TO('GET FRAGMENT(SALESORDER) SERVICE_EXCHANGE(*FIELD)')
USE BUILTIN(JSM_COMMAND) WITH_ARGS(#JSMCMD) TO_GET(#JSMSTS #JSMMSG)
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
CHANGE FIELD(#JSMCMD) TO('GET FRAGMENT(CUSTOMER) SERVICE_EXCHANGE(*FIELD)')
USE BUILTIN(JSM_COMMAND) WITH_ARGS(#JSMCMD) TO_GET(#JSMSTS #JSMMSG)
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
CHANGE FIELD(#JSMCMD) TO('GET FRAGMENT(ORDERDATE) SERVICE_EXCHANGE(*FIELD)')
USE BUILTIN(JSM_COMMAND) WITH_ARGS(#JSMCMD) TO_GET(#JSMSTS #JSMMSG)
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
* The line details of our order technically make up a
* list. In this example though, we have defined the lines
* as fragments. As such, we need to handle them a little
* differently. We need to place the GET FRAGMENT(LINE) in
* a loop and continue in the loop until all the line
* details are retrieved. The following demonstrates how
* we do this.
CLR_LIST NAMED(#ORDLINES)
BEGIN_LOOP
USE BUILTIN(JSM_COMMAND) WITH_ARGS('GET FRAGMENT(LINE) SERVICE_EXCHANGE(*FIELD)') TO_GET(#JSMSTS #JSMMSG)
IF COND('#JSMSTS *EQ NOFRAGMENT')
LEAVE
ENDIF
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
* We also need to get the part details for this line.
USE BUILTIN(JSM_COMMAND) WITH_ARGS('GET FRAGMENT(PART) SERVICE_EXCHANGE(*FIELD)') TO_GET(#JSMSTS #JSMMSG)
ADD_ENTRY TO_LIST(#ORDLINES)
END_LOOP
* Now that we have everything for the order let us
* display it. In a real application of course you would
* carry on an load it into your ERP system or what have
* you, but we will just display it.
DISPLAY FIELDS(#SONUM #ORDDTE #CUSTNUM #CUSTNME #STREET #CITY #STATE #ZIP) BROWSELIST(#ORDLINES) EXIT_KEY(*YES *NEXT) MENU_KEY(*YES *NEXT)
IF_KEY WAS(*MENU *EXIT)
* Nicely close down JSM if the user has select the Exit
* or Menu key (F3 or F12)
USE BUILTIN(JSM_CLOSE) TO_GET(#JSMSTS #JSMMSG)
ENDIF
* Now that we have the data we need and have saved a copy
* of the file in the archive, we can close the bind on
* this file and delete the file from our inbound
* directory.
USE BUILTIN(JSM_COMMAND) WITH_ARGS(CLOSE) TO_GET(#JSMSTS #JSMMSG)
CHANGE FIELD(#JSMCMD) TO('DELETE FILE(')
USE BUILTIN(TCONCAT) WITH_ARGS(#JSMCMD #FILENAME ')') TO_GET(#JSMCMD)
USE BUILTIN(JSM_COMMAND) WITH_ARGS(#JSMCMD) TO_GET(#JSMSTS #JSMMSG)
* Now we will work on a response XML in reply to this
* order. To keep things clean, we will do this in a
* separate subroutine.
EXECUTE SUBROUTINE(RESPONSE)
ENDSELECT
*
* SUB ROUTINES
* The CHECK subroutine is used to capture errors returned
* from the JSM commands. For any errors encountered, a
* screen will be displayed with the error message and the
* program will exit after this.
SUBROUTINE NAME(CHECK) PARMS((#JSMSTS *RECEIVED) (#JSMMSG *RECEIVED))
IF COND('#JSMSTS *NE OK')
DISPLAY FIELDS(#JSMSTS #JSMMSG)
USE BUILTIN(JSM_CLOSE) TO_GET(#JSMSTS #JSMMSG)
MENU MSGTXT('Java service error has occured')
ENDIF
ENDROUTINE
* The ARCHIVE subroutine will build the file name and
* path for the ARCHIVE keyword of the READ command.
SUBROUTINE NAME(ARCHIVE)
USE BUILTIN(SCANSTRING) WITH_ARGS(#FILENAME 'neworders' *DEFAULT '0') TO_GET(#X_POSN)
CHANGE FIELD(#X_POSN) TO('#X_POSN + 10')
SUBSTRING FIELD(#FILENAME #X_POSN) INTO_FIELD(#ARCHIVE)
USE BUILTIN(TCONCAT) WITH_ARGS('archive/arc_' #ARCHIVE) TO_GET(#ARCHIVE)
ENDROUTINE
* This RESPONSE subroutine will do all the processing
* required to build and create the response XML document.
SUBROUTINE NAME(RESPONSE)
* The first thing we need to do is create a new empty
* outbound XML document and BIND it to the outbound
* service that we created with the XML Binding Wizard.
* Note that we specify the type as *OUTBOUND.
USE BUILTIN(JSM_COMMAND) WITH_ARGS('BIND SERVICE(OUTBOUNDRESPONSE) TYPE(*OUTBOUND)') TO_GET(#JSMSTS #JSMMSG)
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
* Much of the information for the response we already
* have, but a couple of fields need to be populated so
* we will make up some fictitious data here. We will
* create some comments, and add them to a list first.
* The other new data we need to add is the LINSTAT field
* to give an indication of the status of the order line.
CLR_LIST NAMED(#LSTCMNT)
CHANGE FIELD(#COMMENTS) TO('''Thank you for your order''')
ADD_ENTRY TO_LIST(#LSTCMNT)
USE BUILTIN(BCONCAT) WITH_ARGS('We have processed your order' #SONUM 'on date:' *DDMMYYYYC) TO_GET(#COMMENTS)
ADD_ENTRY TO_LIST(#LSTCMNT)
CHANGE FIELD(#COMMENTS) TO('''Please refer below for the full details of your order.''')
ADD_ENTRY TO_LIST(#LSTCMNT)
CHANGE FIELD(#COMMENTS) TO('''One of our sales people will be in touch with you shortly.''')
ADD_ENTRY TO_LIST(#LSTCMNT)
CHANGE FIELD(#COMMENTS) TO('''For immediate assistance on your order please call 1234567''')
ADD_ENTRY TO_LIST(#LSTCMNT)
CHANGE FIELD(#COMMENTS) TO('''one more line''')
ADD_ENTRY TO_LIST(#LSTCMNT)
CHANGE FIELD(#COMMENTS) TO('''two more lines''')
ADD_ENTRY TO_LIST(#LSTCMNT)
CHANGE FIELD(#COMMENTS) TO('''three more lines''')
ADD_ENTRY TO_LIST(#LSTCMNT)
CLR_LIST NAMED(#RSPLINES)
* NOTE: The #TOGGLE field is used to alternate status
* messages - to add a bit of variety.
SELECTLIST NAMED(#ORDLINES)
IF COND('#TOGGLE = 0')
CHANGE FIELD(#TOGGLE) TO(1)
CHANGE FIELD(#LINSTAT) TO(OK)
ELSE
CHANGE FIELD(#TOGGLE) TO(0)
CHANGE FIELD(#LINSTAT) TO('OUT OF STOCK')
ENDIF
ADD_ENTRY TO_LIST(#RSPLINES)
ENDSELECT
* Now that we have some data, we can start using the SET
* command to populate the outbound document object. As
* per the reading of data from the order documents, since
* this example is using fragments only, we will need to
* set up loops to add any repeating data (specifically
* the comments and the order lines).
CHANGE FIELD(#JSMCMD) TO('SET FRAGMENT(ORDERRESPONSE) SERVICE_EXCHANGE(*FIELD)')
USE BUILTIN(JSM_COMMAND) WITH_ARGS(#JSMCMD) TO_GET(#JSMSTS #JSMMSG)
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
SELECTLIST NAMED(#LSTCMNT)
CHANGE FIELD(#JSMCMD) TO('SET FRAGMENT(RESPONSECOMMENTS) SERVICE_EXCHANGE(*FIELD)')
USE BUILTIN(JSM_COMMAND) WITH_ARGS(#JSMCMD) TO_GET(#JSMSTS #JSMMSG)
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
ENDSELECT
CHANGE FIELD(#JSMCMD) TO('SET FRAGMENT(SALESORDER) SERVICE_EXCHANGE(*FIELD)')
USE BUILTIN(JSM_COMMAND) WITH_ARGS(#JSMCMD) TO_GET(#JSMSTS #JSMMSG)
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
CHANGE FIELD(#JSMCMD) TO('SET FRAGMENT(CUSTOMER) SERVICE_EXCHANGE(*FIELD)')
USE BUILTIN(JSM_COMMAND) WITH_ARGS(#JSMCMD) TO_GET(#JSMSTS #JSMMSG)
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
SELECTLIST NAMED(#RSPLINES)
CHANGE FIELD(#JSMCMD) TO('SET FRAGMENT(LINE) SERVICE_EXCHANGE(*FIELD)')
USE BUILTIN(JSM_COMMAND) WITH_ARGS(#JSMCMD) TO_GET(#JSMSTS #JSMMSG)
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
CHANGE FIELD(#JSMCMD) TO('SET FRAGMENT(PART) SERVICE_EXCHANGE(*FIELD)')
USE BUILTIN(JSM_COMMAND) WITH_ARGS(#JSMCMD) TO_GET(#JSMSTS #JSMMSG)
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
ENDSELECT
* Now we have all the data prepared, we will serialize
* the document object out to a specified file using the
* WRITE command. We will construct a file name based on
* the customer and order number.
USE BUILTIN(TCONCAT) WITH_ARGS('response/RSP_' #CUSTNUM '_' #SONUM '.xml') TO_GET(#RESPONSE)
USE BUILTIN(TCONCAT) WITH_ARGS('WRITE FILE(' #RESPONSE ') INDENT(*YES) INDENT-AMOUNT(1)') TO_GET(#JSMCMD)
USE BUILTIN(JSM_COMMAND) WITH_ARGS(#JSMCMD) TO_GET(#JSMSTS #JSMMSG)
EXECUTE SUBROUTINE(CHECK) WITH_PARMS(#JSMSTS #JSMMSG)
* Finally we will CLOSE the bind then go onto the next
* XML document.
USE BUILTIN(JSM_COMMAND) WITH_ARGS(CLOSE) TO_GET(#JSMSTS #JSMMSG)
ENDROUTINE