diff --git a/docs/Makefile b/docs/Makefile index 1b8d299..e370c2a 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -1,20 +1,20 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SPHINXPROJ = XMLSERVICE -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXPROJ = XMLSERVICE +SOURCEDIR = . +BUILDDIR =_build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/ccsids.rst b/docs/ccsids.rst new file mode 100755 index 0000000..35b7e17 --- /dev/null +++ b/docs/ccsids.rst @@ -0,0 +1,255 @@ + + +XMLSERVICE/Toolkit CCSID +======================== + +`Goto Main Page`_ + +.. _Goto Main Page: index.html + +XMLSERVICE should just work +------------------------------- +In general if you are using DB2 connections 1/2 tier and settings for your Apache or user profile are valid (not 65535), you will not need any additional CCSID manipulation/configuration. + +.. + If you are unfamiliar with 2-tier DB2 CCSID please try following link before reading this page [[PHP/DB2CCSID | DB2 CCSID]]. + +CCSID - what do? +---------------- + +Few other topics dealing with web programs cause more frustrations over IBM i CCSID settings. + +CCSID conflict between web scripts (clients) and IBM i PGMs (server) +-------------------------------------------------------------------- +Any good conflict needs a basic difference in philosophy causing all the trouble. + +* *Client (ASCII)* - web scripts typically wish to run ASCII 819/1208 (PHP), code set(s) laptop +* *Server (EBCDIC)* - IBM i PGMs generally run EBCDIC (RPG/Cobol/CLP), code set(s) main frame + +CCSID rule -- always a conversion client/server +----------------------------------------------- +The basic operational code set differences between client (ASCII) / server (EBCDIC) required a conversion for all character data moving between client/server to be of value on either side. Your CCSID setting options are many, experimentation is usually required, but perhaps this web page helps avoid hours of frustrating tinkering attempting to adjust multitude of software "products" where CCSID conversions is possible. + +Happens 99 times out of 100 when not working (CCSID 65535) +---------------------------------------------------------- +You cannot run IBM i PHP and interact with IBM i DB2, PGM, SRVPGM (xmlservice), when your IBM i machine is default ship setting of 65535. Change the default ship Zend Server setting and you will likely be up and running. + +I run 65535 xmlservice on my V6R1 machine (among other machines) +:: + + DSPSYSVAL SYSVAL(QCCSID) + Coded character set + identifier . . . . . : 65535 1-65535 + + +Change these files and restart everything (web, db2, xmlservice, tec.) + +I. Apache: /www/zendsvr/conf/httpd.conf <-- (ILE Apache side) +:: + + DefaultFsCCSID 37 ... or 280 (Italian) ... or so on ... + CGIJobCCSID 37 ... or 280 (Italian) ... or so on ... + +This often fixes majority of web script issue + +II. FastCGI: /www/zendsvr/conf/fastcgi.conf <-- THIS file (PASE side) +:: + + CCSID=819 and LANG=C, + which works nearly anywhere (UNIX default). + +Note: + + - This file must be ASCII 819, so careful with editor or will not work (EDTF especially). + - CCSID=1208 can be a problem for PHP extensions, so most configurations use CCSID=819 and take specific script action to encode/decode 1208 data (see xmlservice hex/before/after) + +III. User profile -- DB2 connections +:: + + CHGUSRPRF USRPRF(IAM37) CCSID(37) + +This fixes most DB2 CCSID client/server problems both 1-tier and 2-tier + +*-- or (skip I. - III.)--* + +IV. change system ccsid for everything on the machine +:: + + CHGSYSVAL SYSVAL(QCCSID) VALUE(37) + +Note: + + - some legacy applications may not work, should work but..., so check things out + - *65535* means HEX (or binary), which also means no conversion and 65535 is essentially doomsday for most any application trying to work between PASE and DB2 (most any client and DB2). + + :: + + client (ASCII 819) + server (EBCDIC 65535) = junk-you-can't-read-or-use = xmlservice fails + + +Now **the rest of the story** 65535 problem is common, so IBM i DB2 folks force a reasonable job CCSID for SOME remote connections (based on primary language generally), but NOT all connections (PASE and others), therefore observationally you may see some applications work (client access products) and others will not (PHP). + +1) Specific examples for XMLSERVICE + + - Command user ccsid override + + :: + + if ($i5persistentconnect) $conn = db2_pconnect($database,$user,$password); + else $conn = db2_connect($database,$user,$password); + if (!$conn) die("Bad connect: $database,$user"); + $stmt = db2_prepare($conn, "call $libxmlservice.iPLUG4K(?,?,?,?)"); + if (!$stmt) die("Bad prepare: ".db2_stmt_errormsg()); + + $ipc = "/tmp/packers"; // *** RIGHT HERE internalKey/IPC + $ctl = "*sbmjob"; // *** run state full + + $clob = "\n"; + $clob .= "\n"; + $clobIn = $clob; + $clobOut = ""; + + $ret=db2_bind_param($stmt, 1, "ipc", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 2, "ctl", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 3, "clobIn", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 4, "clobOut", DB2_PARAM_OUT); + $ret=db2_execute($stmt); + + var_dump($clobOut); + + $xmlobj = simplexml_load_string($clobOut); + + if (!$xmlobj) die("Bad XML output"); + + $clobOut = pack("H*",(string)$xmlobj->cmd->hex); + + var_dump($clobOut); + + OUTPUT: + > php zzhexccsidcmd.php + string(527) " + " + string(176) " + QGPL QTEMP QDEVELOP QBLDSYS QBLDSYSR + + + QSYS QSYS2 QHLPSYS QUSRSYS + + + + + - Program ccsid override + + :: + + if ($i5persistentconnect) $conn = db2_pconnect($database,$user,$password); + else $conn = db2_connect($database,$user,$password); + if (!$conn) die("Bad connect: $database,$user"); + $stmt = db2_prepare($conn, "call $libxmlservice.iPLUG512K(?,?,?,?)"); + if (!$stmt) die("Bad prepare: ".db2_stmt_errormsg()); + + $ipc = "/tmp/packers"; // *** RIGHT HERE internalKey/IPC + $ctl = "*sbmjob"; // *** run state full + + $clob = "\n"; + $clob .= "\n"; + $clobIn = $clob; + $clobOut = ""; + + $ret=db2_bind_param($stmt, 1, "ipc", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 2, "ctl", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 3, "clobIn", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 4, "clobOut", DB2_PARAM_OUT); + $ret=db2_execute($stmt); + + var_dump($clobOut); + + $xmlobj = simplexml_load_string($clobOut); + + if (!$xmlobj) die("Bad XML output"); + + $clobOut = pack("H*",(string)$xmlobj->pgm->parm->data); + + var_dump($clobOut); + + + OUTPUT: + > php zzhexccsidpgm.php + string(273) " + " + string(43) "Hi there i am ok on return from xmlservice." + + + + +2) Specific examples for New PHP Toolkit + + - CCSID override - PHP Toolkit/CW + + Simple CCSIDs only require setting the CCSID via QCCSID or in Apache. The overrides directly below are intended for for languages with more complex needs such as Hebrew or Japanese, or when individual pieces of data are encoded differently (such as a combination of 819 and 1208). + + The easiest way to try these CCSID settings is with three new settings in toolkit.ini: + :: + + advanced CCSID options. Use all three options together. + ccsidBefore = '819/37' + ccsidAfter = '37/819' + useHex = true + + Uncomment the three settings and then adjust the ccsidBefore and ccsidAfter values according to your needs. + + Another way to set these global CCSID settings is with the method setToolkitServiceParams(). In your code, after connecting with $conn::getInstance(* etc.), set the parameters with this statement: + :: + + $conn->setToolkitServiceParams(array('ccsidBefore'=>'819/37', 'ccsidAfter'=>'37/819', 'useHex'=>true)); + + This technique works identically to changing INI values, except that this coding technique can be re-done over and over with different settings before each program/command call. + + These "global" CCSID techniques work with both the new API and the CW, and will convert not only data/commands and command output, but the names of programs, libraries, and functions. You may notice that your data will be converted to hex inside the toolkit and then converted back to readable text by the toolkit. + + For more fine-grained control over parameter data--that is, the ability to use a different CCSID conversion for each parameter, if desired--chain several new methods to AddParameterChar() like so: (new API only--not in CW): + :: + + $param[] = $conn->AddParameterChar('both', 10,'CODE', 'CODE', $code) + ->setParamCcsidBefore('819/37') + ->setParamCcsidAfter('37/819') + ->setParamUseHex(true); + + + These parameters can also be passed as AddParameterChar() function parameters directly but it's easier to use the setParam… methods above. + + Note: + + These advanced CCSID settings do not affect some of the handmade API calls in the CW such as getting object lists. Helping those may be a future enhancement. + + +If you wish to see how XMLSERVICE implements these overrides, see the following URL, under the heading: "CCSID user override - xmlservice options (hex/before/after)". + + + +.. + [--Author([[http://youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICECCSID?action=expirediff | s ]])--] + [--Tony "Ranger" Cairns - IBM i PHP / PASE--] + + + diff --git a/docs/connections.rst b/docs/connections.rst new file mode 100755 index 0000000..1f8303f --- /dev/null +++ b/docs/connections.rst @@ -0,0 +1,424 @@ +XMLSERVICE Connections +====================== + +`Goto Main Page`_ + +.. _Goto Main Page: index.html + +Sometimes a real conversation helps ... +--------------------------------------- + +**Customer wants to know how to remain stateless, but get the benefit of being able to audit the use of RPG subprocedure invocations via XMLToolkit without toggling back and forth (different QSQSRVR jobs).** + + Stateless may use multiple QSQSRVR jobs (toggling), this is definition of 'stateless' in PHP fastcgi environment (working correct). + + +**Will ToolkitService object using persistent db2 connections and "stateless => true" help single QSQ job?** + + Toolkit "stateless => true" via db2 transport using db2_connect or db2_pconnect makes no difference, fastcgi implies unpredictable QSQSRVR job will be used (command line slightly more predictable over web). + + +**Why 'unpredictable' QSQ?** + + It's all web PHP fastcgi 'random' worker selection, NOTHING to do with ibm_db2 'connection' persistent or non-persistent, toolkit connection is irrelevant. Apache web site using fastcgi routes "randomly" to any available PHP worker job php-cgi, therefore 'random' php-cgi worker using db2_(p)connect<>QSQSRVR will also appear 'random'. To wit, you end up different QSQSRVR jobs (toggle back and forth). Technically, fastcgi protocol, PHP workers poll a single fastcgi socket waiting to take some work, ZS on i at /www/zendsvr6/logs/fcgi-njjjineh.sock (sock name random). As each php worker strips work off fastcgi socket (1st come == 1st do), the worker is busy communicating on 'private web socket' running script until finished (no longer waiting). Combination of natural web worker selection (browser +/- KeepAlive), and fastcgi socket poll for work, results in 'random' appearance QSQ job toolkit usage, DB2 connection just came along for ride in back seat of the 1950 PHP fastcgi roadster. + + +**Help me be one-and-only-one job?** + + The only way to assure private connection back to same XTOOLKIT job is 'internalKey'=>'/tmp/XTOOLKIT_job_1' ('ipc'=/tmp/XTOOLKIT_job_1'). + + + +XMLSERVICE connections discussed this page +---------------------------------------------- + +- Stateless -- clean running come/go, "full open/close," start/stop connection to be used by any requester. Must set LIBL during each request +- State full -- State retained between requests: LIBL, transactions, etc., "private" connection used by one requester/task for a long period of time (like 5250) +- State full -- hybrid "private/persistent" connection shared by many requesters, but keep open PGM, files, etc. +- State full -- reservation hybrid "private/persistent" connection exclusively held for a period each requesters, but returned back to pool for re-use (rare use) + +The jobs involved in "connections": +:: + + I. Stateless + (job 1) (job 2) (job 3) + Apache FastCGI DB2 (stateless) + ------- --------------- --------------------- + browser/client ->thread-->socket->php-cgi + --->$ctl="*here"; -->QSQSRVR+XMLSERVICE + shut down after + each request + ("stateless") + + II. State full + (job 2) (job 3) (job 4) + FastCGI QSQ (proxy) XMLSERVICE (state full) + --------------- --------------------- ------------------------ + -->socket->php-cgi + --->$ctl="*sbmjob"; + --->$ipc="/tmp/sally"; -->QSQSRVR+XMLSERVICE -->XMLSERVICE + alive until stopped + ("state full") + + + Jobs originates:: + + (job 1) Apache picks any thread (1st level routing) + (job 2) FastCGI all "non-busy" worker php-cgi wait on unix domain socket /www/zend2/logs/fcgi-hmjadgek.sock (2nd level routing) + (job 3) php-cgi - database connections odbc, ibm_db2, pdo_ibm (3rd level routing) + db2_pconnect() attach to pooled/persistent QSQ (matching profile) but leaves connection open on exit + db2_connect() acquires a unused pre-start QSQ (or starts one) then attaches to QSQ (profile), and returns to unused pool on exit + (job 3) XMLSERVICE -- Stateless -- run inside QSQ job and clean-up after each request (3rd level routing) + $ctl = "*here"; + (job 4) XMLSERVICE -- State full -- run in separate job that any QSQ job can call using IPC (4th level routing) + $ctl = "*sbmjob"; + $ipc = "/tmp/sally"; + +Drivers involved in conection:: + + 400 server start-up Common usage Big picture Comments + STRTCPSVR SERVER(*HTTP) + + port 80 or 10088 or … (REST/HTTP interface) + + 1-tier - XMLCGI CLIENT <==> IBM HTTP Server <==> XMLCGI (CLI server mode) <==> QSQxxxx <==> IBM i Resources XMLCGI + CLI + PASE library (no start needed) + + no port + + 1-tier - PHP ibm_db2+pdo_ibm+odbc (Native PASE CLI libdb400.a driver) CLIENT (PASE libdb400.a driver) <==> QSQxxx <==> IBM i Resources Native PASE CLI libdb400.a driver (IBM Rochester) + STRHOSTSVR SERVER(*DATABASE) + + port 8471 (database) + + 2-tier - PHP odbc interface (IBM i Client Access odbc driver interface) CLIENT (Client Access drivers) <==> QZDAxxxx <==> IBM i Resources Client Access odbc-based drivers (IBM Rochester) + STRTCPSVR SERVER(*DDM) + + port 446 (DDM/DRDA) + + 2-tier - PHP ibm_db2+pdo_ibm (DB2 Connect driver interface) CLIENT (DB2 Connect drivers) <==> QRWxxxx <==> IBM i Resources DB2 CLI DRDA-based DB2 Connect drivers (IBM Toronto) + + + + +1) Stateless -- no LIBL, come/go +>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +These connections are traditional web requests "full open/close" clean running start/stop connection to be used by any requester. + +- Stateless + +:: + + $ctl = "*here"; + + (1) (2) (3) + Apache FastCGI DB2 (server mode) + ------- --------------- --------------------- + browser/client -->thread--socket->php-cgi--->QSQSRVR(profile fred) + XMLSERVICE (fred) <--shut down after each request + --->QSQSRVR(profile sally) + XMLSERVICE (sally) <--shut down after each request + --->QSQSRVR(profile john) + XMLSERVICE (john) <--shut down after each request + + +Example new Toolkit (stateless): + +:: + + if ($i5persistentconnect) $conn = db2_pconnect($database,$user,$password); + else $conn = db2_connect($database,$user,$password); + try { $ToolkitServiceObj = ToolkitService::getInstance($conn); } + catch (Exception $e) { die($e->getMessage()); } + $ToolkitServiceObj->CLCommand("CHGLIBL LIBL(FREDFLIN WILMAFLIN) CURLIB(FREDFLIN)"); + +**Stateless:** +If you choose $ctl='\*here', you will run in the calling process DB2 connection (QSQSRVR job). +When XMLSERVICE completes your XML script it will shut down to nothing, considered stateless and holds zero state on return. + ++ In general you will run slower in stateless mode (CW default / Toolkit default), because XMLSERVICE has to keep starting things over and over and over again, but perhaps not an issue if you have CPU to burn.* ++ The is no semaphore locking or shared memory ipc when running as stateless (\*here), because only one sally client/server is a pair, but of course there may be many sally client/server pairs on the same machine.* ++ There is no "memory" of the LIBL in stateless, so it must be set EVERY time before use.* + + +2) State full -- LIBL, transactions, etc. +>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +These connections are traditional 5250-like "private" connection used by one requester/task for a long period of time. + +State full (most RPG programs) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + $ctl = "*sbmjob"; + $ipc = "/tmp/sally"; + -- or -- + $ipc = "/tmp/john"; + (1) (2) (3) (4) + Apache FastCGI DB2 (server mode) XMLSERVICE + ------- --------------- --------------------- ---------- + -->thread--socket->php-cgi--->QSQSRVR(profile sally)---.->XMLSERVICE (sally) <--alive until stopped (or idle timemout) + --->QSQSRVR(profile john)--. | + -->thread--socket->php-cgi--->QSQSRVR(profile fred) | | + --->QSQSRVR(profile sally)---. + --->QSQSRVR(profile fred) | + --->QSQSRVR(profile john)--.--->XMLSERVICE (john) <--alive until stopped (or idle timemout) + +Example new Toolkit (state full):: + + $internalKey = '/tmp/packers'; + if ($i5persistentconnect) $conn = db2_pconnect($database,$user,$password); + else $conn = db2_connect($database,$user,$password); + try { $ToolkitServiceObj = ToolkitService::getInstance($conn); } + catch (Exception $e) { die($e->getMessage()); } + $ToolkitServiceObj->setToolkitServiceParams(array( + 'InternalKey'=>$internalKey, // *** RIGHT HERE internalKey/IPC + // *** run state full + // use SBMJOB command run in new job + // PHP can call again, again, again + // with /tmp/packers and get + // same job every time + // same library list (*LIBL) + // same PGMs with open files, etc. + // exactly like 5250 sign-on screen + 'plug'=>"iPLUG32K")); // max size data i/o (iPLUG4K,32K,65K.512K,1M,5M,10M,15M) + // state full - MUST do this ONCE ONLY after start/sbmjob of XMLSERVICE job + // then forget about it (unless you choose to change libl) + $ToolkitServiceObj->CLCommand("CHGLIBL LIBL(FREDFLIN WILMAFLIN) CURLIB(FREDFLIN)"); + /* Do not use the disconnect() function for "state full" connection */ + /* NEVER EVER USE THIS ... $ToolkitServiceObj->disconnect(); */ + /* Why? *immed kill of job, not nice or sync, just kill */ + /* Use idle timeout for "state full" / "private" connections */ + +**State full**: If you choose ``$ctl="\*sbmjob"`` + ``$ipc="/tmp/packers"``, you will run in a separate job past +the calling DB2 connection (child job of QSQSRVR). This $ctl/$ipc combination will allow you to return to the +same XMLSERVICE job from any connection to the machine, therefore considered "state full" and any called program +can keep open files, transactions, etc. (just like a real RPG 5250 program does mate). + ++ $ipc='/tmp/anything' can be any unique/accessible directory you want to route you back to same XMLSERVICE job (\*sbmjob), but usually anchored in /tmp directory because xmlservice will try to create it if missing. ++ Technically $ipc="/tmp/packers" is a unique IFS machine location in posix function ftok('/tmp/packers') which presents a unique numeric key representing /tmp/packers that is used for XMLSERVICE shared memory and semaphores creation/attach (XMLSERVICE uses shared memory/semaphores for communication). ++ Shared memory + semaphore locking is only required for state full connections ($ctl="\*sbmjob" + $ipc="/tmp/packers"), where each sally XMLSERVICE semaphore "front door lock" will allow only one sally client to chat with a XMLSERVICE job, the other sally requesters will wait until they are invited to chat (just like the dentist office). ++ Security is managed through IFS shared memory / semaphores access control just like any other IFS file, so once profile sally owns an active XMLSERVICE ctl+ipc then no other profile can attach to the active XMLSERVICE job ... well ... except for high authority profiles like \*SECOFR (of course). ++ With version 1.6.6 state full XMLSERVICE connections are ended via configurable idle timeout $ctl .= " \*idle(3000)", you may keep the jobs alive forever using $ctl .= " \*idle(0)" to match the original version behavior. There are other options for client wait $clt .= " \*wait(30)" and waiting for called program to return $ctl .= " \*call(200)" and various actions that can be taken for each wait/timer (busy,kill,etc.). ++ In this example we have been using one sally client/server $ipc="/tmp/packers", you can of course have many different sally client/server ($ipc="/tmp/packers", $ipc="/tmp/vikings", $ipc="/tmp/bears", etc.) and each of these sally ipcs may have many clients chatting with each sally server ipc ... sort of a sally work load balancing dream situation where we can clone a new sally ipc server for each task at hand. ++ You only need to set the LIBL once in state full (unless you want to change LIBL for some reason). + +**Toolkit State full (with IPC) - avoid start up/shut down XMLSERVICE (NOT toolkit default)** + ++ avoid using toolkit disconnect ($ctl="\*immed") to leave XMLSERVICE up and running (will timeout shut down if idle for 1/2 hour) ++ choose a the minimum plug size need for the task to avoid send/receive extra blanks ++ TURN DEBUG and LOGS off in toolkit to avoid IFS file write (takes forever in computer timings) ++ db2_pconnect() - persistent or "shared" connection with toolkit avoids acquire/release QSQ jobs (NOT toolkit default) + + +XMLSERVICE adopt authority issues +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When using ctl+ipc "state full" jobs is a generally bad idea to "adopt authority" as originating profile sally will lose all access ... and ... in fact ipc may become unreachable causing a orphan XMLSERVICE (client is still sally, ipc is still sally's, but adopt xmlservice server becomes fred). + +Two choices: + + a) If you MUST "adopt authority" do it in a stateless job (\*here), where full connection processing may undo "left over switch profile" potential damage on the way out of XMLSERVICE script. This option should always work. + b) Be very careful to return back to sally profile EACH TIME leaving xmlservice sending data back to the waiting sally client ... sort of good manners talk to sally client as sally server (adopted fred can speak/do only when asked, then go away) + +**Note**: +We are thinking about forcing "switch back to originating profile on the way back" within XMLSERVICE code, but have not yet understood what that means to PHP wrappers like CW, so the mission is at the moment in your called program and/or PHP wrapper/user code. + +3) State full -- hybrid "private/persistent" connection +>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +These connections are hybrid "private/persistent" connection shared by many requesters, but keep open PGM, files, etc. + +Worried about too many IPC's/XMLSERVICE jobs?? + +The following gives you a hybrid "private/persistent" connection + +* most all the benefits for called RPG (state full, open files, etc.) +* but only $maxpool XMLSERVICE jobs + +Try this simple technique for pooled IPC's/XMLSERVICE jobs ``$internalKey = '/tmp/packers'.rand(1,$maxpool)`` + +* IF your application set can tolerate multi-client shared access to a pool of persistent/private/semi-stateless connections the random technique should work well. +* However, if you need your client make a exclusive reservation see the next topic + +**State full -- hybrid "private/persistent" connection** + +:: + + $maxpool = 3; + // -- PHP raw --- + $ctl = "*sbmjob"; + $ipc = "/tmp/sally".rand(1,$maxpool); + // -- or PHP toolkit -- + $internalKey = '/tmp/sally'.rand(1,$maxpool) + + (1) (2) (3) (4) + Apache FastCGI DB2 (server mode) XMLSERVICE + ------- --------------- --------------------- ---------- + -->thread--socket->php-cgi--->QSQSRVR(profile sally)--. + -->thread--socket->php-cgi--->QSQSRVR(profile sally)--| + : | + -->thread--socket->php-cgi--->QSQSRVR(profile sally)--|->XMLSERVICE (/tmp/sally1) <--alive until stopped (or idle timemout) + -->thread--socket->php-cgi--->QSQSRVR(profile sally)--|->XMLSERVICE (/tmp/sally2) <--alive until stopped (or idle timemout) + -->thread--socket->php-cgi--->QSQSRVR(profile sally)--|->XMLSERVICE (/tmp/sally3) <--alive until stopped (or idle timemout) + : | + -->thread--socket->php-cgi--->QSQSRVR(profile sally)--| + -->thread--socket->php-cgi--->QSQSRVR(profile sally)--. + +3 XMSLERVICE jobs handle work for all sally clients using the site + +Example new Toolkit (hybrid "private/persistent" connection):: + + $maxpool = 40; // 40 jobs good enough to handle my machine needs + + if ($i5persistentconnect) $conn = db2_pconnect($database,$user,$password); + else $conn = db2_connect($database,$user,$password); + + try { $ToolkitServiceObj = ToolkitService::getInstance($conn); } + catch (Exception $e) { die($e->getMessage()); } + + $internalKey = '/tmp/packers'.rand(1,$maxpool); + $ToolkitServiceObj->setToolkitServiceParams(array( + 'InternalKey'=>$internalKey, // *** RIGHT HERE internalKey/IPC $maxpool jobs for service + 'plug'=>"iPLUG32K")); // max size data i/o (iPLUG4K,32K,65K.512K,1M,5M,10M,15M) + + /* Do not use the disconnect() function for "state full" connection */ + /* NEVER EVER USE THIS ... $ToolkitServiceObj->disconnect(); */ + /* Why? *immed kill of job, not nice or sync, just kill */ + /* Use idle timeout for "state full" / "private" connections */ + + +* So simple, why does it work??? + + + Works much same as Apache FastCGI PHP jobs (even using random), because $maxpool "child XMLSERVICE workers" can be increased to match machine workload (tinker-trial-error) ... just like Apache threads ... just like PHP children ... all the same + + Most web requests are sub-second, so even on routing collision by random it is a short wait. + + +* Could i dedicate different pools to different tasks ??? + + + Yes, a bag full of really low effort work ``(/tmp/packers1-40, /tmp/vikings1-40)``. + + +* Could i dedicate different user ids to different pools as well as tasks??? + + + Yes, a bag full of really low effort work ``(/tmp/packers1-40, /tmp/vikings1-40)``. + + +* Can i idle timeout unused XMLSERVICE jobs ??? + + Yes of course, toolkit.ini setting or specify manually. + + NEVER EVER USE THIS ... ``$ToolkitServiceObj->disconnect();`` + + +* Should i use persistent connections?? + + + db2_pconnect -- Yes of course, it will save the time "attaching" a QSQSRVR job + + db2_connect -- However, you can use same technique with full open/close (yes it does work, try it) + + +* Can i prestart jobs? + + + Yes, but they will start on web demand which i think is much better (just like Apache) + + :: + + SBMJOB CMD(CALL PGM(ZENDSVR/XMLSERVICE) PARM('/tmp/packers1')) JOBD(ZENDSVR/ZSVR_JOBD) USER(PACKERS) + SBMJOB CMD(CALL PGM(ZENDSVR/XMLSERVICE) PARM('/tmp/vikings1')) JOBD(ZENDSVR/ZSVR_JOBD) USER(VIKINGS) + + + +4) State full -- reservation hybrid "private/persistent" connection +>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +These connections are hybrid "private/persistent" connection exclusively held for a period of time by each requesters, but returned back to pool for re-use. + +If your client needs to start/use/stop a reservation hybrid "private/persistent" connection, use the appropriate keyword in your XML sent to XMLSERVICE to gain exclusive rights to the hybrid "private/persistent" connection. + +* unique-user-key -- acquire exclusive IPC if available +* unique-user-key -- must appear XML every request job held forever until see +* unique-user-key -- release IPC for any other use +* Errors: + + + no-match-user-key -- non-matching key results in error almost instantly (no wait) + :: + + busy response (1301060): + + 1301060 + IPC owner busy + + + + thoughtful setting server idle timeout can control unwanted reservation hangs due to careless users or errors ** $ctl .= " \*idle(60)" ** + +**hybrid "private/persistent" connection with reservation** + +:: + + $maxpool = 3; + // -- PHP raw --- + $ctl = "*sbmjob"; + $ipc = "/tmp/sally".rand(1,$maxpool); + // -- or PHP toolkit (not available yet -- Alan) -- + + (1) (2) (3) (4) + Apache FastCGI DB2 (server mode) XMLSERVICE + ------- --------------- --------------------- ---------- + -->thread--socket->php-cgi--->QSQSRVR(profile sally)--. + -->thread--socket->php-cgi--->QSQSRVR(profile sally)--| + : | + -->thread--socket->php-cgi--->QSQSRVR(profile sally)--|->XMLSERVICE (/tmp/sally1) <--alive until stopped (or idle timemout) + unique-user-key <--exclusive reservation until stopped + unique-user-key + unique-user-key + -->thread--socket->php-cgi--->QSQSRVR(profile sally)--|->XMLSERVICE (/tmp/sally2) <--alive until stopped (or idle timemout) + unique-user-key <--exclusive reservation until stopped + unique-user-key + unique-user-key + -->thread--socket->php-cgi--->QSQSRVR(profile sally)--|->XMLSERVICE (/tmp/sally3) <--alive until stopped (or idle timemout) + unique-user-key <--exclusive reservation until stopped + unique-user-key + unique-user-key + : | + -->thread--socket->php-cgi--->QSQSRVR(profile sally)--| + -->thread--socket->php-cgi--->QSQSRVR(profile sally)--. + +3 XMSLERVICE jobs handle work for all sally clients using the site +However, reservation locks exclusive use until reservation is stopped. + +Example new Toolkit (hybrid "private/persistent" connection with reservation):: + + --- unfortunately reservation is not available in PHP wrapper yet (Alan) --- + --- raw xml pseudo code version of what happens follows start/use/stop --- + -- no time out -- + $ctl .= " *idle(0)" + -- request 1 -- + + + -- request 2 (two minutes later) -- + + + -- request 3 (1/2 hour later) -- + + + -- request n (2 hours later) -- + + + +.. + [--Author([[http://youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICEConnect?action=expirediff | s ]])--] + [--Tony "Ranger" Cairns - IBM i PHP / PASE--] + diff --git a/docs/date-time-timestamp.rst b/docs/date-time-timestamp.rst new file mode 100755 index 0000000..1a0ea97 --- /dev/null +++ b/docs/date-time-timestamp.rst @@ -0,0 +1,682 @@ + +XMLSERVICE/Toolkit Date, Time, Timestamp +======================================== + +`Goto Main Page`_ + +.. _Goto Main Page: index.html + +Date, Time, Timestamp - what do? +-------------------------------- +Time and date are easy, correct size character buffer and pass any format you want. Essentially xmlservice/toolkit works without formal knowledge of date, time, timestamp formats, simply passing another set of characters in memory for a parameter (pass-by-reference mostly), called program most likely throws exception when format incompatible (see with any MI debugger). + + +1) New PHP Toolkit Samples +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +ZZDATE (D datfmt(\*iso)) +"""""""""""""""""""""""""" + +:: + + getMessage()); } + $ToolkitServiceObj->setToolkitServiceParams( + array('InternalKey'=>$internalKey, // route to same XMLSERVICE job /tmp/myjob1 + 'subsystem'=>"QGPL/QDFTJOBD", // subsystem/jobd to start XMLSERVICE (if not running) + 'plug'=>"iPLUG32K")); // max size data i/o (iPLUG4K,32K,65K.512K,1M,5M,10M,15M) + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // * zzdate: check date parm + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // P zzdate B export + // D zzdate PI D + // D myDate D datfmt(*iso) + // * vars + // D retDate s D datfmt(*iso) + // /free + // retDate=myDate; + // myDate=d'2007-09-30'; + // return retDate; + // /end-free + // P E + $param[] = $ToolkitServiceObj->AddParameterChar ('both', 10, 'ZZDATE', 'myDate', '2009-05-11'); + $retrn[] = $ToolkitServiceObj->AddParameterChar ('both', 10, 'ZZDATE', 'retDate', '2002-02-02'); + $result = $ToolkitServiceObj->PgmCall('ZZSRV', $libxmlservice, $param, $retrn, array('func'=>'ZZDATE')); + // var_dump($result); + /* in/out param myDate */ + $myDate = "XMLSERVICE i/o param myDate: ".$result["io_param"]["myDate"]; + echo "$myDate\n"; + $expect = '2007-09-30'; + if (strpos($myDate,$expect)<1) die("Fail missing $expect\n"); + /* return value retDate */ + $retDate = "XMLSERVICE return retDate: ".$result["retvals"]["retDate"]; + echo "$retDate\n"; + $expect = '2009-05-11'; + if (strpos($retDate,$expect)<1) die("Fail missing $expect\n"); + /* all good */ + echo "Success\n"; + ?> + + +ZZDATEUSA (D datfmt(\*USA)) +""""""""""""""""""""""""""""" + +:: + + getMessage()); } + $ToolkitServiceObj->setToolkitServiceParams( + array('InternalKey'=>$internalKey, // route to same XMLSERVICE job /tmp/myjob1 + 'subsystem'=>"QGPL/QDFTJOBD", // subsystem/jobd to start XMLSERVICE (if not running) + 'plug'=>"iPLUG32K")); // max size data i/o (iPLUG4K,32K,65K.512K,1M,5M,10M,15M) + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // * zzdateUSA: check date parm + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // P zzdateUSA B export + // D zzdateUSA PI D datfmt(*USA) + // D myDate D datfmt(*USA) + // * vars + // D retDate s D datfmt(*USA) + // /free + // retDate=myDate; + // myDate=d'2007-09-30'; + // return retDate; + // /end-free + // P E + $param[] = $ToolkitServiceObj->AddParameterChar ('both', 10, 'ZZDATEUSA', 'myDate', '05/11/2009'); + $retrn[] = $ToolkitServiceObj->AddParameterChar ('both', 10, 'ZZDATEUSA', 'retDate', '2002-02-02'); + $result = $ToolkitServiceObj->PgmCall('ZZSRV', $libxmlservice, $param, $retrn, array('func'=>'ZZDATEUSA')); + // var_dump($result); + /* in/out param myDate */ + $myDate = "XMLSERVICE i/o param myDate: ".$result["io_param"]["myDate"]; + echo "$myDate\n"; + $expect = '09/30/2007'; + if (strpos($myDate,$expect)<1) die("Fail missing $expect\n"); + /* return value retDate */ + $retDate = "XMLSERVICE return retDate: ".$result["retvals"]["retDate"]; + echo "$retDate\n"; + $expect = '05/11/2009'; + if (strpos($retDate,$expect)<1) die("Fail missing $expect\n"); + /* all good */ + echo "Success\n"; + ?> + +ZZTIME (T timfmt(\*iso)) +"""""""""""""""""""""""""" + +:: + + getMessage()); } + $ToolkitServiceObj->setToolkitServiceParams( + array('InternalKey'=>$internalKey, // route to same XMLSERVICE job /tmp/myjob1 + 'subsystem'=>"QGPL/QDFTJOBD", // subsystem/jobd to start XMLSERVICE (if not running) + 'plug'=>"iPLUG32K")); // max size data i/o (iPLUG4K,32K,65K.512K,1M,5M,10M,15M) + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // * zztime: check time parm + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // P zztime B export + // D zztime PI T + // D myTime T timfmt(*iso) + // * vars + // D retTime s T timfmt(*iso) + // /free + // retTime=myTime; + // myTime=t'12.34.56'; + // return retTime; + // /end-free + // P E + $param[] = $ToolkitServiceObj->AddParameterChar ('both', 8, 'ZZTIME', 'myTime', '09.45.29'); + $retrn[] = $ToolkitServiceObj->AddParameterChar ('both', 8, 'ZZTIME', 'retTime', '02.02.02'); + $result = $ToolkitServiceObj->PgmCall('ZZSRV', $libxmlservice, $param, $retrn, array('func'=>'ZZTIME')); + // var_dump($result); + /* in/out param myDate */ + $myTime = "XMLSERVICE i/o param myTime: ".$result["io_param"]["myTime"]; + echo "$myTime\n"; + $expect = '12.34.56'; + if (strpos($myTime,$expect)<1) die("Fail missing $expect\n"); + /* return value retTime */ + $retTime = "XMLSERVICE return retTime: ".$result["retvals"]["retTime"]; + echo "$retTime\n"; + $expect = '09.45.29'; + if (strpos($retTime,$expect)<1) die("Fail missing $expect\n"); + /* all good */ + echo "Success\n"; + ?> + + +ZZTIME (T timfmt(\*USA)) +"""""""""""""""""""""""""" + +:: + + getMessage()); } + $ToolkitServiceObj->setToolkitServiceParams( + array('InternalKey'=>$internalKey, // route to same XMLSERVICE job /tmp/myjob1 + 'subsystem'=>"QGPL/QDFTJOBD", // subsystem/jobd to start XMLSERVICE (if not running) + 'plug'=>"iPLUG32K")); // max size data i/o (iPLUG4K,32K,65K.512K,1M,5M,10M,15M) + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // * zztimeUSA: check time parm + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // P zztimeUSA B export + // D zztimeUSA PI T timfmt(*USA) + // D myTime T timfmt(*USA) + // * vars + // D retTime s T timfmt(*USA) + // /free + // retTime=myTime; + // myTime=t'12.34.00'; + // return retTime; + // /end-free + // P E + $param[] = $ToolkitServiceObj->AddParameterChar ('both', 8, 'ZZTIMEUSA', 'myTime', '09:45 AM'); + $retrn[] = $ToolkitServiceObj->AddParameterChar ('both', 8, 'ZZTIMEUSA', 'retTime', '02:02 PM'); + $result = $ToolkitServiceObj->PgmCall('ZZSRV', $libxmlservice, $param, $retrn, array('func'=>'ZZTIMEUSA')); + // var_dump($result); + /* in/out param myDate */ + $myTime = "XMLSERVICE i/o param myTime: ".$result["io_param"]["myTime"]; + echo "$myTime\n"; + $expect = '12:34 PM'; + if (strpos($myTime,$expect)<1) die("Fail missing $expect\n"); + /* return value retTime */ + $retTime = "XMLSERVICE return retTime: ".$result["retvals"]["retTime"]; + echo "$retTime\n"; + $expect = '09:45 AM'; + if (strpos($retTime,$expect)<1) die("Fail missing $expect\n"); + /* all good */ + echo "Success\n"; + ?> + +ZZSTAMP (Z) +""""""""""" + +:: + + getMessage()); } + $ToolkitServiceObj->setToolkitServiceParams( + array('InternalKey'=>$internalKey, // route to same XMLSERVICE job /tmp/myjob1 + 'subsystem'=>"QGPL/QDFTJOBD", // subsystem/jobd to start XMLSERVICE (if not running) + 'plug'=>"iPLUG32K")); // max size data i/o (iPLUG4K,32K,65K.512K,1M,5M,10M,15M) + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // * zzstamp: check timestamp parm + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // P zzstamp B export + // D zzstamp PI Z + // D myStamp Z + // * vars + // D retStamp s Z + // /free + // retStamp=myStamp; + // myStamp=z'1960-12-31-12.32.23.000000'; + // return retStamp; + // /end-free + // P E + $param[] = $ToolkitServiceObj->AddParameterChar ('both', 26, 'ZZSTAMP', 'myStamp', '2011-12-29-12.45.29.000000'); + $retrn[] = $ToolkitServiceObj->AddParameterChar ('both', 26, 'ZZSTAMP', 'retStamp', '2002-02-02-02.02.02.000000'); + $result = $ToolkitServiceObj->PgmCall('ZZSRV', $libxmlservice, $param, $retrn, array('func'=>'ZZSTAMP')); + // var_dump($result); + /* in/out param myDate */ + $myStamp = "XMLSERVICE i/o param myStamp: ".$result["io_param"]["myStamp"]; + echo "$myStamp\n"; + $expect = '1960-12-31-12.32.23.000000'; + if (strpos($myStamp,$expect)<1) die("Fail missing $expect\n"); + /* return value retStamp */ + $retStamp = "XMLSERVICE return retStamp: ".$result["retvals"]["retStamp"]; + echo "$retStamp\n"; + $expect = '2011-12-29-12.45.29.000000'; + if (strpos($retStamp,$expect)<1) die("Fail missing $expect\n"); + /* all good */ + echo "Success\n"; + ?> + + + + +2) XMLSERVICE Raw XML Samples +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +ZZDATE (D datfmt(\*iso)) +"""""""""""""""""""""""""" + +:: + + xpath('/script/pgm'); + if (!$allpgms) die("Missing XML pgm info"); + // ----------------- + // output pgm call + // ----------------- + // only one program this XML script + $pgm = $allpgms[0]; + $name = $pgm->attributes()->name; + $lib = $pgm->attributes()->lib; + $func = $pgm->attributes()->func; + // pgm parms + $parm = $pgm->xpath('parm'); + if (!$parm) die("Fail XML pgm parms missing ($lib/$name.$func)\n"); + $var = $parm[0]->data->attributes()->var; + $actual = (string)$parm[0]->data; + $expect='2007-09-30'; + if ($actual != $expect) die("parm: $var ($actual not $expect) ($lib/$name.$func)\n"); + // pgm data returned + $retn = $pgm->xpath('return'); + if (!$retn) die("Fail XML pgm return missing ($lib/$name.$func)\n"); + $var = $retn[0]->data->attributes()->var; + $actual = (string)$retn[0]->data; + $expect='2009-05-11'; + if ($actual != $expect) die("return: $var ($actual not $expect) ($lib/$name.$func)\n"); + + // good + echo "Success ($lib/$name.$func)\n"; + + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // * zzdate: check date parm + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // P zzdate B export + // D zzdate PI D + // D myDate D datfmt(*iso) + function getxml() { + $clob = << + + ENDPROC; + return test_lib_replace($clob); + } + ?> + + +ZZDATEUSA (D datfmt(\*USA)) +""""""""""""""""""""""""""""" + +:: + + xpath('/script/pgm'); + if (!$allpgms) die("Missing XML pgm info"); + // ----------------- + // output pgm call + // ----------------- + // only one program this XML script + $pgm = $allpgms[0]; + $name = $pgm->attributes()->name; + $lib = $pgm->attributes()->lib; + $func = $pgm->attributes()->func; + // pgm parms + $parm = $pgm->xpath('parm'); + if (!$parm) die("Fail XML pgm parms missing ($lib/$name.$func)\n"); + $var = $parm[0]->data->attributes()->var; + $actual = (string)$parm[0]->data; + $expect='09/30/2007'; + if ($actual != $expect) die("parm: $var ($actual not $expect) ($lib/$name.$func)\n"); + // pgm data returned + $retn = $pgm->xpath('return'); + if (!$retn) die("Fail XML pgm return missing ($lib/$name.$func)\n"); + $var = $retn[0]->data->attributes()->var; + $actual = (string)$retn[0]->data; + $expect='05/11/2009'; + if ($actual != $expect) die("return: $var ($actual not $expect) ($lib/$name.$func)\n"); + + // good + echo "Success ($lib/$name.$func)\n"; + + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // * zzdateUSA: check date parm + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // P zzdateUSA B export + // D zzdateUSA PI D datfmt(*USA) + // D myDate D datfmt(*USA) + function getxml() { + $clob = << + + ENDPROC; + return test_lib_replace($clob); + } + ?> + +ZZTIME (T timfmt(\*iso)) +"""""""""""""""""""""""""" + +:: + + xpath('/script/pgm'); + if (!$allpgms) die("Missing XML pgm info"); + // ----------------- + // output pgm call + // ----------------- + // only one program this XML script + $pgm = $allpgms[0]; + $name = $pgm->attributes()->name; + $lib = $pgm->attributes()->lib; + $func = $pgm->attributes()->func; + // pgm parms + $parm = $pgm->xpath('parm'); + if (!$parm) die("Fail XML pgm parms missing ($lib/$name.$func)\n"); + $var = $parm[0]->data->attributes()->var; + $actual = (string)$parm[0]->data; + $expect='12.34.56'; + if ($actual != $expect) die("parm: $var ($actual not $expect) ($lib/$name.$func)\n"); + // pgm data returned + $retn = $pgm->xpath('return'); + if (!$retn) die("Fail XML pgm return missing ($lib/$name.$func)\n"); + $var = $retn[0]->data->attributes()->var; + $actual = (string)$retn[0]->data; + $expect='09.45.29'; + if ($actual != $expect) die("return: $var ($actual not $expect) ($lib/$name.$func)\n"); + + // good + echo "Success ($lib/$name.$func)\n"; + + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // * zztime: check time parm + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // P zztime B export + // D zztime PI T + // D myTime T timfmt(*iso) + function getxml() { + $clob = << + + ENDPROC; + return test_lib_replace($clob); + } + ?> + +ZZTIMEUSA (T timfmt(\*USA)) +""""""""""""""""""""""""""""" + +:: + + xpath('/script/pgm'); + if (!$allpgms) die("Missing XML pgm info"); + // ----------------- + // output pgm call + // ----------------- + // only one program this XML script + $pgm = $allpgms[0]; + $name = $pgm->attributes()->name; + $lib = $pgm->attributes()->lib; + $func = $pgm->attributes()->func; + // pgm parms + $parm = $pgm->xpath('parm'); + if (!$parm) die("Fail XML pgm parms missing ($lib/$name.$func)\n"); + $var = $parm[0]->data->attributes()->var; + $actual = (string)$parm[0]->data; + $expect='12:34 PM'; + if ($actual != $expect) die("parm: $var ($actual not $expect) ($lib/$name.$func)\n"); + // pgm data returned + $retn = $pgm->xpath('return'); + if (!$retn) die("Fail XML pgm return missing ($lib/$name.$func)\n"); + $var = $retn[0]->data->attributes()->var; + $actual = (string)$retn[0]->data; + $expect='09:45 AM'; + if ($actual != $expect) die("return: $var ($actual not $expect) ($lib/$name.$func)\n"); + + // good + echo "Success ($lib/$name.$func)\n"; + + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // * zztimeUSA: check time parm + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // P zztimeUSA B export + // D zztimeUSA PI T timfmt(*USA) + // D myTime T timfmt(*USA) + function getxml() { + $clob = << + + ENDPROC; + return test_lib_replace($clob); + } + ?> + + +ZZSTAMP (Z) +""""""""""" + +:: + + xpath('/script/pgm'); + if (!$allpgms) die("Missing XML pgm info"); + // ----------------- + // output pgm call + // ----------------- + // only one program this XML script + $pgm = $allpgms[0]; + $name = $pgm->attributes()->name; + $lib = $pgm->attributes()->lib; + $func = $pgm->attributes()->func; + // pgm parms + $parm = $pgm->xpath('parm'); + if (!$parm) die("Fail XML pgm parms missing ($lib/$name.$func)\n"); + $var = $parm[0]->data->attributes()->var; + $actual = (string)$parm[0]->data; + $expect='1960-12-31-12.32.23.000000'; + if ($actual != $expect) die("parm: $var ($actual not $expect) ($lib/$name.$func)\n"); + // pgm data returned + $retn = $pgm->xpath('return'); + if (!$retn) die("Fail XML pgm return missing ($lib/$name.$func)\n"); + $var = $retn[0]->data->attributes()->var; + $actual = (string)$retn[0]->data; + $expect='2011-12-29-12.45.29.000000'; + if ($actual != $expect) die("return: $var ($actual not $expect) ($lib/$name.$func)\n"); + + // good + echo "Success ($lib/$name.$func)\n"; + + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // * zzstamp: check timestamp parm + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // P zzstamp B export + // D zzstamp PI Z + // D myStamp Z + function getxml() { + $clob = << + + ENDPROC; + return test_lib_replace($clob); + } + ?> + + + + +.. + [--Author([[http://youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICEDate?action=expirediff | s ]])--] + [--Tony "Ranger" Cairns - IBM i PHP / PASE--] diff --git a/docs/debugging.rst b/docs/debugging.rst new file mode 100755 index 0000000..65b48c2 --- /dev/null +++ b/docs/debugging.rst @@ -0,0 +1,1112 @@ +XMLSERVICE/Toolkit debugging and service +======================================== + +`Goto Main Page`_ + +.. _Goto Main Page: index.html + +Who is this page for? +--------------------- +Instructions designed for IBM i developer learning PHP and XMLSERVICE ... + +Debug technique: It's as easy as 1-2-3-4-5-6-7-8-9 :) +----------------------------------------------------- +a) Run the test script that contains control "\*debug" and script will "hang" while it waits on #2 + + ``$ctl .= " *debug";`` + + 1. A MSGW inquiry message in DSPMSG QSYSOPR will be generated by the toolkit. Note the job information (number, name, user) provided in the MSGW. + + #. STRSRVJOB using that job information as parameters. + + #. STRDBG with the program and library you wish to debug. + + #. Answer the MSGW. Any answer will do--"G" is fine. + + #. The RPG program source will appear in debug mode in your terminal, ready to step through, allowing you to inspect variables, etc. + + #. When done inspecting and stepping, let the RPG program complete (using function keys indicated on screen). + + #. ENDDBG + +b) ENDSRVJOB + +Other debug options:: + + Job1 (threaded) Job 2 Job 3 (DB2 userid/password) Job 4 (optional XTOOLKIT job) + (ctl=*debugcgi) (ctl=*debugproc) (ctl=*debug) + browser -> Apache ->XMLCGI (Apache CGI child) -> QSQSRVR (XMLSERVICE *here) + -> QSQSRVR (XMLSERVICE client) -> XTOOLKIT (XMLSERVICE ipc=/tmp/flinstone) + + $ctl .= " *debugcgi"; // Job 2 - debug XMLCGI to see REST/HTTP data passed by client (when using REST only) + $ctl .= " *debugproc"; // Job 3 - debug XMLSERVICE "client" to see DB2 passed data (DB2 interface) + $ctl .= " *debug"; // Job 4 - debug XMLSERVICE "server" to see XMLSERVICE calls (DB2 interface) + // Note: when ctl='*here', both XMLSERVICE "client"/"server" + // are in QSQSRVSR job (NO XTOOLKIT job) + // remote: Attaching with LUW drivers changes QSQSRVR ... + // CLIENT (Client Access drivers) <==> QZDAxxxx + // CLIENT (DB2 Connect drivers) <==> QRWxxxx + + +.. + Tips + ---- + This is a collection of debug tips and where you may find your problem data. + * [[#logs | Check the logs (click here) ...]] -- viewing PHP logs is often enough to solve a problem + ** [[#phplog | Check PHP log for messages (click here) ...]] + ** [[#httplog | Check Apache logs for messages (click here) ...]] + ** [[#newlog | Check PHP toolkit logs for messages (click here) ...]] + * [[#jobactive | Check active XMLSERVICE job (click here) ...]] + ** WRKACTJOB -- ``5=work -> 10. Display job log -> F10=Display detailed messages`` can be very revealing + ** WRKACTJOB -- ``5=work -> 11. Display call stack -> F5=Refresh`` watch stack during stress runs + * [[#levels | Check one level at a time (click here) ...]] -- run simple tests up web site food chain can be very revealing + ** Download tests: 2012-05-18 - [[ Attach:levels-1.4.zip | levels-1.4.zip]] -- simple tests for each level + ** [[#level0 | Level 0 -- PHP working (click here) ...]] + ** [[#level1 | Level 1 -- Apache/PHP working (click here) ...]] + ** [[#level2 | Level 2 -- db2 connection working (click here) ...]] + ** [[#level3 | Level 3 - XMLSERVICE XML interface working (click here) ...]] + ** [[#level4 | Level 4 -- PHP New Toolkit working (click here) ...]] + ** [[#level5 | Level 5 -- PHP CW Toolkit working (optional) (click here) ...]] + * [[#debugger | Stop XMLSERVICE for debugger attach (click here) ..]] + ** [[#debuggerfind | WRKACTJOB find XMLSERVICE private mode only (click here) ...]] + ** [[#debuggerstop | XMLSERVICE QSYSOPR message stop anytime (click here) ...]] + * additional links common XMLSERVICE error information + ** [[XMLSERVICEFAQ | %blue%{XMLSERVICE FAQ}%%]] - have issues, try here + ** [[XMLSERVICEERROR | %blue%{XMLSERVICE ERRORS}%%]] - errors, try here + ** [[XMLSERVICEConnect | %blue%{XMLSERVICE Connections}%%]] - XMLSERVICE/Toolkit connections + ** [[XMLSERVICEConfig | %blue%{XMLSERVICE Performance}%%]] - performance XMLSERVICE + * PASE debug secrets (for a PASE geek) ... + ** [[PASE/Geek]] - everything about PASE from a geek + ** [[PASE/PASEDebug]] - debug secrets + * The following links are associated with my machine environment and may be helpful. + ** [[Apache/ab | Apache ab tool]] -- Apache ab web site stress tests are performed from the 5250 command line (call qp2term) or ssh myibmi using PASE (see Apache ab link install instructions) + *** You can run stress tests from 2-tier Linux/Windows using Apache ab tool, but i am using Apchae ab from PASE. + *** Apache ab tool is not perfect, but if you use relatively "sane" number of browsers like -c 10 it will work. + *** Apache ab test is designed to drive CPU to 100% (a good thing), so don't panic about CPU + ** [[Java/SystemDebugger | GUI System Debugger]] -- GUI debugger always available on IBM i + ** [[FastCGI/FastCGI | Apache FastCGI]] -- Apache FastCGI is used for PHP on IBM i, this link may be helpful. + ** [[PHP/IASP | PHP via iASP]] -- My machine setup using iASP Zend Server data. + *** Many of the examples below reference %red%/MYASP2/www/zend2%% because i am running all my Zend Server data in an Apache ZEND2 configuration on port 80 using iASP /MYASP2 ``(http://myibmi/hello.php)``. + *** Your configuration is more likely Zend Server out-of-the-box-install running in /www/zendsvr on port 10088 (http://myibmi:10088/hello.php) + +Working with service provider? +------------------------------ + +Here are a few common things that can provide useful information if working with outside support people. + +0) easy way 1.7.1 ... just send provider XMLSERVLOG/LOG and XMLSERVLOG/DUMP in SAVF + +assuming you have a trace of the issue (see xmlservice logging instructions) + +1) what do you see in error logs for simple tests ??? + +:: + + call qp2term + > tail /usr/local/zendsvr/var/log/php.log + > tail /myasp2/www/zend2/logs/error_log.Q112061800 + > tail /myasp2/www/zend2/logs/access_log.Q112061800 + +2) Summarize configuration (below)??? + + My Example + + :: + + SYSASP -- ZENDSVR library ... everything as installed + SYSASP -- /usr/local/zendsvr ... everything as installed + SYSASP -- /tmp ... many Zend "enterprise" components use /tmp + MYASP2 -- /myasp2/www/zend2 ... ALL "user data" moved from /www/zendsvr (/conf, /logs, /htdocs) + More ... /myasp2/www/zend2 + -- NO symbolic links between SYSAPS and MYASP2 for true "independent"ASP. + -- config files /myasp2/www/zend2/conf + attach: fastcgi.conf + attach: httpd.conf + +3) Around time of simple test failure ... do you see job logs? + + *wrkoutq* + + :: + + If jobs are still active (php-cgi, etc.) ... + wrkactjob + 5 ZEND2 QTMHHTTP BCI .0 PGM-QZSRHTTP SIGW + ZEND2 QTMHHTTP BCI .0 PGM-php-cgi.bi THDW + 5 ZEND2 QTMHHTTP BCI .0 PGM-php-cgi.bi TIMW + 10. Display job log, if active, on job queue, or pending + F10 -- full job log + +4) Around time of simple test failure ... do you see VLOGS??? + + :: + + STRSST + 1. Start a service tool + 5. Licensed Internal Code log + 1. Select entries from the Licensed Internal Code (LIC) log + Specify Licensed Internal Code Log Selection Values + -- leave as is enter --- + 0100A890 i5/OS PASE 4700 0013 06/15/12 09:03:43 7 <--- PASE + 0100A891 LIC log interface 0401 0100 06/15/12 09:04:08 1 + 0100A892 Signals management 4600 0001 06/15/12 09:04:51 255 + 0100A893 Process management 1300 0001 06/18/12 21:02:06 1 + + Maybe look for PASE, storage management, ASP, so on around "failure time" + + Note: + You can also dump logs to spool ... + +When you have no idea (dumping many processes)? +----------------------------------------------- + + Some times you just have no idea what is going on, here is a handy macro to dump a lot of stacks. + + :: + + STRSST/STRDST + 1. Start a service tool + 4. Display/Alter/Dump + 1. Display/Alter storage + ... or option for dump to printer ... + 2. Licensed Internal Code (LIC) data + 14. Advanced analysis + Option Command + 1 processinfo + + In this case dumping all process dealing with keyword "ZEND" appearing in job ... + + Specify Advanced Analysis Options + Output device . . . . . . : Display + Type options, press Enter. + Command . . . . : PROCESSINFO + Options . . . . . -NAMES ZEND + + Note: + Information dumped printer/display is same as paseps macro. + + + +Check active XMLSERVICE job +--------------------------- + +If you are using private connections (InternalKey or $ipc='/tmp/packers'), the XMLSERVICE job is probably available for examination with wrkactjob. +:: + + Work with Active Jobs LP0264D + 05/17/12 11:35:12 + CPU %: .0 Elapsed time: 00:00:00 Active jobs: 313 + + Type options, press Enter. + 2=Change 3=Hold 4=End 5=Work with 6=Release 7=Display message + 8=Work with spooled files 13=Disconnect ... + Current + Opt Subsystem/Job User Type CPU % Function Status + 5 XTOOLKIT DB2 BCH .0 PGM-XMLSERVICE SEMW + +1) Use ``option 5=work -> 10. Display job log -> F10=Display detailed messages`` to examine joblog on errors ... + +:: + + Display All Messages + System: LP0264D + Job . . : XTOOLKIT User . . : DB2 Number . . . : 435915 + + >> CALL PGM(XMLSERVICE/XMLSERVICE) PARM('/tmp/packers') + Pointer not set for location referenced. + Application error. MCH3601 unmonitored by ZZSRV at statement 0000000448, + instruction X'0000'. + + +2) Use ``option 5=work -> 11. Display call stack -> F5=Refresh`` to examine stack during stress tests ... + +:: + + Display Call Stack + System: LP0264D + Job: XTOOLKIT User: DB2 Number: 437582 + Thread: 0000000C + + + Type Program Statement Procedure + 1 QCMD QSYS /01C8 + XMLSERVICE XMLSERVICE _QRNP_PEP_XMLSERVICE + XMLSERVICE XMLSERVICE 1133 XMLSERVICE + XMLSERVICE XMLSERVICE 4607 RUNSERVER + XMLSERVICE XMLSERVICE 2983 SIGSETTIMEOUT + XMLSERVICE XMLSERVICE 2876 SIGTIMEROFF + QP0SSRV1 QSYS 19 setitimer + QP0SSRV2 QSYS 159 qp0sitimer__F12qp0sitimer_t > + +Check the logs +-------------- + +Check PHP log for messages +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +On my IBM i machine:: + + EDTF STMF('/usr/local/zendsvr/var/log/php.log') + -- or -- + call qp2term (or ssh myibmi) + > tail /usr/local/zendsvr/var/log/php.log + ... stuff + ... in /MYASP2/www/zend2/htdocs/hello.php on line 1 + +On my Linux machine:: + + $ tail /usr/local/zend/var/log/php.log + [16-May-2012 16:30:12] PHP Warning: db2_close() expects parameter 1 to be resource ... + + +Check Apache logs for messages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error logs for date in question:: + + EDTF STMF('/myasp2/www/zend2/logs/error_log.Q112051500') + -- or -- + call qp2term (or ssh myibmi) + > tail /myasp2/www/zend2/logs/error_log.Q112051500 + [Tue May 15 17:10:11 2012] [error] [client 9.5.158.38] CGI PROGRAM /QSYS.LIB/XMLSERVICE.LIB/XMLCGI.PGM RETURNED EXCEPTION ID CEE9901 + [Tue May 15 17:10:11 2012] [error] [client 9.5.158.38] SEE JOBLOG FOR JOB 428979/QTMHHTTP /ZEND2 + +access logs for date in question:: + + EDTF STMF('/myasp2/www/zend2/logs/access_log.Q112051500') + -- or -- + call qp2term (or ssh myibmi) + > tail /myasp2/www/zend2/logs/access_log.Q112051500 + 9.5.158.38 - - [15/May/2012:17:47:41 -0500] "GET /cgi-bin/xmlcgi.pgm?db2=LP0264D + + +Check PHP Toolkit logs for messages +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +toolkit.ini logfile +:: + + EDTF STMF('/usr/local/zendsvr/share/ToolkitApi/toolkit.log') + -- or -- + call qp2term (or ssh myibmi) + > tail /usr/local/zendsvr/share/ToolkitAPI/toolkit.log + 15 May 2012 22:53:35.752099 Running stateless; no IPC needed. Service library: ZENDSVR + 15 May 2012 22:53:36.588466 i5Error: num=14 cat=9 msg="No more entries." desc="No more entries." + + location set in toolkit.ini ... + EDTF STMF('/usr/local/zendsvr/share/ToolkitApi/toolkit.ini') + [log] + ; warnings and errors will be written to the logfile. + logfile = "/usr/local/zendsvr/share/ToolkitApi/toolkit.log" + +toolkit.ini debugLogFile +:: + + EDTF STMF('/usr/local/zendsvr/share/ToolkitApi/debug.log') + -- or -- + call qp2term (or ssh myibmi) + > tail /usr/local/zendsvr/share/ToolkitAPI/debug.log + + + + location set in toolkit.ini ... + EDTF STMF('/usr/local/zendsvr/share/ToolkitApi/toolkit.ini') + ; debug turns PHP toolkit's debug mode on or off (true/false). Default log file: /usr/local/zendsvr/share/ToolkitApi/debug.log + ; This log will grow large, so leave this false when you do not need to log everything. + debug = true + debugLogFile = "/usr/local/zendsvr/share/ToolkitApi/debug.log" + +PHP and XMLSERVICE bad XML +:: + + EDTF STMF('/tmp/bad.xml') + -- or -- + > tail /tmp/bad.xml + start + + ENDPROC; + $clobIn = test_lib_replace($xml); + + // xml common text replacement + function test_lib_replace($xml) { + global $libxmlservice, $iOPM; + if (!$iOPM) { + $was = array("xyzlibxmlservicexyz"); + $now = array("$libxmlservice"); + } + else { + $was = array("xyzlibxmlservicexyz","setOptions(array('customControl'=>'*debug')).`` + + Run your script. + The script will "hang" while it waits on #2 below... + (move to green screen 5250 for steps 2-10) + +2. A MSGW inquiry message in DSPMSG QSYSOPR will be generated by the toolkit. +3. Note the job information (number, name, user) provided in the MSGW. +4. STRSRVJOB using that job information as parameters. +5. STRDBG with the program and library you wish to debug. +6. Answer the MSGW. Any answer will do--"G" is fine. +7. The RPG program source will appear in debug mode in your terminal, ready to step through, allowing you to inspect variables, etc. +8. When done inspecting and stepping, let the RPG program complete (using function keys indicated on screen). +9. ENDDBG +#. ENDSRVJOB + + +.. + [--Author([[http://youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICEDebug?action=expirediff | s ]])--] + [--Tony "Ranger" Cairns - IBM i PHP / PASE--] diff --git a/docs/errors.rst b/docs/errors.rst new file mode 100755 index 0000000..b8fcf51 --- /dev/null +++ b/docs/errors.rst @@ -0,0 +1,238 @@ +XMLSERVICE Errors +================= +`Goto Main Page`_ + +.. _Goto Main Page: index.html + + +Common errors +------------- + +:: + + 3401 + + 1301011 + + + + +errnoile 3401 -- usually means another process with a different user profile is using IPC (/tmp/packers3) + +:: + + 3021 + 1301009 + IPC getshm fail + + +'Hung' semaphores/shared memory associated with a user never suppose to happen, but have seen in rare occasion. The following commands can be used to remove "hung" semaphores/shared memory associated with a user assuming you have appropriate authority to run like SECOFR, etc. (Ranger welcomes you to Unix geek-ville). + +Example:: + + grep -i qtm means ipcrm for (QTM)HHTTP ... + + endTCPSVR SERVER(*HTTP) INSTANCE(ZENDSVR) -- suggest end web server + + call qp2term + > ipcs | grep -i qtm | awk '{print "ipcrm -" tolower($1) " "$2}' -- show action, but NOT do action + > ipcs | grep -i qtm | awk '{print "ipcrm -" tolower($1) " "$2}' | sh -- remove semaphores/shared memory + + strTCPSVR SERVER(*HTTP) INSTANCE(ZENDSVR) -- suggest start web server + + +:: + + 1000005 + PASE resolve failed + + +The program you tried to call, shown here as "MYPGM" (in the CDATA tag), was not found. Make sure you specified the library and program correctly, including upper or lower case (usually upper case). + +:: + + 1480002 + XMLCGI invalid + *NONE + +\*NONE requires a special compile of the RPG source and is NOT enabled in production versions of the toolkit by default. +It is most useful for demos with custom security like this site, if you try on your machine you will likely get 1480002 +error XMLCGI. You can find details in plugerr_h. + + +ILE errno +--------- + +Errno Values for UNIX-Type Functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Programs using the UNIX(R)-type functions may receive error information as errno values. +The possible values returned are listed here in ascending errno value sequence. + +:: + + Name Value Text + EDOM 3001 A domain error occurred in a math function. + ERANGE 3002 A range error occurred. + ETRUNC 3003 Data was truncated on an input, output, or update operation. + ENOTOPEN 3004 File is not open. + ENOTREAD 3005 File is not opened for read operations. + EIO 3006 Input/output error. + ENODEV 3007 No such device. + ERECIO 3008 Cannot get single character for files opened for record I/O. + ENOTWRITE 3009 File is not opened for write operations. + ESTDIN 3010 The stdin stream cannot be opened. + ESTDOUT 3011 The stdout stream cannot be opened. + ESTDERR 3012 The stderr stream cannot be opened. + EBADSEEK 3013 The positioning parameter in fseek is not correct. + EBADNAME 3014 The object name specified is not correct. + EBADMODE 3015 The type variable specified on the open function is not correct. + EBADPOS 3017 The position specifier is not correct. + ENOPOS 3018 There is no record at the specified position. + ENUMMBRS 3019 Attempted to use ftell on multiple members. + ENUMRECS 3020 The current record position is too long for ftell. + EINVAL 3021 The value specified for the argument is not correct. + EBADFUNC 3022 Function parameter in the signal function is not set. + ENOENT 3025 No such path or directory. + ENOREC 3026 Record is not found. + EPERM 3027 The operation is not permitted. + EBADDATA 3028 Message data is not valid. + EBUSY 3029 Resource busy. + EBADOPT 3040 Option specified is not valid. + ENOTUPD 3041 File is not opened for update operations. + ENOTDLT 3042 File is not opened for delete operations. + EPAD 3043 The number of characters written is shorter than the expected record length. + EBADKEYLN 3044 A length that was not valid was specified for the key. + EPUTANDGET 3080 A read operation should not immediately follow a write operation. + EGETANDPUT 3081 A write operation should not immediately follow a read operation. + EIOERROR 3101 A nonrecoverable I/O error occurred. + EIORECERR 3102 A recoverable I/O error occurred. + EACCES 3401 Permission denied. + ENOTDIR 3403 Not a directory. + ENOSPC 3404 No space is available. + EXDEV 3405 Improper link. + EAGAIN 3406 Operation would have caused the process to be suspended. + EWOULDBLOCK 3406 Operation would have caused the process to be suspended. + EINTR 3407 Interrupted function call. + EFAULT 3408 The address used for an argument was not correct. + ETIME 3409 Operation timed out. + ENXIO 3415 No such device or address. + EAPAR 3418 Possible APAR condition or hardware failure. + ERECURSE 3419 Recursive attempt rejected. + EADDRINUSE 3420 Address already in use. + EADDRNOTAVAIL 3421 Address is not available. + EAFNOSUPPORT 3422 The type of socket is not supported in this protocol family. + EALREADY 3423 Operation is already in progress. + ECONNABORTED 3424 Connection ended abnormally. + ECONNREFUSED 3425 A remote host refused an attempted connect operation. + ECONNRESET 3426 A connection with a remote socket was reset by that socket. + EDESTADDRREQ 3427 Operation requires destination address. + EHOSTDOWN 3428 A remote host is not available. + EHOSTUNREACH 3429 A route to the remote host is not available. + EINPROGRESS 3430 Operation in progress. + EISCONN 3431 A connection has already been established. + EMSGSIZE 3432 Message size is out of range. + ENETDOWN 3433 The network currently is not available. + ENETRESET 3434 A socket is connected to a host that is no longer available. + ENETUNREACH 3435 Cannot reach the destination network. + ENOBUFS 3436 There is not enough buffer space for the requested operation. + ENOPROTOOPT 3437 The protocol does not support the specified option. + ENOTCONN 3438 Requested operation requires a connection. + ENOTSOCK 3439 The specified descriptor does not reference a socket. + ENOTSUP 3440 Operation is not supported. + EOPNOTSUPP 3440 Operation is not supported. + EPFNOSUPPORT 3441 The socket protocol family is not supported. + EPROTONOSUPPORT 3442 No protocol of the specified type and domain exists. + EPROTOTYPE 3443 The socket type or protocols are not compatible. + ERCVDERR 3444 An error indication was sent by the peer program. + ESHUTDOWN 3445 Cannot send data after a shutdown. + ESOCKTNOSUPPORT 3446 The specified socket type is not supported. + ETIMEDOUT 3447 A remote host did not respond within the timeout period. + EUNATCH 3448 The protocol required to support the specified address family is not available at this time. + EBADF 3450 Descriptor is not valid. + EMFILE 3452 Too many open files for this process. + ENFILE 3453 Too many open files in the system. + EPIPE 3455 Broken pipe. + ECANCEL 3456 Operation cancelled. + EEXIST 3457 File exists. + EDEADLK 3459 Resource deadlock avoided. + ENOMEM 3460 Storage allocation request failed. + EOWNERTERM 3462 The synchronization object no longer exists because the owner is no longer running. + EDESTROYED 3463 The synchronization object was destroyed, or the object no longer exists. + ETERM 3464 Operation was terminated. + ENOENT1 3465 No such file or directory. + ENOEQFLOG 3466 Object is already linked to a dead directory. + EEMPTYDIR 3467 Directory is empty. + EMLINK 3468 Maximum link count for a file was exceeded. + ESPIPE 3469 Seek request is not supported for object. + ENOSYS 3470 Function not implemented. + EISDIR 3471 Specified target is a directory. + EROFS 3472 Read-only file system. + EUNKNOWN 3474 Unknown system state. + EITERBAD 3475 Iterator is not valid. + EITERSTE 3476 Iterator is in wrong state for operation. + EHRICLSBAD 3477 HRI class is not valid. + EHRICLBAD 3478 HRI subclass is not valid. + EHRITYPBAD 3479 HRI type is not valid. + ENOTAPPL 3480 Data requested is not applicable. + EHRIREQTYP 3481 HRI request type is not valid. + EHRINAMEBAD 3482 HRI resource name is not valid. + EDAMAGE 3484 A damaged object was encountered. + ELOOP 3485 A loop exists in the symbolic links. + ENAMETOOLONG 3486 A path name is too long. + ENOLCK 3487 No locks are available. + ENOTEMPTY 3488 Directory is not empty. + ENOSYSRSC 3489 System resources are not available. + ECONVERT 3490 Conversion error. + E2BIG 3491 Argument list is too long. + EILSEQ 3492 Conversion stopped due to input character that does not belong to the input codeset. + ETYPE 3493 Object type mismatch. + EBADDIR 3494 Attempted to reference a directory that was not found or was destroyed. + EBADOBJ 3495 Attempted to reference an object that was not found, was destroyed, or was damaged. + EIDXINVAL 3496 Data space index used as a directory is not valid. + ESOFTDAMAGE 3497 Object has soft damage. + ENOTENROLL 3498 User is not enrolled in system distribution directory. + EOFFLINE 3499 Object is suspended. + EROOBJ 3500 Object is a read-only object. + EEAHDDSI 3501 Hard damage on extended attribute data space index. + EEASDDSI 3502 Soft damage on extended attribute data space index. + EEAHDDS 3503 Hard damage on extended attribute data space. + EEASDDS 3504 Soft damage on extended attribute data space. + EEADUPRC 3505 Duplicate extended attribute record. + ELOCKED 3506 Area being read from or written to is locked. + EFBIG 3507 Object too large. + EIDRM 3509 The semaphore, shared memory, or message queue identifier is removed from the system. + ENOMSG 3510 The queue does not contain a message of the desired type and (msgflg logically ANDed with IPC_NOWAIT). + EFILECVT 3511 File ID conversion of a directory failed. + EBADFID 3512 A file ID could not be assigned when linking an object to a directory. + ESTALE 3513 File handle was rejected by server. + ESRCH 3515 No such process. + ENOTSIGINIT 3516 Process is not enabled for signals. + ECHILD 3517 No child process. + EBADH 3520 Handle is not valid. + ETOOMANYREFS 3523 The operation would have exceeded the maximum number of references allowed for a descriptor. + ENOTSAFE 3524 Function is not allowed. + EOVERFLOW 3525 Object is too large to process. + EJRNDAMAGE 3526 Journal is damaged. + EJRNINACTIVE 3527 Journal is inactive. + EJRNRCVSPC 3528 Journal space or system storage error. + EJRNRMT 3529 Journal is remote. + ENEWJRNRCV 3530 New journal receiver is needed. + ENEWJRN 3531 New journal is needed. + EJOURNALED 3532 Object already journaled. + EJRNENTTOOLONG 3533 Entry is too large to send. + EDATALINK 3534 Object is a datalink object. + ENOTAVAIL 3535 IASP is not available. + ENOTTY 3536 I/O control operation is not appropriate. + EFBIG2 3540 Attempt to write or truncate file past its sort file size limit. + ETXTBSY 3543 Text file busy. + EASPGRPNOTSET 3544 ASP group not set for thread. + ERESTART 3545 A system call was interrupted and may be restarted. + ESCANFAILURE 3546 An object has been marked as a scan failure due to processing by an exit program associated with the scan-related integrated file system exit points. + + + + +.. + [--Author([[http://youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICEError?action=expirediff | s ]])--] + [--Tony "Ranger" Cairns - IBM i PHP / PASE--] diff --git a/docs/examples.rst b/docs/examples.rst new file mode 100755 index 0000000..8fbda50 --- /dev/null +++ b/docs/examples.rst @@ -0,0 +1,341 @@ + + +XMLSERVICE Examples +=================== +`Goto Main Page`_ + +.. _Goto Main Page: index.html + +The following foils include running examples you can click to help get started as you learn about this Open Source. + +* :ref:`part-1` +* :ref:`part-2` + +Goal of Open Source XMLSERVICE RPG library is flexibility, enabling ANY sort of transport local/remote connection with any language available in your enterprise to call resources on your IBM i machine. XMLSERVICE primary transport interface in production today is DB2 connections using included stored procedures (xmlstoredp.srvpgm iPLUGxxxx). XMLSERVICE secondary transport interface used mostly demos is Apache REST connection (xmlcgi.pgm). However, XMLSERVICE allows writing your own custom transport (anything you imagine). XML Service and Toolkit on-going mission is to add new function, improve performance, expand uses, with goal of never impacting existing customers. + + +**Warning**: This is an active IBM i education machine, occasionally examples may not work, try back later. + +.. _part-1: + +Part 1 - Protect your investment +-------------------------------- + +* XML Service Access Native IBM i Objects from any language using XML +* XML Service completely free/safe commercial use download (Github) +* XML Service examples HTML/XML (no PHP, no RPG) +* XML Service examples RPG (no PHP) +* XML Service vs. DB2 Stored Procedures - Why use XML Service at all? +* XML Service protect your investment summary + + +XML Service - is free +^^^^^^^^^^^^^^^^^^^^^ + +* XML Service is completely free/safe commercial use + + * BSD license, download, keep any source copy forever (safe) + +* XML Service written in RPG open source (you can change it) + + * Techniques used IBM i calls are stable, unlikely to change (ever) + +* XML Service is supported Open Source + + * XML Service fix/add improvement goal is never impact current customers + + +XML Service - XML everything +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* XML input <> XML output + + * Any device + * Any language + * Any local/remote connection + * Any IBM i service (PGM, CMD, System API, DB2, PASE) + + + +XML Service - IBM i Native Access through XML +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* Access Native IBM i Objects from any language using XML + + * Local or remote IBM i: IBM i local, IBM i-2-IBM i, Linux/Unix/Windows-2-IBM, etc. + * Any language access: PHP, Ruby, Java, Perl, RPG, no language at all (HTML/XML) + * Many Native Object Types: DB2 SQL and Native, Program call, Procedure call, Data Area, Data Queue, Message Queue, Commands, System values, Spool files, PASE utilities + +* Local or remote call interfaces to XMLSERVICE (included RPG download) + + * Primary: call DB2 stored procedures local or remote (iPLUG4K - iPLUG15M) + * Secondary: call REST HTTP local or remote (xmlcgi.pgm) + + +XML Service - Moving Parts +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* Any language + + * Browser HTML/XML + * Script PHP, Ruby, JavaScript, etc. + * Compiled RPG, C, etc. + +* Any local/remote connection + + * Linux/Unix/Windows/Mac ibm_db2/odbc/REST to IBM i + * Native IBM i ibm_db2/odbc/REST to IBM i + + +XML Service public "stateless" (\*here) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* XMLSERVICE public "stateless" (CTL='\*here', IPC='\*NA') + + * profile FRED (any public QSQ) + * profile SALLY (any public QSQ) + * profile RITA (any public QSQ) + * profile XAVIER (any public QSQ) + +* XMLSTOREDP->XMLSERVICE (QSQ) + + * QSQ temporary profile use (stateless) + * QSQ return to pool on script end + * XMLSERVICE restart every request (web style) + + +XML Service private "state full" (\*sbmjob) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* XMLSERVICE private "state full" (CTL='\*sbmjob', IPC='/tmp/xxxx') + + * profile FRED XTOOLKIT myjob1,myjob2 (private) + * profile SALLY XTOOLKIT sallyjob1 (private) + * profile RITA XTOOLKIT nursejob (private) + * profile XAVIER XTOOLKIT xjob1,xjob2,xjob3,xjob4,xjob5 (private) + +* XMLSTOREDP (QSQ) + + * QSQ temporary profile use (stateless) + * QSQ return to pool on script end + +* XMLSERVICE (XTOOLKIT) + + * XTOOLKIT owned by profile (private) + * XTOOLKIT job never ends (until killed) + * XTOOLKIT full state programming (5250 style) + + + +XML Service Configuration +^^^^^^^^^^^^^^^^^^^^^^^^^ + +* Apache REST (xmlcgi.pgm) +* DB2 stored procedures (xmlstoredp.srvpgm iPLUGxx) + + +XML Service - Example HTML/XML (no PHP, no RPG) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* XML Service HTTP browser direct (xmlcgi.pgm) + + * web enable via httpd.conf + + :: + +
+ ScriptAlias /cgi-bin/ /QSYS.LIB/XMLSERVICE.LIB/ + + order allow,deny + allow from all + SetHandler cgi-script + Options +ExecCGI + + + +XML Service - Example RPG (no PHP) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* XML Service stored procedure interface + + (1) RPG DB2 using Exec Sql (iPLUGxxx) + (2) RPG DB2 using CLI (iPLUGxxx) + +* web enable via httpd.conf + +:: + + # demo callpase + ScriptAlias /demo/ /QSYS.LIB/CALLPASE.LIB/ + + order allow,deny + allow from all + SetHandler cgi-script + Options +ExecCGI + + +Note: Exec Sql RPG CGI uses profile QTMHHTP1, however PHP is usually running QTMHHTTP, so you may fail authorisation sharing same XMLSERVICE job (1). Therefore, i recommend use RPG CLI technique to run any profile, where XMLSERVICE job(s) PHP/RPG is no problem (2). + + + +XML Service vs. DB2 Stored Procedures - Why use XML Service at all? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Why not write my own stored procedures? Why use XML Service at all? + ++------------------------------------------+-------------------------------------------+ +| XML Service | Stored procedure | ++==========================================+===========================================+ +| Device, protocol, & language portability | ++------------------------------------------+-------------------------------------------+ +|browser, REST, DB2, RPG... |device/driver/language specific | ++------------------------------------------+-------------------------------------------+ +| Complex Data Structures | ++------------------------------------------+-------------------------------------------+ +|trivial |near impossible | ++------------------------------------------+-------------------------------------------+ +| Call CMDs and collect data | ++------------------------------------------+-------------------------------------------+ +|trivial |very difficult | ++------------------------------------------+-------------------------------------------+ +| Call PASE utilities and collect data | ++------------------------------------------+-------------------------------------------+ +|trivial |very difficult | ++------------------------------------------+-------------------------------------------+ +| Route same job (IPC/internalKey) | ++------------------------------------------+-------------------------------------------+ +|trivial | near impossible | ++------------------------------------------+-------------------------------------------+ +| 30,000 records around 2 seconds | ++------------------------------------------+-------------------------------------------+ +|fast |faster | ++------------------------------------------+-------------------------------------------+ + + +Protect your investment +^^^^^^^^^^^^^^^^^^^^^^^ + +* XML Service protects your wallet 100% free download +* XML Service protects your skills 100% RPG source code +* XML Service protects your project costs with XML based low budget features +* XML Service protects your investment applications functions/performance over time +* XML Service protects your investment across device proliferation +* XML Service protects your investment across script language proliferation +* XML Service protects your investment across any transport driver (XML is a string) + + +.. _part-2: + +Part 2 - Production use today +----------------------------- + +**Topics** + +* PHP Toolkit included with Zend Server for IBM i +* XML Interface ibm_db2, pdo_ibm, odbc included with Zend Server for IBM i +* XML Interface and PHP Toolkit - Performance +* XML Interface and PHP Toolkit - Debugging +* XML Interface and PHP Toolkit - Active community/support + + +New PHP Toolkit included with Zend Server for IBM i +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +* Zend Server PHP Toolkit (included) + + * PHP CW Layer - old toolkit + * PHP New Toolkit - OO toolkit + + +XML Interface ibm_db2, pdo_ibm, odbc included with Zend Server for IBM i +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* XML Service stored procedure interface (included) + + * DB2 ibm_db2 param in/out (iPLUG 4k-15m) + * DB2 odbc result set (iPLUGR 4k-15m) + +.. + XML Service - Performance Speed Limits + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +--------------------------------------------------------------------------------------+ + | **Avg IBM i machine speed limits today (Oct 2012)** | + +--------------------------------------------------------------------------------------+ + |* Avg Apache serves HTML 800/hits second (non-FRCA) | + |* Avg persistent DB2 driver serves 400/hits second (db2_pconnect) | + |* Avg non-persistent DB2 driver serves 40/hits second (db2_connect) | + +--------------------------------------------------------------------------------------+ + | **PHP direct XMLSERVICE** | + +--------------------------------------------------------------------------------------+ + |* Avg PHP direct 200-400 calls/sec | + |* 30,000 records around 2 seconds | + +--------------------------------------------------------------------------------------+ + +XML Interface and PHP Toolkit - Active community/support +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You are not alone, seek out help ... + +* `Zend Server IBM i forums`_ + +.. _Zend Server IBM i forums: http://forums.zend.com/viewforum.php?f=67 + +* `PHP Toolkit forum`_ + +.. _PHP Toolkit forum: http://forums.zend.com/viewforum.php?f=113 + +* `Zend Manuals`_ + +.. _Zend Manuals: http://files.zend.com/help/Zend-Server-IBMi/zend-server.htm#php_toolkit_xml_service_functions.htm + + +Debug technique: It's as easy as 1-2-3-4-5-6-7-8-9 :) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +1. Run the test script that contains control "\*debug" and script will "hang" while it waits on #2 + + :: + + $ctl .= "*debug"; + +2. A MSGW inquiry message in DSPMSG QSYSOPR will be generated by the toolkit. Note the job information (number, name, user) provided in the MSGW. + +#. STRSRVJOB using that job information as parameters. + +#. STRDBG with the program and library you wish to debug. + +#. Answer the MSGW. Any answer will do--"G" is fine. + +#. The RPG program source will appear in debug mode in your terminal, ready to step through, allowing you to inspect variables, etc. + +#. When done inspecting and stepping, let the RPG program complete (using function keys indicated on screen). + +#. ENDDBG + +#. ENDSRVJOB + +Other debug options ... +:: + + Job1 (threaded) Job 2 Job 3 (DB2 userid/password) Job 4 (optional XTOOLKIT job) + (ctl=*debugcgi) (ctl=*debugproc) (ctl=*debug) + browser -> Apache ->XMLCGI (Apache CGI child) -> QSQSRVR (XMLSERVICE *here) + -> QSQSRVR (XMLSERVICE client) -> XTOOLKIT (XMLSERVICE ipc=/tmp/flinstone) + + $ctl .= " *debugcgi"; // Job 2 - debug XMLCGI to see REST/HTTP data passed by client (when using REST only) + $ctl .= " *debugproc"; // Job 3 - debug XMLSERVICE "client" to see DB2 passed data (DB2 interface) + $ctl .= " *debug"; // Job 4 - debug XMLSERVICE "server" to see XMLSERVICE calls (DB2 interface) + // Note: when ctl='*here', both XMLSERVICE "client"/"server" + // are in QSQSRVSR job (NO XTOOLKIT job) + // remote: Attaching with LUW drivers changes QSQSRVR ... + // CLIENT (Client Access drivers) <==> QZDAxxxx + // CLIENT (DB2 Connect drivers) <==> QRWxxxx + + + + +.. + [--Author([[http://youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICEIntro?action=expirediff | s ]])--] + [--Tony "Ranger" Cairns - IBM i PHP / PASE--] diff --git a/docs/faq.rst b/docs/faq.rst new file mode 100755 index 0000000..1d61c7a --- /dev/null +++ b/docs/faq.rst @@ -0,0 +1,420 @@ +XMLSERVICE FAQ +============== + +`Goto Main Page`_ + +.. _Goto Main Page: index.html + +Tip +--- +Some webserver products take issue with `` this + + + +XMLSERVICE is hanging right out of box (because CCSID 65535) +------------------------------------------------------------ +:: + + Q: XMLSERVICE is returning junk, how can i fix? + Q: XMLSERVICE is not working at all and IPC /tmp/xxx dir not created, how can i fix? + Q: XMLSERVICE is hanging? + A: You need to change Apache settings. + + The following steps lead to a successful test:: + + end current running jobs: + ENDTCPSVR SERVER(*HTTP) HTTPSVR(ZENDSVR) + ENDPJ SBS(QSYSWRK) PGM(QSQSRVR) OPTION(*IMMED) + + check the system CCSID value is 65535: + DSPSYSVAL SYSVAL(QCCSID) + Coded character set + identifier . . . . . : 65535 1-65535 + + edit configuration: + /www/zendsvr/conf/fastcgi.conf (optional from editor): + SetEnv="LANG=C" + + /www/zendsvr/conf/httpd.conf (web admin GUI port 2001 - ZENDSVR): + DefaultFsCCSID 37 ... or 208 (Italian) ... or so on ... + CGIJobCCSID 37 ... or 208 (Italian) ... or so on ... + + restart jobs: + STRPJ SBS(QSYSWRK) PGM(QSQSRVR) + STRTCPSVR SERVER(*HTTP) HTTPSVR(ZENDSVR) + + + wrkactjob + If you see ANY XMLSERVICE jobs, kill them before retry. + + This will allow XMLSERVICE to work (IPC was junk not /tmp/whatever). + This should fix "junk" from ibm_db2 on i (odbc, pdo_ibm, etc.). + + +php-cli command line issues (CCSID 65535) ... +--------------------------------------------- + +The easy way to fix this is to simply CHGUSRPRF and specify a valid CCSID like 37, then as you sign-on (ssh, call qp2term) you will not be stuck with nasty 65535 problems. + +And now i can run my test cases (ssh->tcsh) +:: + + >setenv PATH /usr/local/zendsvr/bin:$PATH + >setenv LIBPATH /usr/local/zendsvr/lib + >cd /mytests + >pear run-tests *.phpt + >php test.php + + + +XMLSERVICE DBCS (CCSID 5035) +---------------------------- + +:: + + Q: Did anyone try DBCS with XMLSERVICE? + A: Yes. Japanese 5035 appears to work. + The following steps lead to a successful test: + + end current running jobs: + ENDTCPSVR SERVER(*HTTP) HTTPSVR(ZENDSVR) + ENDPJ SBS(QSYSWRK) PGM(QSQSRVR) OPTION(*IMMED) + + change the system CCSID value: + CHGSYSVAL SYSVAL(QCCSID) VALUE(5035) + + edit configuration: + /www/zendsvr/conf/fastcgi.conf (editor: + SetEnv="CCSID=1208" SetEnv="LANG=C" + + /www/zendsvr/conf/httpd.conf (web admin GUI port 2001 - ZENDSVR): + DefaultFsCCSID 5035 + CGIJobCCSID 5035 + + restart jobs: + STRPJ SBS(QSYSWRK) PGM(QSQSRVR) + STRTCPSVR SERVER(*HTTP) HTTPSVR(ZENDSVR) + + Note: If using ODBC will likely have to start/stop prestart jobs as well. + + +XMLSERVICE multi-CCSID or problems? +----------------------------------- +Using default PHP toolkit DB2 clob interface (iPLUGxxx/iPLUGRxxx), ccsid conversion occurs naturally as DB2 client/server and you will not have to code before/after, but method is available if you have a specific concern or you have scripts returning many different languages. + +Example:: + + bin2hex('Hebrew_ascii_raw_chars') + bin2hex('Farsi_ascii_raw_chars') + bin2hex('Russia_ascii_raw_chars') + bin2hex('Italy_ascii_raw_chars') + bin2hex('Germany_ascii_raw_chars') + bin2hex('Korea_ascii_raw_chars') + bin2hex('Japan_ascii_raw_chars') + bin2hex('China_ascii_raw_chars') + where: + before - XMLSERVICE convert CCSID before ILE program call + after - XMLSERVICE convert CCSID after ILE program call for client return + bin2hex() - script hex string unaltered ascii image (also returned hex string avoid any conversion) + pack() - script uses pack('H*',"xml_hex_back") function in PHP program for ascii characters + Note: + Up to four conversions can take place for the truly diabolical ccsid issues + bin2hex('wild_ascii_raw_chars') + flow: + -> PHP client bin2hex('wild_ascii_raw_chars') + -> xmlservice hex2bin back to 'wild_ascii_raw_chars' + -> xmlservice convert cc1->cc2->cc3->cc4 (before) + -> xmlservice make ILE call + -> xmlservice convert cc4->cc3->cc2->cc1 (after) + -> xmlservice tohex "xml_hex_back" + -> PHP client $chars = pack('H*',"xml_hex_back") + + +Can i use CDATA for XML special characters? +------------------------------------------- + +Many common XMLSERVICE tags already support a form of CDATA +:: + + ]]> + 10.0]]> + ?]]> + +BUT there are restrictions for speed of parsing (i think reasonable) + +* not allowed to put reserved words in cdata - NO ``]]>`` +* no binary data (character data only) - ```` +* there may be other restrictions because i don't know everything about CDATA "abuse" in XML. + +.. + LIBL, LIBL, LIBL + ---------------- + + Information moved to performance page *[[XMLSERVICELibl | %blue%{XMLSERVICE Libl}%%]]*. + + prestart XMLSERVICE jobs dramatically improve first-call performance? + --------------------------------------------------------------------- + + Information moved to performance page *[[XMLSERVICEConfig | %blue%{XMLSERVICE Performance}%%]]*. + + Where do i find messages? + ------------------------------------ + + Information moved to error page *[[XMLSERVICEError | %blue%{XMLSERVICE Errors}%%]]*. + +How do i kill/end XMLSERVICE jobs? +---------------------------------- + +XMLSERVICE jobs stay active until killed with CTL option \*immed. You need only know the ipc name (/tmp/fred01, /tmp/sally43, etc.), and have appropriate profile abilities to issue the XML/CTL kill to a running XMLSERVICE job. + +Example kill XMLSERVICE for '/tmp/rangerusr':: + + $ipc='/tmp/rangerusr'; + $ctlKill="*immed"; + $clobInKill = ''; + $sql = "call $libxmlservice.iPLUGR4K('$ipc','$ctlKill','$clobInKill')"; + $ret=db2_exec($conn,$sql); + + +Can i run "stateless" XMLSERVICE jobs (\*here)? +----------------------------------------------- + +XMLSERVICE can also run in the stored procedure (or web) job by using the ctl='\*here' option. +When you run in \*here mode you do NOT need an IPC (ipc is ignored). XMLSERVICE is just another +program in the job in \*here mode, so when the job ends so does XMLSERVICE. + +Example XMLSERVICE running in stored procedure job:: + + $ipc='not need ipc it is ignored'; + $ctl="*here"; + $clobIn = ' + '; + $sql = "call $libxmlservice.iPLUGR512K('$ipc','$ctl','$clobIn')"; + $ret=db2_exec($conn,$sql); + +**Note**: Performance is generally slower when running in this mode, +because XMLSERVICE has to start from scratch each time. There are varying +degrees of overall XMLSERVICE performance using \*here ("stateless") +depending on application interface supporting +persistent/non-persistent/private connections (ie., db2_connect vs. db2_pconnect). + +**Warning**: It should be noted XMLSERVICE "private" connection +with ipc (ipc=/tmp/fred01, ipc=/tmp/fred02, ipc=/tmp/sally43) is most often +the best high performance fit for calling "real" RPG programs because most +RPG programs actually do something other than "hello world" with +multiple files open and lot's of state you do not want to restart each time +called (much better to dedicate a XMLSERVICE job to the task). +So, while \*here is an easy answer for operator style process +management (older toolkit options), and works ok for small "calculate this" +SRVPGMs, you will likely find \*here does not work in a practical world +where business tasks are much more complex (ie. exactly why XMLSERVICE +is optimized for using a IPC connection). + +What is IPC?? +------------- + +IPC is traditional computer science abbreviation for InterProcess Communication (IPC), which in the case of XMLSERVICE is simply a unique directory name /tmp/mydir01 used for machine-wide unique 'key/token' to route XML script documents between various clients calling XMLSERVICE job(s)(/tmp/fred01, /tmp/sally02, etc.). + +Behind the scenes of XMLSERVICE the unique IPC provided by any client interface (/tmp/fred01, /tmp/sally02, etc.), establishes a shared memory area for XML information/control passing and a semaphore to provide one-at-time processing in XMLSERVICE job(s). All XMLSERVICE InterProcess Communication activities are keyed and routed to appropriate XMLSERVICE jobs by the IPC name in the /tmp directory (/tmp/fred01, /tmp/sally02, etc.). + +As far as security is concerned, all normal IBM i IFS authority rules apply to the IPC access (/tmp/fred01, /tmp/sally02, etc.), +therefore assuming no group profile override precedence, only profile fred will be able to access IPC /tmp/fred01 XMLSERVICE job, +and only sally profile will be able to access /tmp/sally XMLSERVICE job, and so on for all profiles used. Of course, +high authority profiles like \*SECOFR have more abilities over \*USER profiles, but general rule is the profile you see +in wrkactjob "owns/uses" the XMLSERVICE job. + + +deployment centric, not development centric (caches used XMLSERVICE) +-------------------------------------------------------------------- + +XMLSERVICE is a deployment/production centric service, not development centric, therefore XMLSERVICE internal caches are used to speed up next call processing. XMLSERVICE job(s) caching allows production sites to simply ignore costly operations like XMLSERVICE find/load called modules (your RPG programs), under the assumption that production machine is stable (no recompiles going on). + +However, if you are doing development work on your machine (recompiles CL/RPG), you will have to end active/running XMLSERVICE job(s) and restart to call your new program. + +Example (actual email): My coworker has told me on more than one occasion that I'm calling his "old" CL or RPG programs even though he recompiled them. I resolve this by restarting the correct XMLSERVICE jobs, allowing me to call the current versions of CL/RPG programs. + + +Why not use pcml? +----------------- + +pcml falls short in terms of supported RPG types and common attribute conventions (varying) and simply cannot do what this XMLSERVICE custom XML interface is capable of doing. pcml falls even shorter when trying to return complex structures as elements, which is very popular for modern RPG SRVPGMs (XMLSERVICE supports of course). Last, pcml is especially short of the "complete scripting" mark when calling PASE shells, or CMDs (XMLSERVICE supports). + +I leave it to the entrepreneurial user to XSLT map PCML to XMLSERVICE (hint). + +DB2 XML SQL not work ctl='\*here'? +---------------------------------- + +DB2 SQL XML does not work in-line stateless (``$ctl='*here'``), but works fine with normal private connections +(``ipc='/tmp/fred', $ctl='*sbmjob'``). + +.. + DB2 Connect (2 tier) + -------------------- + + See this wiki link for details [[Tier2/DB2Connect | DB2 Connect]] + + ODBC (2 tier) + -------------- + + See this link [[Tier2/IAccess | ODBC IAccess]] + +Workaround for "bad" drivers leaving junk back of output clob +------------------------------------------------------------- +The world is not perfect 1-2 tier DB2 drivers IBM i, Windows, Linux, etc., so occasionally a "hack" is handy. + +I always "scope" my XML input requests with ````, so anything past tailing ```` is 'junk' (errors return as ``...``). + +The new XMLSERVICE keyword \*hack adds ```` back of every record return result set can be very useful for drivers that do not support stored procedure +in/out parameters like PHP odbc. + +:: + + function driverJunkAway($xml) + { + $clobOut = $xml; + if (! trim($clobOut)) return $clobOut; + + // result set has extra data (junk) + $fixme = ''; + $pos = strpos($clobOut,$fixme); + if ($pos > -1) { + $clobOut = substr($clobOut,0,$pos); + } + else { + $fixme = ''; + $pos = strpos($clobOut,$fixme); + if ($pos > -1) { + $clobOut = substr($clobOut,0,$pos+strlen($fixme)); + } + // maybe error/performance report + else { + $fixme = ''; + $pos = strpos($clobOut,$fixme); + if ($pos > -1) { + $clobOut = substr($clobOut,0,$pos+strlen($fixme)); + } + } + } + return $clobOut; + } + + +Can i use curl to test XMLSERVICE? +---------------------------------- + +This works from my Linux machine to IBM i. +:: + + curl http://myibmi/cgi-bin/xmlcgi.pgm --data-urlencode db2=*LOCAL --data-urlencode uid=*NONE + --data-urlencode pwd=*NONE --data-urlencode ipc=/tmp/rangerhtmlonly --data-urlencode ctl=*sbmjob + --data-urlencode xmlin="" --data-urlencode xmlout=32768 + +I need to start over kill everything +------------------------------------ + +... doing a lot of machine "updating" test cw/xmlservice versions ... many install errors ... state unknown ... i recommend following actions assuming only Zend Server running this system (be kind to others) ... + +1) end zendsrvr http +:: + + ENDTCPSVR SERVER(*HTTP) INSTANCE(ZENDSVR) + +wrkactjob wait all php-cgi to go down, kill php-cgi \*immed by hand if won't die. + +2) clear left over IPC attributes for cw/xmlservice test profiles +:: + + call qp2term + > ipcs | grep -i qtm | awk '{print "ipcrm -" tolower($1) " "$2}' | sh + > ipcs | grep -i myid1 | awk '{print "ipcrm -" tolower($1) " "$2}' | sh + > ipcs | grep -i myid1 | awk '{print "ipcrm -" tolower($1) " "$2}' | sh + Where: + > qtm - clear IPCs for QTMHHTTP + > myid1 - clear IPCs for profile MYID1 + > myid2 - clear IPCs for profile MYID2 + +3) carefully reset /tmp +:: + + call qp2term + > cd /tmp + > pwd + /tmp --- if you do not see /tmp, stop now rm -R may kill whole system + > rm -R * + +4) recycle DB2 connections (optional) +:: + + ENDPJ SBS(QSYSWRK) PGM(QSQSRVR) OPTION(*IMMED) + STRPJ SBS(QSYSWRK) PGM(QSQSRVR) + +5) re-check configuration + +Change these files and restart everything (web, db2, xmlservice, tec.) +:: + + I. Apache: /www/zendsvr/conf/httpd.conf <-- (ILE Apache side) + DefaultFsCCSID 37 ... or 280 (Italian) ... or so on ... + CGIJobCCSID 37 ... or 280 (Italian) ... or so on ... + +6) restart http +:: + + STRTCPSVR SERVER(*HTTP) HTTPSVR(ZENDSVR) + + + +PASE php-cgi out of memory (SetEnv="LDR_CNTRL=MAXDATA=0x80000000") +------------------------------------------------------------------ + +*This is a very rare condition, but if you find your huge size PHP script runs out of PASE memory ...* + +You can force most any 32-bit PASE program to leave more heap space (including php-cgi), just specify environment variable up to LDR_CNTRL=MAXDATA=0x80000000 (8 * 256MB). However, please be aware while you are increasing the heap, you are also limiting the number of 256MB segments available for shared memory (not commonly used by PHP programs anyway). + +* [[http://www.ibm.com/developerworks/aix/library/j-nativememory-aix/index.html]] - Thanks for the memory article applies to PASE as well (therefore also PHP)* + +In FastCGI php-cgi just add directive to fastcgi.config. +:: + + /www/zendsvr/conf/fastcgi.conf + Server type="application/x-httpd-php" ... normal stuff ... SetEnv="LDR_CNTRL=MAXDATA=0x80000000" + + +Graphically memory of PASE 32 bit LDR_CNTRL=MAXDATA=0x80000000. Variations of LDR_CNTRL allow you to rearrange even reserved segments and shared libraries, but these uses of LDR_CNTRL are infrequently deployed (except for the most gruesome memory hog Java programs MAXDATA=0xD0000000\@DSA). +:: + + >export LDR_CNTRL=MAXDATA=0x80000000 + >php -v + memory of PASE program PHP + 00000000 - 0FFFFFFF - PASE kernel heap (mostly read or no access to user programs) + 10000000 - 1FFFFFFF - PASE main program text/code (php) + 20000000 - 2FFFFFFF - PASE stack (programs usually share stack/heap in this one segment 256MB) + 30000000 - 3FFFFFFF ------ + 40000000 - 4FFFFFFF | + 50000000 - 5FFFFFFF | + 60000000 - 6FFFFFFF |--- PASE heap (LDR_CNTRL=MAXDATA=0x80000000) + 70000000 - 7FFFFFFF | used for new/delete/malloc/free allocations PHP to run scripts + 80000000 - 8FFFFFFF | + 90000000 - 9FFFFFFF | *Note: no more segments avail for shared memory + A0000000 - AFFFFFFF ------ + B0000000 - BFFFFFFF - reserved + C0000000 - CFFFFFFF - reserved + D0000000 - DFFFFFFF - machine wide shared library text/code (libc.a, etc.) + E0000000 - EFFFFFFF - shared memory + F0000000 - FFFFFFFF - machine wide shared library data + + +.. + [--Author([[http://youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICEFAQ?action=expirediff | s ]])--] + [--Tony "Ranger" Cairns - IBM i PHP / PASE--] + diff --git a/docs/faster.rst b/docs/faster.rst new file mode 100755 index 0000000..6c3416a --- /dev/null +++ b/docs/faster.rst @@ -0,0 +1,204 @@ +Toolkit go faster +================= + +`Goto Main Page`_ + +.. _Goto Main Page: index.html + +Before you start ... + +This page is about running faster using persistent and private pooled connections workload balancing techniques. This is not a discussion about using QTEMP or \*LDA in called RPG programs across browser clicks, that is a different topic entirely. + +New version Zend Server include ZRAY +------------------------------------ + +Server set up asks if Developer or Production. If Developer, Z-Ray is on by default, but easy to turn off. If Production, Z-Ray is off by default, but easy to turn on. Z-Ray is recommended to be off in Production for performance and security reasons. It can be set up in secured mode in production to only be used on pages deliberately accessed by a developer. + +Toolkit connections performance +------------------------------- +There are many ways to workload balance PHP Toolkit connections: + +* slower ... public "stateless" job - no 'InternalKey' (default) + +:: + +$ToolkitServiceObj->setToolkitServiceParams( array('stateless'=>true)); + +* faster ... private "state full " single job - $user="SALLY" connected to single xmlservice job + +:: + + // php:db2_connect <- XML IN/OUT -> QSQSRVR:XMLSERVICE <- "/tmp/$user" -> XTOOLKIT:XMLSERVICE + $ToolkitServiceObj->setToolkitServiceParams(array('InternalKey'=>"/tmp/$user")) + +* fastest ... private "state full" pool jobs - $user="SALLY".rand(1,10) connected to 10 random xmlservice jobs + +:: + + // php:db2_connect :<- XML IN/OUT -> QSQSRVR:XMLSERVICE <- "/tmp/$user1" -> XTOOLKIT:XMLSERVICE(1) + // rand pick server: + // :<- XML IN/OUT -> QSQSRVR:XMLSERVICE <- "/tmp/$user10" -> XTOOLKIT:XMLSERVICE(10) + $ToolkitServiceObj->setToolkitServiceParams(array('InternalKey'=>"/tmp/$user".rand(1,10)) + +The following is a relative performance guideline: + +* slower ... public "stateless" job ... stateless connection (safe default) + +:: + + QSQSRVR:XMLSERVICE + + $extension='ibm_db2'; + try { $ToolkitServiceObj = ToolkitService::getInstance($db, $user, $pass, $extension); } + catch (Exception $e) { echo $e->getMessage(), "\n"; exit(); } + $options = array('stateless'=>true,'plugSize'=>'4K'); + $ToolkitServiceObj->setToolkitServiceParams($options); + $ToolkitServiceObj->disconnect(); + +* slower ... uses db2_connect / odbc_connect (full open/close of QSQSRVR job) + +* slower ... starts/stops xmlservice within QSQSRVR job each script request + +* What to watch for ... + + not much, this is a simple full start/stop model (slow, but safe mostly) + +* faster ... private pool jobs ... state full connection (private) + +:: + + QSQSRVR:XMLSERVICE <- "/tmp/$user1" -> XTOOLKIT:XMLSERVICE(1) + // :<- XML IN/OUT -> QSQSRVR:XMLSERVICE <- "/tmp/$user2" -> XTOOLKIT:XMLSERVICE(2) + // rand pick a server: + // :<- XML IN/OUT -> QSQSRVR:XMLSERVICE <- "/tmp/$user10" -> XTOOLKIT:XMLSERVICE(10) + + $extension='ibm_db2'; + try { $ToolkitServiceObj = ToolkitService::getInstance($db, $user, $pass, $extension); } + catch (Exception $e) { echo $e->getMessage(), "\n"; exit(); } + $maxpool = 10; // 10 jobs good enough to handle my machine needs + $internalKey = '/tmp/packers'.rand(1,$maxpool); + $ToolkitServiceObj->setToolkitServiceParams(array('InternalKey'=>$internalKey)); + /* Do not use the disconnect() function for "state full" connection */ + /* NEVER EVER USE THIS ... $ToolkitServiceObj->disconnect(); */ + /* Why? *immed kill of job, not nice or sync, just kill */ + +* slower ... uses db2_connect / odbc_connect (full open/close of QSQSRVR job) + +* fastest ... starts another xmlservice /tmp/packers1-to-10 beyond QSQSRVR job (XTOOLKIT PGM-XMLSERVICE), and jobs 1-10 use over and over and over ... until killed by $ToolkitServiceObj->disconnect() or by IBM i operator + +* What to watch for ... + + Security 1: profile FRED can not attach to profile SALLY XMLSERVICE jobs (SALLY owns /tmp/packers1-10, FRED will have to make his own jobs /tmp/bears1-10) + + Co-operate with web site: develop procedures start/stop xmlservice when doing system maintenance (kill xmlservice jobs, etc.) + + A live job: QTEMP/\*LDA are re-used, therefore your called applications must be ready to handle/clear + +* fastest ... private pool jobs ... add persistent db2 connections (db2_pconnect) + +:: + + QSQSRVR:XMLSERVICE <- "/tmp/$user1" -> XTOOLKIT:XMLSERVICE(1) + // :<- XML IN/OUT -> QSQSRVR:XMLSERVICE <- "/tmp/$user2" -> XTOOLKIT:XMLSERVICE(2) + // rand pick a server: + // :<- XML IN/OUT -> QSQSRVR:XMLSERVICE <- "/tmp/$user10" -> XTOOLKIT:XMLSERVICE(10) + + require_once("ToolkitService.php"); + $i5persistentconnect = true; + if ($i5persistentconnect) $conn = db2_pconnect($database,$user,$password); + else $conn = db2_connect($database,$user,$password); + if (!$conn) echo "Bad connect: $conn,$database,$user,perm=$i5persistentconnect"; + try { $ToolkitServiceObj = ToolkitService::getInstance($conn); } + catch (Exception $e) { die($e->getMessage()); } + $maxpool = 10; // 10 jobs good enough to handle my machine needs + $ToolkitServiceObj->setToolkitServiceParams(array('InternalKey'=>'/tmp/packers'.rand(1,$maxpool),'plug'=>'iPLUG32K')); + +* fastest ... uses db2_pconnect / odbc_pconnect (persistent QSQSRVR job stays alive "forever") + +* fastest ... starts another xmlservice /tmp/packers1-to-10 beyond QSQSRVR job (XTOOLKIT PGM-XMLSERVICE), and jobs 1-10 use over and over and over ... until killed by $ToolkitServiceObj->disconnect() or by IBM i operator + +* What to watch for ... + + Security 1: profile FRED can not attach to profile SALLY XMLSERVICE jobs (SALLY owns /tmp/packers1-10, FRED will have to make his own jobs /tmp/bears1-10) + + Security 2: profile FRED owns a db2_pconnect(tion), and SALLY owns a db2_pconnect(ion), XMLSERVICE connect InternalKey profile must match (db2_pconnect("SALLY") owns /tmp/packers1-10, db2_pconnect("FRED") owns /tmp/bears1-10) + + Co-operate with web site: develop procedures start/stop xmlservice when doing system maintenance (kill xmlservice jobs, etc.) + + A live job: QTEMP/\*LDA are re-used, therefore your called applications must be ready to handle/clear + + +Toolkit operations performance +------------------------------ + +Always use PgmCall API for speed including data area, job attributes, etc. (V6+ also call CL and OPM \*PGM with PgmCall), most command functions will run significantly slower. + +* slower ... PASE sh utilities (system wrkactjob, ls, ps, etc.) + +:: + + $ToolkitServiceObj->CLInteractiveCommand + +* slightly faster ... CMDS that return data (RTVJOBA, etc.) + +:: + + $ToolkitServiceObj->CLCommandWithOutput + +* faster ... CMDS that do not return data (ADDLIBLE, etc.) + +:: + + $ToolkitServiceObj->CLCommand + +* fastest ... calling PGMs/SRVPGMs (RPG, CLP, Cobol, System API, etc.) + +:: + + $ToolkitServiceObj->PgmCall + +Toolkit plug size performance +----------------------------- + +Setting plug size to match your data size can offer increased performance. + +* slower ... 15 MB plug size (max) + +:: + + $ToolkitServiceObj->setToolkitServiceParams(array('InternalKey'=>'/tmp/packers'.rand(1,$maxpool),'plugSize' => '15M')); + +* faster ... 512K plug size (default) + +:: + + $ToolkitServiceObj->setToolkitServiceParams(array('InternalKey'=>'/tmp/packers'.rand(1,$maxpool),'plugSize'=>'512K')); + +* fastest ... 4K plug size (min) + +:: + + $ToolkitServiceObj->setToolkitServiceParams(array('InternalKey'=>'/tmp/packers'.rand(1,$maxpool),'plugSize'=>'4K')); + +Why a plug size at all? +^^^^^^^^^^^^^^^^^^^^^^^ + DB2 connections are safe reliable transport for XML documents between client (PHP) and server (XMLSERVICE), but DB2 forces you to declare IN/OUT parameter size of any call procedure, XMLSERVICE download includes a few different stored procedure sizes (iPLUG4k .. iPLUG15M), so your script needs to choose the IN/OUT size that fits your data. + + +.. + [--Author([[http://youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICEFaster?action=expirediff | s ]])--] + [--Tony "Ranger" Cairns - IBM i PHP / PASE--] + + diff --git a/docs/functions.rst b/docs/functions.rst new file mode 100755 index 0000000..b4c2749 --- /dev/null +++ b/docs/functions.rst @@ -0,0 +1,1993 @@ +XMLSERVICE Functions +==================== +`Goto Main Page`_ + +.. _Goto Main Page: index.html + +XMLSERVICE features +------------------- +* :ref:`face` + + * `REST interface (xmlcgi.pgm)`_ + * `DB2 stored procedure interface (lib/iPLUGxxx - xmlstoredp.srvpgm)`_ + +* :ref:`ctl` +* :ref:`cmd` +* :ref:`sh` +* :ref:`pgm` +* :ref:`db2` + + +One sample worth 1,000 words +---------------------------- +* XMLSERVICE can handle multiple requests in a single XML input document. +* XMLSERVICE works with standard ``https://``, wherein, "secure" as any other web facility SSL encrypted (actual user profile, see xmlcgi.pgm). +* XMLSERVICE works with Basic Auth, etc., because XMLSERVICE is just another RPG CGI (\*NONE user/pwd, see crtnone.clp/xmlnone.pgm). +* XMLSERVICE REST example is simple html/xml, but any language will do, with/without a language toolkit (Common lab): + :: + + + +
User: (see instructor) +
Password: + + +
XML Input: +
+ +
+ + + + + +.. _face: + +XMLSERVICE interfaces (with download) +------------------------------------- + +XMLSERVICE includes two basic interfaces, REST (Apache), and, database (DB2). Either interface receives XML input and returns XML output. +The samples below show XMLSERVICE library (download/test library), but multiple language products ship XMLSERVICE in different libraries, +so adjust your LIB accordingly (PHP - ZENDSVR/ZENDSVR6, Ruby-POWERRUBY, etc.). XMLSERVICE uses hard coded library schemes (PLUGCONF), to +avoid interfering with user set library lists, therefore, you can only SAV/RST XMLSERVICE to +the original library. If you move XMLSERVICE non-originating libraries it will fail at runtime. + +.. _`REST interface (xmlcgi.pgm)`: + +* REST interface (xmlcgi.pgm) + +:: + + http://myibmi/cgi-bin/xmlcgi.pgm?db2=x&uid=x&pwd=x&ipc=x&ctl=x&xmlin=x&xmlout=x&persis=x + db2 - what database (*LOCAL tested) + uid - user profile (*NONE - no uid version 1.5+) + pwd - profile password (*NONE - no password version 1.5+) + ipc - IPC key name/security route to XMLSERVICE job (/tmp/fred01, etc.) + ctl - CTL admin control XMLSERVICE job (see control below) + xmlin - XML input document (request) + xmlout - expected size of XML output document (response size in bytes) + optional: + persis - name persistent DB2 connection 8 chars or less (not often used) + + Configure (LIB match actual product, php, ruby, etc.): + /www/myinstance/conf/httpd.conf + ScriptAlias /cgi-bin/ /QSYS.LIB/XMLSERVICE.LIB/ + + AllowOverride None + order allow,deny + allow from all + SetHandler cgi-script + Options +ExecCGI + + +.. _`DB2 stored procedure interface (lib/iPLUGxxx - xmlstoredp.srvpgm)`: + +* DB2 stored procedure interface (lib/iPLUGxxx - xmlstoredp.srvpgm) + +:: + + call XMLSERVICE.iPLUG512K(ipc, ctl, xmlin [, xmlout]) + IN IPC CHAR(1024) - IPC key name/security + IN CTL CHAR(1024) - CTL admin control XMLSERVICE job + IN CI CLOB(15M) - XML input document (request) + OUT CO CLOB(15M) - XML output document (response) + Note: iPLUGRxxx procedures return a result set that is collected by fetch. + + call XMLSERVICE.iPLUG512K(ipc, ctl, xmlin xmlout) + --- + XMLSERVICE.iPLUG4K(IN IPC CHAR(1024), IN CTL CHAR(1024),IN CI CHAR(4064), OUT C0 CHAR(4064)) + XMLSERVICE.iPLUG32K(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(32000), OUT CO CLOB(32000)) + XMLSERVICE.iPLUG65K(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(65K), OUT CO CLOB(65K)) + XMLSERVICE.iPLUG512K(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(512K), OUT CO CLOB(512K)) + XMLSERVICE.iPLUG1M(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(1M), OUT CO CLOB(1M)) + XMLSERVICE.iPLUG5M(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(5M), OUT CO CLOB(5M)) + XMLSERVICE.iPLUG10M(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(10M), OUT CO CLOB(10M)) + XMLSERVICE.iPLUG15M(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(15M), OUT CO CLOB(15M)) + + stmt = call XMLSERVICE.iPLUG512K(ipc, ctl, xmlin) + while (row = fetch(stmt)) xmlout += row[0] + --- + XMLSERVICE.iPLUGR4K(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CHAR(4096)) + XMLSERVICE.iPLUGR32K(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CHAR(32000)) + XMLSERVICE.iPLUGR65K(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(65K)) + XMLSERVICE.iPLUGR512K(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(512K)) + XMLSERVICE.iPLUGR1M(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(1M)) + XMLSERVICE.iPLUGR5M(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(5M)) + XMLSERVICE.iPLUGR10M(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(10M)) + XMLSERVICE.iPLUGR15M(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(15M)) + + Enable DB2 drivers with no LOB support (JTOpen lite enabler - 1.9.0+): + XMLSERVICE.iPLUGRC32K(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI VARCHAR(32700), IN CNT INTEGER) + Note iPLUGRC32K: + XML input VARCHAR(32700) can be a limit, therefore interface allows + accumulated sends of partial XML document. Any call with non-zero + counter (CNT > 0), XMLSERVICE assumes to be XML document partial + accumulation. When counter reaches zero (CNT = 0), + XMLSERVICE will process the request. + +.. _ctl: + +XMLSERVICE Control words +------------------------ + +Control (CTL) keywords for operator control over XMLSERVICE jobs. + +* stateless connection -- xmlservice here ``($ctl="*here";)`` -- run in QSQSRVR job, db2_connect current profile control. \*here overrides/ignores ipc, runs "\*here" in the QSQSRVR job. +* private connection 1 -- xmlservice spawn ``($ctl=""; $ipc="/tmp/user";)`` -- inherit parent job attributes, include QSQ job profile, and, job description. Per manual (below), xmlservice spawn server job has always been default when ipc included (internalKey), but missing \*sbmjob. + + * Change: 1.9.3 - Rod asked QSQSRVR name changed on spawn (spawn child name also QSQSRVR), therefore, new name spawn child will be XMLSERVICE (XMLSERVICE PGM-XMLSERVICE). + +* private connection 2 -- xmlservice sbmjob ``($ctl="*sbmjob"; $ipc="/tmp/user";)`` -- control sbmjob, job description, etc. \*sbmjob allows control of job description for xmlservice server job. + + * Alternative \*sbmjob, xmlservice allows full control via ``SBMJOB CMD(CALL PGM(XMLSERVICE/XMLSERVICE) PARM('/tmp/user') ... other sbmjob parms ...)`` (XTOOLKIT PGM-XMLSERVICE). + +:: + + ***************************************************** + * IPC control options: + *---------------------------------------------------- + *--- very high peformance ignore all flag parsing (loop calls, etc.) + * *ignore + * - do not parse flags (high performance) + * example: $ctl="*ignore"; + *---------------------------------------------------- + *--- kill XMLSERVICE job + * *immed + * - end server immed destroy IPC + * example: $ctl="*immed"; + *---------------------------------------------------- + *--- misc functions XMLSERVICE + * *license + * - return license for this code + * *session + * - retrieve session key (IPC name /tmp/fred042) + * *clear + * - clear internal XMLSERVICE caches, + * but will not deactivate loaded PGM/SRVPGM, etc. + *---------------------------------------------------- + * -- if you need to fix result set return drivers (iPLUGRxxx) + * hack for pesky DB2 drivers adding "junk" to back of records. + * *hack + * - add each record of a result set + * example: $ctl="*hack"; + * - iPLUGRxxx XMLSERVICE stored procedures + * return result sets of 3000 byte records, + * you must loop fetch and concat records + * to recreate output XML ... except + * some DB2 drivers have junk end ... + * - enable easy rec truncate ill behaved drivers + * remove all past during concat + * loop fetch records 1-n + * rec1: (<3000 bytes) + *---------------------------------------------------- + * -- pause XMLSERVICE job(s) for debugger attach (message to qsysopr) + * *debug + * - stop call server with message qsysopr (XMLSERVICE) + * example: $ctl="*debug"; + * *debugproc + * - stop stored proc with message qsysopr (client QSQSRVR) + * *debugcgi + * - stop CGI with message qsysopr (XMLCGI only) + * *test[(n)] + * - test parse XML in/out and report n level information + *---------------------------------------------------- + * -- override default XMLSERVICE client/server spawn child behaviour + * *sbmjob[(lib/jobd/job/asp)] + * - sbmjob job (instead of XMLSERVICE default spawn) + * example: $ctl="*sbmjob"; + * example: $ctl="*sbmjob(QSYS/QSRVJOB/XTOOLKIT)"; + * example: $ctl="*sbmjob(ZENDSVR/ZSVR_JOBD/XTOOLKIT)"; + * - default values provided plugconf.rpgle + * - optional asp INLASPGRP(ASP1) (added 1.6.5) + * -- Notes: + * - See embedded XML overrides for user full control + * of XMLSERVICE start behavior SBMJOB settings + * *here + * - run stateless in stored proc job (client only job) + * example: $ctl="*here"; + * - commonly known as running in PHP job, but in fact + * more likely runs in database job you connected + * on/off machine DRDA/ODBC/PASE (QSQSRVR, etc.) + * - generally runs slower using "one process" + * because XMLSERVICE has to restart itself, + * wake up PASE, find/load your PGM call, etc. + * *nostart + * - disallow spawn and sbmjob (web not start anything) + * example: $ctl="*nostart"; + * - probably prestart all your XMLSERVICE jobs + * SBMJOB CMD(CALL PGM(XMLSERVICE/XMLSERVICE) + * PARM('/tmp/db2ipc042')) USER(DB2) + * - consider using a custom plugconf to disable + * issues with timeout defaults (*idle/*wait) + * *java (1.9.2) + * - start JVM allowing user classpath + * ADDENVVAR ENVVAR(CLASSPATH) VALUE('$ours') + * REPLACE(*YES) + * ... calling my RPG w/JAVA ... + * *sqljava or *dbgjava (port 30000) (1.9.2) + * - start JVM allowing DB2 classpath (no user control) + * SQLJ.INSTALL_JAR into schema + * /QIBM/UserData/OS400/SQLLib/Function/jar/(schema) + *---------------------------------------------------- + * -- CDATA xml output + * *cdata(on|off) + * - xml data fields returned with cdata + * + * - on - CDATA (default 1.6.2) + * - off - CDATA off + * -- escape xml reserved chars + * *escp + * 5 chars will be escaped in the output + * & --> & + * > --> > + * < --> < + * ' --> ' + * " --> " + * -- special hex encoded XML document + * *hex(in/out/both) + * - xml document hex encoded avoiding conversions + * - in - HEX convert input side only + * - out - HEX convert all output + * - both - HEX convert input and output + * example (php sudo): + * $doc = " ... "; + * $hexin = bin2hex($doc); // doc-2-hexin + * XMLSERVICE($ipc, $ctl="*hex", $hexin, $hexout); + * $doc = pack("H*", $hexout); // hexout-2-doc + * -- Notes + * - hex string is '0123456789ABCDEF' or '...abcdef' + * which are CCSID immutable (or upper most always) + * which will arrive at XMLSERVCE in ebcdic via + * "natural" clob/rest interface transport conversion + * (ie., iBLOBxxx would not work *hex, arrive ascii) + * YES (clob): ebcdic (x'F0F1...C6') or (x'...86') + * NO (blob): ascii (x'3031...46') or (x'...66') + * - hex option is useful with CCSID below + * with following order of operations: + * - input : *hex followed by *before + * - output: *after followed by *hex + * -- CCSID conversion XMLSERVICE (1.6.2) + * *before(CCSIDFrom/CCSIDTo[/action]) + * - conversion before calling XML processing + * - CCSIDFrom - XML document client CCSID + * - CCSIDTo - XML document XMLSERVICE CCSID + * - action + * call - call XMLSERVICE (default) + * nocall - convert only (no call) + * *after(CCSIDFrom/CCSIDTo) + * - conversion after calling XML processing + * - CCSIDFrom - XML document XMLSERVICE CCSID + * - CCSIDTo - XML document client CCSID + * *pase(CCSIDpase/CCSIDile) + * - conversion PGM, LIB, etc, names call processing + * - CCSIDpase - name CCSID PASE side (ascii) + * - CCSIDile - name CCSID ILE side (ebcdic) + * + * example: $ctl="*before(819/37) *after(37/819)"; + * - DB2 interface provides many possibilities + * iPLUGxxx - original CLOB automatic DB2 converts + * iBLOBxxx - raw binary no DB2 converts (1.6.2) + * - action='nocall' is used to convert + * anything on IBM i and send it back + * without calling XMLSERVICE + * - DB2 interface conversion effects are + * virtually unlimited performed on IBM i + * avoiding additional "code tables" on client + * - *pase default (should work) + * ILE ccsid 0 means job CCSID (all ebcdic) + * PASE csid default Qp2paseCCSID (see API) + *---------------------------------------------------- + * -- server time out jobs XMLSERVICE (1.6.2) + * *wait[(seconds[/action])] + * - client side wait for XMLSERVICE call (client side) + * example: $ctl="*wait(10)"; + * - default action *wait(60/busy) (see plugconfx) + * *call[(seconds[/action[/duration[/job]]])] + * - client/server side XMLSERVICE call wait (PGM, SRVPGM, PASE, etc) + * example: $ctl="*wait(10) *call(5/busy/client)"; + * - default for both client/server is *call(0) + * means wait on call forever (user code flawless), + * but can be overriden client/server/both + * *idle[(seconds[/action[/duration]])] + * - server side XMLSERVICE idle no activity + * example: $ctl="*wait(10/kill) *call(30/kill) *idle(30/kill/perm)"; + * - default action *idle(1800/kill) (see plugconfx) + * -- time out parameters + * seconds: + * -1 - current default timer + * 0 - no timer, no timeout, wait forever + * n - idle timer "pop" seconds + * action: + * kill - end job immed + * user - user override signal behaviour (see plugconfx) + * busy - return busy XML (client side) + * busy response (1301050): + * + * 1301050 + * IPC timeout busy + * + * duration: + * perm - set and use new defaults all requests + * orig - reset and use original compile defaults (see plugconfx) + * job: + * client - *call action applies client side + * server - *call action applies server side + * -- Notes: + * - default timeout/action provided plugconf.rpgle, + * but each request may override/reset to fit task(s) + * - signal SIGALRM used with this function + * can affect user program calls, + * *call(0) may be used to turn off timer + * during user program calls + * - action 'user' allows for custom signal + * processing in the RPG code (see plugconfx) + * - if duration not specified, attributes + * *wait(),*call(),*idle() are temporary + * for this call only and return to last defaults. + * - if 'job' not specified on *call(), + * attribute settings apply to both sides + * - end job immed kills XMLSERVICE job (server) + * and destroys IPC, so any waiting client is + * released with an IPC missing error. + *---------------------------------------------------- + * -- batch XMLSERVICE processing (version 1.6.2) + * *batch + * - use a free batch slot 1 - 16 (release client) + * responses: + * 1-16 - set batch processing + * 0 - no slots available + * example: $ctl="*sbmjob *batch"; + * - *batch holds output XML memory until retrieved + * - *get(n) retrieve of XML memory (some time later) + * - *batch releases XMLSERVICE client (caller), and + * returns batch slot number (n) assigned for work. + * *get[(n)] + * - get XML results from batch slot 1 - 16 (release slot) + * responses (report not available): + * 1-16 - buffer too small + * 1-16 - complete removed + * example: $ctl="*get"; + * example: $ctl="*get(3)"; + * - use with *wait(sec/busy) to avoid hang during + * *batch running (do something else while waiting) + * - *get without slot number will get one result + * any completed batch slot + * - *get(3) will only get result of batch slot 3 + *---------------------------------------------------- + * -- flight data options (affects performance, state of disrepair) -- + * *rpt + * - performance report last call + * *fly + * - flight record performance + * *nofly + * - no flight record performance (default) + * *justproc + * - call stored proc, get into XMLSERVICE client, + * do nothing while in XMLSERVICE, return back + * - used to check transport speed only + * -- log to database file (1.7.1) -- + * *log[(key)] + * - log records into database + * *nolog + * - no log records into database (default) + * Note: + * - *log key is unique allowing both PHP and XMLSERVICE + * to record event log data and produce queries of collected + * reports. + * Log file layout: + * create table XMLSERVLOG/LOG ( + * key varchar(64) NOT NULL WITH DEFAULT, + * log TIMESTAMP NOT NULL WITH DEFAULT, + * text varchar(64) NOT NULL WITH DEFAULT) + * Supplemental log dump XML data layout: + * create table XMLSERVLOG/DUMP ( + * key varchar(64) NOT NULL WITH DEFAULT, + * log TIMESTAMP NOT NULL WITH DEFAULT, + * text clob(15M) NOT NULL WITH DEFAULT) + * - programers/vendors can alter xmlservice log database + * with plugconf (or custom) + ***************************************************** + * embedded XML control overrides and function + ***************************************************** + * sbmjob full user override of SBMJOB for XMLSERVICE start-up -- (1.7.0+) + * SBMJOB CMD(CALL PGM(ZENDSVR/XMLSERVICE) PARM('/tmp/override')) + * Where SBMJOB can be any user settings (cut/paste green screen command) ... + * ... required parameters for for XMLSERVICE to start CALL + PARM + * example: + * CMD(CALL PGM(ZENDSVR/XMLSERVICE) PARM('/tmp/xxxxxx')) <-- xmlservice test lib + * -- or -- + * CMD(CALL PGM(ZENDSVR/XMLSERVICE) PARM('/tmp/xxxxxx')) <-- Zend Server production lib + * -- all other sbmjob parms at your control -- + * Note: + * - SBMJOB full control allows user to set any type of LIBL, + * or SBMJOB options, or even custom PGM to call XMLSERVICE + ***************************************************** + * start/use/stop exclusive use shared IPC (hotel reservation) -- (1.6.8+) + * KEY - acquire IPC -- first request + * KEY - match IPC -- each request + * KEY - release IPC -- last request + * Where KEY anything managed by user (key-2-IPC session data) ... + * ... random based key -- scaling open any users, + * want benefit of private RPG call (open files, etc), + * do not care about reservation multi-request transaction + * -> hybrid stateless/private call, + * hold IPC for life of script only and release, + * but limit jobs + * ... user based key -- scaling come/go users, + * want benefit of private RPG call (open files, etc), + * also want reservation transactions + * -> hybrid persistent/private, + * transaction across multi-request (browser clicks), + * but limited jobs + * ... task based key -- everyone uses same task/tasks + * limited pool jobs and all must wait a turn + * -> hybrid private/persistent with pre-start pool, + * transaction across multi-request (browser clicks), + * load balancing design to limit machine stress + * example many requests exclusive use IPC + * -- no time out -- + * $ctl .= " *idle(0)" + * -- request 1 -- + * + * + * -- request 2 (two minutes later) -- + * + * + * -- request 3 (1/2 hour later) -- + * + * + * -- request n (2 hours later) -- + * + * + * Note: + * - unique-user-key + * acquire exclusive IPC if available, + * - unique-user-key + * must appear XML every request + * job held forever until see + * - unique-user-key + * release IPC for any other use + * - no-match-key + * or unique-user-key + * non-matching key results in error + * almost instantly (no wait) + * busy response (1301060): + * + * 1301060 + * IPC owner busy + * + * - thoughtful setting server idle timeout + * can control unwanted reservation hangs + * due to careless users or errors + * $ctl .= " *idle(60)" + ************************************************************************* + + +.. _cmd: + +XMLSERVICE call CMD +------------------- +Syntax can largely be cut/paste from a 5250 command line. + +:: + + ************************************************************************* + * 1) call i CMD + * XMLSERVICE allows calls of *CMDS on IBM i. Typically, you cut/paste + * from a 5250 QCMD line using prompt (F4). You may use choose the utility + * to run your command with attribute 'exec'. However, *CMDS with + * in/out parameters, like RTVJOBA., you must use 'exec'='rexx'. + * --- + * values (see IBM i *CMD) + * --- + * cmd - command tag + * values - (see IBM i *CMD IBM i- 5250 cut/paste) + * options + * exec + * cmd - qcmdexe only return true/false (default) + * system - system utility return CPFxxxx + * rexx - rexx output parms and return CPFxxxx + * (?) character type + * (?N) explicit cast numeric + * hex (1.6.8) + * on - input character hex (5147504C20202020) + * before + * cc(n) - input ccsid1->ccsid2->ccsid3->ccsid4 + * after + * cc(n) - output ccsid1->ccsid2->ccsid3->ccsid4 + * error (1.7.6) + * on - script stops, full error report + * off - script continues, job error log (default) + * fast - script continues, brief error log + * --- + * example run command (original) + * + * + * ADDLIBLE LIB(DB2) POSITION(*FIRST) + * + * --- + * example output command (exec='rexx') + * + * + * RTVJOBA USRLIBL(?) SYSLIBL(?) + * RTVJOBA CCSID(?N) OUTQ(?) + * RTVSYSVAL SYSVAL(QDATETIME) RTNVAR(?) + * + * --- + * Note: + * - command should be all on one line (no LFs) + * - run in XMLSERVICE job. + * cmd - qcmdexe only return true/false (default) + * system - system utility return CPFxxxx (1.5.2) + * CPF2103 + * rexx - rexx output parms and return CPFxxxx (1.5.2) + * CPF2103 + * - exec='rexx' + * All parms are assume to be character unless + * (?N) to explicit cast to numeric (rtvjoba). Most + * RTVxxxx that ask for a CL variable RTNVAR will + * not require the (?N) cast (IBM i manuals). + * QTEMP/XMLREXX(HOW) is created on demand + * by RPG module plugile (Github download). + * QTEMP/OUTREXX(OUTREXX) is created for + * command temp data between RPG and REXX. + * - Up to four conversions can take place + * for the truly complex ccsid issues (1.6.8) + * + * flow: + * -> PHP client bin2hex('wild_ascii_raw_chars') + * -> xmlservice hex2bin back to 'wild_ascii_raw_chars' + * -> xmlservice convert cc1->cc2->cc3->cc4 (before) + * -> xmlservice make ILE call + * -> xmlservice convert cc4->cc3->cc2->cc1 (after) + * -> xmlservice tohex "xml_hex_back" + * -> PHP client $chars = pack('H*',"xml_hex_back") + * output (incompatible change hex/ccsid 1.7.4+): + * + * + * + * + * + * - error='on,off,fast' (1.7.6) + * on - script stops, full error report + * off - script continues, job error log (default) + * fast - script continues, brief error log + ************************************************************************* + +.. _sh: + +XMLSERVICE call PASE +-------------------- + +Syntax can mostly be cut/paste from PASE shell (call qp2term). + +:: + + ************************************************************************* + * 2) call PASE utility + * XMLSERVICE allows calls of PASE utilities on IBM i. Typically, you cut/paste + * from a PASE command line (call qp2term). PASE shell 'sh' is used for + * execution of your utilities, which, is default behavior of PASE popen() API. + * --- + * values (see PASE utility) + * --- + * sh - shell tag + * values - (see PASE utility - call qp2term cut/paste) + * options + * rows + * on - return rows lines + * off - return one string (default) + * hex (1.7.4) + * on - input character hex (5147504C20202020) + * before + * cc(n) - input ccsid1->ccsid2->ccsid3->ccsid4 + * after + * cc(n) - output ccsid1->ccsid2->ccsid3->ccsid4 + * --- + * error (1.7.6) + * on - script stops, full error report + * off - script continues, job error log (default) + * fast - script continues, brief error log + * --- + * example run PASE shell + * + * + * /QOpenSys/usr/bin/system 'wrkactjob' | grep -i fr + * + * --- + * Note: + * - syntax looks as if typed on console (call qp2term) + * pase utility runs "slower" because a child job + * is created to run each PASE utility (normal Unix behavior). + * All other XML/ILE functions run within XMLSERVICE job. + * - Using nested shells within this sh shell may + * produce unpredictable results. + * - hex='on' before='' after='' -- same as (1.7.0) + * output (incompatible change hex/ccsid 1.7.4+): + * + * 746F74616C2031363636313034 + * + * output (rows='off' 1.7.4+): + * + * 746F74616C2031363636313034 + * + * - error='on,off,fast' (1.7.6) + * on - script stops, full error report + * off - script continues, job error log (default) + * fast - script continues, brief error log + ************************************************************************* + + +.. _qsh: + +XMLSERVICE call QSH (1.9.8+) +---------------------------- + +Syntax can mostly be cut/paste from QSH shell (qsh). +:: + + ************************************************************************* + * 2.5) call QSH utility (1.9.8+) + * XMLSERVICE allows calls of QSH utilities on IBM i. Typically, you cut/paste + * from a QSH command line. STRQSH is used for execution of your utilities. + * --- + * values (see QSH utility) + * --- + * qsh - shell tag + * values - (see QSH utility - qsh cut/paste) + * options + * rows + * on - return rows lines + * off - return one string (default) + * hex (1.7.4) + * on - input character hex (5147504C20202020) + * before + * cc(n) - input ccsid1->ccsid2->ccsid3->ccsid4 + * after + * cc(n) - output ccsid1->ccsid2->ccsid3->ccsid4 + * --- + * error (1.7.6) + * on - script stops, full error report + * off - script continues, job error log (default) + * fast - script continues, brief error log + * --- + * example run QSH shell + * + * + * /usr/bin/system 'wrkactjob' | /usr/bin/grep -i fr + * + * --- + * Note: + * - Recommend qualify qsh utilities with /usr/bin. + * This will avoid ccsid conversion between PASE/QSH utilities. + * - syntax looks as if typed on console (qsh) + * QSH utility runs "slower" because a child job + * is created to run each QSH utility (normal Unix behavior). + * - Using nested shells within this qsh shell may + * produce unpredictable results. + * - hex='on' before='' after='' -- same as (1.7.0) + * - error='on,off,fast' (1.7.6) + * on - script stops, full error report + * off - script continues, job error log (default) + * fast - script continues, brief error log + + +.. _pgm: + +XMLSERVICE call PGM +------------------- + +Call PGM, SRVPGM, or system API, using XML syntax. + +1) Call PGM using this XML syntax. + +2) Call SRVPGM using this XML syntax. + +:: + + ************************************************************************* + * 3) call PGM/SRVPGM + * XMLSERVICE allows calls of *PGM and *SRVPGM on IBM i. Typically, you match + * call parameters, including data structures, and/or simple data elements. + * --- + * pgm name (*PGM or *SRVPGM) + * values (see and ) + * --- + * pgm - IBM i *PGM or *SRVPGM name (tag) + * values - (see parm and return) + * options + * lib + * library - IBM i library name + * func + * function- IBM i *SRVPGM function name + * mode + * ile - ILE and PASE memory (default) + * opm - ILE only memory (PASE can not view) + * error (1.7.6) + * on - script stops, full error report + * off - script continues, job error log (default) + * fast - script continues, brief error log + * + * --- + * pgm parameters + * values (see and ) + * --- + * parm - parm name (tag) + * values - (see ds or data) + * options + * io + * in - input only + * out - output only + * both - input/output only (default) + * omit - omit (1.2.3) + * by + * ref - pass by reference (default) + * val - pass by value (1.9.9.3+) + * + * --- + * pgm return + * values (see and ) + * --- + * return - return tag + * values - (see ds or data) + * options + * na - no options + * + * --- + * pgm data structure + * values (see or ) + * --- + * ds - data structure tag + * values - (see ds or data) + * options + * dim + * n - array dimension value (default dim1) + * dou + * label - match array dou terminate parm label (see data) + * len (1.5.4) + * label - match calculate length of ds parm lable (see data) + * data (1.7.5) + * records - data in records tag + * + * --- + * pgm data elements + * (value) + * --- + * data - data value name (tag) + * values - value, + * type + * 3i0 int8/byte D myint8 3i 0 + * 5i0 int16/short D myint16 5i 0 + * 10i0 int32/int D myint32 10i 0 + * 20i0 int64/int64 D myint64 20i 0 + * 3u0 uint8/ubyte D myint8 3u 0 + * 5u0 uint16/ushort D myint16 5u 0 + * 10u0 uint32/uint D myint32 10u 0 + * 20u0 uint64/uint64 D myint64 20u 0 + * 32a char D mychar 32a + * 32a {varying2} varchar D mychar 32a varying + * 32a {varying4} varchar4 D mychar 32a varying(4) + * 12p2 packed D mydec 12p 2 + * 12s2 zoned D myzone 12s 2 + * 4f2 float D myfloat 4f + * 8f4 real/double D myfloat 8f + * 3b binary D mybin (any) + * 40h hole (no out) D myhole (any) + * options + * dim + * n - array dimension value (default dim1) + * varying + * on - character varying data (same as varying2) + * off - character non-varying data (default) + * 2 - character varying data + * 4 - character varying data + * enddou + * label - match array dou terminate parm label (see ds) + * setlen (1.5.4) + * label - match calculate length of ds parm lable (see ds) + * offset + * label - match offset label (see overlay) + * hex (1.6.8) + * on - input character hex (5147504C20202020) + * before + * cc(n) - input ccsid1->ccsid2->ccsid3->ccsid4 + * after + * cc(n) - output ccsid1->ccsid2->ccsid3->ccsid4 + * trim (1.7.1) + * on - trim character (default) + * off - no trim character + * next (1.9.2) + * label - match next offset label (see overlay) + * + * --- + * pgm parameters/return overlay + * (see and ) + * --- + * overlay - structure overlay name (tag) + * values - (see ds or data) + * options + * io + * in - input only + * out - output only + * both - input/output only (default) + * offset + * n - overlay bytes offset relative + * label - overlay match bytes offset label (see data) + * setnext (1.9.2) + * label - overlay match next offset label (see data) + * top + * n - overlay parm number (see parm) + * on - overlay parm first (see parm) + * off - overlay parm last seen (see parm) + * --- + * example run a PGM + * + * + * CHGLIBL LIBL(XMLSERVICE) CURLIB(XMLSERVICE) + * + * + * a + * + * + * b + * + * + * 11.1111 + * + * + * 222.22 + * + * + * + * x + * y + * 66.6666 + * 77777.77 + * + * + * + * 0 + * + * + * + * --- + * example run a SRVPGM + * + * + * + * + * Ranger + * + * + * 5 + * + * + * 0 + * + * + * + * na + * na + * 0 + * 0.0 + * + * + * + * + * --- + * example optional ccsid convert name/lib format (1.6.8) + * + * + * + * bin2hex('&fredflin') + * bin2hex('omlated') + * bin2hex('me&proc') + * + * + * a + * + * + * + * + * 0 + * + * + * + * + * --- + * Note: + * - data types (similar RPG): + * ---------------------------------------------------------------------- + * int8/byte D myint8 3i 0 + * int16/short D myint16 5i 0 + * int32/int D myint32 10i 0 + * int64/int64 D myint64 20i 0 + * uint8/ubyte D myint8 3u 0 + * uint16/ushort D myint16 5u 0 + * uint32/uint D myint32 10u 0 + * uint64/uint64 D myint64 20u 0 + * char D mychar 32a + * varchar D mychar 32a varying + * varchar4 D mychar 32a varying(4) + * packed D mydec 12p 2 + * zoned D myzone 12s 2 + * float D myfloat 4f + * real/double D myfloat 8f + * binary D mybin (any) F0F1F2 + * hole (no out) D myhole (any) + * ------------------------------------------------------------------------ + * type='na' [varying='on|off|2|4'] - character (32A) + * + * ranger + * ]]> + * + * bin2hex($japan_raw_ascii_data) + * + * type='npn' - packed decimal (12p2) + * + * 30.29 + * type='nsn' - zoned decimal (12s2) + * + * 30.29 + * type='nin' - signed integer (5i0, 10i0, 20i0) + * + * -30 + * type='nun' - unsigned integer (5u0, 10u0, 20u0) + * + * 30 + * type='nfn' - floating point (4f2, 8f4) + * + * 30.34 + * 30.34 + * type='nb' - binary HEX char (2b, 400b) + * F0F1F2CDEF + * 1FBC + * 0F0F + * - HEX upper case ('1FBC' not '1fbc') + * - high/low bits (HEX='0F0F' not HEX='F0F') + * type='nh' - 'hole' zero in, nothing out (4096h) (1.2.3) + * + * - PGM/SRVPGM calls (,,,) use syntax + * that looks like RPG to describe the data parameters + * (type='4b', type='32a', type='4f', type='10i0', type='12p2', + * etc.). + * - - dim='n' is new to 1.2 version and beyond, + * older versions did not include this feature. + * - Parameters using dou='label', enddo='label', + * label must match for this to work, + * then processing will only return records up to enddo limits. + * - Type 'h' for 'hole' is used to input x'00' fill 'hole' + * in the parameter geometry. It can be used to skip over + * a chunk of complex data that you really did not want to + * deal with or see in output XML. It is also very handy to + * use with overlay when output data is variable + * or unpredictable (1.2.3) + * input: + * + * good stuff <---offset 0 + * <---400 x'00' input + * more good stuff<---offset 440 + * + * output: + * + * stuff back <--- offset 0 + * <--- ignored output + * stuff back <--- offset 440 + * + * - Added parm='omit' for RPG OPTIONS(*OMIT) parameter. A + * *NULL will be passed in this location. + * All parm io='omit' will be excluded from XML + * output returned because *NULL parameter has no data (1.2.3). + * + * Ranger <--ignore *NULL + * + * RPG procedure (SRVPGM function): + * D zzomit PI 50A varying + * D myName 10A options(*OMIT) <---- optional omitted (*NULL) + * D yourName 10A + * - Added len='label'/setlen='label' to allow for + * automatic length calculation for various system + * APIs that want a %size(thing) parameter. + * This should work across parameters and within + * parameters (any order), but nesting len/setlen is + * not allowed. + * + * + * 0 + * 0 + * + * + * + * + * - Up to four conversions can take place + * for the truly complex ccsid issues (1.6.8) + * + * flow: + * -> PHP client bin2hex('wild_ascii_raw_chars') + * -> xmlservice hex2bin back to 'wild_ascii_raw_chars' + * -> xmlservice convert cc1->cc2->cc3->cc4 (before) + * -> xmlservice make ILE call + * -> xmlservice convert cc4->cc3->cc2->cc1 (after) + * -> xmlservice tohex "xml_hex_back" + * -> PHP client $chars = pack('H*',"xml_hex_back") + * - V5R4 accomidation for OPM programs like CLP (1.6.8) + * - mode='opm' uses non-teraspace memory to build parm lists + * that are used with _CALLPGMV for a "pure" OPM call mode + * - mode='ile' default using teraspace for "mixed" memory + * compatible with PASE calls (IBM i possiblilities) + * - Allow trim control character/binary + * - trim='on' -- right trim (default character type='na') + * - trim='off' -- include all (default binary type='nb') + * - see for offset='label' + * <-- memory location to pop off a + * variable/changing offset value + * for use in overlay() + * <-- top='n' overlay parameter 'n', + * then add offset='label' pop value + * - offset='label' allows label location to pop off a offset value + * at this data location to add position offset + * - 'label' is NOT a position location for , it only holds + * a offset value in this memory location for things like + * system APIs with offset-2-next. + * - data='records' - data follows in record format + * fast "many records" i/o big data (see below) (1.7.5) + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * :Rgr:B:Ok:nd1:nd1:1:1.1:...: + * + * a) simply match in order input + * of any complex structure. Output matches + * order input (see above) + * b) delimit can be any character + * not in your complex records (see above) + * c) works with any or + * d) dou/enddo works, but tricky script to design (be careful) + * - setnext='nextoff' / next='nextoff' - see overlay (1.9.2) + * - len/setlen - auto-len calculate ds setlen='here' (1.5.4) + * - error='on,off,fast' (1.7.6) + * on - script stops, full error report (default) + * off - script continues, job error log + * fast - script continues, brief error log + * - pgm parameters/return overlay (custom offset='bytes', input/output): + * works "relative" to "previous" in + * "order of appearance XML" + * or absolute position to (top='n') + * + * --->absolute parm <---relative parm + * --->complex stuff<------------------- + * | complex over parm 1 ____| + * | + * |-->complex stuff<------------------- + * || complex over parm 2 ____| + * || : + * || complex stuff<------------------- + * || complex over last parm____| + * || : + * |___over top parm + * | : + * |__over parm 2 + * + * - top='on|n' allow overlay position to parameter n + * ... top='on' absolute parm='1' (1.2.1) + * ... top='n' absolute parm='n' (1.2.2) + * ... offset='n' bytes offset relative + * to top='n' position (parm 1,2,3, etc) + * - Once the top='n' parm location is etablished, offset='n' + * will move overlay to offset within the parameter. + * <-- memory location to pop off a + * variable/changing offset value + * for use in overlay() + * <-- top='n' overlay parameter 'n', + * then add offset='label' pop value + * - offset='label' allows label location to pop off a offset value + * at this data location to add position offset + * - 'label' is NOT a position location for , it only holds + * a offset value in this memory location for things like + * system APIs with offset-2-next. + * - setnext='nextoff' / next='nextoff' (1.9.2) + * + * + * + * : + * + * : + * + * + * : + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + ************************************************************************* + + +*Note: Additional XML attributes added for comments, var names, and documentation will simply be returned untouched, so you may build your own label conventions for XML parsing in client code.* + +*Hint: use your own labels and comments XML attributes for easy client XML parsing work (var='MYVAR').* + + +Advanced CCSID +^^^^^^^^^^^^^^ + +Using default PHP toolkit DB2 clob interface (iPLUGxxx/iPLUGRxxx), ccsid conversion occurs naturally as DB2 client/server and you will not have to code before/after, but method is available if you have a specific concern or you have scripts returning many different languages. + +Theory follows that most of XML document “intent” will remain immutable across db2 clob conversion (keywords, numbers, etc.), but for character data on some occasion there will be mixed/competing client/server ccsid conversion intention (client running 819, but data 1208), or there may be multiple language ccsid in the same XML document request (German, English, French), therefore using a combination of transfer in hex (hex=‘on’) and IBM i server ccsid translation before/after should allow complete control over transforms at user need. + +Example:: + + bin2hex('Hebrew_ascii_raw_chars') + bin2hex('Farsi_ascii_raw_chars') + bin2hex('Russia_ascii_raw_chars') + bin2hex('Italy_ascii_raw_chars') + bin2hex('Germany_ascii_raw_chars') + bin2hex('Korea_ascii_raw_chars') + bin2hex('Japan_ascii_raw_chars') + bin2hex('China_ascii_raw_chars') + where: + before - XMLSERVICE convert CCSID before ILE program call + after - XMLSERVICE convert CCSID after ILE program call for client return + bin2hex() - script hex string unaltered ascii image (also returned hex string avoid any conversion) + pack() - script uses pack('H*',"xml_hex_back") function in PHP program for ascii characters + Note: + Up to four conversions can take place for the truly complex ccsid conversion issues + bin2hex('wild_ascii_raw_chars') + flow: + -> PHP client bin2hex('wild_ascii_raw_chars') + -> xmlservice hex2bin back to 'wild_ascii_raw_chars' + -> xmlservice convert cc1->cc2->cc3->cc4 (before) + -> xmlservice make ILE call + -> xmlservice convert cc4->cc3->cc2->cc1 (after) + -> xmlservice tohex "xml_hex_back" + -> PHP client $chars = pack('H*',"xml_hex_back") + + +.. _db2: + +XMLSERVICE DB2 SQL +------------------ + +DB2 queries using only XML with syntax cut/paste STRSQL (cool, cool, cool version 1.5+). + +*Note: DB2 SQL XML does not work in-line stateless ($ctl='\*here'), but works fine with normal private connections (ipc='/tmp/fred', $ctl='\*sbmjob').* + +:: + + ***************************************************** + * Run XML SQL statements: + * ... - start/end run XML SQL statements + * + * Example easy way (chglibl, default connection, default statement): + * Input: + * + * + * Note: You only need chglibl once for all scripts because + * XMLSERVICE jobs remain active until killed (*immed). + * XMLSERVICE default is system naming (not sql naming), + * so library list is significant for unqualified + * statements (see options below). + * Output: + * + * + * + * Reserved SQL words (syntax): + * + * + * Connect to DB2 (optional): + * + * + * Options template (optional): + * + * These alternate db2 features available ... + * error='on,off,fast' (1.7.6) + * on - script stops, full error report (default) + * off - script continues, job error log + * fast - script continues, brief error log + * Note: + * commit sets transaction-isolation level + * - none - no commit (*NONE) + * - uncommitted - uncommitted read (*CHG) + * - committed - cursor stability (*CS) + * - repeatable - read stability (*RS, *ALL) + * - serializable - repeatable read (*RR, *ALL) + * + * Commit or rollback transaction (optional): + * + * These alternate db2 features available ... + * error='on,off,fast' (1.7.6) + * on - script stops, full error report (default) + * off - script continues, job error log + * fast - script continues, brief error log + * + * Execute statement directly (SQLExecDirect): + * + * call storedproc('fred flinrock',42.42) + * -- or -- + * select * from table where abc = 'abc' + * -- or (use CDATA if xml trouble special characters) -- + * 10.0]]> + * + * These alternate db2 features available ... + * error='on,off,fast' (1.7.6) + * on - script stops, full error report (default) + * off - script continues, job error log + * fast - script continues, brief error log + * Note: + * - options='label' (version 1.5.1+) + * + * Prepare a statement (SQLPrepare/SQLExecute): + * + * call storedproc(?,?) + * -- or -- + * select * from table where abc = ? + * -- or (use CDATA if xml trouble special characters) -- + * ?]]> + * + * These alternate db2 features available ... + * error='on,off,fast' (1.7.6) + * on - script stops, full error report (default) + * off - script continues, job error log + * fast - script continues, brief error log + * Note: + * - options='label' (version 1.5.1+) + * + * Execute a prepared statement (SQLPrepare/SQLExecute): + * + * These alternate db2 features available ... + * error='on,off,fast' (1.7.6) + * on - script stops, full error report (default) + * off - script continues, job error log + * fast - script continues, brief error log + * my string + * 42.42 + * + * Note: + * - substitution parameters '?' are taken in order of + * appearence with data types decided internally in + * XMLSERVICE (SQLNumParams/SQLDescribeParam) + * and bound at call time (SQLBindParameter) + * + * Fetch result(s) of a statement: + * + * (default=all) (default=on) (default='off') + * These alternate db2 features available ... + * error='on,off,fast' (1.7.6) + * on - script stops, full error report + * off - script continues, job error log (default) + * fast - script continues, brief error log + * Output (column description included via desc='on'): + * + * Rip9 + * Bee3 + * + * Note: + * - result set column types and descriptions are + * decided internally in XMLSERVICE (SQLNumResultCols/SQLDescribeCol) + * and bound at call time (SQLBindCol) + * - rec='n' (version 1.5.1+) + * + * After query/execute statement: + * Statment rows affected by change (SQLRowCount): + * + * (default=off) + * Output: + * 24 + * + * After insert of identity id: + * Statment last identity id (IDENTITY_VAL_LOCAL): + * + * (default=off) + * Output: + * 23 + * + * Statment describe parms (SQLNumParams/SQLDescribeParam): + * columns (SQLNumResultCols/SQLDescribeCol): + * + * (default=off) + * Output (parm): + * + * or + * FRED + * DECIMAL + * 12 + * 2 + * 0 + * or + * + * + * Statment count parms (SQLNumParams): (1.7.6) + * columns (SQLNumResultCols): + * + * (default=off) + * Output (parm): + * + * nbr + * nbr + * + * + * Free resources: + * + * These alternate db2 features available ... + * error='on,off,fast' (1.7.6) + * on - script stops, full error report + * off - script continues, job error log (default) + * fast - script continues, brief error log + * + * Meta data - tables: + * + * (default=off) + * qualifier or catalog + * schema name + * table name + * table type + * + * Output (desc varies V5R4 to V6+): + * + * + * LP0164D + * XMLSERVTST + * ANIMAL + * BASE TABLE + * + * + * + * + * Meta data - table privileges: + * + * (default=off) + * qualifier or catalog + * schema name + * table name + * + * Output (desc varies V5R4 to V6+): + * + * + * LP0164D + * XMLSERVTST + * ANIMAL + * + * PUBLIC + * SELECT + * NO + * + * + * + * + * Meta data - columns: + * + * (default=off) + * qualifier or catalog + * schema name + * table name + * column name + * + * Output (desc varies V5R4 to V6+): + * + * + * LP0164D + * XMLSERVTST + * ANIMAL + * BREED + * 12 + * VARCHAR + * 0 + * 34 + * 0 + * 0 + * 1 + * + * + * 0 + * 32 + * 2 + * + * + * + * Meta data - special columns: + * + * (default=off) + * qualifier or catalog + * schema name + * table name + * row|transaction|session + * no|nullable + * + * + * Meta data - column privileges: + * + * (default=off) + * qualifier or catalog + * schema name + * table name + * column name + * + * Output (desc varies V5R4 to V6+): + * + * + * LP0164D + * XMLSERVTST + * data desc='TABLE_NAME'>ANIMAL + * BREED + * + * PUBLIC + * SELECT + * NO + * + * + * + * + * Meta data - procedures: + * + * (default=off) + * qualifier or catalog + * schema name + * procedure name + * + * Output (desc varies V5R4 to V6+): + * + * + * LP0164D + * XMLSERVTST + * MATCH1 + * 2 + * 1 + * 1 + * + * + * + * Meta data - procedure columns: + * + * (default=off) + * qualifier or catalog + * schema name + * proc name + * column name + * + * Output (desc varies V5R4 to V6+): + * + * + * LP0164D + * XMLSERVTST + * MATCH1 + * FIRST_NAME + * 1 + * 12 + * CHARACTER VARYING + * 0 + * 128 + * 0 + * 0 + * YES + * + * 0 + * 12 + * 0 + * 128 + * 1 + * YES + * + * + * + * Meta data - primary keys: + * + * (default=off) + * qualifier or catalog + * schema name + * table name + * + * Output (desc varies V5R4 to V6+): + * + * + * LP0164D + * XMLSERVTST + * ANIMAL2 + * NOTEID + * 1 + * Q_XMLSERVTST_ANIMAL2_NOTEID_00001 + * + * + * + * Meta data - foreign keys: + * + * (default=off) + * primary qualifier or catalog + * primary schema name + * primary table name + * foreign qualifier or catalog + * foreign schema name + * foreign table name + * + * Output (desc varies V5R4 to V6+): + * + * + * LP0164D + * XMLSERVTST + * ANIMAL2 + * NOTEID + * LP0164D + * XMLSERVTST + * FKEY2 + * IDF + * 1 + * 3 + * 3 + * Q_XMLSERVTST_FKEY2_IDF_00001 + * Q_XMLSERVTST_ANIMAL2_NOTEID_00001 + * + * + * + * Meta data - statistics: + * + * (default=off) + * qualifier or catalog + * schema name + * table name + * all|unique + * + * Output (desc varies V5R4 to V6+): + * + * + * + * XMLSERVTST + * ANIMAL2 + * 1 + * XMLSERVTST + * INDEX2 + * 3 + * 1 + * ANIMALID + * A + * 0 + * 0 + * + * + * + * + * + * NOTES: + * + * The XMLSERVICE sql rules: + * > Connection rules: + * - if connect is omitted, then XMLSERVICE will open + * a default connection under the current profile. + * - if servermode='na' (off default), ONLY ONE connection + * allowed for the XMLSERVICE job. This is a DB2 rule of + * one activation equals one active connection/transaction, + * so you must free/commit a connection/transaction before + * attempting to create a new connection/transaction + * (or connect another profile). This is the correct + * mode for sharing QTEMP with XMLSERVICE called + * PGMs/SRVPGMs, so it is also the XMLSERVICE default. + * - if servermode='on', XMLSERVICE may have multiple + * connection/transactions active, BUT each connection + * will be running in a seperate QSQSRVR job. This means + * QTEMP in XMLSERVICE job will NOT BE USEABLE to communicate + * between XMLSERVICE called PGM/SRVPGM, etc. (QTEMP useless). + * Once an XMLSERVICE job enters servermode='on', + * it will NEVER stop using server mode until the + * process is ended (choose wisely). + * > Commit rules (see options): + * - default is autocommit='on', where all create, insert, + * delete actions will be committed at operation time. + * - if autocommit='off', commit action may be delayed + * across multiple requests to XMLSERVICE and you + * may also rollback the transaction. + * > Statement rules: + * - if stmt='label' is omitted (normal), the first active + * statement available will be used for a sql operation + * such as fetch or describe. Therefore it is not wise + * to mix stmt='label' and omitted in the same XMLSERVICE + * task. + * - if a stmt is left active (no free), and a subsequent + * 'label'/omitted is attempted, the active statement will + * be released along with all result sets, etc. (free), + * and the new statement will become the active statement. + * Therefore if you are attempting to use multiple result + * sets from different queries, you should manually + * specify stmt='label' to avoid fetching from the + * wrong result set. + * > Options rules: + * -Using servermode='on' universally executes statements + * in child process (QSQSRVR jobs), not XMLSERVICE job. + * Once servermode='on', it stays 'on' for all connections + * for the life of the XMLSERVICE job, therefore you cannot + * expect to share QTEMP between PGM calls/DB2 SQL, etc. + * (ie. think very careful before you use this option) + * -Default mode is servermode='na' (off), or 'normal mode', + * which means a single connect/transaction is allowed + * active at any given time. Therefore, you must end + * any transation with and , + * before attempting to switch between connection profiles. + * (normal CLI rules transaction/connect in a single process) + * -System vs. SQL naming libl, where: + * - naming='system' with libl='curlib1 mylib2' (list) + * example: select * from mylib3/table (specific library) + * select * from table (uses library list) + * (system naming is the default naming mode XMLSERVICE) + * - naming='sql' with *sqllib='mylib' (one) + * example: select * from mylib3.table (specific schema) + * select * from table (uses *sqllib='schema') + * Do not try to mix these two modes in the same + * connection as this always leads to program errors + * (ie. make up your mind before you write your scripts). + * > Connection/statements/options label rules (optional labels): + * conn='label' - unique name connection (optional) + * stmt='label' - unique name statement (optional) + * options='label' - unique name options template (optional) + * - a label is ten characters or less + * - a unique 'label' is used as XMLSERVICE/DB2 routing 'key' + * thereby allowing multiple XML calls to XMLSERVICE + * routing back to open/active DB2 connection(s)/statement(s) + * - If optional conn/stmt 'label' is omitted, XMLSERVICE + * will return a unique 'label' attribute in output XML + * for subsequent sql prepare, execute, fetch, etc. + * - If optional conn/stmt 'label' is omitted, XMLSERVICE + * will attempt to use any active conn/stmt. No 'label(s)' + * works just fine with less XML, but you need to be very careful + * that other scripts do not introduce additional conn/stmt + * 'label(s)' that spoil your generic XML DB2 statements. + * > Connection/statements/options free rules (optional free): + * - Connections/statements remain active/open until released: + * - release all + * - release options template + * - release all options template + * - release connection (and statements) + * - release all connections (and statements) + * - release all statements 'label' connection + * - release this statement + * - release all statements + * - conn='all' : free all connections, + * also frees all statements + * - cstmt='label': free all statements under 'label' connection, + * other connections/statements remain active + * - stmt='all' : free all statements, + * connections remain active + * > These alternate db2 features available ... + * error='on,off,fast' (1.7.6) + * on - script stops, full error report + * off - script continues, job error log + * fast - script continues, brief error log + * + ***************************************************** + +XMLSERVICE job log +------------------ +Job log info has been added to all XMLSERVICE returned fatal errors. An additonal function was also added to retrieve a job of the XMLSERVICE job or another job (1.5.8+). +:: + + * 0) Optional get diagnostics (1.5.8) + * + * example run + * + * + * Note: + * The current XMLSERVICE job log is assumed if optional + * attributes job='job' user='uid' nbr='nbr' missing. + * if you wish to provide custom diagnostics, + * info='conf' calls optional hook in plugconfx. + + +One sample by value (xmlservice 1.9.9.3+) +------------------------------------------ + +This is only about RPG parameters marked 'const' or 'value'. If you do not understand these 'by value' RPG parameter types, this will only confuse (ignore). + +A fix was added for by value in xmlservice 1.9.9.3. All data types test work except zoned decimal by value (although may work). This will be handled later in next base version of xjservice. Please note ``by="val"`` is an attribute of ````, not ```` (see below). + +BTW -- Attribute of ```` is not an error (no debate). That is, ``by='ref,val'`` is indeed attribute of ````, same as ``io='in,out,both'``, and other exotic parameter attributes like \*omit and \*nopass. Unfortunately RPG syntax obfuscates spoken words 'passing parameters' into minds eye of 'passing data(s) and structures and arrays of data' (which make no sense). Aka, ```` is correct. +:: + + dcl-proc GetPacked export; + dcl-pi *N; + i2d char(8) Value; + p1 packed(4:2) Value; + p2 packed(3:2) Value; + p3 packed(12:2) Value; + p4 packed(6:2) Value; + p5 packed(8:2) Value; + p6 packed(24:4) Value; + p7 packed(48:8) Value; + ppd char(15) Value; + zzd char(30); + i2 int(5) value; + i1d char(30); + i4 int(10) value; + i8 int(20) value; + f4 float(4) value; + f4d char(30); + f8 float(8) value; + i4d char(30); + i8d char(30); + f8d char(30); + i1 int(3) value; + end-pi; + p1 += 2.22; + p2 += 2.22; + p3 += 2.22; + p4 += 2.22; + p5 += 2.22; + p6 += 2.22; + p7 += 2.22; + ppd = 'pack man'; + zzd = 'zone man'; + i1 += 2; + i1d = 'byte man'; + i2 += 2; + i2d = 'short man'; + i4 += 2; + i4d = 'integer man'; + i8 += 2; + i8d = 'longlong man'; + f4 += 2.22; + f4d = 'float man'; + f8 += 2.22; + f8d = 'double man'; + end-proc; + + xjInData = + '' + + '' + + '' + + 'CHGLIBL LIBL('+TEST_LIB+')' + + '' + + '' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '1' + + + '' + + '' + + x'00'; + + + + +.. + [--Author([[http://youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICEQuick?action=expirediff | s ]])--] + [--Tony "Ranger" Cairns - IBM i PHP / PASE--] diff --git a/docs/generic-interface.rst b/docs/generic-interface.rst new file mode 100755 index 0000000..3115bf0 --- /dev/null +++ b/docs/generic-interface.rst @@ -0,0 +1,776 @@ +XMLSERVICE Interfaces +===================== +`Goto Main Page`_ + +.. _Goto Main Page: index.html + +Who is this page for? +--------------------- +Instructions designed for IBM i developer exploring XMLSERVICE ... + + +XMLSERVICE introduction +----------------------- + +:: + + ----------------------------------------- + | Browser | + |---------------------------------------| + (1)->| Download RPG (1) | Download PHP (2) | + | 1) XMLSERVICE | a) PHP CW Toolkit |<-(4) cw-tk-php-x.x.x.zip + | HTML/XML/REST | b) New PHP Toolkit |<-(3) cw-tk-php-x.x.x.zip + | no PHP |--------------------| + | (xmlcgi.pgm) | c) PHP “Raw XML” |<-(2) Zend Server for IBM i or Linux or Windows + | (optional) | (ibm_db2, odbc) | + | |--------------------| + | 2) XMLSERVICE DB2 stored procedures |<-(1) + | (iPLUG4K, iPLUG32K, ..., iPLUG15M) | + | 3) XMLSERVICE (xmlservice.pgm) |<-(1) xmlservice-rpg-x.x.x.zip + | call most anything on IBM i ... | + | (PGM, SRVPGM, PASE, DB2, etc.) | + ----------------------------------------- + +(1) XMLSERVICE is designed to allow you flexibility to develop from your laptop and run on your IBM i production with no changes. (RPG download) +(2) PHP DB2 stored procedure interface can be called form your laptop and run on your IBM i production with no changes. (PHP Zend Server) +(3) Zend New PHP Toolkit is one way to work with XMLSERVICE that ships with Zend Server for IBM i. (PHP download) +(4) A PHP old toolkit compatibility layer is also available for old PHP Zend toolkit users. (PHP download) + +**(3-4) You may update IBM i PHP toolkit via Zend PTF or see main page for update download.** + +Why do you call it XMLSERVICE?? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* same name as RPG source installation library (XMLSERVICE library) +* name of the job(s)/program(s) seen in wrkactjob (see below) +* services XML input scripts and returns XML output data (a rose by any other name) + +:: + + wrkactjob + + Opt Subsystem/Job User Type CPU % Function Status + QSYSWRK QSYS SBS .0 DEQW + XTOOLKIT QTMHHTP1 BCH .0 PGM-XMLSERVICE SEMW + +Note: XTOOLKIT naming 1.5+ + + +Why is XMLSERVICE Open Source? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* IBM has many nice folks that believe in Open Source for IBM i, sharing common utilities in IBM i community +* Business friendly BSD license makes it easy to include in any Open Source project (Zend PHP, your applications, etc.) +* XML documents over transport neutral design lends itself to "easy" inclusion is your proprietary/custom applications (XML is just a big string, you can use it anywhere) +* Pure RPG source because many IBM i folks know the language, but not the tricks like using PASE for functions + +XMLSERVICE highlights +^^^^^^^^^^^^^^^^^^^^^ + +* 100% RPG code BSD licensed to be completely free to use (XMLSERVICE library download) +* allows develop on PC and deploy on either PC (2-tier) or IBM i (1-tier) +* all communication is XML documents +* called RPG programs can maintain state, commit boundaries, etc +* provided IPC mechanism that synchronizes web clients multiple sources +* traditional DB2 connection security 1-2 tier (when using DB2 stored procedures provided) +* conversions of XML string text to/from actual program required parameter geometery is handled (packed decimal 12p2, zoned decimal 7s2, integer 10i0/5i0, float 4f2/8f4, hex binary 400b), and character data converted automatically ASCII/EBCDIC (1024A/10a varying='on') +* arrays of data structures are allowed both as paramters and return, including nested data structurers and/or arrays of data structures (try it to believe it) +* Many limits of other toolkit PGM/SRVPGM callers are not an issue with XMLSERVICE (up to 15MB to date), therefore "old school" RPG using occurs can often pass back all data records in one call. + +We have been experimenting with the following configurations. +:: + + 1-tier (running on IBM i): + 1) HTML REST : |PC|browser<----------------->|IBM i|<-ApacheCGI--| + 2) PHP ibm_db2 : |PC|browser<----------------->|IBM i|<-FastCGI/PHP| + 3) PHP pdo_ibm : |PC|browser<----------------->|IBM i|<-FastCGI/PHP| + 4) PHP odbc : |PC|browser<----------------->|IBM i|<-FastCGI/PHP| + 5) Zend Toolkit: (using one of above transport interfaces) | ...xml ... + ->XMLSERVICE + | ...call... + 2-tier (Linux/Windows to IBM i): | >PGM + 1) PHP REST : |PC|Apache/PHP<-REST--------->|IBM i|<-ApacheCGI--| >SRVPGM + 2) PHP ibm_db2 : |PC|Apache/PHP<-DB2 Connect-->|IBM i|<-QRWTSRVR---| >CMD + 3) PHP pdo_ibm : |PC|Apache/PHP<-DB2 Connect-->|IBM i|<-QRWTSRVR---| >PASE shell + 4) PHP odbc : |PC|Apache/PHP<-CA ODBC------>|IBM i|<-QRWTSRVR---| + 5) Zend Toolkit: (using one of above transport interfaces) + +**Note:** + +- PHP was used to test, but any DB2/REST capable scripting language will work +- REST interface used for test does not require PHP on IBM i (not fast) +- ibm_db2 has been the best interface for production use (fast) +- 2-tier does not require PHP on IBM i +- odbc and pdo_ibm had trouble with large contiguous data (PDF) + + +XMLSERVICE syntax (XML in, XML out) +----------------------------------- +:: + + ----------------------------------------- + | Browser | + |---------------------------------------| + | Download RPG (1) | Download PHP (2) | + | 1) XMLSERVICE | a) PHP CW Toolkit | + | HTML/XML/REST | b) New PHP Toolkit | + | no PHP |--------------------| + | (xmlcgi.pgm) | c) PHP “Raw XML” | + | (optional) | (ibm_db2, odbc) | + | -----------------------------------| + | 2) XMLSERVICE DB2 stored procedures | + | (iPLUG4K, iPLUG32K, ..., iPLUG15M) | + | 3) XMLSERVICE (xmlservice.pgm) |<-(1) + | call most anything on IBM i ... | + | (PGM, SRVPGM, PASE, DB2, etc.) | + ----------------------------------------- + +(1) XMLSERVICE parses and executes XML input for IBM i commands (CMD), programs (PGM/SRVPGM), and PASE utilites (ls, system, etc.), data results are then returned in XML output. XMLSERVICE processes XML scripts in "order of appearance". Every effort is made to make one pass through the XML to keep performance, therefore XML input "order of appearance" is important and left up to the XML script writer (see below). + +:: + + Order is important: + * followed by + * followed by (1.2.1 beta) + * followed by (1.2.1 beta) + * followed by + + Example: + + + +(2) XMLSERVICE setting parameter data + +Setting parameter data is a simple task that looks much the same as any language, using XML in this case. As you can see below you must send the entire XML document/script each time, because XMLSERVICE will not remember "everything" about each program (), command () or PASE utility called (). + +:: + + 1st XMLSERVICE call ... + + + 2nd XMLSERVICE call ... + + + +Why send a full XML document description each time you call program/script? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Because XMLSERVICE is simply an script interpreter like BASIC or PHP, not a true compiler like RPG where "binary program" lasts forever, therefore you cannot just "pass XML data" without also describing what the data means (type='12p2'). + +If you are writing a language wrapper around this support (like Zend PHPToolkit), you can easily "hide" the XML document specifics of passing a full XML document (the whole idea). However, if you are using the RAW XML interface (this document), you will have to bite your tongue and send the whole document. + +Note: However we are working short-cuts like overlay for some XML parameter "customization", but we intend stopping short of complete "programming by XML" because we do not want to reinvent PHP (or the like). + +Full example (2 complete calls): + +1) XMLSERVICE call number 1 complete call with data XMLSERVICE will work: +:: + + + + + +2) XMLSERVICE call number 2 complete call with "different" data XMLSERVICE will work: +:: + + + + + +XMLSERVICE REST interface (HTML/XML, no PHP) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + ----------------------------------------- + | Browser | + |---------------------------------------| + (1)->| Download RPG (1) | Download PHP (2) | + | 1) XMLSERVICE | a) PHP CW Toolkit | + | HTML/XML/REST | b) New PHP Toolkit | + | no PHP |--------------------| + | (xmlcgi.pgm) | c) PHP “Raw XML” | + | (optional) | (ibm_db2, odbc) | + | -----------------------------------| + | 2) XMLSERVICE DB2 stored procedures | + | (iPLUG4K, iPLUG32K, ..., iPLUG15M) | + | 3) XMLSERVICE (xmlservice.pgm) | + | call most anything on IBM i ... | + | (PGM, SRVPGM, PASE, DB2, etc.) | + ----------------------------------------- + + (1) HTTP REST xmlcgi.pgm (tests, demos): + http://myibmi/cgi-bin/xmlcgi.pgm&db2=x@uid=x@pwd=x@ipc=x@ctl=x@xmlin=x@xmlout=x + + A example interface is included that does not require scripting language on the IBM i (no PHP). + The sample program xmlservice/xmlcgi is an RPG program that supports HTTP GET/POST interface. + This is an easy interface to call XMLSRVICE during testing without the bother of writing a script. + +If you open up the source code of xmlcgi.pgm you will see that is calls the XMLSERVICE stored procedure interface in the next section. This allows for common access methodology including REST clients (like a browser), because no matter if calling from any application/device choice all security design remains within the consistent DB2 database connection(s). Also, because XMLSERVICE has a built in "one at a time" lock mechanism (semaphore) all/any clients can access the same XMLSERVICE job at the same time and you the script developer don't have to worry about coordination. + + +REST parameters (xmlcgi.pgm) + + * db2 - what database (\*LOCAL tested) + * uid - user profile (\*NONE - no uid version 1.5+) + * pwd - profile password (\*NONE - no password version 1.5+) + * ipc - IPC key name/security route to XMLSERVICE job (/tmp/fred01, etc.) + * ctl - CTL admin control XMLSERVICE job (see control below) + * xmlin - XML input document (request) + * xmlout - expected size of XML output document (response size in bytes) + +:: + + Apache rest configuration (CGI interface) + Example: Add the following to /www/zendsvr/conf/httpd.conf + ScriptAlias /cgi-bin/ /QSYS.LIB/XMLSERVICE.LIB/ + + AllowOverride None + order allow,deny + allow from all + SetHandler cgi-script + Options +ExecCGI + + + Optional for server side includes (very handy for pure html/xml sites): + # server side includes + # AddOutputFilter INCLUDES .htm + # AddOutputFilter INCLUDES .html + Options +ExecCGI +Includes + AddType text/html .shtml + AddOutputFilter INCLUDES .shtml + AddOutputFilter INCLUDES .html + + Example (html interface xmlcgi.pgm): + + + +
+ + + + + + + + +
+ + + + Output: + + + C + D + 321.1234 + 1234567890.12 + + + E + F + 333.3330 + 4444444444.44 + + + 0 + + +Note: uid / pwd \*NONE available XMLSERVICE version 1.5+. + + + +XMLSERVICE stored procedure interface (production) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + ----------------------------------------- + | Browser | + |---------------------------------------| + | Download RPG (1) | Download PHP (2) | + | 1) XMLSERVICE | a) PHP CW Toolkit | + | HTML/XML/REST | b) New PHP Toolkit | + | no PHP |--------------------| + | (xmlcgi.pgm) | c) PHP “Raw XML” | + | (optional) | (ibm_db2, odbc) | + | -----------------------------------| + | 2) XMLSERVICE DB2 stored procedures |<-(2) + | (iPLUG4K, iPLUG32K, ..., iPLUG15M) | + | 3) XMLSERVICE (xmlservice.pgm) | + | call most anything on IBM i ... | + | (PGM, SRVPGM, PASE, DB2, etc.) | + ------------------------------------------ + + (2) DB2 stored procedures (production): + call iPLUG[R]xxx(ipc, ctl, xmlin, [xmlout]) + + This DB2 stored procedure interface was designed for production level machines Zend Server on i (1 tier) + and remote machines over DB2 Connect (2 tier). + +When you first start using XMLSERVICE generic stored procedures (see below), you may wonder why not just write custom stored procedures for each program i wish to call vs. using XMLSERVICE "dynamic stored procedures"? + +* XML documents are very easy to send around over any transport, therefore you can upgrade your application capabilities instantley without all the custom stored procedure programming, In a practical sense this means you can use different scripting languages (php, java, perl, ruby, etc.), with various database connections (db2 connect, odbc, etc.) and/or simply HTTP (seen previously) +* XMLSERVICE supports full state programming via IPC routing (/tmp/fred1, /tmp/sally2, etc.), thereby traditional RPG programming techniques with many open files, transactions, etc., can be employed in called programs/modules lowering the learning curve (and rewrites not needed) +* XMLSERVICE XML documents are very easy to wrapper with any custom scripting interface, such as the new PHP toolkit interface Zend is working on. + +:: + + DB2 in/out parameters (connections supporting in/out parameters): + ... sizes: 4K, 32K, 65K, 512K, 1M, 5M, 10M up to 15M (see crtsql in download) ... + iPLUG4K(IN IPC CHAR(1024), IN CTL CHAR(1024),IN CI CHAR(4064), OUT C0 CHAR(4064)) + iPLUG32K(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(32000), OUT CO CLOB(32000)) + iPLUG65K(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(65K), OUT CO CLOB(65K)) + iPLUG512K(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(512K), OUT CO CLOB(512K)) + iPLUG1M(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(1M), OUT CO CLOB(1M)) + iPLUG5M(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(5M), OUT CO CLOB(5M)) + iPLUG10M(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(10M), OUT CO CLOB(10M)) + iPLUG15M(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(15M), OUT CO CLOB(15M)) + + $stmt = db2_prepare($conn, "call XMLSERVICE.iPLUG4K(?,?,?,?)"); + $ret=db2_bind_param($stmt, 1, "ipc", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 2, "ctl", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 3, "clobIn", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 4, "clobOut", DB2_PARAM_OUT); + $ret=db2_execute($stmt); + + + DB2 Result set returned (connections not supporting in/out parameters): + ... sizes: 4K, 32K, 65K, 512K, 1M, 5M, 10M up to 15M (see crtsql in download) ... + CREATE PROCEDURE XMLSERVICE.iPLUGR4K(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CHAR(4096)) + iPLUGR32K(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CHAR(32000)) + iPLUGR65K(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(65K)) + iPLUGR512K(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(512K)) + iPLUGR1M(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(1M)) + iPLUGR5M(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(5M)) + iPLUGR10M(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(10M)) + iPLUGR15M(IN IPC CHAR(1024), IN CTL CHAR(1024), IN CI CLOB(15M)) + + $stmt = db2_prepare($conn, "call $libxmlservice.iPLUGR4K(?,?,?)"); + $ret=db2_execute($stmt,array($ipc,$ctl,$clobIn)); + while ($row = db2_fetch_array($stmt)){ + $clobOut .= trim($row[0]); + } + + +**Why different sizes??** + +The idea is simply pick the "best fit" max size of XML documents to/from IBM i per call, +so you don't transport a whole bunch of blanks between you scripting/IBM i applications. + + + +Stored procedure parameters (details follow): + +* IN IPC CHAR(1024) - IPC key name/security +* IN CTL CHAR(1024) - CTL admin control XMLSERVICE job +* IN CI CLOB(15M) - XML input document (request) +* OUT CO CLOB(15M) - XML output document (response) + +*Note: iPLUGRxxx procedures return a result set that is collected by fetch.* + +**1) IN IPC CHAR(1024) - IPC key (InternalKey)** + +The IPC/InternalKey is used to route XML input/output documents to the correct XMLSERVICE job. The IPC is fully qualified path commonly in the /tmp directory where each unique path is the target of given XMLSERVICE job being used by a given user/password profile (normal profile authority applies). + +#. InternalKey must be a fully IFS qualified path, if not exist then XMLSERVICE will attempt to create on first use. +#. InternalKey object authorization is standard IBM i profile mechanism. +#. XMLSERVICE provides a sync IPC lock (/tmp/$anything), so only one user/request is active at a time (like 5250). + +Example call PGM ZZCALL, run in Zend subsystem, \*debug wait before call (qsysopr msg):: + + $ipc='/tmp/ranger001'; + $ctl = '*sbmjob(ZENDSVR/ZSVR_JOBD) *debug'; + $clobIn = + " + "; + $clobOut = ""; + $stmt = db2_prepare($conn, "call XMLSERVICE.iPLUG4K(?,?,?,?)"); + $ret=db2_bind_param($stmt, 1, "ipc", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 2, "ctl", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 3, "clobIn", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 4, "clobOut", DB2_PARAM_OUT); + $ret=db2_execute($stmt); + + +**2) IN CTL CHAR(1024) - CTL admin** + +These control options are used to control the XMLSERVICE jobs. + +:: + + // *immed - end server immed destroy ipc + // *sbmjob[(lib/jobd)] - sbmjob(PLUGJOBLIB/PLUGJOBD) + // *nostart - disallow spawn + // *here - run stateless in stored proc job + // *session - retrieve session key (IPC) + // *license - license this code + : + +Example kill XMLSERVICE for '/tmp/rangerusr':: + + $ipc='/tmp/rangerusr'; + $ctlKill="*immed"; + $clobInKill = ''; + $sql = "call $libxmlservice.iPLUGR4K('$ipc','$ctlKill','$clobInKill')"; + $ret=db2_exec($conn,$sql); + + +**3) IN CI CLOB(4K to 15M) - XML input** + +This is the input XML request document (see XML syntax next section). The XML input document will run all actions in the XMLSERVICE job and produce an XML output document. + +:: + + + + + +**4) OUT CO CLOB(15M) - XML output** + +The output XML document will be produced in the same order that the XML input script runs, The output can be collected from the in/out parameter (iPLUGxxx) or from fetch of a result set (iPLUGRxxx). + +:: + + + + +**Workaround for "bad" drivers leaving junk back of output clob** + +Alas, the world is not perfect 1-2 tier DB2 drivers IBM i, Windows, Linux, etc., so occasionally a "hack" is handy. + +I always "scope" my XML input requests with , so anything past tailing is 'junk' (errors return as ...). + +The new XMLSERVICE keyword \*hack adds back of every record return result set can be very useful for drivers that do not support stored procedure in/out parameters like PHP odbc. + +:: + + function driverJunkAway($xml) + { + // trim blanks (opps no) + $clobOut = $xml; + if (! trim($clobOut)) return $clobOut; + + // result set has extra data (junk) + $fixme = ''; + $pos = strpos($clobOut,$fixme); + if ($pos > -1) { + $clobOut = substr($clobOut,0,$pos); + } + else { + $fixme = ''; + $pos = strpos($clobOut,$fixme); + if ($pos > -1) { + $clobOut = substr($clobOut,0,$pos+strlen($fixme)); + } + // maybe error/performance report + else { + $fixme = ''; + $pos = strpos($clobOut,$fixme); + if ($pos > -1) { + $clobOut = substr($clobOut,0,$pos+strlen($fixme)); + } + } + } + return $clobOut; + } + +**Example PHP client use driverJunkAway($xml)** + +:: + + function xmlservice_ibm_db2($xml) { + global $fast, $db, $user, $pass, $ipc, $ctl, $conn, $lib, $plug, $plugR; + $xmlIn = $xml; + $xmlOut = ''; + if (!$conn) { + if ($fast) $conn = db2_pconnect($db, $user, $pass); // persistent/pooled connection + else $conn = db2_connect($db, $user, $pass); // full open/close connection + } + if (!$conn) die("Bad connect: $db, $user"); + $stmt = db2_prepare($conn, "call $lib.$plug(?,?,?,?)"); // Call XMLSERVICE + // stored procedure interface + // in/out parameter (xmlOut) + // sizes: iPLUG4K - iPLUG15M + if (!$stmt) die("Bad prepare: ".db2_stmt_errormsg()); + $ret=db2_bind_param($stmt, 1, "ipc", DB2_PARAM_IN); // ? - /tmp/raw_$user (*sbmjob) + $ret=db2_bind_param($stmt, 2, "ctl", DB2_PARAM_IN); // ? - *here or *sbmjob + $ret=db2_bind_param($stmt, 3, "xmlIn", DB2_PARAM_IN); // ? - XML input script + $ret=db2_bind_param($stmt, 4, "xmlOut", DB2_PARAM_OUT); // ? - XML output return + $ret=db2_execute($stmt); + if (!$ret) die("Bad execute: ".db2_stmt_errormsg()); + return driverJunkAway($xmlOut); // just in case driver odd + // ... possible junk end record, + // recordjunk + } + + function xmlservice_odbc($xml) { + global $fast, $db, $user, $pass, $ipc, $ctl, $conn, $lib, $plug, $plugR; + $xmlIn = $xml; + $xmlOut = ''; + if (!$conn) { + if ($fast) $conn = odbc_pconnect($db, $user, $pass); // persistent/pooled connection + else $conn = odbc_connect($db, $user, $pass); // full open/close connection + } + $stmt = odbc_prepare($conn, "call $lib.$plugR(?,?,?)"); // Call XMLSERVICE + // stored procedure interface + // result set return (fetch) + // sizes: iPLUGR4K - iPLUGR15M + if (!$stmt) die("Bad prepare: ".odbc_errormsg()); + $options = array($ipc, // ? - /tmp/raw_$user (*sbmjob) + $ctl, // ?- *here or *sbmjob + $xmlIn); // ?- XML input script + // bad behavior odbc extension + // ... IBM i result set warning??? + error_reporting(~E_ALL); // bad behavior odbc extension + // ... IBM i result set warning??? + $ret=odbc_execute($stmt,$options); + if (!$ret) die("Bad execute: ".odbc_errormsg()); + error_reporting(E_ALL); + while(odbc_fetch_row($stmt)) { + $xmlOut .= driverJunkAway(odbc_result($stmt, 1)); // bad behavior odbc extension + // ... possible junk end record, + // xmlservice provided $ctl='*hack' + // recordjunk + } + return $xmlOut; + } + + function xmlservice_pdo_ibm($xml) { + global $fast, $db, $user, $pass, $ipc, $ctl, $conn, $lib, $plug, $plugR; + $xmlIn = $xml; + $xmlOut = ''; + if (!$conn) { + $database = "ibm:".$db; + try { + if ($fast) $opt = array(PDO::ATTR_PERSISTENT=>true, PDO::ATTR_AUTOCOMMIT=>true); // persistent/pooled connection + else $opt = array(PDO::ATTR_AUTOCOMMIT=>true); // full open/close connection + $conn = new PDO($database, strtoupper($user), strtoupper($pass), $opt); + if (!$conn) throw new Exception("Bad"); + } catch( Exception $e ) { + die("Bad connect: $database, $user"); + } + } + try { + $stmt = $conn->prepare("call $lib.$plug(?,?,?,?)"); // Call XMLSERVICE + // stored procedure interface + // in/out parameter (xmlOut) + // sizes: iPLUG4K - iPLUG15M + if (!$stmt) throw new Exception('Bad'); + } catch( Exception $e ) { + $err = $conn->errorInfo(); + $cod = $conn->errorCode(); + die("Bad prepare: ".$cod." ".$err[0]." ".$err[1]." ".$err[2]); + } + try { + $r1=$stmt->bindParam(1,$ipc, PDO::PARAM_STR); + $r2=$stmt->bindParam(2,$ctl, PDO::PARAM_STR); + $r3=$stmt->bindParam(3,$xmlIn, PDO::PARAM_STR); + $r4=$stmt->bindParam(4,$xmlOut, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT); + $ret = $stmt->execute(); + if (!$ret) throw new Exception('Bad'); + } catch( Exception $e ) { + $err = $stmt->errorInfo(); + $cod = $stmt->errorCode(); + die("Bad execute: ".$cod." ".$err[0]." ".$err[1]." ".$err[2]); + } + return driverJunkAway($xmlOut); // just in case driver odd + // ... possible junk end record, + // recordjunk + } + + +XMLSERVICE advanced CCSID +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Using default PHP toolkit DB2 clob interface (iPLUGxxx/iPLUGRxxx), ccsid conversion occurs naturally as DB2 client/server and you will not have to code before/after, but method is available if you have a specific concern or you have scripts returning many different languages. + +Example:: + + bin2hex('Hebrew_ascii_raw_chars') + bin2hex('Farsi_ascii_raw_chars') + bin2hex('Russia_ascii_raw_chars') + bin2hex('Italy_ascii_raw_chars') + bin2hex('Germany_ascii_raw_chars') + bin2hex('Korea_ascii_raw_chars') + bin2hex('Japan_ascii_raw_chars') + bin2hex('China_ascii_raw_chars') + where: + before - XMLSERVICE convert CCSID before ILE program call + after - XMLSERVICE convert CCSID after ILE program call for client return + bin2hex() - script hex string unaltered ascii image (also returned hex string avoid any conversion) + pack() - script uses pack('H*',"xml_hex_back") function in PHP program for ascii characters + Note: + Up to four conversions can take place for the truly diabolical ccsid issues + bin2hex('wild_ascii_raw_chars') + flow: + -> PHP client bin2hex('wild_ascii_raw_chars') + -> xmlservice hex2bin back to 'wild_ascii_raw_chars' + -> xmlservice convert cc1->cc2->cc3->cc4 (before) + -> xmlservice make ILE call + -> xmlservice convert cc4->cc3->cc2->cc1 (after) + -> xmlservice tohex "xml_hex_back" + -> PHP client $chars = pack('H*',"xml_hex_back") + + + + +.. + [--Author([[http://youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICEGeneric?action=expirediff | s ]])--] + [--Tony "Ranger" Cairns - IBM i PHP / PASE--] + + diff --git a/docs/idle-timeout.rst b/docs/idle-timeout.rst new file mode 100755 index 0000000..cc4d45b --- /dev/null +++ b/docs/idle-timeout.rst @@ -0,0 +1,178 @@ + + +XMLSERVICE/Toolkit idle time out +================================ +`Goto Main Page`_ + +.. _Goto Main Page: index.html + +Who is this page for? +--------------------- + +Instructions designed for IBM i developer learning PHP and XMLSERVICE ... + + +How to make your toolkit jobs time out +-------------------------------------- + +XMLSERVICE from 1.6.2 onward contains flexible timeout features, including the ability to have a program (RPG, COBOL, CL, whatever) time out if it runs too long. This is useful to end jobs that get hung with a MSGWAIT condition. + +The PHP toolkit wrappers currently enable only one of these timeout features: the "idle timeout." When using a private connection job, also known as jobs that have an IPC or InternalKey, the job can be made to time out when a certain number of seconds of inactivity have elapsed. + +**Cleanliness vs. performance** + +Causing jobs to time out quickly will give you a nice, empty, clean-looking ZENDSVR subsystem, +but will drag down performance the next time the job is started up. Try to find a balance between +cleanliness and performance. If you plan to use the same jobs over and over, you may wish to NOT +time out the jobs, either never ending them or ending them \*IMMED at night, or some other scheme. + +**How to choose a timeout value** +If you have many transient users who will briefly use the site and then not return, you may want a quick timeout (30 seconds?). For users who will return over and over again, you may want a long or nonexistent timeout. + +* Idle timeout with the new toolkit API + +The "idle timeout" can be set or changed any time in this way:: + + // let's assume the toolkit connection has been established using getInstance() and is present in the variable $conn: + $idleTimeoutSeconds = 1800; // time out job after 1800 seconds (30 minutes) of inactivity + // a value of 0 means no timeout (infinite wait) + $conn->setOptions(array('idleTimeout' => $idleTimeoutSeconds)); + +* Idle timeout with the Compatibility Wrapper (CW) + +Just as with the old toolkit, when you connect with i5_pconnect(), you can set the timeout interval and the number of seconds to wait. As with the old toolkit, idle timeouts only work with private connections. Use the constant I5_OPTIONS_IDLE_TIMEOUT to provide the number of seconds. Use zero (0) seconds to never time out (the default). +:: + + $privateNum = 0; // private conn number of zero causes the CW to generate a number for next time. + $idleTimeoutSeconds = 1800; // time out job after 1800 seconds (30 minutes) of inactivity + // a value of 0 means no timeout (infinite wait) + $options = array(I5_OPTIONS_PRIVATE_CONNECTION => $privateNum, + I5_OPTIONS_IDLE_TIMEOUT => $idleTimeoutSeconds + ); + + // connect (note: the "p" for persistent is required for CW private connections) + // and specify a private connection and timeout + $conn = i5_pconnect('localhost', 'user', 'pw', $options); + +Note: The CW also supports the new API's "setToolkitServiceParams" technique described above, because the CW uses the new toolkit underneath. + + +Full support RAW XMLSERVICE Samples +----------------------------------- + +Programs with qsysopr message idle timeout may help. +:: + + + + ENDPROC; + return test_lib_replace($clob); + } + ?> + + +Timeout is a setting control setting with various properties allowed. +:: + + *---------------------------------------------------- + * -- server time out jobs XMLSERVICE (1.6.2) + * *wait[(seconds[/action])] + * - client side wait for XMLSERVICE call (client side) + * example: $ctl="*wait(10)"; + * - default action *wait(60/busy) (see plugconfx) + * *call[(seconds[/action[/duration[/job]]])] + * - client/server side XMLSERVICE call wait (PGM, SRVPGM, PASE, etc) + * example: $ctl="*wait(10) *call(5/busy/client)"; + * - default for both client/server is *call(0) + * means wait on call forever (user code flawless), + * but can be overriden client/server/both + * *idle[(seconds[/action[/duration]])] + * - server side XMLSERVICE idle no activity + * example: $ctl="*wait(10/kill) *call(30/kill) *idle(30/perm)"; + * - default action *idle(1800/kill) (see plugconfx) + * -- time out parameters + * seconds: + * -1 - current default timer + * 0 - no timer, no timeout, wait forever + * n - idle timer "pop" seconds + * action: + * kill - end job immed + * user - user override signal behaviour (see plugconfx) + * busy - return busy XML (client side) + * busy response (1301050): + * + * 1301050 + * IPC timeout busy + * + * duration: + * perm - set and use new defaults all requests + * orig - reset and use original compile defaults (see plugconfx) + * job: + * client - *call action applies client side + * server - *call action applies server side + * -- Notes: + * - default timeout/action provided plugconf.rpgle, + * but each request may override/reset to fit task(s) + * - signal SIGALRM used with this function + * can affect user program calls, + * *call(0) may be used to turn off timer + * during user program calls + * - action 'user' allows for custom signal + * processing in the RPG code (see plugconfx) + * - if duration not specified, attributes + * *wait(),*call(),*idle() are temporary + * for this call only and return to last defaults. + * - if 'job' not specified on *call(), + * attribute settings apply to both sides + * - end job immed kills XMLSERVICE job (server) + * and destroys IPC, so any waiting client is + * released with an IPC missing error. + *---------------------------------------------------- + + + + + +.. + [--Author([[http://youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICETimeOut?action=expirediff | s ]])--] + [--Tony "Ranger" Cairns - IBM i PHP / PASE--] diff --git a/docs/index.rst b/docs/index.rst old mode 100644 new mode 100755 index bf0407c..233e227 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,17 +3,36 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. + Welcome to XMLSERVICE's documentation! ====================================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 :caption: Contents: + intro.rst + functions.rst + generic-interface.rst + examples.rst + source-layout.rst + connections.rst + ipc.rst + idle-timeout.rst + debugging.rst + date-time-timestamp.rst + library-list.rst + qtemp.rst + varchar.rst + ccsids.rst + faster.rst + performance.rst + errors.rst + faq.rst +.. + Indices and tables + ================== -Indices and tables -================== - -* :ref:`genindex` -* :ref:`search` + * :ref:`genindex` + * :ref:`search` diff --git a/docs/intro.rst b/docs/intro.rst new file mode 100755 index 0000000..85356c4 --- /dev/null +++ b/docs/intro.rst @@ -0,0 +1,1836 @@ +XMLSERVICE Introduction +======================= + +`Goto Main Page`_ + +.. _Goto Main Page: index.html + +XMLSERVICE Introduction +----------------------- + +XMLSERVICE is Open Source RPG, created for web programming simplicity and deployment flexibility, +any language, any transport, avoiding complexities dealing with big package 'web services'. +Internally, XMLSERVICE is designed Plain Old XML (POX), which enables simple REST XML protocol, +avoiding complexity of SOAP/WSDL based Web Services. XMLSERVICE is 100% RPG, never requires Java, +PHP, or any other front end language to serve your IBM i RPG programs to the web. In fact, +XMLSERVICE is so simple, that you can almost run your entire IBM i machine using only simple +HTML/XML (demonstrated later section). + +XMLSERVICE is a single library of Open Source RPG code that enables XML scripting calls of System i resources. +XMLSERVICE RPG server library does not require other language products to run on your IBM i, however language teams often provide a client toolkit to greatly simplify XML calls to XMLSERVICE. + +XMLSERVICE, as name implies, enables XML services on your IBM i machine. XMLSERVICE library is simply +a collection of Open Source RPG modules that essentially allow your to access anything on your IBM i machine, +assuming proper profile authority. Simply stated, XMLSERVICE accepts XML document containing actions/parameters +``(,,,,etc.)``, performs requested operations on IBM i, then sends an XML document of results back to the client. This simple concept has become a powerful tool for scripting language clients on IBM i including Zend Server PHP Toolkit and PowerRuby Toolkit. However, PHP and Ruby are not unique to XMLSERVICE, XML is a universal protocol support by nearly every language, therefore nearly any language can use XMLSERVICE, and most any transport between client/server. + +Installation +------------ +**Build requirements** + +Building requires Python 3 and GNU make. These can be installed with yum: yum install python3 make-gnu + +You will also need the ILE RPG compiler installed (5770-WDS option 31) along with the following PTFs:: + + 7.3: SI62605 + 7.2: SI62604 + 7.1: SI62580 + +**Building** + +:: + + PATH=/QOpenSys/pkgs/bin:$PATH + + git clone https://github.com/IBM/xmlservice.git + + cd xmlservice + + python3 ./configure + + make + +**Customizing the Build** + +You can customize the build by passing options to configure:: + + --library: set the build library + --debug: set the debug level (DBGVIEW CL parameter) + + +REST interface +^^^^^^^^^^^^^^ + +Optional: Set up your Apache to enable XMLSERVICE REST using RPG program XMLCGI.PGM included in XMLSERVICE installation, then restart your Apache instance. + +:: + + /www/apachedft/conf/httpd.conf: + ScriptAlias /cgi-bin/ /QSYS.LIB/XMLSERVICE.LIB/ + + AllowOverride None + order allow,deny + allow from all + SetHandler cgi-script + Options +ExecCGI + + + stop/start Apache instance: + endTCPSVR SERVER(*HTTP) HTTPSVR(APACHEDFT) + STRTCPSVR SERVER(*HTTP) HTTPSVR(APACHEDFT) + +Operation and transports +------------------------ + +XMLSERVICE XML documents can be transported between IBM i and clients over any connection, any language. + + +XMLSERVICE APIs (included) +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +XMLSERVICE library includes language transports for popular REST and DB2 connections, which fulfills needs for most internet services applications. + +* XMLSERVICE/XMLCGI.PGM-- RPG CGI HTTP/REST method GET or POST (traditional web service interface) + + * http://myibmi/cgi-bin/xmlcgi.pgm?db2=x@uid=x@pwd=x@ipc=x@ctl=x@xmlin=x@xmlout=x + +* XMLSERVICE/XMLSTOREDP.SRVPGM -- RPG DB2 stored procedure (IBM i's premier DB2 for i) + + * DB2 drivers local/remote with stored procedure IN/OUT capabilities (traditional DB2 interface) + + :: + + iPLUG4K (IN IPC CHAR(1024), IN CTL CHAR(1024), IN XMLIN CHAR(4064), OUT XMLOUT CHAR(4064)) + iPLUG32K (IN IPC CHAR(1024), IN CTL CHAR(1024), IN XMLIN CLOB(32000), OUT XMLOUT CLOB(32000)) + iPLUG65K (IN IPC CHAR(1024), IN CTL CHAR(1024), IN XMLIN CLOB(65K), OUT XMLOUT CLOB(65K)) + iPLUG512K(IN IPC CHAR(1024), IN CTL CHAR(1024), IN XMLIN CLOB(512K), OUT XMLOUT CLOB(512K)) + iPLUG1M (IN IPC CHAR(1024), IN CTL CHAR(1024), IN XMLIN CLOB(1M), OUT XMLOUT CLOB(1M)) + iPLUG5M (IN IPC CHAR(1024), IN CTL CHAR(1024), IN XMLIN CLOB(5M), OUT XMLOUT CLOB(5M)) + iPLUG10M (IN IPC CHAR(1024), IN CTL CHAR(1024), IN XMLIN CLOB(10M), OUT XMLOUT CLOB(10M)) + iPLUG15M (IN IPC CHAR(1024), IN CTL CHAR(1024), IN XMLIN CLOB(15M), OUT XMLOUT CLOB(15M)) + + * DB2 drivers local/remote without stored procedure IN/OUT capabilities (loop fetch required) + + :: + + iPLUGR4K (IN IPC CHAR(1024), IN CTL CHAR(1024), IN XMLIN CHAR(4064)) + iPLUGR32K (IN IPC CHAR(1024), IN CTL CHAR(1024), IN XMLIN CLOB(32000)) + iPLUGR65K (IN IPC CHAR(1024), IN CTL CHAR(1024), IN XMLIN CLOB(65K)) + iPLUGR512K(IN IPC CHAR(1024), IN CTL CHAR(1024), IN XMLIN CLOB(512K)) + iPLUGR1M (IN IPC CHAR(1024), IN CTL CHAR(1024), IN XMLIN CLOB(1M)) + iPLUGR5M (IN IPC CHAR(1024), IN CTL CHAR(1024), IN XMLIN CLOB(5M)) + iPLUGR10M (IN IPC CHAR(1024), IN CTL CHAR(1024), IN XMLIN CLOB(10M)) + iPLUGR15M (IN IPC CHAR(1024), IN CTL CHAR(1024), IN XMLIN CLOB(15M)) + +* XMLSERVICE/XMLSTOREDP.SRVPGM -- optional custom transport (programmers only) + + * if included XMLSERVICE transports do not fill your need, please feel free to create your own (sockets, data queues, ftp, etc.). Multiple entry APIs exist in XMLSERVICE that you may find useful: + + :: + + xmlstoredp.srvpgm - *SRVPGM interface for calls + + Native stored procedure call target (iPLUG4K - iPLUG15M): + D iPLUG4K PR 1N extproc(*CL:'iPLUG4K') + D pIPC 1024A + D pCtl 1024A + D pXmlIn * + D pXmlOut * + + RPG call target: + D runClient PR 1N + D pIPCSP 1024A + D pCtl 1024A + D pIClob * + D szIClob 10i 0 + D pOClob * + D szOClob 10i 0 + + PASE call target (also use RPG when CCSID issues): + D runASCII PR 1N + D pIPCSP2 * + D szIPCSP2 10i 0 + D pCtlSP2 * + D szCtlSP2 10i 0 + D pIClob2 * + D szIClob2 10i 0 + D pOClob2 * + D szOClob2 10i 0 + D ccsidPASE2 10i 0 + D ccsidILE2 10i 0 + + +IBM i CCSID 65535 +^^^^^^^^^^^^^^^^^ + +XMLSERVICE REST and DB2 connections have implicit CCSID conversion between client and server, therefore your XMLIN/XMLOUT XML document will be implicitly CCSID converted by the transport layer, to wit, XMLSERVICE should just work. However, IBM i CCSID 65535 (hex), will destroy the entire XMLSERVICE scheme, and you will witness horrible hangs, junk data, dead processes, etc., so please take action on your IBM i to live with the modern ASCII world (all clients, all new scripting languages, remote/local including PASE). + +**Check your IBM i for CCSID convert safety.** + +If you see DSPSYSVAL 65535, you have a big problem with the ASCII world, but you can take corrective IBM i action. + +:: + + DSPSYSVAL SYSVAL(QCCSID) + Coded character set + identifier . . . . . : 65535 1-65535 + + +Here are some corrective IBM i suggestions (CCSID 37 an example), but remember you have to end current running jobs and restart for the CCSID changes to enable (including XTOOLKIT jobs): + +* change system ccsid + +:: + + CHGSYSVAL SYSVAL(QCCSID) VALUE(37) + +* change Apache instances (/www/instance/httpd.conf) + +:: + + # protect FastCGI against bad CCSID (dspsysval qccsid 65535) + DefaultFsCCSID 37 + CGIJobCCSID 37 + +* change user profile(s) + +:: + + CHGUSRPRF USRPRF(FRED) CCSID(37) + + +Connection public or private +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +XMLSERVICE parameters CTL and IPC enable two types of connections. + +* public connection -- XMLSERVICE stateless, many jobs, every use a fresh start + + * ``CTL='*here' , IPC="*NA"`` + + * "public" because any IBM i profile use is a fresh start + * life scope -- life of a single XML IN/OUT request + +* private connection -- XMLSERVICE state full, single job, where a given profile is routed to XTOOLKIT job(s) + + + ``CTL='*sbmjob' , IPC="/tmp/myjob1"`` + + + ``CTL='*sbmjob'`` -- if not running, submit a new XTOOLKIT job + + ``IPC="/tmp/myjob1"`` -- all requests of IPC route to XTOOLKIT job (\*sbmjob) + + "private" because ONE IBM i profile owns XTOOLKIT job, any other IBM i profile will fail to attach + + life scope -- forever, until ended by operator or user program ends XTOOLKIT job (like a 5250) + +**When to use XMLSERVICE public or private?** + +Of course any discussion that presumes to predict usage is likely to have legitimate user exception, but a guideline may be appropriate for use of XMLSERVICE public vs. private connection. + +**Web programming style (public, stateless)** + +XMLSERVICE public connections are generally used when IBM i resource can be called once by many different user profiles, +current data returned, no lasting persistent data needed by the IBM i resource. The programmer meta description of +transient resource services is "stateless programming". + +XMLSERVICE public "stateless" +(CTL='\*here', IPC='\*NA') + +- profile FRED (any public QSQ) +- profile SALLY (any public QSQ) +- profile RITA (any public QSQ) +- profile XAVIER (any public QSQ) + +XMLSTOREDP->XMLSERVICE (QSQ) + +- QSQ temporary profile use (stateless) +- QSQ return to pool on script end +- XMLSERVICE restart every request (web style) + + +Although very handy, clean wrkactjob, no hanging locks, etc., public connection is not a high performance use of XMLSERVICE. + +* public connection -- XMLSERVICE stateless, many jobs, every use a fresh start + + + ``CTL='*here' , IPC="*NA"`` -- profile FRED (RPG company stock service) + + ``CTL='*here' , IPC="*NA"`` -- profile SALLY (RPG IRS flat tax rate service) + + ``CTL='*here' , IPC="*NA"`` -- profile RITA (RPG calculate pound to kilo) + + ``CTL='*here' , IPC="*NA"`` -- profile XAVIER (RPG company stock service) + +**Traditional programming style (private, state full)** + +XMLSERVICE private connections are generally used when IBM i resource will be called many times by the same user profile, lasting persistent data needed by the IBM i resource (RPG variables, open files, etc.). The programmer meta description of required data services is "state full programming". + +XMLSERVICE private "state full" +(CTL='\*sbmjob', IPC='/tmp/xxxx') + +- profile FRED XTOOLKIT myjob1,myjob2 (private) +- profile SALLY XTOOLKIT sallyjob1 (private) +- profile RITA XTOOLKIT nursejob (private) +- profile XAVIER XTOOLKIT xjob1 - xjob5 (private) + +XMLSTOREDP (QSQ) + +- QSQ temporary profile use (stateless) +- QSQ return to pool on script end + +XMLSERVICE (XTOOLKIT) + +- XTOOLKIT owned by profile (private) +- XTOOLKIT job never ends (until killed) +- XTOOLKIT full state programming (5250 style) + + +Traditional RPG programs usually need to track local variables, open files, etc., between requests, both for correct functionality and performance. The XTOOLKIT jobs that result from ``CTL='*sbmjob' , IPC="/tmp/xxxx"`` are similar to 5250 jobs, where a user profile signs on, then uses 5250 job to run other programs (aka XMLSERVICE design), also, like multiple 5250 sessions (PC emulators), many different XTOOLKIT jobs can be used by the same user profile. + +* private connection -- XMLSERVICE state full, single job, where a given profile is routed to same XTOOLKIT job + + + ``CTL='*sbmjob' , IPC="/tmp/myjob1"`` -- profile FRED XTOOLKIT myjob1 (Fred's use only RPG payroll application) + + ``CTL='*sbmjob' , IPC="/tmp/myjob2"`` -- profile FRED XTOOLKIT myjob2 (Fred's use only RPG inventory application) + + ``CTL='*sbmjob' , IPC="/tmp/sallyjob1"`` -- profile SALLY XTOOLKIT sallyjob1 (Sally's use only RPG admissions application) + + ``CTL='*sbmjob' , IPC="/tmp/nursejob"`` -- profile RITA XTOOLKIT nursejob (Rita's use only RPG nurse scheduling application) + + ``CTL='*sbmjob' , IPC="/tmp/xjob1"`` -- profile XAVIER XTOOLKIT xjob1 (Xavier's use only RPG payroll application) + + ``CTL='*sbmjob' , IPC="/tmp/xjob2"`` -- profile XAVIER XTOOLKIT xjob2 (Xavier's use only RPG inventory application) + + ``CTL='*sbmjob' , IPC="/tmp/xjob3"`` -- profile XAVIER XTOOLKIT xjob3 (Xavier's use only RPG admissions application) + + ``CTL='*sbmjob' , IPC="/tmp/xjob4"`` -- profile XAVIER XTOOLKIT xjob4 (Xavier's use only RPG nurse scheduling application) + + ``CTL='*sbmjob' , IPC="/tmp/xjob5"`` -- profile XAVIER XTOOLKIT xjob5 (Xavier's use only RPG super hero application) + + IBM i programmer: Profile Xavier is using many XTOOLKIT jobs (xjob1 - xjob5), many different applications, but Xavier cannot use Fred's, Sally's or Rita's XTOOLKIT jobs (myjob1,myjob2,sallyjob1,nursejob), because Xavier does not own other profile XTOOLKIT jobs. XTOOLKIT jobs should be an easy pattern for RPG programmers familiar with single session 5250 job(s), owned by a profile, one thing at a time (not threaded), long running RPG programs, many IBM i files open, etc. + + * However, XTOOLKIT jobs have an interesting characteristic that 5250 emulator jobs cannot match, profile owned XTOOLKIT jobs can be accessed by many different client devices all at the same time, to wit, Xavier can use a laptop and a smart phone to all jobs at the same time (xjob1 - xjob5), or Xavier can leave his work laptop running (connected), go home, have dinner, and continue working all XTOOLKIT jobs from the family iPAD. + + * IBM i operator: You may wrkactjob and kill ``*immed`` XTOOLKIT jobs (same as 5250). + + * However, an ipcs administrative cleaner solution may suggest you write a custom XMLSERVICE program ``CTL='*immed' , IPC="/tmp/myjob1"``, to remove "in the way" XTOOLKIT jobs (suggestion only). + +Open Source goal +---------------- + +XMLSERVICE is constantly growing Open Source, so new functions are added over time. +XMLSERVICE goal is never impact existing applications with new features, to date, +XML's natural ability to add keywords and attributes has been very successful in keeping this goal. + +XMLSERVICE CMDs +^^^^^^^^^^^^^^^ +:: + + + + +XMLSERVICE PASE +^^^^^^^^^^^^^^^ + +:: + + + + +XMLSERVICE DB2 +^^^^^^^^^^^^^^ + +:: + + + + + +XMLSERVICE PGMs, SRVPGMs, APIs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + PGM: + + + + SRVPGM: + + + + System APIs: + + + + +XMLSERVICE HTML/XML interface +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Using your IBM i and XMSERVICE download (see installation), without writing one line of code in any language, we can already check out XMLSERVICE functions using HTML/XML forms. We should note, that by any standard the following trivial XMLSERVICE example is clearly REST web services, but no SOAP, no WSDL, no Java, no PHP, no Ruby, nothing but 2 cents worth of HTML/XML. + + +**XMLSERVICE input** - Plain Old XML input to XMLSERVICE for request ``select * from db2/animals``. + +:: + + + + select * from db2/animals + + + + +**XMLSERVICE output** - Plain Old XML output from XMLSERVICE with records returned from ``db2/animals``. + +:: + + + + + + + + + + + + + + + +Instructions for your IBM i machine +----------------------------------- + +**Step 1)** - Add XMLSERVICE to any Apache instance (APACHEDFT) + +Set up your Apache to enable XMLSERVICE REST using RPG program XMLCGI.PGM included in XMLSERVICE installation, then restart your Apache instance. + +:: + + /www/apachedft/conf/httpd.conf: + ScriptAlias /cgi-bin/ /QSYS.LIB/XMLSERVICE.LIB/ + + AllowOverride None + order allow,deny + allow from all + SetHandler cgi-script + Options +ExecCGI + + + start Apache instance: + STRTCPSVR SERVER(*HTTP) HTTPSVR(APACHEDFT) + + +**Step 2)** - Ready to use XMLSERVICE for HTML/XML + +Cut/Paste following HTML/XML form to your laptop Desktop/strsql.html: + +* change action target to your actual machine ``action="http://myibmi/cgi-bin/xmlcgi.pgm"`` +* point your favorite browser at the HTML file ``file:///home/adc/Desktop/strsql.html`` + + * enter database (\*LOCAL), user (your profile), password (your password) + * enter a SQL query in HTML strsql command and press button ``STRSQL`` + +:: + + desktop/strsql.html: + + + + + +

STRSQL

+
+
database +
user +
password + + + + +
strsql command (select * from db2/animals) + +

+
+ + + + +**desktop/strsql.html example** + +As strsql.html name implies, this simple html enables STRSQL running from your laptop to your IBM i. Enter any SQL statement you wish in the html form and XMLSERVICE will run just like STRSQL green screen when you press the ``STRSQL`` button. XMLSERVICE output returned will be XML (of course), so if your browser has issues displaying XML, you may have to view page source. + +HTML form strsql.html uses simple JavaScript function ``getVal()`` with ``document.getElementById('strsql').value``, which reads HTML text input `` + -- change -- +
+ + The follow cut/paste is one continuous browser line (split for viewing): + http://myibmi/cgi-bin/xmlcgi.pgm?db2=*LOCAL + &uid=MYUID + &pwd=MYPWD + &ipc=*NA + &ctl=*here+*cdata + &xmlin=%3C%3Fxml+version%3D%271.0%27%3F%3E + %3Cmyscript%3E + %3Csql%3E + %3Cquery%3Eselect+*+from+db2%2Fanimals + %3C%2Fquery%3E + %3Cfetch+block%3D%27all%27+desc%3D%27on%27%3E + %3C%2Ffetch%3E + %3C%2Fsql%3E + %3C%2Fmyscript%3E + &xmlout=500000 + &strsql=select+*+from+db2%2Fanimals + + +The flow: + +* We point our browser ``file:///home/adc/Desktop/strsql.html``, enter SQL query and press ``STRSQL`` button +* IBM i Apache XMLCGI.PGM receives our encoded HTML/XML form ``action="http://myibmi/cgi-bin/xmlcgi.pgm"`` +* XMLCGI.PGM calls XMLSERVICE.PGM (using DB2 stored procedures iPLUGxxx, but you do not need to know this yet). +* XMLSERVICE.PGM parses XML input and runs internal DB2 driver ``...``. +* XMLSERVICE.PGM parses result set from DB2 driver into output XML document ```` +* browser sees XML return of DB2 data + +XMLSERVICE is Open Source, so you can examine internals of XMLCGI.PGM, for now , we simply need to understand XMLCGI.PGM decodes HTML/XML document, passes XML document request to XMLSERVICE.PGM (DB2 request example), and returns XML output to client (browser). If REST client is not a browser, but a scripting language like PHP or Ruby, exact same sequence occurs, except additionally most languages offer an XML parser to parse output XML into variables or structures (PHP Toolkit or Ruby Toolkit). + +Quick test functions HTML/XML +----------------------------- + +XMLSERVICE HTML/XML technique can be used for nearly anything on IBM i machine CMD, PGM, SRVPGM, system APIs, PASE utilities, DB2, etc. Feel free to copy strsql.html form, modify, and try other XMLSERVICE functions ``other functions``. HTML/XML technique is a very handy testing a potential XMLSERVICE program service without writing a line of code, and, clearly demonstrates elegant simplicity embodied by XMLSERVICE. + +XMLSERVICE DB2 interface +^^^^^^^^^^^^^^^^^^^^^^^^ + +DB2 connection is not a web service, but many languages support high speed DB2 local/remote requests, so XMLSERVICE included a stored procedures interface (iPLUG4K - iPLUG15M). The nature of DB2 stored procedures requires a size specified on in/out parameters, therefore XMLSERVICE library includes various iPLUGxx sizes to fit your XML document data needs (4K, 32K, 65K, 512K, 1M, 5M, 10M, up to 15M). + +We should note, XMLSERVICE DB2 is much faster over REST interface, so many language toolkits offer DB2 connection as the premier service. + +* RPG DB2 (no toolkit) + +:: + + myIPC = '/tmp/thebears1'; + myCtl = '*sbmjob'; + // call XMLSERVICE/ZZCALL(...) using XML only + myXmlIn = + '' + x'0D' + + '' + x'00'; + myXmlOut = *BLANKS; + // make call to XMLSERVICE provided stored procedure(s) + // sizes from iPLUG4k to iPLUG15M (see crtsql xmlservice package) + Exec Sql call XMLSERVICE/iPLUG4K(:myIPC,:myCtl,:myXmlIn,:myXmlOut); + +* PHP DB2 (toolkit) + +:: + + require_once("ToolkitService.php"); + try { $ToolkitServiceObj = ToolkitService::getInstance($database, $user, $password); } + catch (Exception $e) { die($e->getMessage()); } + $ToolkitServiceObj->setToolkitServiceParams( + array('InternalKey'=>$ipc, // route to same XMLSERVICE job /tmp/myjob1 + 'subsystem'=>"QGPL/QDFTJOBD", // subsystem/jobd to start XMLSERVICE (if not running) + 'plug'=>"32K")); // max size data i/o (iPLUG4K,32K,65K, 512K,1M,5M,10M,15M) + $param[] = $ToolkitServiceObj->AddParameterChar ('both', 1, 'INCHARA', 'var1', 'Y'); + $param[] = $ToolkitServiceObj->AddParameterChar ('both', 1, 'INCHARB', 'var2', 'Z'); + $param[] = $ToolkitServiceObj->AddParameterPackDec('both', 7,4,'INDEC1', 'var3', '001.0001'); + $param[] = $ToolkitServiceObj->AddParameterPackDec('both', 12,2,'INDEC2', 'var4', '0000000003.04'); + $ds[] = $ToolkitServiceObj->AddParameterChar ('both', 1, 'DSCHARA', 'ds1', 'A'); + $ds[] = $ToolkitServiceObj->AddParameterChar ('both', 1, 'DSCHARB', 'ds2', 'B'); + $ds[] = $ToolkitServiceObj->AddParameterPackDec('both', 7,4,'DSDEC1', 'ds3', '005.0007'); + $ds[] = $ToolkitServiceObj->AddParameterPackDec('both', 12,2,'DSDEC1', 'ds4', '0000000006.08'); + $param[] = $ToolkitServiceObj->AddDataStruct($ds); + $result = $ToolkitServiceObj->PgmCall('ZZCALL', $testLib, $param, null, null); + echo "good so far ...\n"; + var_dump($result); + + +* PHP DB2 (without toolkit) + +:: + + $fast = false; + $ipc = "*NA"; + $ctl = "*here *cdata"; + $xmlIn = " + "; + $xmlOut = ''; + if ($fast) $conn = db2_pconnect($db, $user, $pass); // persistent/pooled connection + else $conn = db2_connect($db, $user, $pass); // full open/close connection + if (!$conn) die("Bad connect: $db, $user"); + $stmt = db2_prepare($conn, "call $lib.$plug(?,?,?,?)"); // Call XMLSERVICE + // stored procedure interface + // in/out parameter (xmlOut) + // sizes: iPLUG4K - iPLUG15M + if (!$stmt) die("Bad prepare: ".db2_stmt_errormsg()); + $ret=db2_bind_param($stmt, 1, "ipc", DB2_PARAM_IN); // ? - /tmp/raw_$user (*sbmjob) + $ret=db2_bind_param($stmt, 2, "ctl", DB2_PARAM_IN); // ? - *here or *sbmjob + $ret=db2_bind_param($stmt, 3, "xmlIn", DB2_PARAM_IN); // ? - XML input script + $ret=db2_bind_param($stmt, 4, "xmlOut", DB2_PARAM_OUT); // ? - XML output return + $ret=db2_execute($stmt); + if (!$ret) die("Bad execute: ".db2_stmt_errormsg()); + var_dump($xmlOut); + + + + PowerRuby DB2 (toolkit) + ----------------------- + +:: + + require 'active_record' + require 'xmlservice' + + ActiveRecord::Base.establish_connection( + :adapter => 'ibm_db', + :database => '*LOCAL', + :username => 'MYUID', + :password => 'MYPWD' + ) + ActiveXMLService::Base.establish_connection( + :connection => 'ActiveRecord', + :install => "XMLSERVICE", + :ctl => "*here *cdata", + :ipc => "*NA", + :size => 4096, + :head => "" + ) + zzcall = XMLService::I_PGM.new("zzcall","xmlservice") + zzcall << XMLService::I_a.new('inchara',1,'a') + zzcall << XMLService::I_a.new('incharb',1,'b') + zzcall << XMLService::I_p.new('indec1',7,4,11.1111) + zzcall << XMLService::I_p.new('indec2',12,2,222.22) + zzcall << XMLService::I_DS.new('inds1',1, + [XMLService::I_a.new('dschara',1,'x'), + XMLService::I_a.new('dscharb',1,'y'), + XMLService::I_p.new('dsdec1',7,4,66.6666), + XMLService::I_p.new('dsdec2',12,2,77777.77)]) + zzcall.xmlservice + puts zzcall.out_xml + +* PowerRuby DB2 (without toolkit) + +:: + + require 'active_record' + + ipc = "*NA" + ctl = "*here *cdata" + xmlin = ' + ' + xmlout = "" + ActiveRecord::Base.establish_connection( + :adapter => 'ibm_db', + :database => '*LOCAL', + :username => 'MYUID', + :password => 'MYPWD' + ) + conn = ActiveRecord::Base.connection.connection + stmt = IBM_DB::prepare(conn, 'CALL XMLSERVICE.iPLUG512K(?,?,?,?)') + ret = IBM_DB::bind_param(stmt, 1, "ipc", IBM_DB::SQL_PARAM_INPUT) + ret = IBM_DB::bind_param(stmt, 2, "ctl", IBM_DB::SQL_PARAM_INPUT) + ret = IBM_DB::bind_param(stmt, 3, "xmlin", IBM_DB::SQL_PARAM_INPUT) + ret = IBM_DB::bind_param(stmt, 4, "xmlout", IBM_DB::SQL_PARAM_OUTPUT) + ret = IBM_DB::execute(stmt) + puts xmlout + + +* Java DB2 (no toolkit) + +:: + + import java.io.*; + import java.util.*; + import java.sql.*; + import java.math.*; + + public class javaXMLserviceDemoJDBC { + public static void main(String[] args) { + Connection conn = null; + Statement stmt=null; + CallableStatement cstmt = null ; + PreparedStatement pstmt = null; + String url = "jdbc:db2://localhost"; // Set URL for data source + String user = "MYUID"; + String password = "MYPWD"; + try + { // Load the DB2(R) Universal JDBC Driver with DriverManager + Class.forName("com.ibm.db2.jdbc.app.DB2Driver"); + conn = DriverManager.getConnection(url, user, password); + String inputClob = + "" + + " "; + String sql="CALL XMLSERVICE.iPLUG512K(?,?,?,?)"; + cstmt = conn.prepareCall(sql); + System.out.println("Calling with valid name"); + cstmt.setString(1,"/tmp/packers01"); + cstmt.setString(2,"*sbmjob"); + cstmt.setString(3,inputClob); + cstmt.registerOutParameter(4, Types.CLOB); + cstmt.execute(); + String doc = cstmt.getString(4); + System.out.println("****** Documento XML: **********"); + System.out.println(doc); + } + catch (Exception e) + { System.out.println("******* Eccezione !!! *********"); + e.printStackTrace(); + } + } + } + + +* perl DB2 (no toolkit) + +:: + + use DBI; + use DBD::DB2::Constants; + use DBD::DB2; + + $dbh = DBI->connect("dbi:DB2:*LOCAL") + or die $DBI::errstr; + + $stmt = 'call XMLSERVICE.iPLUG65K(?,?,?,?)'; + $sth = $dbh->prepare($stmt) + or die "prepare got error " . $dbh->err; + $ipc = "/tmp/perlme"; + $sth->bind_param(1, $ipc) + or die "bind 1 got error " . $dbh->err; + $ctl = "*sbmjob"; + $sth->bind_param(2, $ctl) + or die "bind 2 got error " . $dbh->err; + $xmlin = ' + + '; + $sth->bind_param(3, $xmlin) + or die "bind 3 got error " . $dbh->err; + $xmlout = ""; + $xmloutlen = 4096; + $sth->bind_param_inout(4, \$xmlout, $xmloutlen) + or die "bind 4 got error " . $dbh->err; + $sth->execute() + or die "execute got error" . $dbh->err; + + + +XMLSERVICE REST interface +^^^^^^^^^^^^^^^^^^^^^^^^^ + +XMLSERVICE includes a simple REST interface (XMLCGI.PGM), we demonstrated using the REST service using only HTML/XML in a previous section. Most languages support REST calls, so XMLSERVICE REST interface can be very useful for cloud applications where DB2 drivers are not available. XMLSERVICE has REST production clients in most every scripting language you can imagine (PHP, Ruby, perl, python, etc.). + +* JavaScript REST (no toolkit) + +:: + + + + + + + +

This page demonstrates calling XMLSERVICE by JavaScript. Display source in your browser to see JavaScript used.

+ +
    +
  • {XMLSERVICE JavaScript table (click me)} - DoJo REST call RPG build table element
  • + +

    +

    +

    + + + + + +* PHP REST (no toolkit) + +:: + + + + ENDPROC; + return $clob; + } + // make the call + $i5persistentconnect = false; + $database = "*LOCAL"; + $user = "MYUID"; + $password = "MYPWD"; + $libxmlservice = "XMLSERVICE"; + $ipc = '/tmp/rangerhtmlonly'; + $ctl = '*sbmjob'; + $clobIn = getxml(); + $clobOut = ""; + $clobOutMax = "32000"; + $i5rest = "http://174.79.32.155/cgi-bin/xmlcgi.pgm"; + $postdata = http_build_query( + array( + 'db2' => $database, + 'uid' => $user, + 'pwd' => $password, + 'ipc' => $ipc, + 'ctl' => $ctl, + 'xmlin' => $clobIn, + 'xmlout' => $clobOutMax // size expected XML output + ) + ); + $opts = array('http' => + array( + 'method' => 'POST', + 'header' => 'Content-type: application/x-www-form-urlencoded', + 'content' => $postdata + ) + ); + $context = stream_context_create($opts); + $clobOut = file_get_contents($i5rest, false, $context); + ?> + + +* PowerRuby REST (no toolkit) + +:: + + require 'adapters/abstract_adapter' + require 'net/http' + require 'uri' + + xmlin = ' + ' + xmlout = "" + + post_args = { + :db2 => "*LOCAL", + :uid => "MYUID", + :pwd => "MYPWD", + :ipc => "*NA", + :ctl => "*here *cdata", + :xmlin => xmlin, + :xmlout => 4096 + } + uri = URI("http://myibmi/cgi-bin/xmlcgi.pgm") + res = Net::HTTP.post_form(uri, post_args) + xmlout = res.body + puts xmlout + + +* Java REST (no toolkit) + +:: + + import java.net.*; + import java.io.*; + public class javaXMLserviceDemoREST { + public static void main(String[] args) + { try + { // The URL class does not itself encode or decode any URL components according to the escaping mechanism defined in RFC2396. + // It is the responsibility of the caller to encode any fields, which need to be escaped prior to calling URL, + // and also to decode any escaped fields, that are returned from URL. Furthermore, because URL has no knowledge of URL escaping, + // it does not recognise equivalence between the encoded or decoded form of the same URL + String inputURL = + "http://174.79.32.155/cgi-bin/xmlcgi.pgm?" + + java.net.URLEncoder.encode( + "db2=*LOCAL" + + "&uid=MYUID" + + "&pwd=MYPWD" + + "&ipc=/tmp/rangerhtmlonly" + + "&ctl=*sbmjob" + + "&xmlin=" + + "" + + " " + + "&xmlout=32768", + "ISO-8859-1"); + // make REST call to XMLSERVICE + URL restUrl = new URL(inputURL); + URLConnection conn = restUrl.openConnection(); + // output processing + System.out.println("Content type: " + conn.getContentType()); + System.out.println("Content length: " + conn.getContentLength()); + BufferedReader strm = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String inputLine; + String doc = ""; + while ((inputLine = strm.readLine()) != null) { + doc = doc + inputLine; + } + System.out.println("****** Documento XML: **********"); + System.out.println(doc); + } + catch (Exception e) + { System.out.println("******* Eccezione !!! *********"); + e.printStackTrace(); + } + } + } + `` + + +RPG and XMLSERVICE +------------------ + +RPG is usually considered server side of XMLSERVICE, a called RPG PGM or SRVPGM (web service), but RPG is well versed in DB2, therefore can also be used as RPG client to XMLSERVICE. In fact, client RPG DB2 connection to XMLSERVICE will likely be fastest and easiest choice, especially for your remote IBM i systems (WRKRDBDIRE). + +The RPG client examples will demonstrate RPG CGI using XMLSERVICE via Apache configuration, but the techniques also work as batch or green screen application, after using XMLSERVICE for a while, you may realize just how little code you have to write to do a great deal of work. + +* Apache CGI configuration (httpd.conf) + +:: + + ScriptAlias /cgi-bin/ /QSYS.LIB/XMLSERVICE.LIB/ + + order allow,deny + allow from all + SetHandler cgi-script + Options +ExecCGI + + + +* RPG client XMLSERVICE (\*PGM) + +RPG program ZZRPGSQL.PGM demonstrates client use of XMLSERVICE calling a \*PGM (ZZCALL.pgm). + +:: + + XMLSERVICE/ZZCALL (*PGM) + D INCHARA S 1a + D INCHARB S 1a + D INDEC1 S 7p 4 + D INDEC2 S 12p 2 + D INDS1 DS + D DSCHARA 1a + D DSCHARB 1a + D DSDEC1 7p 4 + D DSDEC2 12p 2 + *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * main(): Control flow + *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + C *Entry PLIST + C PARM INCHARA + C PARM INCHARB + C PARM INDEC1 + C PARM INDEC2 + C PARM INDS1 + + +The simple example is of course a bit contrived, simply extracting strings between ``output`` returned XMLSERVICE output, but serves to demonstrate XMLSERVICE call using ``Exec Sql call XMLSERVICE/iPLUG4K(:myIPC,:myCtl,:myXmlIn,:myXmlOut);``. + +ZZRPGSQL.PGM calling XMLSERVICE via ``Exec Sql call XMLSERVICE/iPLUG4K`` + +:: + + H AlwNull(*UsrCtl) + H BNDDIR('QC2LE') + + * vars + D myIPC s 1024A inz(*BLANKS) + D myCtl s 1024A inz(*BLANKS) + D myXmlIn s 4096A inz(*BLANKS) + D myXmlOut s 4096A inz(*BLANKS) + + D i s 10i 0 inz(0) + D rn s 10i 0 inz(0) + + D data s 65000A inz(*BLANKS) + D xml1 s 65000A inz(*BLANKS) + D xml2 s 65000A inz(*BLANKS) + D xml3 s 65000A inz(*BLANKS) + D xml4 s 65000A inz(*BLANKS) + + D strstr PR * ExtProc('strstr') + D pVal1 * Value options(*string) + D pVal2 * Value options(*string) + + D strlen PR 10I 0 ExtProc('strlen') + D pVal * Value options(*string) + + D writeIFS PR 20I 0 ExtProc('write') + D fd 10I 0 value + D buf * value + D size 10I 0 value + + D xmlfind PR 65000A + D xml * value + D nbr 10i 0 value + D xbeg 32A value + D xbeg1 32A value + D xend 32A value + + D Main PR ExtPgm('ZZRPGSQL') + *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * main(): Control flow + *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + D Main PI + /free + Monitor; + + // ----- + // input + + myIPC = '*NA'; + myCtl = '*here'; + myXmlIn = + '' + x'0D' + + '' + x'00'; + myXmlOut = *BLANKS; + + // ----- + // sql call XMLSERVICE provided stored procedure(iPLUG4k -iPLUG15M) + Exec Sql call XMLSERVICE/iPLUG4K(:myIPC,:myCtl,:myXmlIn,:myXmlOut); + + // ----- + // output (CGI) + + // ----- + // send header + end (LFLF) + data = 'Content-type: text/html' + x'15' + x'15' + x'00'; + rn = writeIFS(1:%addr(data):strlen(%addr(data))); + // ----- + // send return data + // HTML table + data = '

    RPG call XMLSERVICE

    ' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + for i = 1 to 8; + xml1 = xmlfind(%addr(myXmlIn): i:'var=' :'"':'"'); + xml2 = xmlfind(%addr(myXmlIn): i:'type=':'"':'"'); + xml3 = xmlfind(%addr(myXmlIn): i:'':''); + xml4 = xmlfind(%addr(myXmlOut):i:'':''); + // HTML table row + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + endfor; + data = %trim(data) + '
    Parameter nameType valueInput valueOutput value
    ' + %trim(xml1) + '' + %trim(xml2) + '' + %trim(xml3) + '' + %trim(xml4) + '
    ' + x'00'; + rn = writeIFS(1:%addr(data):strlen(%addr(data))); + + On-error; + Endmon; + + return; + /end-free + + ***************************************************** + * xmlfind + ***************************************************** + P xmlfind B + D xmlfind PI 65000A + D xml * value + D nbr 10i 0 value + D xbeg 32A value + D xbeg1 32A value + D xend 32A value + * vars + D i s 10i 0 inz(0) + D cbeg s 33A inz(*BLANKS) + D cbeg1 s 33A inz(*BLANKS) + D cend s 33A inz(*BLANKS) + D pbeg s * inz(*NULL) + D pend s * inz(*NULL) + D xmlFragment s 65000A inz(*BLANKS) + /free + cbeg = %trim(xbeg) + x'00'; + cbeg1 = %trim(xbeg1) + x'00'; + cend = %trim(xend) + x'00'; + pbeg = xml; + for i = 1 to nbr; + // 123 + // x + if pbeg <> *NULL; + pbeg = strstr(pbeg + 1:%addr(cbeg)); + endif; + endfor; + // 123 + // x + if pbeg <> *NULL; + pbeg = strstr(pbeg:%addr(cbeg1)); + endif; + if pbeg <> *NULL; + // 123 + // x x + pbeg += 1; + pend = strstr(pbeg:%addr(cend)); + if pend <> *NULL and pend > pbeg; + xmlFragment = %str(pbeg:pend - pbeg); + endif; + endif; + return xmlFragment; + /end-free + P E + + +**What is happening?** + +The flow of ZZRPGSQL.PGM is similar to HTML/XML, except we are using the XMLSERVICE provided stored procedure interface to call XMLSERVICE. + +* We point our browser to ``http://lp0364d:10022/cgi-bin/zzrpgsql.pgm``, which is RPG CGI program ZZRPGSQL.PGM. +* ZZRPGSQL.PGM uses XMLSERVICE DB2 interface ``Exec Sql call XMLSERVICE/iPLUG4K(:myIPC,:myCtl,:myXmlIn,:myXmlOut);``, passing XML input request myXmlIn to XMLSERVICE. +* XMLSERVICE.PGM parses XML input and dynamically loads/activates/calls target ZZCALL.PGM with included parameters ``...``. +* ZZCALL.PGM runs using normal in/out parameters (memory) +* XMLSERVICE.PGM parses in/out parameters from ZZCALL.PGM into output XML document +* ZZRPGSQL.PGM myXmlOut variable contains XML output document +* ZZRPGSQL.PGM parses myXmlOut into a HTML table output (writeIFS) +* browser sees the HTML table of ZZCALL data + +* RPG client XMLSERVICE (\*SRVPGM) + +RPG program ZZVRYSQL.PGM demonstrates client use of XMLSERVICE calling a \*SRVPGM (ZZSRV.ZZVARY). As you read source code for ZZVRYSQL.PGM, you will see it is nearly identical to previous example ZZRPGSQL.PGM. + +:: + + XMLSERVICE/ZZSRV.ZZVARY (*SRVPGM) + P zzvary B export + D zzvary PI 20A varying + D myName 10A varying + +XMLSERVICE called ZZSRV.ZZARRY SRVPGM demonstrates advance functions problematic for PCML based web services. + +* difficult -- parameter ``10A varying`` - PCML requires multiple XML definitions for length, data +* impossible -- return ``20A varying`` - PCML has no ability to return complex data (only single integer) + + +ZZVRYSQL.PGM calling XMLSERVICE via ``Exec Sql call XMLSERVICE/iPLUG4K`` + +:: + + H AlwNull(*UsrCtl) + H BNDDIR('QC2LE') + + * vars + D myIPC s 1024A inz(*BLANKS) + D myCtl s 1024A inz(*BLANKS) + D myXmlIn s 4096A inz(*BLANKS) + D myXmlOut s 4096A inz(*BLANKS) + + D i s 10i 0 inz(0) + D rn s 10i 0 inz(0) + + D data s 65000A inz(*BLANKS) + D xml1 s 65000A inz(*BLANKS) + D xml2 s 65000A inz(*BLANKS) + D xml3 s 65000A inz(*BLANKS) + D xml4 s 65000A inz(*BLANKS) + + D strstr PR * ExtProc('strstr') + D pVal1 * Value options(*string) + D pVal2 * Value options(*string) + + D strlen PR 10I 0 ExtProc('strlen') + D pVal * Value options(*string) + + D writeIFS PR 20I 0 ExtProc('write') + D fd 10I 0 value + D buf * value + D size 10I 0 value + + D xmlfind PR 65000A + D xml * value + D nbr 10i 0 value + D xbeg 32A value + D xbeg1 32A value + D xend 32A value + + D Main PR ExtPgm('ZZVRYSQL') + *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * main(): Control flow + *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + D Main PI + /free + Monitor; + + // ----- + // input + // P zzvary B export + // D zzvary PI 20A varying + // D myName 10A varying + myIPC = '*NA'; + myCtl = '*here'; + myXmlIn = + '' + x'0D' + + '' + x'00'; + myXmlOut = *BLANKS; + + // ----- + // sql call XMLSERVICE provided stored procedure(iPLUG4k -iPLUG15M) + Exec Sql call XMLSERVICE/iPLUG4K(:myIPC,:myCtl,:myXmlIn,:myXmlOut); + + // ----- + // output (CGI) + + // ----- + // send header + end (LFLF) + data = 'Content-type: text/html' + x'15' + x'15' + x'00'; + rn = writeIFS(1:%addr(data):strlen(%addr(data))); + // ----- + // send return data + // HTML table + data = '

    RPG call XMLSERVICE

    ' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + for i = 1 to 2; + xml1 = xmlfind(%addr(myXmlIn): i:'var=' :'"':'"'); + xml2 = xmlfind(%addr(myXmlIn): i:'type=':'"':'"'); + xml3 = xmlfind(%addr(myXmlIn): i:'':''); + xml4 = xmlfind(%addr(myXmlOut):i:'':''); + // HTML table row + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + endfor; + data = %trim(data) + '
    Parameter nameType valueInput valueOutput value
    ' + %trim(xml1) + '' + %trim(xml2) + '' + %trim(xml3) + '' + %trim(xml4) + '
    ' + x'00'; + rn = writeIFS(1:%addr(data):strlen(%addr(data))); + + On-error; + Endmon; + + return; + /end-free + + ***************************************************** + * xmlfind + ***************************************************** + P xmlfind B + D xmlfind PI 65000A + D xml * value + D nbr 10i 0 value + D xbeg 32A value + D xbeg1 32A value + D xend 32A value + * vars + D i s 10i 0 inz(0) + D cbeg s 33A inz(*BLANKS) + D cbeg1 s 33A inz(*BLANKS) + D cend s 33A inz(*BLANKS) + D pbeg s * inz(*NULL) + D pend s * inz(*NULL) + D xmlFragment s 65000A inz(*BLANKS) + /free + cbeg = %trim(xbeg) + x'00'; + cbeg1 = %trim(xbeg1) + x'00'; + cend = %trim(xend) + x'00'; + pbeg = xml; + for i = 1 to nbr; + // 123 + // x + if pbeg <> *NULL; + pbeg = strstr(pbeg + 1:%addr(cbeg)); + endif; + endfor; + // 123 + // x + if pbeg <> *NULL; + pbeg = strstr(pbeg:%addr(cbeg1)); + endif; + if pbeg <> *NULL; + // 123 + // x x + pbeg += 1; + pend = strstr(pbeg:%addr(cend)); + if pend <> *NULL and pend > pbeg; + xmlFragment = %str(pbeg:pend - pbeg); + endif; + endif; + return xmlFragment; + /end-free + P E + + +RPG client XMLSERVICE (DataQueue) +--------------------------------- + +RPG program ZZQUESQL.PGM demonstrates client use of XMLSERVICE calling a CMDs and System APIs for Data Queue. As you read source code for ZZQUESQL.PGM, you will see it is nearly identical to previous example ZZRPGSQL.PGM. + +:: + + DLTDTAQ DTAQ(XMLSERVICE/MYDATAQ) + CRTDTAQ DTAQ(XMLSERVICE/MYDATAQ) MAXLEN(100) + *************************************** + * Send Data Queue (QSNDDTAQ) API + *************************************** + * 1 Data queue name Input Char(10) + * 2 Library name Input Char(10) + * 3 Length of data Input Packed(5,0) + * 4 Data Input Char(*) Input + *************************************** + * Receive Data Queue (QRCVDTAQ) API + *************************************** + * 1 Data queue name Input Char(10) + * 2 Library name Input Char(10) + * 3 Length of data Input Packed(5,0) + * 4 Data Char(*) Output + * 5 Wait time Input Packed(5,0) + + +ZZQUESQL.PGM calling XMLSERVICE via ``Exec Sql call XMLSERVICE/iPLUG4K`` + +:: + + H AlwNull(*UsrCtl) + H BNDDIR('QC2LE') + + * vars + D myIPC s 1024A inz(*BLANKS) + D myCtl s 1024A inz(*BLANKS) + D myXmlIn s 4096A inz(*BLANKS) + D myXmlOut s 4096A inz(*BLANKS) + + D i s 10i 0 inz(0) + D rn s 10i 0 inz(0) + + D data s 65000A inz(*BLANKS) + D xml1 s 65000A inz(*BLANKS) + D xml2 s 65000A inz(*BLANKS) + D xml3 s 65000A inz(*BLANKS) + D xml4 s 65000A inz(*BLANKS) + + D strstr PR * ExtProc('strstr') + D pVal1 * Value options(*string) + D pVal2 * Value options(*string) + + D strlen PR 10I 0 ExtProc('strlen') + D pVal * Value options(*string) + + D writeIFS PR 20I 0 ExtProc('write') + D fd 10I 0 value + D buf * value + D size 10I 0 value + + D xmlfind PR 65000A + D xml * value + D nbr 10i 0 value + D xbeg 32A value + D xbeg1 32A value + D xend 32A value + + D Main PR ExtPgm('ZZVRYSQL') + *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * main(): Control flow + *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + D Main PI + /free + Monitor; + + // ----- + // input + // *************************************** + // * Send Data Queue (QSNDDTAQ) API + // *************************************** + // * 1 Data queue name Input Char(10) + // * 2 Library name Input Char(10) + // * 3 Length of data Input Packed(5,0) + // * 4 Data Input Char(*) Input + // *************************************** + // * Receive Data Queue (QRCVDTAQ) API + // *************************************** + // * 1 Data queue name Input Char(10) + // * 2 Library name Input Char(10) + // * 3 Length of data Input Packed(5,0) + // * 4 Data Char(*) Output + // * 5 Wait time Input Packed(5,0) + myIPC = '*NA'; + myCtl = '*here'; + myXmlIn = + '' + x'0D' + + '' + x'00'; + myXmlOut = *BLANKS; + + // ----- + // sql call XMLSERVICE provided stored procedure(iPLUG4k -iPLUG15M) + Exec Sql call XMLSERVICE/iPLUG32K(:myIPC,:myCtl,:myXmlIn,:myXmlOut); + + // ----- + // output (CGI) + + // ----- + // send header + end (LFLF) + data = 'Content-type: text/html' + x'15' + x'15' + x'00'; + rn = writeIFS(1:%addr(data):strlen(%addr(data))); + // ----- + // send return data + // HTML table + data = '

    RPG call XMLSERVICE

    ' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + for i = 1 to 9; + xml1 = xmlfind(%addr(myXmlIn): i:'var=' :'"':'"'); + xml2 = xmlfind(%addr(myXmlIn): i:'type=':'"':'"'); + xml3 = xmlfind(%addr(myXmlIn): i:'':''); + xml4 = xmlfind(%addr(myXmlOut):i:'':''); + // HTML table row + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + data = %trim(data) + '' + x'0D'; + endfor; + data = %trim(data) + '
    Parameter nameType valueInput valueOutput value
    ' + %trim(xml1) + '' + %trim(xml2) + '' + %trim(xml3) + '' + %trim(xml4) + '
    ' + x'00'; + rn = writeIFS(1:%addr(data):strlen(%addr(data))); + + On-error; + Endmon; + + return; + /end-free + + ***************************************************** + * xmlfind + ***************************************************** + P xmlfind B + D xmlfind PI 65000A + D xml * value + D nbr 10i 0 value + D xbeg 32A value + D xbeg1 32A value + D xend 32A value + * vars + D i s 10i 0 inz(0) + D cbeg s 33A inz(*BLANKS) + D cbeg1 s 33A inz(*BLANKS) + D cend s 33A inz(*BLANKS) + D pbeg s * inz(*NULL) + D pend s * inz(*NULL) + D xmlFragment s 65000A inz(*BLANKS) + /free + cbeg = %trim(xbeg) + x'00'; + cbeg1 = %trim(xbeg1) + x'00'; + cend = %trim(xend) + x'00'; + pbeg = xml; + for i = 1 to nbr; + // 123 + // x + if pbeg <> *NULL; + pbeg = strstr(pbeg + 1:%addr(cbeg)); + endif; + endfor; + // 123 + // x + if pbeg <> *NULL; + pbeg = strstr(pbeg:%addr(cbeg1)); + endif; + if pbeg <> *NULL; + // 123 + // x x + pbeg += 1; + pend = strstr(pbeg:%addr(cend)); + if pend <> *NULL and pend > pbeg; + xmlFragment = %str(pbeg:pend - pbeg); + endif; + endif; + return xmlFragment; + /end-free + P E + + + + diff --git a/docs/ipc.rst b/docs/ipc.rst new file mode 100755 index 0000000..67e05bd --- /dev/null +++ b/docs/ipc.rst @@ -0,0 +1,87 @@ +XMLSERVICE IPC +============== + +`Goto Main Page`_ + +.. _Goto Main Page: index.html + + +Toolkit internalKey, IPC, toolkit persistent connection ... +----------------------------------------------------------- + +With exception of old CW layer, DB2 connection persistent/full makes NO difference, internalKey (IPC) applies ONLY to XMLSERVICE layer to route back to same xmlservice job every time. This routing technique is referred to as a "state full" XMLSERVICE connection. + +The naming of the internalKey (IPC) varies across layers PHP Toolkit, but it means the same thing. + +* New PHP Toolkit - refers to internalKey ('InternalKey'=>"/tmp/packers") + + + Note: works with both persistent and non-persistent connections (db2_pconnect or db2_connect, odbc_pconnect or odbc_connect) + +* XMLSERVICE RAW - refers to IPC ($ipc="/tmp/packers") + + + Note: works with both persistent and non-persistent connections (db2_pconnect or db2_connect, odbc_pconnect or odbc_connect) + +* New PHP Toolkit CW Layer - refers to "private connection" and provides APIs to get/set private "key/nbr" + + + Note: CW Layer requires "persistent connection" to achieve "private connection", this old technology is just for compatibly with old toolkit concepts + +How IPC/internalKey works? +-------------------------- + +The internalKey (IPC) provided by the user (or PHP wrapper) is simply a unique place in the IFS file system. That is to say that one and only one /tmp/packers lives on the machine (LPAR IBM i instance), therefore it is very handy to hash this location into a key (google ftok), that can be used to create unique purpose semaphores (locks) and shared memory (shared data) on the IBM i. + +IFS /tmp/path only provides a unique key (hash key), therefore all manner of IPC-2-xmlservice workload/user balancing could be imagined, in fact you could restrict your site to only the IFS paths pre-created in some lower level directory to avoid any unwanted user ability to start an xmlservice job assuming toolkit wrapper plays along beyond using just /tmp. + +When an active xmlservice session is running on /tmp/packers you can see the semaphores and shared memory using the utility ipcs. Please note authorizations ipcs displays are exactly like any other IFS file, including owner access (RW, read/write) and \*PUBLIC access (--, none), etc. This is how xmlservice controls authorization front door to a state full XMLSERVICE job allowing only correct/matching profiles to call any specific xmlservice service job (one request at a time). + +:: + + call qp2term + > ipcs + SHARED MEMORY: + T ID KEY MODE OWNER GROUP + M 2306 0X010404F7 T-RW------- DB2 *NONE <---- /tmp/packers (shared data) + SEMAPHORES: + T ID KEY MODE OWNER GROUP + S 377 0X010404F7 --RW------- DB2 *NONE <---- /tmp/packers (lock one use at time) + + +PHP program decode hex IPC key +------------------------------ + +Unfortunately ipcs "KEY" column is displayed in hex, so if you want to see what goes with /tmp/packers +you will need to run a little program using ``$ctl="*session"``. + +:: + + zzftok2.php: + "; + $clobOut = ""; + $ret=db2_bind_param($stmt, 1, "ipc", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 2, "ctl", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 3, "clobIn", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 4, "clobOut", DB2_PARAM_OUT); + $ret=db2_execute($stmt); + if (!$ret) die("Bad execute: ".db2_stmt_errormsg()); + var_dump($clobOut); + ?> + + > http://myibmi/zzftok2.php + string(70) " + /tmp/packers + + + +.. + [--Author([[http://youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICEIPC?action=expirediff | s ]])--] + [--Tony "Ranger" Cairns - IBM i PHP / PASE--] + diff --git a/docs/library-list.rst b/docs/library-list.rst new file mode 100755 index 0000000..d56c3bd --- /dev/null +++ b/docs/library-list.rst @@ -0,0 +1,243 @@ + + +XMLSERVICE/Toolkit \*LIBL +========================= + +`Goto Main Page`_ + +.. _Goto Main Page: index.html + +change XMLSERVICE 1.7.3+ +------------------------ + +The default setting for SBMJOB of all XMLSERVICE jobs was changed to INLLIBL(\*CURRENT) better follow the user profile of QSQSRVR job. If you are having difficulty with private connections libl you may want to try this version. + +setting \*LIBL - what do i do? +------------------------------ + +Few other topics dealing with web programs cause more frustrations then setting IBM i library list (\*LIBL), but if you follow a few rules it all works. + +\*LIBL conflict between web scripts and IBM i PGMs +-------------------------------------------------- + +Any good conflict needs a basic difference in philosophy causing all the trouble. + +* '''Stateless''' - web scripts typically wish to run stateless (PHP), with no environmental settings (such as \*LIBL) set on the server +* '''State Full''' - IBM PGMs generally run state full (RPG/Cobol/CLP), meaning need \*LIBL to run at all + +Rule of toolkit web \*LIBL ... +------------------------------ + +The following rules apply to all toolkit DB2 connections persistent (db2_pconnect, odbc_pconnect) and non-persistent (db2_connect, odbc_connect). + +* '''Stateless''' - PHP scripts using Toolkit without internalKey (IPC) MUST set \*LIBL on every request before calling PGMs (see specific examples) +* '''State Full''' - PHP scripts using Toolkit with internalKey (IPC) set \*LIBL only once for life of job before calling PGMs (see specific examples) + +1) Specific examples for New PHP Toolkit +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* '''Stateless''' - set \*LIBL every single request + +:: + + // *** stateless occurs when no internalKey (e.g. '/tmp/packers') was specified *** + // *** also when ->setToolkitServiceParams('stateless'=>true)) is called + try { $ToolkitServiceObj = ToolkitService::getInstance($database, $user, $password); } + catch (Exception $e) { die($e->getMessage()); } + $ToolkitServiceObj->setToolkitServiceParams(array( + 'plug'=>"iPLUG32K")); // max size data i/o (iPLUG4K,32K,65K.512K,1M,5M,10M,15M) + + // stateless - MUST do this every single script after connect/getInstance() + // even if using persistent connections (db2_pconnect, odbc_pconnect) + $ToolkitServiceObj->CLCommand("CHGLIBL LIBL(FREDFLIN WILMAFLIN) CURLIB(FREDFLIN)"); + + // another option might be to call a setup program that sets *LIBL for you. + + +* '''State Full''' - set \*LIBL once and forget it + +:: + + $internalKey = '/tmp/packers'; + try { $ToolkitServiceObj = ToolkitService::getInstance($database, $user, $password); } + catch (Exception $e) { die($e->getMessage()); } + $ToolkitServiceObj->setToolkitServiceParams(array( + 'InternalKey'=>$internalKey, // *** RIGHT HERE internalKey/IPC + // *** run state full ... + // use SBMJOB command run in new job + // PHP can call again, again, again + // with /tmp/packers and get ... + // same job every time + // same library list (*LIBL) + // same PGMs with open files, etc. + // ... exactly like 5250 sign-on screen + 'plug'=>"iPLUG32K")); // max size data i/o (iPLUG4K,32K,65K.512K,1M,5M,10M,15M) + + + // state full - MUST do this ONCE ONLY after start/sbmjob of XMLSERVICE job + // then forget about it (unless you choose to change libl) ... + $ToolkitServiceObj->CLCommand("CHGLIBL LIBL(FREDFLIN WILMAFLIN) CURLIB(FREDFLIN)"); + + +* '''persistent connections''' - later releases PHP Toolkit allow reused/shared connections with other work, including persistent connections, but internalKey (IPC) rules remain the same + +:: + + if ($i5persistentconnect) $conn = db2_pconnect($database,$user,$password); + else $conn = db2_connect($database,$user,$password); + + try { $ToolkitServiceObj = ToolkitService::getInstance($conn); } + catch (Exception $e) { die($e->getMessage()); } + + $internalKey = '/tmp/packers'; + $ToolkitServiceObj->setToolkitServiceParams(array( + 'InternalKey'=>$internalKey, // *** RIGHT HERE internalKey/IPC + // *** run state full ... + // use SBMJOB command run in new job + // PHP can call again, again, again + // with /tmp/packers and get ... + // same job every time + // same library list (*LIBL) + // same PGMs with open files, etc. + // ... exactly like 5250 sign-on screen + 'plug'=>"iPLUG32K")); // max size data i/o (iPLUG4K,32K,65K.512K,1M,5M,10M,15M) + +* '''plug size''' - later releases PHP Toolkit 1.2.4+ calculates the correct plug name via 'plugSize', taking db type (ODBC or DB2) into account. + +:: + + if ($userPickFast) $conn = db2_pconnect($database,$user,$password); + else $conn = odbc_pconnect($database,$user,$password); + + try { $ToolkitServiceObj = ToolkitService::getInstance($conn); } + catch (Exception $e) { die($e->getMessage()); } + + $internalKey = '/tmp/packers'; + $ToolkitServiceObj->setToolkitServiceParams(array( + 'InternalKey'=>$internalKey, // *** RIGHT HERE internalKey/IPC + // *** run state full ... + 'plugSize'=>"32K")); // max size data i/o (iPLUG4K,32K,65K.512K,1M,5M,10M,15M) + + +2) Specific examples for XMLSERVICE +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* '''Stateless''' - set \*LIBL every single request + +:: + + if ($i5persistentconnect) $conn = db2_pconnect($database,$user,$password); + else $conn = db2_connect($database,$user,$password); + if (!$conn) die("Bad connect: $database,$user"); + $stmt = db2_prepare($conn, "call $libxmlservice.iPLUG4K(?,?,?,?)"); + if (!$stmt) die("Bad prepare: ".db2_stmt_errormsg()); + $ipc = ""; // *** RIGHT HERE MISSING internalKey/IPC + $ctl = "*here"; // *** run stateless ... + // here in any available database job + // must set *LIBL evey time + // stateless - MUST do this every single script after connect/getInstance() + // even if using persistent connections (db2_pconnect, odbc_pconnect) + $clobIn = + " + "; + $clobOut = ""; + $ret=db2_bind_param($stmt, 1, "ipc", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 2, "ctl", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 3, "clobIn", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 4, "clobOut", DB2_PARAM_OUT); + $ret=db2_execute($stmt); + +* '''State Full''' - set \*LIBL once and forget it + +:: + + if ($i5persistentconnect) $conn = db2_pconnect($database,$user,$password); + else $conn = db2_connect($database,$user,$password); + if (!$conn) die("Bad connect: $database,$user"); + $stmt = db2_prepare($conn, "call $libxmlservice.iPLUG4K(?,?,?,?)"); + if (!$stmt) die("Bad prepare: ".db2_stmt_errormsg()); + $ipc = "/tmp/packers"; // *** RIGHT HERE internalKey/IPC + $ctl = "*sbmjob"; // *** run state full ... + // use SBMJOB command run in new job + // PHP can call again, again, again + // with /tmp/packers and get ... + // same job every time + // same library list (*LIBL) + // same PGMs with open files, etc. + // ... exactly like 5250 sign-on screen + // state full - MUST do this ONCE ONLY after start/sbmjob of XMLSERVICE job + // then forget about it (unless you choose to change libl) ... + $clobIn = + " + "; + $clobOut = ""; + $ret=db2_bind_param($stmt, 1, "ipc", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 2, "ctl", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 3, "clobIn", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 4, "clobOut", DB2_PARAM_OUT); + $ret=db2_execute($stmt); + + +3) Specific examples for New PHP Toolkit CW layer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* '''Stateless''' - set \*LIBL every single request, but for CW, specify libraries in reverse order (via ADDLIBLE) + +:: + + $options = array(I5_OPTIONS_INITLIBL => 'WILMAFLIN FREDFLIN' ); + $conn = i5_connect($host, $user, $password, $options); + // or persistent (which is also stateless): still must specify library each time + $conn = i5_pconnect($host, $user, $password, $options); + +* '''State Full''' - set \*LIBL once and forget it + +:: + + session_start(); + + // if we previously saved a connection number in PHP session, use it. + // otherwise, use 0 (which means create a new connection) + $conNum = (isset($_SESSION['conectionNum']) ? $_SESSION['conectionNum'] : 0; + + // I5_OPTIONS_PRIVATE_CONNECTION: connection is private for the session + // I5_OPTIONS_IDLE_TIMEOUT: After a delay of the specified number of seconds with no activity, the job will end. + $options = array(I5_OPTIONS_PRIVATE_CONNECTION => $conNum, + I5_OPTIONS_IDLE_TIMEOUT => "60"); + + // connect as a private connection, starting with a persistent conn + $conn = i5_pconnect ($host, $user, $password, $options); + + if (!$conn) { + echo "Something went wrong:
    " . print_r(i5_error(), true) . "
    "; + $_SESSION['conectionNum'] = 0; // reset number + } else { + // connected successfully + + // if original conNum was 0, let's retrieve the new number. + if ($conNum == 0) { + + // Session variable was 0: Get connection ID and store it in session variable. + $ret = i5_get_property(I5_PRIVATE_CONNECTION, $conn); + + if (!$ret) { + echo "Something went wrong:
    " . print_r(i5_error(), true) . "
    "; + } else { + + // We have a good new private connection. Store connection ID in session variable + $_SESSION['conectionNum'] = $ret; + + // and set library list, too. + i5_command("CHGLIBL LIBL(FREDFLIN WILMAFLIN) CURLIB(FREDFLIN)"); + } + } + } + + +.. + [--Author([[http://youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICELibl?action=expirediff | s ]])--] + [--Tony "Ranger" Cairns - IBM i PHP / PASE--] diff --git a/docs/performance.rst b/docs/performance.rst new file mode 100755 index 0000000..b058bfa --- /dev/null +++ b/docs/performance.rst @@ -0,0 +1,203 @@ + + +XMLSERVICE Performance +====================== + +`Goto Main Page`_ + +.. _Goto Main Page: index.html + +Performance at a glance +----------------------- + +The following tests are full browser simulations (Apache ab tool), not PHP loop tests + +* Apache ab web site stress tests are performed from the 5250 command line (call qp2term) or ssh myibmi using PASE (see Apache ab link install instructions) + + * ``> ab -t 15 -c 10 http://lp0264d/level/xxtoolkit_new.php`` + + * You can run stress tests from 2-tier Linux/Windows using Apache ab tool, but i am using Apchae ab from PASE. + * Apache ab tool is not perfect, but if you use relatively "sane" number of browsers like -c 10 it will work. + * Apache ab test is designed to drive CPU to 100% (a good thing), so don't panic about CPU + ++-----------------------+-------------------------------------------------------------------------------------------+ +| Connection | Sample (ab tool) | ++=======================+===========================================================================================+ +| Apache | ``

    Hello world

    `` ................................. Requests per second: 767.87 | ++-----------------------+-------------------------------------------------------------------------------------------+ +| PHP | ```` ......................... Requests per second: 560.05 | ++-----------------------+-------------------------------------------------------------------------------------------+ +| ibm_db2 | ``$conn = db2_connect($database,$user,$password);`` ....... Requests per second: 64.54 | +| | | +| | ``$conn = db2_pconnect($database,$user,$password);`` ...... Requests per second: 504.04 | +| | | +| | :: | +| | | +| | $ipc = ""; | +| | $ctl = "*justproc"; // *justproc (no progam call) | +| | $clobIn = ""; | +| | $clobOut = ""; | +| | $stmt = db2_prepare($conn, "call XMLSERVICE.iPLUG4K(?,?,?,?)"); | +| | $ret = db2_bind_param($stmt, 1, "ipc", DB2_PARAM_IN); | +| | $ret = db2_bind_param($stmt, 2, "ctl", DB2_PARAM_IN); | +| | $ret = db2_bind_param($stmt, 3, "clobIn", DB2_PARAM_IN); | +| | $ret = db2_bind_param($stmt, 4, "clobOut", DB2_PARAM_OUT); | +| | $ret = db2_execute($stmt); | +| | echo "success"; | ++-----------------------+-------------------------------------------------------------------------------------------+ +|XMLSERVICE | ``$ipc = ''; $ctl = '*here';`` ............................ Requests per second: 69.58 | +| | | +| | ``$ipc = '/tmp/packers'; $ctl = '*sbmjob';`` ............... Requests per second: 320.72 | +| | | +| | :: | +| | | +| | | +| | | +| | | +| | a | +| | | +| | | +| | b | +| | | +| | | +| | 11.1111 | +| | | +| | | +| | 222.22 | +| | | +| | | +| | | +| | x | +| | y | +| | 66.6666 | +| | 77777.77 | +| | | +| | | +| | | +| | 0 | +| | | +| | "; | +| | $clobOut = ""; | +| | $stmt = db2_prepare($conn, "call XMLSERVICE.iPLUG4K(?,?,?,?)"); | +| | $ret=db2_bind_param($stmt, 1, "ipc", DB2_PARAM_IN); | +| | $ret=db2_bind_param($stmt, 2, "ctl", DB2_PARAM_IN); | +| | $ret=db2_bind_param($stmt, 3, "clobIn", DB2_PARAM_IN); | +| | $ret=db2_bind_param($stmt, 4, "clobOut", DB2_PARAM_OUT); | +| | $ret=db2_execute($stmt); | +| | // var_dump($clobOut); | +| | if (strpos($clobOut,"4444444444.44")>0) echo "success"; | +| | else echo "fail"; | +| | ?> | ++-----------------------+-------------------------------------------------------------------------------------------+ +|Toolkit |``setToolkitServiceParams(array('stateless'=>true));`` ..... Requests per second: 62.97 | +| |:: | +| | | +| | getMessage()); } | +| | // $ToolkitServiceObj->setToolkitServiceParams(array('stateless'=>true, | +| | 'plug'=>'iPLUG32K')); | +| | ToolkitServiceObj->setToolkitServiceParams(array('InternalKey'=>'/tmp/packers', | +| | 'plug'=>'iPLUG32K')); | +| | $param[] = $ToolkitServiceObj->AddParameterChar ('both', 1, 'INCHARA', 'var1', 'Y'); | +| | $param[] = $ToolkitServiceObj->AddParameterChar ('both', 1, 'INCHARB', 'var2', 'Z'); | +| | $param[] = $ToolkitServiceObj->AddParameterPackDec('both', 7,4,'INDEC1', 'var3', | +| | '001.0001'); | +| | $param[] = $ToolkitServiceObj->AddParameterPackDec('both', 12,2,'INDEC2', 'var4', | +| | '0000000003.04'); | +| | $ds[] = $ToolkitServiceObj->AddParameterChar ('both', 1, 'DSCHARA', 'ds1', 'A'); | +| | $ds[] = $ToolkitServiceObj->AddParameterChar ('both', 1, 'DSCHARB', 'ds2', 'B'); | +| | $ds[] = $ToolkitServiceObj->AddParameterPackDec('both', 7,4,'DSDEC1', 'ds3', | +| | '005.0007'); | +| | $ds[] = $ToolkitServiceObj->AddParameterPackDec('both', 12,2,'DSDEC1', 'ds4', | +| | '0000000006.08'); | +| | $param[] = $ToolkitServiceObj->AddDataStruct($ds); | +| | $clobOut = $ToolkitServiceObj->PgmCall('ZZCALL', $libxmlservice, $param, null, null); | +| | // var_dump($clobOut); | +| | $value = "what is ...".$clobOut["io_param"]["ds4"]; | +| | if (strpos($value,"4444444444.44")>-1) echo "success"; | +| | else echo "fail"; | +| | ?> | ++-----------------------+-------------------------------------------------------------------------------------------+ + + +How many users? +--------------- + +Apache ab tool is designed to "exercise" CPU/Apache engine as fast as possible, therefore 'ab tool' requests/second multiply by a factor of 5-10x to estimate 'user capacity' assuming humans actually read/scan each browser page (~ 5-10 seconds per page). So, on this machine using just ibm_db2 without toolkit, the following is about as fast as any given connection can run using only ONE job. +:: + + Drive the CPU to 100% capacity, then estimate actual users supported to drive the same CPU 100%. + >> ab -t 15 -c 15 http://lp0264d/ibm_db2_fetch.php?pool + 483 requests/second..............483 users/second (varies widely per application base line) + 28,980 requests/minute..............600 users/minute (relatively constant user capacity ibm_db2 scripts persistent connections) + 1,738,800 requests/hour.............36,000 users/hour (relatively constant user capacity ibm_db2 scripts persistent connections) + 41,731,200 requests/day (24 hours)..864,000 users/day (relatively constant user capacity ibm_db2 scripts persistent connections) + +Things to consider: + +* Who am i testing with ab tool? + + * lighting fast ab tool "no delay" push CPU 100% (superman users faster than a speeding bullet ... a machine) + * normal thinking users "10 second delay user actions" to push CPU 100% (average Joe ... a human) + * your application complexity may go far beyond simple ibm_db2_fetch.php above, but most any + web 'load runner' tool (like 'ab tool') or worse php script 'loop call test' + requires human action calculation to understand user capacity, so don't believe + blog people until you actually run your application specific performance test. + +If you want to run Apache ab tool on PASE (IBM i) ... + + * ab — Apache ab tool from old Zend Core (simulate browsers) + +XMLSERVICE still evolving +------------------------- + +Toolkit/XMLSERVICE performance will evolve over time, we understand performance is important and we intend push data through faster (perhaps much faster). + +* What runs fast? + + * calling PGMs/SRVPGMs (RPG, CLP, Cobol, System API, etc.) + + * loading your program is relatively slow so keep process alive (see below) + * use persistent connections (db2_pconnect/i5_pconnect) + * use IPC state full XMLSERVICE ('InternalKey'=>'/tmp/packers') + * turn off PHP debug, logs, traces toolkit all ini files + +* What runs slower? + + * CMDs, especially CMDS that return data (RTVJOBA, etc.) + + * CMDs return data use REXX, which is slow first time + * CMDs that do NOT return data, will run fairly quickly + * call CLP using the program interface (not command interface) + * use IPC state full XMLSERVICE ('InternalKey'=>'/tmp/packers') + * turn off PHP debug, logs, traces toolkit all ini files + +* What runs slowest? + + * PASE sh utilities (system wrkactjob, ls, ps, etc.) + + * not much can be done because most time due to fork of another job (Unix/PASE style) + * what little can be done is mostly same all call types + * use IPC state full XMLSERVICE ('InternalKey'=>'/tmp/packers') + * turn off PHP debug, logs, traces toolkit all ini files + + + +.. + [--Author([[http://youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICEConfig?action=expirediff | s ]])--] + [--Tony "Ranger" Cairns - IBM i PHP / PASE--] diff --git a/docs/qtemp.rst b/docs/qtemp.rst new file mode 100755 index 0000000..0b41bf2 --- /dev/null +++ b/docs/qtemp.rst @@ -0,0 +1,248 @@ + + +XMLSERVICE/Toolkit Sharing QTEMP +================================ + +`Goto Main Page`_ + +.. _Goto Main Page: index.html + +Sharing QTEMP - what do? +------------------------ +Sharing QTEMP ibm_db2/odbc/pdo_ibm/RPG/xmlservice has a few quirks, but can be "mostly" done if you understand the jobs in play. + +The big picture ... +------------------- +:: + + xmlservice |--LUW/PASE------|-----------------------IBM i---------------------- + run type |job 1 (1-2 tier)| job 2 (stateless) job 3 (IPC/private) + -------|----------------|------------------------|------------------------- + stateless | php-client-----|-->QSQSRVR | + | | ->ibm_db2,pdo_ibm,odbc | + | | *iPLUGxxxx-> | + | | ->XMLSERVICE<>RPG | + | | ->XMLSERVICE<>DB2 (xml)| + | | ->QTEMP (yes share) | + + IPC/private| php-client-----|--->QSQSRVR | + | | ->ibm_db2,pdo_ibm,odbc| + | | *iPLUGxxxx----------|-->XMLSERVICE<>RPG + | | ->QTEMP (no share) | ->XMLSERVICE<>DB2 (xml) + | | | ->QTEMP (yes share) + + Note: iPLUGxxx stored procedures are included with xmlservice download + +* **php-client (job 1)** - can be 1-tier (PASE) / 2-tier (Linux/Unix/Windows) and PHP using extension ibm_db2,pdo_ibm,odbc. +* **ibm_db2,pdo_ibm,odbc (job 2 ONLY)** - can ONLY "share" QTEMP when running stateless XMLSERVICE<>RPG mode, therefore no IPC/private. + + * PHP ibm_db2,pdo_ibm,odbc are shown in stateless column (job 2), so that everyone is visually reminded that IBM i DB2 actions do not really occur in php-client (job 1), but actually run in an "acquired" DB2 pre-started job (ie. 1-tier/2-tier makes no difference). + + * 2-tier - "true" IBM i connection/job maybe QSQ (PASE), QWI (LUW), etc., but picture above remains consistent behavior for "sharing" QTEMP (job 2). + * persistent connect - persistent connection (db2_pconnect/odbc_pconnect) or full connection (db2_connect/odbc_connect) makes no difference, big picture remains exactly the same for both connections. Of course you will run faster with persistent connections (up to 30-40%), due to QSQ server job already up and waiting, but you cannot rely on persistent connections to reach same QSQ job each script start / browser click. + + + exception - PASE ibm_db2 provides an obscure "in-line" mode only a few people understand where ibm_db2.ini file set to ibm_db2.i5_ignore_userid=1 will run all DB2 work under the default web profile and stateless in PASE php-client (job 1) ... but only if pure PASE ibm_db2 site (no PASE odbc/pdo_ibm) ... and ... well gee ... you are an expert if you understand i5_ignore_userid mode and you probably don't need this documentation. + + Note to future self ibm_db2, etc. ... just like xmlservice has idle timeout today (1/2 hour), we really should have a timeout idle for persistent connections across db2. + +* **XMLSERVICE<>RPG (job 2 or job 3)** - is toolkit call to RPG program wishing to "share" QTEMP. + + * Stateless (job 2) - Generally run slower with more CPU challenges, re-started xmlservice running in "acquired" QSQ job under stored procedure call iPLUGxxx, but on return iPLUGxxx/xmlservice need to close up shop and lose all caches (shut down, wake up, shut down, wake up, ...). + + * YES stateless - ibm_db2/odbc/pdo_ibm (job 2) CAN share QTEMP with XMLSERVICE<>RPG/XMLSERVICE<>DB2 (also job 2). + + * IPC/private (job 3) - Generally run faster with less CPU challenges, because xmlservice stays running until explicitly killed or idle timeout (1/2 hour default / user controlled). + + * NO IPC/private - ibm_db2/odbc/pdo_ibm (job 2) can NOT share QTEMP with XMLSERVICE<>RPG/XMLSERVICE<>DB2 (job 3). + +* **XMLSERVICE<>DB2 (job 2 or job 3)** - is NOT yet available in toolkit wrapper (Alan), but RAW xmlservice interface allows xml based DB2 queries and therefore can "share" QTEMP (available today in RAW xmlservice mode). + +:: + + + + + +1) Specific examples for New PHP Toolkit +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + ----------------------------------------- + | Browser | + |---------------------------------------| + | Download RPG (1) | Download PHP (2) | + | 1) XMLSERVICE | a) PHP CW Toolkit | + | HTML/XML/REST | b) New PHP Toolkit |<- cw-tk-php-x.x.x.zip (*) + | no PHP |--------------------| + | (xmlcgi.pgm) | c) PHP “Raw XML” | + | (optional) | (ibm_db2, odbc) | + | -----------------------------------| + | 2) XMLSERVICE DB2 stored procedures | + | (iPLUG4K, iPLUG32K, ..., iPLUG15M) | + | 3) XMLSERVICE (xmlservice.pgm) | + | call most anything on IBM i ... | + | (PGM, SRVPGM, PASE, DB2, etc.) | + ------------------------------------------ + +* **Stateless** - connection (job 1 / job 2) + +:: + + // *** stateless occurs when no internalKey (e.g. '/tmp/packers') was specified *** + // *** also when ->setToolkitServiceParams('stateless'=>true)) is called + try { $ToolkitServiceObj = ToolkitService::getInstance($database, $user, $password); } + catch (Exception $e) { die($e->getMessage()); } + $ToolkitServiceObj->setToolkitServiceParams(array( + 'plug'=>"iPLUG32K")); // max size data i/o (iPLUG4K,32K,65K.512K,1M,5M,10M,15M) + + +* **State Full** - connection (job 1 / job 2 / job 3) + +:: + + $internalKey = '/tmp/packers'; + try { $ToolkitServiceObj = ToolkitService::getInstance($database, $user, $password); } + catch (Exception $e) { die($e->getMessage()); } + $ToolkitServiceObj->setToolkitServiceParams(array( + 'InternalKey'=>$internalKey, // *** RIGHT HERE internalKey/IPC + // *** run state full ... + // use SBMJOB command run in new job + // PHP can call again, again, again + // with /tmp/packers and get ... + // same job every time + // same library list (*LIBL) + // same PGMs with open files, etc. + // ... exactly like 5250 sign-on screen + 'plug'=>"iPLUG32K")); // max size data i/o (iPLUG4K,32K,65K.512K,1M,5M,10M,15M) + +* **persistent connections** - later releases PHP Toolkit allow reused/shared connections with other work, including persistent connections, but internalKey (IPC) rules remain the same (above) + +:: + + if ($i5persistentconnect) $conn = db2_pconnect($database,$user,$password); + else $conn = db2_connect($database,$user,$password); + + try { $ToolkitServiceObj = ToolkitService::getInstance($conn); } + catch (Exception $e) { die($e->getMessage()); } + + +2) Specific examples for XMLSERVICE +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + ----------------------------------------- + | Browser | + |---------------------------------------| + | Download RPG (1) | Download PHP (2) | + | 1) XMLSERVICE | a) PHP CW Toolkit | + | HTML/XML/REST | b) New PHP Toolkit | + | no PHP |--------------------| + | (xmlcgi.pgm) | c) PHP “Raw XML” |<- Zend Server for IBM i or Linux or Windows (*) + | (optional) | (ibm_db2, odbc) | + | -----------------------------------| + | 2) XMLSERVICE DB2 stored procedures | + | (iPLUG4K, iPLUG32K, ..., iPLUG15M) | + | 3) XMLSERVICE (xmlservice.pgm) | + | call most anything on IBM i ... | + | (PGM, SRVPGM, PASE, DB2, etc.) | + ------------------------------------------ + +* **Stateless** - (job 1 / job 2) + +:: + + if ($i5persistentconnect) $conn = db2_pconnect($database,$user,$password); + else $conn = db2_connect($database,$user,$password); + if (!$conn) die("Bad connect: $database,$user"); + $stmt = db2_prepare($conn, "call $libxmlservice.iPLUG4K(?,?,?,?)"); + if (!$stmt) die("Bad prepare: ".db2_stmt_errormsg()); + $ipc = ""; // *** RIGHT HERE MISSING internalKey/IPC + $ctl = "*here"; // *** run stateless ... + // here in any available database job + // must set *LIBL evey time + // stateless - MUST do this every single script after connect/getInstance() + // even if using persistent connections (db2_pconnect, odbc_pconnect) + $clobIn = + " + "; + $clobOut = ""; + $ret=db2_bind_param($stmt, 1, "ipc", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 2, "ctl", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 3, "clobIn", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 4, "clobOut", DB2_PARAM_OUT); + $ret=db2_execute($stmt); + +* **State Full** - (job 1 / job 2 / job 3) + +:: + + if ($i5persistentconnect) $conn = db2_pconnect($database,$user,$password); + else $conn = db2_connect($database,$user,$password); + if (!$conn) die("Bad connect: $database,$user"); + $stmt = db2_prepare($conn, "call $libxmlservice.iPLUG4K(?,?,?,?)"); + if (!$stmt) die("Bad prepare: ".db2_stmt_errormsg()); + $ipc = "/tmp/packers"; // *** RIGHT HERE internalKey/IPC + $ctl = "*sbmjob"; // *** run state full ... + // use SBMJOB command run in new job + // PHP can call again, again, again + // with /tmp/packers and get ... + // same job every time + // same library list (*LIBL) + // same PGMs with open files, etc. + // ... exactly like 5250 sign-on screen + // state full - MUST do this ONCE ONLY after start/sbmjob of XMLSERVICE job + // then forget about it (unless you choose to change libl) ... + $clobIn = + " + "; + $clobOut = ""; + $ret=db2_bind_param($stmt, 1, "ipc", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 2, "ctl", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 3, "clobIn", DB2_PARAM_IN); + $ret=db2_bind_param($stmt, 4, "clobOut", DB2_PARAM_OUT); + $ret=db2_execute($stmt); + + + + + +.. + [--Author([[http://youngiprofessionals.com/wiki/index.php/XMLSERVICE/XMLSERVICEQTEMP?action=expirediff | s ]])--] + [--Tony "Ranger" Cairns - IBM i PHP / PASE--] diff --git a/docs/source-layout.rst b/docs/source-layout.rst new file mode 100755 index 0000000..fe13741 --- /dev/null +++ b/docs/source-layout.rst @@ -0,0 +1,147 @@ + +XMLSERVICE Layouts +================== + +`Goto Main Page`_ + +.. _Goto Main Page: index.html + +This page is for RPG developers that want to customize XMLSERVICE. + + +XMLSERVICE source files +----------------------- + +:: + + ----------------------------------------- + | Browser | + |---------------------------------------| + (1)->| Download RPG (1) | Download PHP (2) | + | 1) XMLSERVICE | a) PHP CW Toolkit | + | HTML/XML/REST | b) New PHP Toolkit | + | no PHP |--------------------| + | (xmlcgi.pgm) | c) PHP “Raw XML” | + | (optional) | (ibm_db2, odbc) | + | -----------------------------------| + | 2) XMLSERVICE DB2 stored procedures |<-(1) + | (iPLUG4K, iPLUG32K, ..., iPLUG15M) | + | 3) XMLSERVICE (xmlservice.pgm) |<-(1) xmlservice-rpg-x.x.x.zip + | call most anything on IBM i ... | + | (PGM, SRVPGM, PASE, DB2, etc.) | + ----------------------------------------- + +The following is a brief description of the function of the source files included with the XMLSERVICE library Open Source project. +:: + + LIB: + XMLSERVICE + + QSQLSRC: + crtsql - db2 utility script to create stored procedures + + QRPGLESRC: + plugconf_h - compile configuration options + plugmri_h - translateable messages + plugcach_h - interface for all cahces + plugcach - implementation for all caches + plugbug_h - interface debug (*debug) + plugbug - implement debug message qsysopr + plugerr_h - interface errors + plugerr - implement error collection and report + plugile_h - ILE ABI interface + plugile - implement ILE ABI paramter memory layout + plugipc_h -interface IPC (*nostart) + plugipc - implement IPC shared memory and semaphore + pluglic_h - interface license (*license) + pluglic - implement license report + plugpase_h - interface PASE functions + plugpase - implement PASE PGM, SRVPGM, sh + plugperf_h - interface performance (*rpt) + plugperf - implement performance collection and report + plugrun_h - interface run loop (*here *immed *justproc *sbmjob) + plugrun - implement client/server run loop (RPG cycle) + plugxml_h - interface XML + plugxml - implement XML parse and run logic + plugsql_h - interface sql by XML + plugsql - implement sql by XML (1.5) + plugdb2_h - interface sql db2 by XML + plugdb2 - implement sql db2 by XML + plugsig_h.rpgle - interface timeout signal (1.6.6) + plugsig.rpgle - implement timeout signal + plugconv_h.rpgle - interface ccsid conversion (1.6.6) + plugconv.rpgle - implement ccsid conversion + xmlcgi - PGM client interface Apache CGI (HTTP REST) + xmlmain - PGM client interface RPG-2-RPG (experimental) + xmlservice - PGM server deamons (XMLSERVICE jobs) + xmlstoredp - SRVPGM client DB2 stored procedures (DB2 connections) + zzcall - optional PGM for pear tests + zzsrv - optional SRVPGM for pear tests + + +XMLSERVICE hooks plugconf +------------------------- + +XMLSERVICE allows for you to customize a few key security and output decisions in the RPG module plugconf or +provide your own plugconf(x) version. + +**XMLSERVICE hooks APIs plugconf_h** + +The hooks are ONLY available with XMLSERVICE version 1.5.5+. + +plugconf module must implement the following interfaces prototyped by plugconf_h ... +failure to implement these APIs will result in compile failure. + +There are key hooks in plugrun / xmlcgi that allow your to decline service after you snoop the input XML. +Likewise, there are key hooks that allow you to manipulate the XML output returned to the clients +if you wish to "customize" XML (cool), however it should be noted that if you change the output XML +you may break other clients (like Zend PHP wrapper). + + +Running with \*NONE from XMLCGI +------------------------------- +XMLSERVICE via XMLCGI may be too powerful using only HTML/XML with no user/password (\*NONE), +so it is turned OFF by default always. However, if your machine is behind a firewall and you trust your team you can enable \*NONE ... + +#. make your own plugconf(x) +#. ``D PLUGNONEOK S 1N inz(*ON)`` +#. recompile and your machine will be wide open ... + +Note: The other setting PLUGDEMOOK is not used is future consideration for demos not from XMLCGI. + + +XMLSERVICE XMLCGI (RPG CGI) +--------------------------- +A sample XMLCGI interface was included to allow REST calls to XMLSERVICE. This was very handy to check out XML to/from XMLSERVICE before ever committing pen to paper on an actual script like PHP. It also seems to very handy fro one off HTML/XML applications that can reside on your laptop (or other device) and use the built in browser to run IBM i. + + +**BIG DESIGN HINT:** + +Although common practice in IBM i circles, I choose not to use Basic Authentication and/or "switch profile" in RPG CGI programs on the web (httpd.conf ServerId). I much prefer to leave all Apache/CGI related jobs as neutral "low authority" like QTMHHTTP (IBM i) or NOBODY (Linux) for clearly understood security. Also, I prefer keeping any security "switch profile" activity in the database connection (DB2) for portability of design and multiple client support (1-tier/2-tier). An example of this "database security" style of CGI is Apache module XMLCGI included in XMLSERVICE download (main page). XMLCGI implements all "security" preferences and "switch profile" using the DB2 CLI interface in RPG. + +Briefly, reasons i like keep the "profile security" in the database ... +* portable design idea in tradition of millions of PHP/MySql web sites (idea of profile is in database) +* easy switch/sharing between 1-tier and 2-tier applications across common database design (DB2) +* no "protocol" invention required because most languages support database (including PHP, Java, RPG, Cobol, etc.) +* supporting/sharing applications a wide range of device clients such as PDAs, laptops, servers is much easier as most can handle REST and/or DB2 communications (whatever language is available) + +**Do it yourself RPG:** + +* **If you are a do it yourself RPG programmer, you may want to download and study XMLCGI RPG source file included as this would allow you to write any RPG front-end (just steal the DB2 CLI stuff in XMLCGI if you want a server with profiles).** + + * RPG program XMLCGI is a traditional "Apache/Unix" style CGI server (ala PHP/MySql), therefore you do not need "switch profile" in the XMLCGI job. All/any "switch profile" is occurring at the DB2 database layer via DB2 CLI server mode not the Apache level (without using profiles in httpd.conf) + * WARNING: Unless https, XMLCGI sends unencrypted password around web and/or leaves them in files (unless using \*NONE), which is a bad idea of course, but this is a demonstration module for testing (without PHP, etc.), you as the RPG programmer are intended to customize to your production world (Open Source RPG code). + + +XMLSERVICE XMLMAIN (unused) +--------------------------- + +Also included, is a simple RPG program XMLMAIN that calls client interface XMLSERVICE in processes inside or outside web context, +has not been extensively tested (very little in fact), but should be general idea for a IBM i RPG centric interface +(if such a thing is even relevant these Internet days). + +*If you wish to build a traditional web IBM i "switch profile" CGI web service (httpd.conf ServerId), +you would need to combine parts of XMLMAIN (direct call XMLSERVICE) and XMLCGI (excluding DB2 CLI).* + + + diff --git a/docs/varchar.rst b/docs/varchar.rst new file mode 100755 index 0000000..62989fb --- /dev/null +++ b/docs/varchar.rst @@ -0,0 +1,197 @@ + + +XMLSERVICE/Toolkit Character Varying +==================================== + +`Goto Main Page`_ + +.. _Goto Main Page: index.html + +Character varying - what do? +---------------------------- + +Character varying is easy, just add character varying attribute. Varying character works for parameters, return and data structures (DS). + +1) New PHP Toolkit +^^^^^^^^^^^^^^^^^^ + +:: + + ----------------------------------------- + | Browser | + |---------------------------------------| + | Download RPG (1) | Download PHP (2) | + | 1) XMLSERVICE | a) PHP CW Toolkit | + | HTML/XML/REST | b) New PHP Toolkit |<- cw-tk-php-x.x.x.zip (*) + | no PHP |--------------------| + | (xmlcgi.pgm) | c) PHP “Raw XML” | + | (optional) | (ibm_db2, odbc) | + | -----------------------------------| + | 2) XMLSERVICE DB2 stored procedures | + | (iPLUG4K, iPLUG32K, ..., iPLUG15M) | + | 3) XMLSERVICE (xmlservice.pgm) | + | call most anything on IBM i ... | + | (PGM, SRVPGM, PASE, DB2, etc.) | + ------------------------------------------ + + +Character varying (S 20A varying) +--------------------------------- + +:: + + getMessage()); } + $ToolkitServiceObj->setToolkitServiceParams( + array('InternalKey'=>$internalKey, // route to same XMLSERVICE job /tmp/myjob1 + 'subsystem'=>"QGPL/QDFTJOBD", // subsystem/jobd to start XMLSERVICE (if not running) + 'plug'=>"iPLUG32K")); // max size data i/o (iPLUG4K,32K,65K.512K,1M,5M,10M,15M) + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // * zzvary: check return varying + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // P zzvary B export + // D zzvary PI 20A varying + // D myName 10A varying + // * vars + // D tmp S 20A varying + // /free + // tmp = 'my name is '; + // tmp = tmp + myName; + // return tmp; + // /end-free + // P E + $param[] = $ToolkitServiceObj->AddParameterChar('both', 10, 'ZZVARY', 'myVary', 'Ranger', 'on'); // 6th parameter--'on'--is for varying + $retrn[] = $ToolkitServiceObj->AddParameterChar('both', 20, 'ZZVARY', 'retVary', 'Mud', 'on'); // 6th parameter--'on'--is for varying + $result = $ToolkitServiceObj->PgmCall('ZZSRV', $libxmlservice, $param, $retrn, array('func'=>'ZZVARY')); + // var_dump($result); + /* in/out param myDate */ + $myVary = "XMLSERVICE i/o param myVary: ".$result["io_param"]["myVary"]; + echo "$myVary\n"; + $expect = 'Ranger'; + if (strpos($myVary,$expect)<1) die("Fail missing $expect\n"); + /* return value retVary */ + $retVary = "XMLSERVICE return retVary: ".$result["retvals"]["retVary"]; + echo "$retVary\n"; + $expect = 'my name is Ranger'; + if (strpos($retVary,$expect)<1) die("Fail missing $expect\n"); + /* all good */ + echo "Success\n"; + ?> + + + +2) XMLSERVICE Raw XML +^^^^^^^^^^^^^^^^^^^^^ + +:: + + ----------------------------------------- + | Browser | + |---------------------------------------| + | Download RPG (1) | Download PHP (2) | + | 1) XMLSERVICE | a) PHP CW Toolkit | + | HTML/XML/REST | b) New PHP Toolkit | + | no PHP |--------------------| + | (xmlcgi.pgm) | c) PHP “Raw XML” |<- Zend Server for IBM i or Linux or Windows (*) + | (optional) | (ibm_db2, odbc) | + | -----------------------------------| + | 2) XMLSERVICE DB2 stored procedures | + | (iPLUG4K, iPLUG32K, ..., iPLUG15M) | + | 3) XMLSERVICE (xmlservice.pgm) | + | call most anything on IBM i ... | + | (PGM, SRVPGM, PASE, DB2, etc.) | + ------------------------------------------ + + +Character varying (S 20A varying) +--------------------------------- + +:: + + xpath('/script/pgm'); + if (!$allpgms) die("Missing XML pgm info"); + // ----------------- + // output pgm call + // ----------------- + // only one program this XML script + $pgm = $allpgms[0]; + $name = $pgm->attributes()->name; + $lib = $pgm->attributes()->lib; + $func = $pgm->attributes()->func; + // pgm parms + $parm = $pgm->xpath('parm'); + if ($parm) die("Unexpected XML pgm parms io='in' ($lib/$name.$func)\n"); + // pgm data returned + $retn = $pgm->xpath('return'); + if (!$retn) die("Fail XML pgm return missing ($lib/$name.$func)\n"); + $var = $retn[0]->data->attributes()->var; + $actual = (string)$retn[0]->data; + $expect = 'my name is Ranger'; + if ($actual != $expect) die("$var ($actual not $expect) ($lib/$name.$func)\n"); + + // good + echo "Success ($lib/$name.$func)\n"; + + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // * zzvary: check return varying + // *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // P zzvary B export + // D zzvary PI 20A varying + // D myName 10A varying + // * vars + // D tmp S 20A varying + // /free + // tmp = 'my name is '; + // tmp = tmp + myName; + // return tmp; + // /end-free + // P E + function getxml() { + $clob = << + + ENDPROC; + return test_lib_replace($clob); + } + ?> + +