
Unfortunately, there is no function module that uses
that table directly (at least not in SAP release 40B. Note that in newer
SAP releases there are BAPI function modules that deal with user administration).
At
this point we have two choices:
FUNCTION Z_READ_USR02. *"---------------------------------------------------------------------- *"*"Local Interface: *"TABLES *"USERS STRUCTURE USR02 *"---------------------------------------------------------------------- TABLES: USR02.
SELECT * INTO TABLE USERS FROM USR02 CLIENT SPECIFIED.
ENDFUNCTION.
RFC->function("ZReadUsr02",
{},# no import parameters
{},# no export parameters
{ Users => ..
my $usr02 = RFC->struct("Usr02",
Mandt => RFC->type('C', 3),
Bname => RFC->type('C', 12),
...
Connection to CON opened. remote integer format is BIG SAP release is 40B Getting function interface for Z_READ_USR02 ... Got 1 rows. Getting structure USR02 ... Creating Usr02.pm writing ZReadUsr02.pl ... appending sub client to ZReadUsr02.pl ... appending epilog to ZReadUsr02.pl ...
#
# This describes the ABAP structure USR02
#
RFC->struct("Usr02",
"Mandt" => RFC->type('C', 3, 0),
"Bname" => RFC->type('C', 12, 0),
"Bcode" => RFC->type('X', 8, 0),
"Gltgv" => RFC->type('D', 8, 0),
"Gltgb" => RFC->type('D', 8, 0),
"Ustyp" => RFC->type('C', 1, 0),
"Class" => RFC->type('C', 12, 0),
"Locnt" => RFC->type('i', 1, 0),
"Uflag" => RFC->type('i', 1, 0),
"Accnt" => RFC->type('C', 12, 0),
"Aname" => RFC->type('C', 12, 0),
"Erdat" => RFC->type('D', 8, 0),
"Trdat" => RFC->type('D', 8, 0),
"Ltime" => RFC->type('T', 6, 0),
"Ocod1" => RFC->type('X', 8, 0),
"Bcda1" => RFC->type('D', 8, 0),
"Codv1" => RFC->type('C', 1, 0),
"Ocod2" => RFC->type('X', 8, 0),
"Bcda2" => RFC->type('D', 8, 0),
"Codv2" => RFC->type('C', 1, 0),
"Ocod3" => RFC->type('X', 8, 0),
"Bcda3" => RFC->type('D', 8, 0),
"Codv3" => RFC->type('C', 1, 0),
"Ocod4" => RFC->type('X', 8, 0),
"Bcda4" => RFC->type('D', 8, 0),
"Codv4" => RFC->type('C', 1, 0),
"Ocod5" => RFC->type('X', 8, 0),
"Bcda5" => RFC->type('D', 8, 0),
"Codv5" => RFC->type('C', 1, 0),
"Versn" => RFC->type('C', 3, 0),
"Codvn" => RFC->type('C', 1, 0),
"Tzone" => RFC->type('C', 6, 0),
);
mv ZreadUsr02.pl lsr3users.pl
#!c:\programme\perl\bin\MSWin32-x86\perl.exe -w # # This file was generated by meta.pl # # Look for comments starting with "#TODO:" for # further advice how to make this program run. # use strict; use warnings; use RFC;The first line is pretty useless in a Windows environment, but it also does no harm. UNIX users, however, may want to "chmod +x" their script and run it by just typing it's name. We assume clean and safe programming, so "strict" and "warnings" are being used. Finally, we need to use "RFC" to get the contex/P functionality.
For your convenience, places where you have to insert or change some code are marked with "#TODO" comments.
use FindBin; use lib ($FindBin::Bin);These lines make perl look for "require"d files also in the directory where the script lives.
#
# The following files are needed for your program to run.
# They have been automatically generated by meta.pl, too.
# If you move ZReadUsr02.pl to another place, remember to
# move the used *.pm files to the same destination
# or to install them into one of
# the system-wide perl include directories.
#
require q{Usr02.pm};
Our structure definition is executed here. It would be also possible to
replace the require by the contents of file Usr02.pm.#TODO: The next 1 lines define variables for easy
# access to your tables.
my $users = RFC->table("RFC::S::Usr02");
my $ZReadUsr02 = RFC->function("Z_READ_USR02",
{ # parameters the function gets from caller
},
{ # parameters the function gives back to caller
},
{ # tables ...
"USERS" => $users,
});
#
# The following subroutine implements the RFC client
#
sub client {
my $conn = RFC->connect("CON",#TODO: choose destination
"000", #TODO: enter correct client number.
"", #TODO: enter user name
"") #TODO: enter password
or die "Can't connect to R/3: ".RFC::lastError;
$users->create; # refresh table
#TODO: You might want to set parameter
#values here, for example:
# $ZReadUsr02->MYPARAM("foo");
my $rc;
eval { $rc = $ZReadUsr02->call($conn); } or do {
if (!$@) { # no exception
warn "RFC Failure: ".RFC::lastError;
#TODO: handle RFC failures
}
else { # system exception
my $le = RFC::lastError;
my $exc = $@;
$exc =~ s/\n//;
die <<DIE;
While trying to execute function module Z_READ_USR02,
the RFC library raised a $exc.
Usually, this happens for the following reasons:
* The function module does not exist.
* You gave incomplete or wrong log on information.
Consult the following message from the RFC library
to find out why the exception happened:
$le
DIE
}
}; # end error handling
#TODO: process the data you received from ZReadUsr02.
return unless $rc; # warning already printed.
my @users = $users->getrows;
print "Our R/3 has ", scalar @users, " users\n";
foreach (sort { $a->Mandt cmp $b->Mandt } @users) {
printf "%12s client %3s last login was %s %s\n",
$_->Bname, $_->Mandt, $_->Trdat,
$_->Uflag ? "LOCKED!" : "";
};
} # end of sub client # # This is the main program # unlink './dev_rfc'; #remove old trace file client();
Our R/3 has 23 users DDIC client 000 last login was 20000214 SAP* client 000 last login was 19990503 SAPCPIC client 000 last login was 19960903 TMSADM client 000 last login was 00000000 ADMIN client 000 last login was 19990925 SAPCPIC client 001 last login was 19960903 DDIC client 001 last login was 19980421 SAP* client 001 last login was 19980421 DDIC client 005 last login was 19980421 SAP* client 005 last login was 19990429 SAPCPIC client 005 last login was 19960903 MATT client 005 last login was 20010103 BRUCKER client 005 last login was 20010523 KOCH client 005 last login was 20010522 RÖDIGER client 005 last login was 00000000 LOCKED! WECHSUNG client 005 last login was 20010817 ESSENBERG client 005 last login was 20000214 BANNING client 005 last login was 20000526 WF-BATCH client 005 last login was 00000000 MQSDEMO client 005 last login was 00000000 AMTRIX client 005 last login was 20010720 EARLYWATCH client 066 last login was 19980414 SAP* client 066 last login was 19980416
Here are the files the completed solution consists of: lsr3users.pl and Usr02.pm.
We start again by letting meta.pl write a prototype script for RFC_GET_TABLE_ENTRIES.
The results are the files RfcGetTableEnries.pl and Tab512.pm. By looking at the script, we can determine the interface of the function module:meta.pl CON -c RFC_GET_TABLE_ENTRIES
my $entries = RFC->table("RFC::S::TAB512");
my $RfcGetTableEntries = RFC->function("RFC_GET_TABLE_ENTRIES",
{ # parameters the function gets from caller
# BYPASS_BUFFER is optional and defaults to SPACE
"BYPASS_BUFFER" => (RFC->type('C', 1)),
# FROM_KEY is optional and defaults to SPACE
"FROM_KEY" => (RFC->type('C', 72)),
# GEN_KEY is optional and defaults to SPACE
"GEN_KEY" => (RFC->type('C', 72)),
"MAX_ENTRIES" => (RFC->type('I', 4)),
"TABLE_NAME" => (RFC->type('C', 30)),
# TO_KEY is optional and defaults to SPACE
"TO_KEY" => (RFC->type('C', 72)),
},
{ # parameters the function gives back to caller
"NUMBER_OF_ENTRIES" => (RFC->type('I', 4)),
},
{ # tables ...
"ENTRIES" => $entries,
});
This time, we have a lot of parameters. Unused optional parameters can
(and should) be commented out in the name of reduced complexity, memory
usage and network ressources needed. Since we want all users at this time,
we can get rid of FROM_KEY, GEN_KEY and TO_KEY. Also, BYPASS_BUFFER can
be left out.
The remaining parameters are the TABLE_NAME and MAX_ENTRIES. We set
them like this:
#TODO: You might want to set parameter values here, ...
$RfcGetTableEntries->TableName("Usr02");
$RfcGetTableEntries->MaxEntries(999999); # not really a limit, hopefully
The tables section refers to the variable $entries declared above.
Since the purpose of the function module is to read any table, they
decided to put the contents of each row in one single character field of
length 512. Since tables must contain structures, not strings or anything
else, they made a structure TAB512 that contains just such a long character
field.
eval { $rc = $RfcGetTableEntries->call($conn); } or do {
if (!$@) { # no exception
warn "RFC Failure: ".RFC::lastError;
#TODO: handle RFC failures
}
elsif ($@ eq "INTERNAL_ERROR\n") { #
#TODO: Handle the INTERNAL_ERROR exception
die "RFC_GET_TABLE_ENTRIES raised exception $@";
}
elsif ($@ eq "TABLE_EMPTY\n") { #
#TODO: Handle the TABLE_EMPTY exception
die "RFC_GET_TABLE_ENTRIES raised exception $@";
}
elsif ($@ eq "TABLE_NOT_FOUND\n") { #
#TODO: Handle the TABLE_NOT_FOUND exception
die "RFC_GET_TABLE_ENTRIES raised exception $@";
}
else { # system exception
...
}
}
This is the way contex/P catches so called exceptions raised by
the ABAP code. The default reaction is to die with a message.
that tells which function module raised what exception.
elsif ($@ eq "TABLE_EMPTY\n") { #
#TODO: Handle the TABLE_EMPTY exception
# now we must set the result vallues since their
# state is unknown
$RfcGetTableEntries->NumberOfEntries(0);
$RfcGetTableEntries->Entries->create;
}
#TODO: process the data you received from RfcGetTableEntries. my $f = $RfcGetTableEntries; # tired of writing long name ... printf "%d users, %d table rows\n", $f->NumberOfEntries, $f->Entries->rows;Since we don't know what to do in case NumberOfEntries is not the same as the number of rows in the table, we do not even check for that condition (never check for errors you can't handle). Instead, from now on, we'll completely ignore this redundant return value.
iw@pingo:.../contexP > mv RfcGetTableEntries.pl solution2.pl iw@pingo:.../contexP > perl solution2.pl 14 users, 14 table rows
iw@pingo:.../contexP > perl meta.pl CON -t Usr02The -t flag tells meta.pl to interpret the name as table (or structure) name. In solution.pl we have to add the line
require q{Usr02.pm};
A good place is after the line that requires Tab512.pm.
if (RFC::S::Usr02->bytes > RFC::S::Tab512->bytes) {
die "Sorry, Tab512 is too short.";
}
my @users = map {
RFC::S::Usr02->pval(
substr($_->aval, 0, RFC::S::Usr02->bytes)
)
} $f->Entries->getrows;
The rest is exactly the same as in lsr3users.pl:
foreach (sort { $a->Mandt cmp $b->Mandt } @users) {
printf "%12s client %3s last login was %s %s\n",
$_->Bname, $_->Mandt, $_->Trdat,
$_->Uflag ? "LOCKED!" : "";
};
We added 3 lines of code to check for the condition that prevents us from
creating the listing. (Ok, we could have written that in one line using
die ... if ...)
That's it. Here is the completed script, solution2.pl.
By the way, are you still curious how long a Usr02 record really is? Let's see
iw@pingo:.../contexP > perl -MRFC -MUsr02 -e "print RFC::S::Usr02->bytes, qq{\\n}";
195
That's today in our unpatched 40B release. But who knows? Somebody
may upgrade your R/3 system to 67D tomorrow. Most probably, the length
of USR02 will change. You'll notice that all your scripts where you
wrote 195 literally stop working or give strange results. Now you go through
all scripts and replace 195 by the new value, accidently also changing
a place where 195 was not the length of Usr02 but some other important
constant ... a nightmare!