2012-06-10

Using XBP to manage SAP jobs from PHP

SAP XBP vs 3, is the API for accessing SAP background processes. XBP requires you to log on to the XMI-XBP subsystem. XMI= External Monitor Interface , XBP= External Interface for Background Processing . This sounds awful complicated but It is not that complex. You logon to the XBP subsystem by calling 'BAPI_XMI_LOGON'  this is a normal BAPI call and then you just proceed with the XBP BAPI functions you like to use.

Before you proceed you should learn about XMI and XBP .

Calling the XMI and XBP BAPIs from PHP is no different from calling any other BAPIs or RFC functions from PHP. First you need to install an interface between PHP and SAP. Google on SAPRFC by Eduard Koucky, this project looks dead to me, but Axel  Bangert seems to have picked up SAPRFC. Piers Harding have created a Unicode aware interface SAPNWRFC.  You should Google around and pick the right interface for you. (I’m slowly migrating from SAPRFC to SAPNWRFC. Examples in this post are using Koucky’s original  SAPRFC, which is the simpler interface to use.)

I have written a   job scheduler in PHP , there I have created some functions of my own on top of the SAPRFC and SAPNWRFC interfaces, for logging on to SAP and calling SAP BAPI and RFC functions. The examples here are using my scheduler and my interface functions. Before you proceed you should enjoy my post   extract data from SAP , here you can learn a very useful technique how to use SAP’s transaction SE37 to set up call to SAP functions and transform the in- and out-put parameters to my job scheduler in PHP.

I use the SAP XBP interface to monitor SAP batch jobs or background processes, i.e. I check if SAP jobs have run (successfully) before I kick off my external PHP jobs. (XBP is not limited to monitor you can fully manage background processes in SAP via XBP.) Here is another example .

This somewhat artifical example loads all SAP jobs with names starting with ‘TOO’ into MySQL, if the job ‘TRPX_LE_WRHSEMONITOR_ALL’ was run successfully the during the last 2 days. Then an Excel sheet is created and mailed.

The important pieces of code here are:

the prereq, which checks if job(s) are run in SAP.

<xmi><session>XBP</session></xmi>, explicit log on to XMI/XBP

<schedule name='BatchList' logmsg='Automagically load of SAP rfc output  into mysql table(s)'>

        <tag><name>DW_DB</name><value>test</value></tag> <!-- MySql database -->

         <!-- only run if SAP job executed ok the last 2day -->

         <prereq type='sap' jobname='TRPX_LE_WRHSEMONITOR_ALL' username='*' executed='now-2d'/>

        

        <job name='get_Jobs' type='script' data='sap2.php'>

                <sap>

                        <xmi><session>XBP</session></xmi>

                        <rfc>

                                <name>BAPI_XBP_JOB_SELECT</name>

                                <import>

                                        ('EXTERNAL_USER_NAME',$sapContact['xmi']['EXTERNAL_USER_NAME'])

                                        ,('SELECTION','AL')

                                        ,('JOB_SELECT_PARAM',array('JOBNAME'=>'TOO*',     'USERNAME'=>'*','ABORTED'=>'X','FINISHED'=>'X','ACTIVE'=>'X', 'FROM_DATE'=>@TODAY))                                                                                 </import>

                        </rfc>

                </sap>

                <script><sql>

                <autoload>replace</autoload><database>@DW_DB</database><truncate>yes</truncate>

                <table>JOB_HEAD=tooJobHeads</table><tableX>SELECTED_JOBS</tableX>

                </sql></script>

        </job>

        <job name='generate_report' type='sql'>

                <sql>

                        USE @DW_DB;

                        SELECT *  from tooJobHeads;

                </sql>

                <sqlconverter>

                        <name>sqlconverter_ExcelBasic.php</name>

                        <target>report</target>

                </sqlconverter>

        </job>

        <job name='send_mail' type='sendmail'>

                <mailer>phpmailer</mailer>

                <recipients>lasse@mysite.se</recipients>

                <subject>Fyi: batch jobs and XMI</subject>

                <div>

                Hi,

                This is an artificial example but it shows the Sap connectivity implemented in ACTADW.

                

                The mail you recieved contains two attachments this file (schedule.xml) and an SAP job status report.

                The schedule.xml file contains all commands necessary:

                        1 Check that the Sap job TRPX_LE_WRHSEMONITOR_ALL is sucessfully executed during the                        last 2 days, otherwise the execution is intercepted.

                                                2 Extract the information from SAP.

                        3 Load  the extract  into ACTADW.

                        4 Create a report.

                        5 Create this mail and send it.

                Lars Johansson

                </div>

                <attachment>

                        <name>batchStatus.XLS</name><file>@J_generate_report/report0.XLS</file>

                </attachment>

                <attachment>

                        <name>schedule.xml</name><file>/dw/schedule/example10.xml</file>

                </attachment>

        </job>

</schedule>

 

The PHP code for Logon to XMI

/**

*  This function logs on to the Sap XMI subsystem

*

* Before you log on to the SAP XMI subsystem, you should log on to SAP with {@link connect2sap()}, and before you

* disconnect from SAP you should log off from XMI with {@link sapXmiLogoff()}.

*

* Syntax: <samp><xmi session='XAL'/></samp>

* @todo update SAP3.php

* @see sapXmiLogoff()

* @param array $context

* @param object $sap Handle to saprfc interface

* @return string $sessionid The XMI sessionid

* @uses callSapFunction()

*/

function sapXmiLogon($context,&$sap,$xmiType='XBP'){

        $log = $GLOBALS['log'];

        $log->enter('Info');

        if (!$sap) return FALSE;

        $sapContact = $context['sapinfo'];

        switch ($xmiType) {

        case('XBP'):

                $fce=callSapFunction($sap,'BAPI_XMI_LOGON',array(

                        array('import','EXTCOMPANY',$sapContact['xmi']['EXTCOMPANY'])

                        ,array('import','EXTPRODUCT',$sapContact['xmi']['EXTPRODUCT'])

                        ,array('import','INTERFACE','XBP')

                        ,array('import','VERSION','3.0')

                        ));

        break;

                default:

                $log->logit('Error',"Unknown SAP XMI session type=$xmiType");

                return FALSE;

        }

        if ($fce->GetStatus() == SAPRFC_OK) {

                $log->logit('Info',sprintf("SAP XMI session established sessionid=%s",$fce->SESSIONID));

        } else {

                return FALSE;

        }

        $sessionid = $fce->SESSIONID;

        $fce->close();

        return $sessionid;

}

And the code to evaluate the SAP prereq at the beginning of the the XML schedule example ‘BatchList’ above.

/**

*  This function evaluates a Sap job predecessor by calling BAPI_XBP_JOB_SELECT

*

* The parm 'SINCE' allows a more relaxed call to BAPI_XBP_JOB_SELECT.

* <samp>

* <stmt type='sap' JOBNAME='TRPX_LE_WRHSEMONITOR_ALL' USERNAME='*' SINCE='now-1d'/>

* </samp>

* Parm 'SINCE' calls {@link tagday.php} to convert the argument to a point in time, which is translated into

* the parameters 'FROM_DATE' and 'FROM_TIME'. The FINISHED parameter will also be set ('X') as default.

*

* Parms to BAPI_XBP_JOB_SELECT: <br>

* 1  JOBNAME Background job name

* 2  USERNAME Initiator of job/step scheduling

* 3  FROM_DATE Planned Start Date for Background Job

* 4  FROM_TIME Planned start time for background Job

* 5  TO_DATE Planned Start Date for Background Job

* 6  TO_TIME Planned start time for background Job

* 7  NO_DATE Selection flag for jobs without start date

* 8  WITH_PRED Selection flag for jobs with start after predecessor

* 9  EVENTID Background Processing Event

* 10 EVENTPARM Background Event Parameters (Such as, Jobname/Jobcount)

* 11 PRELIM State of Background Job

* 12 SCHEDUL State of Background Job

* 13 READY State of Background Job

* 14 RUNNING State of Background Job

* 15 FINISHED State of Background Job

* 16 ABORTED State of Background Job

*

* @param array $context

* @param array $cb this is $schedule for schedule prereqs and $job for job prereqs

* @param array $stmt the schedule prerequisit statement

* @param object $sap Handle to saprfc interface

* @return int $jobcount no of executed predecessor jobs with status finished|complete, caller can test for TRUE

* @uses callSapFunction()

* @uses tagday.php

*/

function checkSapPredecessor($context,$cb,$stmt,&$sap){

        $log = $GLOBALS['log'];

        $log->enter('Info');

        if (!$sap) return FALSE;

        if(!array_key_exists('JOBNAME',$stmt)) return TRUE;

// We only check if sap is alive, and if we pass here sap is responding so...

        $sapContact = $context['sapinfo'];

        $jsp = array_intersect_key(array_change_key_case($stmt, CASE_UPPER)

                ,array('JOBNAME' => '','USERNAME' => '','FROM_DATE' => '','FROM_TIME' => '','TO_DATE' => ''

                ,'TO_TIME' => '','NO_DATE' => '','WITH_PRED' => '','EVENTID' => '','EVENTPARM' => ''

                ,'PRELIM' => '','SCHEDUL' => '','READY' => '','RUNNING' => '','FINISHED' => '','ABORTED' => ''

                ));

        if(array_key_exists('SINCE',$stmt) or array_key_exists('since',$stmt)){ //relaxed form

            $script = 'tagday.php';

            $proc = $context['opsproclib'];

            $parm = 'YmdHis'.$stmt['SINCE'];

            $log->logit('Info',"Calling $proc$script with parm=$parm");

/**

* Require tagday.php to evaluate the executed statement

*/

            $dtStr = require("$proc$script");

            $fromDate = substr($dtStr,0,8);

            $fromTime = substr($dtStr,8,6);

            if(!array_key_exists('FROM_DATE',$jsp)) $jsp['FROM_DATE'] = $fromDate;

            if(!array_key_exists('FROM_TIME',$jsp)) $jsp['FROM_TIME'] = $fromTime;

            if(!array_key_exists('FINISHED',$jsp)) $jsp['FINISHED'] = 'X';

            if(!array_key_exists('USERNAME',$jsp)) $jsp['USERNAME'] = '*';

        }

        $log->logit('Info',vsprintf("Enter checkSap %s, %s, %s, %s, %s", $jsp));

            $log->mode('None'); //We do not wish to see SAP empty selections warning messages

            $fce=callSapFunction($sap,'BAPI_XBP_JOB_SELECT',array(

                    array('import','EXTERNAL_USER_NAME',$sapContact['xmi']['EXTERNAL_USER_NAME'])

                    ,array('import','SELECTION','AL')

                    ,array('import','JOB_SELECT_PARAM',$jsp)

                    ));

            $log->reset('mode'); // Reset log to original verbose mode

        if ($fce->GetStatus() == SAPRFC_OK || $fce->GetStatus() == '99') {

                $jobcount = 0;

                $fce->JOB_HEAD->Reset();

                while ( $fce->JOB_HEAD->Next() ) {

                        $jobcount++;

                }

        } else {

                return FALSE;

        }

        if(!$jobcount) $log->logit('Note',sprintf("SAP predecessor job %s not executed successfully after $fromDate $fromTime",$stmt['jobname']));

        return $jobcount;

}

No comments:

Post a Comment