When I first started trying to interface to an SAP system with perl, I was not very adept at perl coding. Sure, I had written several scripts in the prior 5 years or so, but I did it so infrequently that I forgot most of what I had learned about arrays, hashes, and other such constructs. Add to that the fact that I was not in the habit of commenting my code, and I not only had to learn how to code new logic, I also had to re-learn how to write what I'd already written!
To add to my challenge, I was not very well-rounded at ABAP. I had spent time modifying programs, troubleshooting, performance tuning database accesses, etc, but I had never worked on a program that was intended to be called by other programs, as is the case with an RFC-enabled function module. So I had a bit of learning to do there as well, such as how to determine what type of input a function module was expecting, what type of output it would provide, and how to test the program. Perhaps it's good that I'm finally writing all this down. Maybe next time I need to write a script, I'll just come here first.
So the first thing you need to do to make an RFC call to an SAP system from a perl script is to ensure you've got the proper configuration on your scripting host (see Perl and SAP Adventures, Part 1 of this blog).
Secondly, you must identify an RFC-enabled function module in your SAP system to execute for whatever it is you wish to accomplish there. This blog will attempt to walk through this second step for those that are as unfamiliar with some of this as I was when I first started.
To find RFC-enabled function modules, go to transaction SE37. Click Utilities -> Find. Then, Edit -> All Selections. You can then click the "RFC Modules" radio button under the "Additional Selections" section. Next, put some text into the Function Module name or Short Description fields (note that many do not have a short description), and click the execute button, or hit F8. You will want to use some text here to limit the search, as there are over 30,000 RFC-enabled function modules in my ECC 6.0 system, for example.
Once you have found a function module that piques your interest, double-click on it to display the code.
I'm going to pick a simple one to start with, which is TH_SERVER_LIST. This function module will list all of the servers in the system. Looking at the code for this function module, the header is as follows:
FUNCTION TH_SERVER_LIST.
*"----------------------------------------------------------------------
*"*"Lokale Schnittstelle:
*" IMPORTING
*" VALUE(SERVICES) LIKE MSXXLIST-MSGTYPES DEFAULT 255
*" VALUE(SYSSERVICE) TYPE MSSYSSERVICE DEFAULT 0
*" TABLES
*" LIST STRUCTURE MSXXLIST OPTIONAL
*" LIST_IPV6 STRUCTURE MSXXLIST_V6 OPTIONAL
*" EXCEPTIONS
*" NO_SERVER_LIST
*"----------------------------------------------------------------------
What I'm concerned with first are the IMPORTING parameters. This tells you what parameters you need to pass to the function module when your script calls it. In this case, each of the import parameters has a default value, and so is not required to be passed in from the script, unless you want to override the default.Component Component Type Data Type Length Decimal Pl Short Description
NAME MSNAME2 CHAR 40 0 Application Server Name
HOST MSHOST2 CHAR 32 0 Name of Application Server
SERV MSSERV CHAR 20 0 Service
MSGTYPES MSTYPES RAW 1 0 Services
HOSTADR MSHOSTADR RAW 4 0 Host IP address
SERVNO MSSERVNO RAW 2 0 Service port number
STATE MSSTATE RAW 1 0 Server Status
Now, the first thing to do from here is to see this function module in action to verify it's what you need before you go writing lots of code. To do this, if you're looking at the code of the function module, you can click on the Test button, which looks a bit like the Winzip icon, or simply press the F8 key. This will give you a screen with the input parameters as fields you can enter values into. With this function module, you don't need to input any parameters, so simply hit the Execute button or, again, press F8. This will execute the script and provide a screen with the output. Clicking on the LIST structure will show the contents of this table. It will look something like this (there are a few more columns than this, but you get the idea - oh yeah, and server names have been changed to protect the innocent):Name Host Serv
apsrv002_RQA_00 apsrv002 sapdp00
apsrv019_RQA_00 apsrv019 sapdp00
apsrv235_RQA_00 apsrv235 sapdp00
apsrv720_RQA_00 apsrv720 sapdp00
apsrv721_RQA_00 apsrv721 sapdp00
apsrv722_RQA_00 apsrv722 sapdp00
apsrv723_RQA_00 apsrv723 sapdp00
apsrv724_RQA_00 apsrv724 sapdp00
In my case, my script would like to grab the NAME of each application server, and I can see that the LIST structure provides that, so I can proceed.
An ABAP structure is made available to a perl script as an array of hashes. An array is a list of items, and a hash is a series of key/value pairs. So each item in the array will be a hash, and the keys for each hash will match up with the fields (components) in the structure above. Conceptually, it will look something like this:
@LIST=(
hash1 {'NAME' => 'apsrv002_RQA_00', 'HOST' => 'apsrv002', 'SERV' => 'sapdp00', ...}
hash2 {'NAME' => 'apsrv019_RQA_00', 'HOST' => 'apsrv019', 'SERV' => 'sapdp00', ...}
hash3 {'NAME' => 'apsrv235_RQA_00', 'HOST' => 'apsrv235', 'SERV' => 'sapdp00', ...}
)
Therefore, to get the name of each application server in the system, the script would need to loop through the hashes in the LIST array, and get the value associated with the NAME key from each hash. This will look something like this (this is short, but it is a complete working script):
#!/usr/bin/perl -w
use strict;
use sapnwrfc; # here's where we load the SAPNW::Rfc module
SAPNW::Rfc->load_config; # load the connection parameters defined in sap.yml
my $rfc = SAPNW::Rfc->rfc_connect; # connect to SAP system
my $rcb = $rfc->function_lookup("TH_SERVER_LIST"); # look up the function module
my $tsl = $rcb->create_function_call; # create the function call
$tsl->invoke; # execute the function call
foreach my $row (@{$tsl->LIST}) { # now loop through each of the rows in the LIST array
print "Server is $row->{'NAME'}
"; # and print the server NAME value for each
}
$rfc->disconnect(); # disconnect from the system
The output this will give us when executed is:[dhull@localhost #]$ ./blog.pl
Server is apsrv002_RQA_00
Server is apsrv019_RQA_00
Server is apsrv235_RQA_00
Server is apsrv720_RQA_00
Server is apsrv721_RQA_00
Server is apsrv722_RQA_00
Server is apsrv723_RQA_00
Server is apsrv724_RQA_00
And there you have it - a completed script!
In the Perl and SAP Adventures, Part 3, I'll expand on this script by taking these server names and doing something with them.