Main | July 2002 »

May 14, 2002

A taste for Inline

Inline is one of those potentially killer apps for Perl. It enables you to bind just about anything you feel like to perl, and access it as native function calls from with your perl program. It started out as an idea to make it so compellingly easy to bind other programming languages to Perl, that Perl could then take over the world ( okay I'm starting to exagerate now ), but it has become more than that. People are now starting to wakeup to the idea of bind other things like SQL, and query languages, through to text processing tools and the like ... see this .
As a celebration, of what can be developed easily with Inline::C I dug out the source code of the old UNIX command line tool cal, turned it into a library, and bound it into a Perl module UNIX::Cal available at a CPAN near you - UNIX::Cal

Posted by PiersHarding at 9:39 PM

May 3, 2002

Reloading Perl Modules in a Server ( using Perl )

Just this week I have been working on a problem on how to reload a Perl module in a continuous process eg. when you have changed the code in a loaded module, you want the parent process to detect this, and then dump the old copy in favour of the new.
What I didn't know was that I would end up deep in Perl Guts, and that it isn't as bad as it sounds.
Scenario: we have an Application server that processes business functions. This server is the back office for web servers that are possibly operational 24/7. When we deploy new code we want to be able to do so with minimal down time ( hopefully none ), so just like with Apache you can use Apache::Reload to refresh mod_perl Modules, We wanted to be able to do the same.
This boiled down to a couple of things:

Baseline

The first thing I needed was the filename of the package, to do this I had to turn the package name into a file name and then find that file in the available paths in @INC. when looping at @INC you have to be very careful not to modify it, so I copied it first.
  $pkg =~ s/::/\//g;
  $pkg .= '.pm';
  if ( -f $pkg ){
    $files->{$mod} = $pkg;
    return $pkg;
  } else {
    my @incy = ( @INC );
    foreach ( @incy ){
      if ( -f $_.'/'.$mod.'.pm' ){
        $files->{$mod} = abs_path($_).'/'.$mod.'.pm';
	  return $files->{$mod};
      }
    }
    return undef;
  }
After that I need the to create a cache of the timestamp of the file
  return (stat($file) )[9];

Test and Reload

Now that I can locate the module and have a baseline to check against, I new to check the timestamp repeatedly ( as above ), and then remove the module from the symbol table, and reload it if the timestamp has been updated.
  delete $INC{$file};
  my $pkg = $mod;

  # expand to full symbol table name if needed
  $pkg .= '::' unless  $pkg =~ /::$/;

  no strict "refs";
  my $symtab = *{$pkg}{HASH};
  return unless defined $symtab;

  # free all the symbols in the package
  foreach my $name (keys %$symtab) {
    debug("undef: $pkg$name");
    undef %{$pkg . $name};
  }

  # delete the symbol table
  undef %{"$mod"};
As is often the case with Perl - here in lies the little bit of magic that makes it all happen. I delete the entry in the %INC hash that holds the directory of loaded modules. Then I figure out the symbol table entry for the package. With this name I can lookup the symbol table and find all the related entries to the package ie. the names of all methods of the package. these are:
undef %{$pkg . $name}; undefined, and then the package as a whole is undefined.
And that's it! The code can be found in Reload.pm
All the credit goes to the respective authors of Apache::Session, and Symbol, where I shamelessly plaigarised all the ideas from. :-)

Posted by PiersHarding at 8:33 PM

Examples of tables and SAP::Rfc

I have had a number of emails lately about using SAP::Rfc, and the most common question I get asked is "How do I put entries in a table for an RFC call". So here is a example ( that I should add into the documentation ):
All complex parameters ( import parameters with structures ) and tables have a helper class SAP::Struct. The idea of this is to assist the user to create correctly structured rows that can then be passed in as values ( if you allready know the structure of your table or parameter, and want to create it using a pack() or some such then go ahead ).
eg.
# create an SAP connection
my $rfc = new SAP::Rfc( .... );

# discover the interface parameters etc.
my $i = $rfc->discover("BAPI_SALESORDER_CREATEFROMDAT1");

# grab the structure object for the table
my $str = $i->tab('ORDER_PARTNERS')->structure();

# set the values of the fields of the structure
$str->PARTN_VALUE("SP");
$str->PARTN_NUMB("44444444");

# add the correctly formated string representing the table row to an array
push(@rows, $str->value);
$str->PARTN_VALUE("AB");
$str->PARTN_NUMB("55555555");
push(@rows, $str->value);

# assign the array reference as the value of the table
$i->ORDER_PARTNERS( \@rows );

# do the call
$rfc->callrfc($i);
So the SAP::Struct object $str helps you construct the full string value for a row of the table ORDER_PARTNERS with all the values correctly padded out etc. This is especially useful for tricky SAP data types like INT1, HEX etc. as the SAP::Struct class takes care of this for you. then you accumulate these rows into a table and pass it into the interface object as an array ref \@rows. Once the call is completed, and you want to get hold of the contents of a table you do something like:
foreach my $row ( $i->tab('ORDER_PARTNERS')->hashRows() ){
  print $row->{'PARTN_VALUE'}."\n";
}

Posted by PiersHarding at 2:26 PM