<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Where on Earth is Piers?</title>
<link>http://www.piersharding.com/blog/</link>
<description></description>
<copyright>Copyright 2007</copyright>
<lastBuildDate>Fri, 11 May 2007 06:27:59 +0000</lastBuildDate>
<generator>http://www.movabletype.org/?v=3.33</generator>
<docs>http://blogs.law.harvard.edu/tech/rss</docs> 

<item>
<title>sapnwrfc for Python</title>
<description><![CDATA[The first version of sapnwrfc for Python is now available.  Like the sapnwrfc for Ruby, and Perl, this is a complete rewrite to take advantage of the new SAP NW RFCSDK, with unicode support, and support for deep structures.  This version provides Client side RFC support only.  The download is available <a href='http://cheeseshop.python.org/pypi/sapnwrfc/' target='_blank'>here</a>]]></description>
<link>http://www.piersharding.com/blog/archives/2007/05/sapnwrfc_for_py.html</link>
<guid>http://www.piersharding.com/blog/archives/2007/05/sapnwrfc_for_py.html</guid>
<category>saprfc</category>
<pubDate>Fri, 11 May 2007 06:27:59 +0000</pubDate>
</item>
<item>
<title>NW RFC SDK is now officially available</title>
<description><![CDATA[<h2>The new SAP NW RFCSDK</h2>
<p>
The new SAP NetWeaver RFCSDK is now available for official download - this opens the way for supported next generation Open Source Connectors such as <a href="http://search.cpan.org/search?dist=sapnwrfc" target="_blank">sapnwrfc</a> for <a href="http://www.perl.org" target="_blank">Perl</a>, and <a href="http://raa.ruby-lang.org/project/sapnwrfc" target="_blank">sapnwrfc</a> for <a href="http://www.ruby-lang.org" target="_blank">Ruby</a>.
</p>
<h2>Where to get it?</h2>
<p>
You need to go to the SAP service Portal <a href="http://service.sap.com/swdc">for Software downloads</a>, and follow the path of:
Download -&gt; Support Packages and Patches                                          
-&gt; Entry by Application Group -&gt; Additional Components  -&gt; SAP NW RFC                                            
SDK -&gt; SAP NW RFC SDK 7.10 -&gt; SAP NW RFC SDK 7.10                  .
</p>
<p>
There is an accompanying   OSS note:    <a href="https://service.sap.com/sap/support/notes/1025361" target="_blank">                                      
 1025361</a>.
</p>
<p>
<h2>Impact on Connectors</h2>
As mentioned in a 
<a href="https://weblogs.sdn.sap.com/cs/weblog/view/wlg/5827" target="_blank">previous blog</a> (but so much better in Ulrichs' <a href="https://www.sdn.sap.com/irj/sdn/weblogs?blog=/pub/wlg/6528" target="_blank">blog</a>), the new NW RFCSDK not only updates the implementation of the library that all 3rd party products use, but it also provides a better interface to develop to, and exposes several key features such as Unicode, and complex structures.</p>
<p>
Clearly - this is SAPs way forward in the RFC area of closely bound system integration, so all existing connectors  (and 3rd party products) will eventually have to migrate to it.
</p>
<p>
This is also true of code that is based on the existing connectors for Perl and Ruby.  For Perl - there will be a migration from SAP::Rfc -&gt; sapnwrfc, and likewise for Ruby moving from saprfc -&gt; sapnwrfc  (The client side implementation for the new sapnwrfc connector for Python is nearly finished, with the same implications).
</p>
<p>
My thanks and congratulations go out to the SAP NW RFC Team (NW AS ABAP Connectivity) - through their efforts, and willingness to engage the community, they have made it possible for us frustrated hackers to have a little more fun :-)
</p>]]></description>
<link>http://www.piersharding.com/blog/archives/2007/05/nw_rfc_sdk_is_n.html</link>
<guid>http://www.piersharding.com/blog/archives/2007/05/nw_rfc_sdk_is_n.html</guid>
<category>sap</category>
<pubDate>Thu, 10 May 2007 08:49:04 +0000</pubDate>
</item>
<item>
<title>sap4rails updated to support new sapnwrfc </title>
<description><![CDATA[<a href='http://raa.ruby-lang.org/project/sap4rails' target='_blank'>sap4rails</a> has been upgraded to support the new RFC Connector for Ruby - <a href='http://raa.ruby-lang.org/project/sapnwrfc' target='_blank'>sapnwrfc</a>.]]></description>
<link>http://www.piersharding.com/blog/archives/2007/03/sap4rails_upgra.html</link>
<guid>http://www.piersharding.com/blog/archives/2007/03/sap4rails_upgra.html</guid>
<category>saprfc</category>
<pubDate>Thu, 01 Mar 2007 09:39:45 +0000</pubDate>
</item>
<item>
<title>New RFC Connector - sapnwrfc</title>
<description><![CDATA[<p>
SAP has undertaken a project to re-engineer the RFC SDK (creating the SAP NetWeaver RFC SDK), which is good news for the  Connectors (<a href="https://www.sdn.sap.com/irj/sdn/thread?forumID=50&threadID=286689">ref.</a>).</p>
<p>
This means that eventually - all the RFC connectors, and 3rd party products will need to be updated to use the new under-carriage.</p>
<p>
<h3>So whats the big deal?</h3>
RFC is a stable technology, and has been for many years, so I can understand why this revelation may not seem very exciting.  What is exciting is the unprecedented level of cooperation, understanding and good will that has come out in a relatively short time, as I have moved through the process of redeveloping the Ruby and Perl RFC Connectors.  The result is (and will be more so), a better fit in terms of how the SDK works with Dynamic Languages, allowing the API that the   Dynamic Languages offer for RFC connectivity, to better reflect the nature of those programming languages.  For example - there are better features in the new NW RFC SDK that allow for easy translation of ABAP types to Ruby/Perl types.
</p>
<p>
<h3>New Features</h3>
If we set aside the rationalisation, and simplification of the NW SDK (which is a bonus in itself), there are new features of the NW SDK that can be drawn upon -
<ul>
<li>unicode in the core, with utf-8 to utf-16 conversion tools</li>
<li>intelligent ABAP <=> X helper functions to ease type translations</li>
<li>deep structures</li>
<li>interface discovery</li>
<li>interface, and structure caching</li>
</ul>
</p>
<p>
This has lead to a complete overhaul of the Ruby, and Perl Connectors, with the aim to take advantage of the new NW SDK features, and to produce connectors that create a more intuitive bond between the underlying RFC API, and the natural features of each Dynamic Language.
</p>
<p>
<h3>Call for testers</h3>
As the new Ruby and Perl RFC Connectors are a complete rewrite and Alpha, I am calling for testers/early adopters from the Community.
</p>
<p>
<h3>Obtaining the Connectors, and Netweaver RFC SDK</h3>
Download the new sapnwrfc connector for Ruby, and get the new RFC SDK, port your applications to it, and let me know how you get on.  I'm interested in usability feedback, problems, and feature requests.
</p>
<p>
For Ruby - download from the <a href="http://raa.ruby-lang.org/project/sapnwrfc/" target="_blank"> RAA</a>.  Follow the instructions in the included README file.  Documentation is available <a href="http://www.piersharding.com/download/ruby/sapnwrfc/doc/" target="_blank">Here</a>.  A GEM install package for Win32 has been built available <a href="http://www.piersharding.com/download/ruby/sapnwrfc/sapnwrfc-0.06-mswin32.gem">here</a>.
</p>
<p>
For Perl - download from <a href="http://search.cpan.org/search?query=sapnwrfc" target="_blank">CPAN</a>.  Again - follow the instructions in the included README file.  Documentation is available <a href="http://search.cpan.org/author/PIERS/sapnwrfc-0.06/sapnwrfc.pm" target="_blank">Here</a>.  A PPM install package for Win32 has been built available <a href="http://www.piersharding.com/download/win32/sapnwrfc-0.06.zip">here</a>.
</p>
<p>
If you are interested in trialling/testing the new connectors, then along with the installing the new connectors (above) you will need to obtain the new Netweaver RFC SDK.  in order to do this, please register your interest with me, ensuring that I know how to contact you, and what your platform requirements are, and with the help of the NW RFC team at SAP I will get the relevant details to you.
</p>
<p>
<h3>Ruby Examples</h3>
There are plenty of examples in the tests/ directory of the sapnwrfc download, but here is a basic walk through of the new API:
<pre>
 # specify a YAML base config file or pass connection
 #   parameters directly to rfc_connect()
 SAPNW::Base.config_location = './config_file.yml'
 SAPNW::Base.load_config
 conn = SAPNW::Base.rfc_connect

 # get the system and connection details
 attrib = conn.connection_attributes
 $stderr.print "Connection Attributes: #{attrib.inspect}\n"

 # lookup the dictionary definition of an Function Module
 fds = conn.discover("STFC_DEEP_STRUCTURE")
 $stderr.print "Parameters: #{fds.parameters.keys.inspect}\n"

 # create an instance of a Function call
 fs = fds.new_function_call

 # populate the parameters - structures and table rows now take hashes of field name/value pairs
 fs.IMPORTSTRUCT = { 'I' => 123,
                     'C' => 'AbCdEf',
                     'STR' =>  'The quick brown fox ...',
                     'XSTR' => ["deadbeef"].pack("H*") }

 # execute the RFC call
 fs.invoke
 $stderr.print "RESPTEXT: #{fs.RESPTEXT.inspect}\n"
 $stderr.print "ECHOSTRUCT: #{fs.ECHOSTRUCT.inspect}\n"
</pre>
</p>
<p>
Config file (refer to the sap.yml file in the download):
<pre>
ashost: ubuntu.local.net
sysnr: "01"
client: "001"
user: developer
passwd: developer
lang: EN
trace: 2
</pre>
</p>
<p>
Test it out - and give your feedback.
</p>
<p>
<h3>Perl Examples</h3>
As with Ruby, there are plenty of examples in the software download in the t/* directory.  Again - here is a taster showing the new API:
<pre>
 use sapnwrfc;
 use Data::Dumper;

 # specify a YAML base config file or pass connection
 #   parameters directly to rfc_connect()
 SAPNW::Rfc->load_config;
 my $conn = SAPNW::Rfc->rfc_connect;

 # lookup the Function Module
 my $fds = $conn->function_lookup("STFC_DEEP_STRUCTURE");

 # initialise a call instance
 my $fs = $fds->create_function_call;

 # set the parameters
 $fs->IMPORTSTRUCT({ 'I' => 123,
                     'C' => 'AbCdEf',
                     'STR' =>  'The quick brown fox ...',
                     'XSTR' => pack("H*", "deadbeef")});

 # invoke the Function Module and then play with the results
 $fs->invoke;
 $stderr.print "RESPTEXT: #{fs.RESPTEXT.inspect}\n"
 $stderr.print "ECHOSTRUCT: #{fs.ECHOSTRUCT.inspect}\n"
 print STDERR "RESPTEXT: ".Dumper($fs->RESPTEXT)."\n";
    ok($c eq 'AbCdEf');
 print STDERR "ECHOSTRUCT: ".Dumper($fs->ECHOSTRUCT)."\n";

 # cleanup
 $conn->disconnect;
</pre>
</p>
<p>
Config file format is the same as for Ruby - refer to the sap.yml file in the download:
<pre>
ashost: ubuntu.local.net
sysnr: "01"
client: "001"
user: developer
passwd: developer
lang: EN
trace: 2
</pre>
</p>
<p>
Test it out - and give your feedback.  The best place would be to carry on the discussion through the <a href="https://forums.sdn.sap.com/category.jspa?categoryID=39">Forums</a>.
</p>
<p>
Special thanks go to:
<ul>
<li> Ulrich Schmidt for fielding all my questions, and the whole of the NW RFC Team (NW AS ABAP Connectivity), for their support, and quick response.</li>
<li>Olivier Boudry - building and testing</li>
<li>and lastly (but by no means least) Craig Cmehil for tirelessly getting people together and making it all possible.</li>
</ul>
</p>

<p>
<a href=""><h3>Notes/Updates:</h3></a>
See the download instructions for the SAP NW RFCSDK  <a href='http://www.piersharding.com/blog/archives/2007/05/nw_rfc_sdk_is_n.html'> here</a>.
</p>]]></description>
<link>http://www.piersharding.com/blog/archives/2007/02/new_rfc_connect.html</link>
<guid>http://www.piersharding.com/blog/archives/2007/02/new_rfc_connect.html</guid>
<category>saprfc</category>
<pubDate>Wed, 28 Feb 2007 12:28:55 +0000</pubDate>
</item>
<item>
<title>RFC_STRING and RFC_XSTRING type support for saprfc for Ruby</title>
<description><![CDATA[<p>Support for string types now available in saprfc for Ruby from version 1.52.  This opens the way for interaction with RFC calls that require variable length storage eg. true strings in either character or binary form.  This was particularily useful for manipulating logon tickets, as shown by this example:
</p>
<p>
<pre>
isusr = rfc.discover("SUSR_CHECK_LOGON_DATA")
isusr.AUTH_METHOD.value = "E"
isusr.AUTH_DATA.value = "p:ompka\\piers"
isusr.EXTID_TYPE.value = "NT"
rfc.call(isusr)
puts "RESULT: "

# access an interface parameter value
print "TICKET:             #{isusr.TICKET.value.to_s}\n"
ticket = isusr.TICKET.value.to_s

rfc2 = SAP::Rfc.new(:ashost => "192.168.1.2",
                   :sysnr  => 00,
                   :lang   => "EN",
                   :client => "010",
                   :mysapsso2 => ticket,
                   :trace => 1
       )
</pre>
</p>
<p>
This code snippet demonstrates using one RFC connection to generate login tickets for another - a very useful trick for brokering connections within an external application.  The login tickets in the standard RFC are carried in an RFC_STRING (isusr.TICKET) type parameter.
</p>
<p>Note: Thanks to <a href='http://www.computerservice-wolf.com/'>Gregor</a> for pointing this out.</p>]]></description>
<link>http://www.piersharding.com/blog/archives/2006/11/rfc_string_and.html</link>
<guid>http://www.piersharding.com/blog/archives/2006/11/rfc_string_and.html</guid>
<category>saprfc</category>
<pubDate>Sat, 11 Nov 2006 11:00:00 +0000</pubDate>
</item>
<item>
<title>Repensando a web com Rails</title>
<description><![CDATA[<a href='http://www.balanceonrails.com.br/'>Fabio Akita</a> has just contacted me to say that his new book <a href='http://www.brasport.com.br/index.php?Escolha=8&Livro=L00209'>"Repensando a web com Rails"</a> has been released for the Brazilian market.  The book is primarily about Ruby on Rails, but contains a section on SAP integration with Rails, which I helped (a little) with<br/>
Congratualtions Fabio - Hope it sells well.]]></description>
<link>http://www.piersharding.com/blog/archives/2006/11/repensando_a_we.html</link>
<guid>http://www.piersharding.com/blog/archives/2006/11/repensando_a_we.html</guid>
<category>rails</category>
<pubDate>Thu, 02 Nov 2006 13:27:22 +0000</pubDate>
</item>
<item>
<title>Ruby and the SOAP RunTime (SRT) Handler</title>
<description><![CDATA[<p>
Following on from my previous post about <a href="http://www.piersharding.com/blog/archives/2006/10/ruby_ruby_on_ra.html" target="_blank">Ruby, Ruby on Rails, and SAP Web Services Integration</a> - I would like to show how to switch to using the SOAP RunTime (SRT) Handler, which makes available SAP Web Services via Virtual Interfaces.
</p>
<p>
<h3>Steps</h3>
<ul>
<li>Follow the steps outlined in <a href="http://www.piersharding.com/blog/archives/2006/09/saprfc_ruby_on.html" target="_blank">Ruby, Ruby on Rails, and SAP Web Services Integration</a> upgrading sapwas to at least version 0.06, and sap4rails to at least version 0.05, making sure that the soap/rfc handler works correctly.</li>
<li>Create the Virtual Interface, Web Service Definition, and Web Service Config to expose the UserAdmin Function Modules</li>
<li>Modify app/models/sap_user.rb to point to the new Web Service definition</li>
</ul>
</p>
<p>
<h3>Create the SAP Web Service</h3>
We need to create a web service that exposes the function modules that were used in the UserAdmin Rails application.  To do this - go to transaction SE80, go to the Enterprise Services tab, and create a Virtual Interface of type Funciton Group called Z_USERADMIN.  Include into this the function modules:
</p>
<p>
<ul>
<li>Z_BAPI_USER_GETLIST</li>
<li>BAPI_USER_GET_DETAIL</li>
<li>BAPI_USER_LOCK</li>
<li>BAPI_USER_UNLOCK</li>
</ul>
</p>
<p>
Activate this, and then create a Web Services definiton - again, using SE80, go to the Enterprise Services tab, and create a Web Services definition called Z_USERADMIN - referencing the previously activated Vitual Interface Z_USERADMIN.<br>
Finally - activate this in the ICF configuration (SICF), by using transaction WSCONFIG, referencing the Web Service definition Z_USERADMIN created above.
<br>
There is an excellent discussion of the details of this process by <a href="https://www.sdn.sap.com/irj/sdn/weblogs?blog=/pub/u/251694270">Thomas Jung</a>, <a href="https://www.sdn.sap.com/irj/sdn/weblogs?blog=/pub/wlg/3329" target="_blank">here</a>, and <a href="https://www.sdn.sap.com/irj/sdn/weblogs?blog=/pub/wlg/1135" target="_blank">here</a>.
</p>
<p>
<h3>Modify model sap_user.rb</h3>
Now for the final part - in the UserAdmin Rails application, edit the SapUser model file app/models/sap_user.rb.  This needs to switch from referencing the method "function_module" for loading the functions, to using "resources", as outlined below:
<pre>
require_gem "sap4rails"

class SapUser < SAP4Rails::WS::Base

# You must define a list of RESOURCES to preload
   resources "http://seahorse.local.net:8000/sap/bc/srt/rfc/sap/Z_USERADMIN"
...
</pre>
Now you can test it as in the previous weblogs.
</p>
<p>
<h3>Unicode!</h3>
One thing that I neglected to say in my previous post, is a  major advantage of using sapwas for accessing SAP is that it has comprehensive Unicode support - free of charge (but not of pain) :-) .
</p>
<p>
<h3>Round Up</h3>
For me - this rounds up SAP Web Services and <a href="http://www.ruby-lang.org" target="_blank">Ruby</a> - you can either access them in Ruby directly by using the library sapwas, or taking advantage of the <a href="http://www.rubyonrails.org/" target="_blank">Rails</a> integration provided by sap4rails.
</p>]]></description>
<link>http://www.piersharding.com/blog/archives/2006/10/ruby_and_the_so.html</link>
<guid>http://www.piersharding.com/blog/archives/2006/10/ruby_and_the_so.html</guid>
<category>ruby</category>
<pubDate>Tue, 31 Oct 2006 11:12:08 +0000</pubDate>
</item>
<item>
<title>sap4rails 0.07 - SAP RFC and auto-reconnect</title>
<description><![CDATA[I released <a href='http://raa.ruby-lang.org/project/sap4rails'>sap4rails</a> 0.07 this week, which corrects a problem with RFC connections not reconnecting correctly (this is using <a href='http://raa.ruby-lang.org/project/saprfc'>saprfc</a> as the driver - there is no similar issue wtih the <a href='http://raa.ruby-lang.org/project/sapwas'>sapwas</a> driver, which uses stateless HTTP).  This is especially important when the R/3 (ABAP) application server your Rails application is connected to goes down temporarily (or indeed any other temporary communication disturbance). Before an RFC call is executed, the conection is ping checked, and then a single reconnection attempt is tried if the ping fails.  If the reconnect fails then an error is raised so it is a good idea to wrap your code in a rescue block, to deal with the situation in your local conditions.  Here is an example log:
</p>
<pre>
...
[SapUser] missing_method: Z_BAPI_USER_GETLIST
[SapUser] ifaces: ["SUSR_USER_LOCKSTATE_GET", "BAPI_USER_GET_DETAIL", "BAPI_USER_UNLOCK", "BAPI_USER_LOCK", "Z_BAPI_USER_GETLIST"]
in check connect...
Think Im connected - lets check ...
RFC PING[]...
Something wrong with connection(1) - do it again ...
new connection(2) ...
allready connected ...
[SapUser] missing_method: Z_BAPI_USER_GETLIST
[SapUser] ifaces: ["SUSR_USER_LOCKSTATE_GET", "BAPI_USER_GET_DETAIL", "BAPI_USER_UNLOCK", "BAPI_USER_LOCK", "Z_BAPI_USER_GETLIST"]
in check connect...
Think Im connected - lets check ...
RFC PING[true]...
...
</pre>
<p>
Here you can see the first RFC_PING return nil, and the second after reconnect return [true]. 
</p>]]></description>
<link>http://www.piersharding.com/blog/archives/2006/10/sap4rails_007_s.html</link>
<guid>http://www.piersharding.com/blog/archives/2006/10/sap4rails_007_s.html</guid>
<category>SOA</category>
<pubDate>Mon, 23 Oct 2006 15:05:09 +0000</pubDate>
</item>
<item>
<title>sapwas for Ruby and HTTPS</title>
<description><![CDATA[<p>
sapwas for Ruby now enables SAP Web Services to be called via HTTPS.  This first requires you to setup SSL support in NW4 - which isn't too much trouble if you follow Gregors' excellent advice <a href='https://www.sdn.sap.com/irj/sdn/weblogs?blog=/pub/wlg/1452'>here</a>.  Once that is in place, then it is just a matter of structuring the URL coorectly, as described in this example:
</p>
<pre>
require "SAP/WAS"

@was = SAP::WAS.new(:url => "https://seahorse.local.net:8443/sap/bc/srt/rfc/sap/Z_RFC_READ_REPORT_01",
                    :lang   => "EN",
                    :client => "010",
                    :user   => "developer",
                    :passwd => "developer",
                    :trace  => true)

# get a list of users
irep = @was.RFC_READ_REPORT
irep.program.value = 'SAPLGRAP'
irep.call()
puts "qtab no rows are: #{irep.qtab.rows.length.to_s}"
puts "qtab rows are: #{irep.qtab.rows.inspect}"
</pre>

<p>
sapwas fully integrates with sap4rails as an alternative driver for accessing either RFCs or SAP  SOAP based Web Services, and is available <a href='http://raa.ruby-lang.org/project/sapwas/'>here</a>.
</p>]]></description>
<link>http://www.piersharding.com/blog/archives/2006/10/sapwas_for_ruby.html</link>
<guid>http://www.piersharding.com/blog/archives/2006/10/sapwas_for_ruby.html</guid>
<category>ruby</category>
<pubDate>Wed, 18 Oct 2006 10:48:14 +0000</pubDate>
</item>
<item>
<title>Ruby, Ruby on Rails, and SAP Web Services Integration</title>
<description><![CDATA[<p>
Something I like about Scripting Languages is the way they revel in having "more than one way to skin a cat".  So, in this spirit I have built a complementary interface to saprfc (for Ruby) called sapwas, that facilitates RFC calls via SAP Web Services.  This has been integrated into sap4rails, and the attached example demonstrates how to substitue Web Services for RFC integration in Ruby on Rails.
</p>
<h3>SAP Web Services vs RFC?</h3>
<p>
Other than curiosity, there is another motivation for trying this out - Since there has been discussion of late on the merits of RFC and Web Service technology such as SOAP, I thought that inorder to do the subject justice I would do some further (tangible) investigation.
</p>
<p>
To me, the most obvious way to draw a comparison, is to develop comparable examples of each, with the only difference being the substitution of the technology in question.
</p>
<p>This led to me focusing on a recent article I wrote: 
<a href="http://www.piersharding.com/blog/archives/2006/09/saprfc_ruby_on.html" target="_blank">Ruby on Rails with AJAX</a>, to use as a base line.</p>

<h3>Activating the SAP Web Service support</h3>
<p>
If we take the example above (<a href="http://www.piersharding.com/blog/archives/2006/09/saprfc_ruby_on.html" target="_blank">Ruby on Rails with AJAX</a>), then the changes are as follows:
<ul>
<li>Install sapwas (version 0.02+)</li>
<li>Upgrade sap4rails (version 0.04+)</li>
<li>modify the SapUser model</li>
<li>set your Rails configuration</li>
</ul>
</p>
<h3>Install sapwas</h3>
<p>
Download either the source distribution, or the gem file  by following the <a href="http://raa.ruby-lang.org/project/sapwas/" target="_blank">sapwas project</a> download links.  Gem files are easier to deal with - all you need to do is:
</p>
<p>
<pre>
gem install sapwas-&lt;version&gt;.gem
</pre>
</p>
<p>
And its done (use gem uninstall to remove if its there allready).
</p>
<p> You will probably need to install <a href="http://raa.ruby-lang.org/project/http-access2/" target="_blank">http-access2</a> inorder to get the  basic authentication working correctly.  This is a requirement of the <a href="http://raa.ruby-lang.org/project/soap4r/" target="_blank">SOAP4R</a> library.
</p>
<h3>Upgrade sap4rails</h3>
<p>
Download either the source distribution, or the gem file   for sap4rails by following the <a href="http://raa.ruby-lang.org/project/sap4rails/" target="_blank">project</a> download links.  First remove the old version, and install the new:
</p>
<p>
<pre>
gem uninstall sap4rails
gem install sap4rails-&lt;version&gt;.gem
</pre>
</p>
<p>
If you have used the source distribution previously, then you will have to manually remove the previous one, and install again (the usual ruby setup.rb dance - gems are soo much easier :-).
</p>
<h3>Modify the SapUser model</h3>
<p>
In the Rails application, edit the app/models/sap_user.rb, and change the super class for SapUser:
<pre>
class SapUser < SAP4Rails::WS::Base
...
</pre>
</p>
<h3>Set your Rails configuration</h3>
<p>
Modify the config/sap.yml file - you really should only need to add in the :url line depicted below, but check anyway:
</p>
<p>
<pre>  client: "010"
  url: "http://seahorse.local.net:8000/sap/bc/soap/rfc"
  user: developer
  passwd: developer
  lang: EN
</pre>
</p>
<h4>note on WAS configuration</h4>
<p>
You must ensure that /sap/bc/soap/rfc is configured/activated correctly in transaction SICF.
</p>
<h3>Go!</h3>
<p>
Once you have completed these changes, you can then test the Rails application as described in <a href="http://www.piersharding.com/blog/archives/2006/09/saprfc_ruby_on.html" target="_blank">Ruby on Rails with AJAX</a> - it should function in exactly the same manner - wasn't that easy!
</p>
<p>
Now you have it working - I'll leave you to draw your own conclussions on it's merits :-)
</p>]]></description>
<link>http://www.piersharding.com/blog/archives/2006/10/ruby_ruby_on_ra.html</link>
<guid>http://www.piersharding.com/blog/archives/2006/10/ruby_ruby_on_ra.html</guid>
<category>rails</category>
<pubDate>Sun, 15 Oct 2006 18:11:03 +0000</pubDate>
</item>
<item>
<title>Changing parameters in saprfc for Ruby</title>
<description><![CDATA[<p>saprfc for Ruby now has the ability to pass Changing type parameters.  These are an evolution of import, and export parameter types rolled into one.  this functionality is available from release 0.31.  This example shows the standard test RFC where COUNTER is a changing type parameter - the value is incremented on each pass:</p>
<pre>
iter = 10
rfc = SAP::Rfc.new(...)
# look up the interface definition for an RFC
i = rfc.discover("STFC_CHANGING")
i.counter.value = 0
iter.times {|cnt|
  puts "\n\n\n\n\nITERATION #{cnt + 1}\n\n\n\n"
  i.start_value.value = cnt
  rfc.call(i)
  puts "RESULT: #{i.result.value}  COUNTER: #{i.counter.value}"
}
# close the connection
print "close connection: ", rfc.close(), "\n"
</pre>]]></description>
<link>http://www.piersharding.com/blog/archives/2006/10/changing_parame.html</link>
<guid>http://www.piersharding.com/blog/archives/2006/10/changing_parame.html</guid>
<category>saprfc</category>
<pubDate>Thu, 12 Oct 2006 06:15:32 +0000</pubDate>
</item>
<item>
<title>SAP::Rfc and saprfc UNICODE support for win32</title>
<description><![CDATA[<p>
Over the last few weeks I have been working with <a href='http://toolbox.boudry.org/'>Olivier</a> to work through the compiling issues to make UNICODE support for SAP::Rfc for Perl, and saprfc for Ruby available under win32.  These both require <a href='http://gettext.sourceforge.net/'>iconv</a> for the charset conversion, but you need not worry about that as they have been compiled for you as <a href='http://www.piersharding.com/download/win32/SAP-Rfc-1.52-unicode.zip'>PPD</a>, and <a href='http://www.piersharding.com/download/ruby/saprfc-0.31-mswin32-unicode.gem'>GEM</a> respecitvely.
</p>]]></description>
<link>http://www.piersharding.com/blog/archives/2006/10/saprfc_and_sapr_1.html</link>
<guid>http://www.piersharding.com/blog/archives/2006/10/saprfc_and_sapr_1.html</guid>
<category>saprfc</category>
<pubDate>Wed, 11 Oct 2006 19:14:19 +0000</pubDate>
</item>
<item>
<title>SAP the Enterprise, and Rails interest is building</title>
<description><![CDATA[<p>I've just recently had contact from Lance, at the <a href='http://www.engineyard.com'>Engine Yard</a>, who specialise in Rails applicaition development, and Hosting.  These guys recognise that for their more Enterprise related clients, there is likely to be an <a href='http://www.sap.com'>SAP</a> related dimension to their requirements.</p>
<p>To me - this kind of interest shows that <a href='http://www.rubyonrails.com'>Ruby on Rails</a> is making a step change in it's progress through the world of IT, where it will likely start penetrating into the Enterprise software development market.]]></description>
<link>http://www.piersharding.com/blog/archives/2006/10/sap_and_rails_i.html</link>
<guid>http://www.piersharding.com/blog/archives/2006/10/sap_and_rails_i.html</guid>
<category>rails</category>
<pubDate>Tue, 10 Oct 2006 07:58:23 +0000</pubDate>
</item>
<item>
<title>IM for SAP with Jabber (XMPP)</title>
<description><![CDATA[<p>
<a href="http://www.jabber.org" target="_blank">Jabber and IM</a> is very much in the ascendancy at the moment, thanks to <a href="http://www.google.com/talk" target="_blank">Google Talk</a> which uses the very same <a href="http://www.xmpp.org" target="_blank">XMPP</a> protocol for messaging.</p>
<p>
With this in mind I would like to demonstrate how to easily integrate at least some minimal IM capability into SAP, being able to pass basic messages back and forth between IM accounts and R/3 accounts, and then to touch on the potential for IM beyond this.
</p>
<h3> A bit of background</h3>
<p>
<h4>XMPP</h4>
Is an XML streaming protocols for instant messaging and presence developed within the Jabber community. Because the transmission of data is encapsulated in XML, and must conform to the controlling rules of XML, coupled with implementation rules for the protocol such as dialback for server to server communication etc., making XMPP a very secure messaging platform.  Testimony to this is the absence of SPAM, in fact it could be robustly argued that if the backbone of SMTP was replaced with XMPP then SPAM would be history (but that is for another day).<br>
Additionally, XMPP is a recognised Standard - the Internet Engineering Task Force (IETF) has formalized the core XML streaming protocols as an approved instant messaging and presence technology under the name of XMPP, and the XMPP specifications have been published as RFC 3920 and RFC 3921.
</p>
<p>
<h4>Jabber</h4>
being the historical root of XMPP, has a number of server, client and component implementations surrounding it.  In brief, a Jabber environment requires a:
<ul>
<li> Server, which minimally consists of a core router or hub that directs XML streams between end points - usually those endpoints are components.</li>
<li>
Components: implement either server type features, or provide gateways or "transports" between the XMPP protocol and what ever else (in this case SMTP).  Server type features may include offline storage for messages, Client to server connection handling, authentication, Server to Server communication etc.
</li>
<li>
Clients - typically what the enduser experiences as a chat interface, but could equally be an automaton that is a message producer/consumer such as an SAP-XI node, MQ-Series  etc.
</li>
</ul>
For a more indepth ovreview, then <a href="http://www.jabber.org/about/techover.shtml" target="_blank">this</a> from the JSF is a good starting point.
</p>
<h3>Jabber and SAP</h3>

<p>
For my trial implementation, I didn't want to change an R/3 system in anyway, so the logical solution is to start with what SAP has to offer by way of a messaging interface, namely the BCS (Business Communication Service).  It is concievably quite easy to modify the BCS to include a new service class along side SMTP, SMS, Faxing etc., but inorder to keep this as "zero modification", I have decided against that route.
</p>
<p>
The basic solution design is to use email on the R/3 side that is to be bi-directionally translated to Jabber based IM via a custom built XMPP transport component (SMTP Component pictured below).
</p>
<p>
<img src="http://www.piersharding.com/download//im.jpg" width="406" height="388" border="0" alt="image" />
</p>
<p>
Flow of messages between R/3 and Jabber Server.
</p>
<h4>Message Flow and Addresses</h4>
<p>
Because the messages are moving between two protocols, it is  necessary to translate the from and to addresses between them, as the messages move through the SMTP transport Component.  This is so that a message can find its correct XMPP destination (R/3 -> Jabber), and that the IM Message can be stamped with the appropriate From address so that when a reply is made, it will be able to find it's way back again (Jabber -> R/3).
</p>
<p>
<img src="http://www.piersharding.com/download/imflowout.jpg" width="398" height="385" border="0" alt="image" />
</p>

<h3>Configure SAPConnect for SMTP</h3>
<p>
  SMTP inbound and outbound must be configured in R/3.  There is a good discussion of this in the SAP help <a href="http://help.sap.com/saphelp_nw04/helpdata/en/af/73563c1e734f0fe10000000a114084/content.htm" target="_blank">here</a>.  In the following section I have highlighted the main parts of the configuration that I used to get my SAP Netweaver <a href="http://www.sap.com/linux" target="_blank">NW4</a> evaluation system working - depending on your release your mileage may vary.</p>
<p>
<h4>Profile Parameters</h4>
Set your profile parameters:
<pre>
   rdisp/start_icman = true
   icm/server_port_2 = PROT=SMTP,PORT=25000,TIMEOUT=180         
   is/SMTP/virt_host_0 = *:25000;                               
</pre>
</p>
<p>
<h4>SICF Virtual Host configuration</h4>
Configure your ICM via transaction SICF:
</p>
<p>
<img src="http://www.piersharding.com/download/sicf_sapconnect2.jpg" width="498" height="400" border="0" alt="image" />
<br>
Ensure that your SAPConnect Virtual Host Data is configured for SMTP.
</p>
<p>
<img src="http://www.piersharding.com/download/sicf_sapconnect.jpg" width="491" height="400" border="0" alt="image" />
<br>
On the Service Data tab, ensure that the R/3 user for email receipt is configured (including client, language etc.).
</p>
<p>
<h4>SCOT Configuration</h4>
<img src="http://www.piersharding.com/download/default_domain.jpg" width="600" height="93" border="0" alt="image" />
<br>
In transaction SCOT, select the menu option for default domain - set this as the host receiving the email.
</p>
<p>
<img src="http://www.piersharding.com/download/scot_receipt.jpg" width="576" height="361" border="0" alt="image" />
<br>
In transaction SCOT, you can optionally specify that recipt email is not expected.  This makes it tidier in terms of the monitoring view of the send process.
</p>
<p>
<img src="http://www.piersharding.com/download/smtp_node.jpg" width="574" height="400" border="0" alt="image" />
<br>
Ensure that the SMTP node is configured - it usually uses the local OS transport mechanism such as sendmail (connecting to localhost port 25), and that the address range that will be sent to has a routing table rule in place.  Make sure that the job that triggers the send process has been scheduled (SCOT => Settings => Send Jobs) - this is what shuffles the emails out on a periodic basis.
</p>
<p>
Finally - make sure that every R/3 account is configured correctly with an Internet Email Address.  This is found in SU01, under address data, and communication methods.
</p>
<h3>The Jabber Component</h3>
<p>
Now that we have R/3 configured, we can move on to the Jabber side of things.  I have created a transport component, that implements a simple gateway between XMPP, and SMTP.  The component has been developed using <a href="http://www.ruby-lang.org" target="_blank">Ruby</a> and  <a href="http://home.gna.org/xmpp4r/" target="_blank">XMPP4R</a>. By nature, a transport component has two main parts to it.  It has the portion that implements the XMPP protocol, and the second part that implements the target protocol - in this case SMTP.  There are other SMTP component implementation available  such as <a href="http://devel.amessage.info/smtp-t/" target="_blank">smtp-t</a>, but - inorder to simplify the sender and receiver addresses (See diagram above and below showing process flow and address translation) - I have decided to "roll my own".   XMPP4R is easy to use, and taps into Rubys simple threads implementation - something that is essential for developing components that have two event cycles - listiening for SMTP messages on ones side, and XMPP on the the other.
</p>
<p>
In my landscape I have a Netweaver R/3 instance with an SMTP interface named seahorse.local.net listening on 2500.  My Jabber SMTP component has an SMTP interface of sapsmtp.local.net listening on port 25, and this is registered with the XMPP router called gecko.local.net.  As both SAP, and the Component have to have both sending and receiving capabilities for SMTP, and this is all configurable, I've tried to ease this pain with a diagram:
</p>
<p>
<img src="http://www.piersharding.com/download/serverids.jpg" width="596" height="352" border="0" alt="image" />
</p>
<p>
Make sure all your designated hostnames are resolvable - this is a common mistake with Jabber.
</p>
<p>
To run the component you must have installed <a href="http://www.ruby-lang.org" target="_blank">Ruby</a> and installed the <a href="http://home.gna.org/xmpp4r/" target="_blank">XMPP4R</a> package.  This also assumes that you have access to a Jabber server - I use <a href="http://jabberd.jabberstudio.org/2/#download" target="_blank">jabberd2</a>, but there are a number available.  You can find a good list <a href="http://www.jabber.org/software/servers.shtml" target="_blank">here</a>.  <a href="http://ejabberd.jabber.ru/" target="_blank">ejabberd</a> is good universal one, especially if you need to run win32.
</p>
<p>
Once you have access to your server, and have XMPP4R installed, download the component from <a href="http://www.piersharding.com/download/ruby/smtpcomp.tgz">here</a>.       
  Unpack the .tgz file, and then cd into the smtpcomp/ directory.  Here, you need to know the addresses, and port numbers of your landscape to convert them into command line options:
</p>
<p>
<pre>
piers@gecko:~/code/ruby/smtpcomp$ ruby smtpcomponent.rb -h
        Usage: smtpcomponent.rb [-h -s -p -a -c -r -q]
        Options
          -h this help
          -s Jabber server router jid (gecko.local.net) - target Jabber server
          -p Jabber server router port (5347) - target Jabber server port for components
          -a Jabber server router auth phrase (secret) 
          -c Jabber component jid and smtpd name (sapsmtp.local.net) - name of THIS component
          -r SAP system smtpd name (seahorse.local.net) - SMTP host of SAP
          -q SAP system smtpd port (2500) - SMTP port of SAP
  
piers@gecko:~/code/ruby/smtpcomp$ 
</pre>

</p>
<p>
For my setup where I have a Jabber server, gecko.local.net, a component name of sapsmtp.local.net, and an SAP system seahorse.local.net with an SMTP service on port 2500, then the command line options operate like:
</p>
<p>
<pre>
piers@gecko:~/code/ruby/smtpcomp$ sudo ruby smtpcomponent.rb -s gecko.local.net -c sapsmtp.local.net -r seahorse.local.net -q 2500
starting component at: sapsmtp.local.net
SMTPD going to listen on: sapsmtp.local.net/25
gecko.local.net http://jabber.org/protocol/disco#info to sapsmtp.local.net

</pre>
</p>
<p>
Note: the use of sudo at the beginning of the command is to make the script run with root priveledges, as the SMTP interface of smtpcomponent.rb listens on port 25 which requires super user access.  Also - you may need to specify the port for the component to connect to the router on (-p) and a password for authenticating with the route (-a).
</p>
<h4>What happens?</h4>
<p>
To show what happens, the following transcript traces first an R/3 email -> Jabber flow, and then the reverse.  In this example, even though the sending R/3 user account is DEVELOPER, I have configured the INET mail address of the R/3 user to be piers@seahorse.local.net, in the user communication section of address details (transaction SU01).

</p>
<p>
This log section shows the SMTP transcript, the translation of addresses, and then the final construction of an IM message (in XML).
<pre>
COMM: EHLO seahorse.local.net

COMM: MAIL From:<piers@SEAHORSE.LOCAL.NET>

DID a 250 for MAIL 
COMM: RCPT To:<piers@gecko.local.net>

DID a 250 for RCPT 
COMM: DATA

Message: <message from='piers@sapsmtp.local.net' type='normal' to='piers@gecko.local.net'><subject>Is it ready yet!</subject><body>I&apos;ve been waiting all day blah blah blah ....
</body></message>
DID a 250 for data 
COMM: QUIT
</pre>
</p>
<p>
An email was created in R/3 by going to the SAP Office assistant (transaction SO01).
</p>
<p>
<img src="http://www.piersharding.com/download/email.jpg" width="485" height="400" border="0" alt="image" />
</p>
<p>
The IM Message arrives...
</p>
<p>
<img src="http://www.piersharding.com/download/emailim.jpg" width="498" height="310" border="0" alt="image" />
</p>
<p>
Now for the return leg using reply to the original IM Message, which is translated back through the SMTP component and sent to R/3.
</p>
<p>
<pre>
Message from piers@gecko.local.net/Psi to piers@sapsmtp.local.net () subject: Re: Is it ready yet! : "no - stop pestering me"
</pre>
</p>
<p>
<img src="http://www.piersharding.com/download/imemailreply.jpg" width="498" height="310" border="0" alt="image" />
</p>
<p>Email in R/3</p>
<p>
<img src="http://www.piersharidng.com/download/nw4email.jpg" width="413" height="400" border="0" alt="image" />
</p>
<h4>So what does this all mean?</h4>
<p>
What we have here is the ability to push events between R/3, and "A" another endpoint.  By virtue of transport components, these events can traverse protocols, and can be integrated with just about any platform you care to think of because of client programming language support for XMPP (C, Java, Perl, Python, Ruby, Erlang to name some).  To me, this all spells a universal messaging platform that is open, reliable, secure, standards compliant, that is ready to be used as a carrier for business data from alerts, to documents, to workflow events.  It is worth taking a look at <a href="http://www.jabber.org/protocol/" target="_blank">the Jabber Protocols</a> page, which describes what processes, and functionality is currently supported, and what things are in the pipeline.
</p>
<p>
Currently Jabber clients routinely handle URLs, which make a good starting point for relaying Workflow items (integration points for BSPs, EP etc.).  It also has specifications for RPC style encapsulation, and reference implementations for SOAP document transmission.  Beyond  this, Jabber messaging has the potential for embedding workflow objects ala <a href="http://www.sap.com/company/press/factsheets/duet.epx" target="_blank">Duet</a>, to be interpreted by an extension to an existing Jabber client, and I haven't even started on mobile technologies yet.
</p>
<p>
Please note that I have quoted extensively from documentation supplied by the <a href="http://www.jabber.org" target="_blank">JSF</a> throughout, and wish to thank them for that and their continuing efforts in developing, and promoting Jabber and XMPP.
</p>]]></description>
<link>http://www.piersharding.com/blog/archives/2006/10/im_for_sap_with.html</link>
<guid>http://www.piersharding.com/blog/archives/2006/10/im_for_sap_with.html</guid>
<category>ruby</category>
<pubDate>Fri, 06 Oct 2006 11:10:05 +0000</pubDate>
</item>
<item>
<title>saprfc Ruby on Rails with AJAX</title>
<description><![CDATA[Since I wrote an <a href="http://www.piersharding.com/blog/archives/2005/08/sap_on_rails_1.html" target="_blank">article</a> about integration of SAP and <a href="http://www.rubyonrails.com" target="_blank">Ruby on Rails</a>, it has been great to see the beginnings of a kernel of interest.  As a result I decided to package up the sap4rails code and distribute it properly on <a href="http://raa.ruby-lang.org/project/sap4rails/" target="_blank">RAA</a>.
<br>
<h3>Rails and AJAX</h3>
One of the interesting things that Ruby on Rails provides is built AJAX functionality by virtue of an <a href="http://api.rubyonrails.org/classes/ActionView/Helpers/JavaScriptHelper.html" target="_blank">API</a> over <a href="http://prototype.conio.net/" target="_blank">prototype</a>, and <a href="http://script.aculo.us/" target="_blank">Scriptalicious</a>.  In this blog, I would like to show how neatly this integration is implemented in RubyOnRails, using a simple example of Locking, and Unlocking SAP R/3 user accounts.
<br>
<h3>Installation Requirements</h3>
For this example you will need to do your own install of Ruby On Rails.  Use the instructions for installing Ruby, Ruby On Rails, the RFCSDK, and saprfc in the <a href="http://www.piersharding.com/blog/archives/2005/08/sap_on_rails_1.html" target="_blank">RadRails</a> blog.<br>
Just be sure to install versions Ruby 1.8.4+ and Rails 1.1.2+.
<br>
Once you have got this far, the last thing to install is <a href="http://raa.ruby-lang.org/project/sap4rails/" target="_blank">sap4rails</a>.  You can either install the <a href="http://www.piersharding.com/download/ruby/rails/">source package</a> or download the <a href="http://www.piersharding.com/download/ruby/rails/">gem</a>, and install this with:<br>
 <pre>gem install sap4rails-&lt;version&gt;.gem</pre>.  
<h3>UserAdmin</h3>
For this example, most of the standard BAPIs are adequate.  We need to be able to list users, with their details, including their lock state.  We also need to be able to lock and unlock them.
<br>
The general functionality of the application is to create two lists of users
on a page - locked and unlocked - and for you to be able to drag a user from
one to the other to change their lock state in SAP.
<br>
<p>
The following RFCs are used:
<ul>
<li>BAPI_USER_GETLIST - list users, and their address details </li>
<li>BAPI_USER_LOCK</li>
<li>BAPI_USER_UNLOCK</li>
</ul>
</p>
<p>
BAPI_USER_GETLIST is not quite enough.  This, I have had to wrap in another function module and also modify the results table to include the lock status information of users.
<br>
Create a new function module called Z_BAPI_USER_GETLIST, and make sure that
you activate it for RFC on the attributes tab (in SE37) <a href="http://www.piersharding.com/download/ruby/rails/z_bapi_user_getlist.txt" target="_blank">(code)</a>.
<br>
Create a new structure called ZBAPIUSNAME, and include the two structures BAPIUSNAME, and USLOCK like this <a href="http://www.piersharding.com/download/ruby/rails/zbapiusname.png" target="_blank"><img src="http://www.piersharding.com/download/ruby/rails/zbapiusname.thumb.png" /></a>.
<br>
  Make sure that you activate the structure.
</p>
<br>
<p>
The code and interface needs to be completed like this:
<pre>
FUNCTION Z_BAPI_USER_GETLIST.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     VALUE(MAX_ROWS) TYPE  BAPIUSMISC-BAPIMAXROW DEFAULT 0
*"     VALUE(WITH_USERNAME) TYPE  BAPIUSMISC-WITH_NAME DEFAULT SPACE
*"  EXPORTING
*"     VALUE(ROWS) TYPE  BAPIUSMISC-BAPIROWS
*"  TABLES
*"      SELECTION_RANGE STRUCTURE  BAPIUSSRGE OPTIONAL
*"      SELECTION_EXP STRUCTURE  BAPIUSSEXP OPTIONAL
*"      USERLISTLOCK STRUCTURE  ZBAPIUSNAME OPTIONAL
*"      RETURN STRUCTURE  BAPIRET2 OPTIONAL
*"----------------------------------------------------------------------
*
data:
  LOCKSTATE LIKE  USLOCK,
  userlist like bapiusname occurs 0 with header line.

  refresh userlistlock.


  CALL FUNCTION 'BAPI_USER_GETLIST'
    EXPORTING
      MAX_ROWS              = 0
      WITH_USERNAME         = with_username
    IMPORTING
      ROWS                  = rows
    TABLES
      SELECTION_RANGE       = selection_range
      SELECTION_EXP         = selection_exp
      USERLIST              = userlist
      RETURN                = return
            .

  loop at userlist.
    move-corresponding: userlist to userlistlock.
    if userlistlock-firstname = space.
       userlistlock-firstname = userlistlock-username.
    endif.

    CALL FUNCTION 'SUSR_USER_LOCKSTATE_GET'
      EXPORTING
        USER_NAME                 = userlist-username
      IMPORTING
        LOCKSTATE                 = lockstate
      EXCEPTIONS
        USER_NAME_NOT_EXIST       = 1
        OTHERS                    = 2
              .

    move-corresponding: lockstate to userlistlock.
    append userlistlock.

  endloop.

ENDFUNCTION.
</pre>
</p>
<p>
Activate the function module and test it.
</p>

<h3>The Rails part</h3>
<p>
The full application can be downloaded from <a href="http://www.piersharding.com/download/ruby/rails/useradmin.tar.gz">here</a> - but what I'd
like to do is quickly describe the meat of what had to be done to get this
type of application working.
</p>
<h4>Config - sap.yml</h4>
<p>
As described in the <a href="https://weblogs.sdn.sap.com/pub/wlg/3516" target="_blank">RadRails</a> blog, you need to adjust the configuration in
config/sap.yml to point to your SAP system:
</p>
<p>
<pre>
development:
  ashost: 10.1.1.1
  sysnr: "00"
  client: "010"
  user: developer
  passwd: developer
  lang: EN
  trace: "1"
...
</pre>
</p>


<h4>Model - sap_user.rb</h4>
<p>
The SapUser object now inherits from the new SAP4Rails::Base class.  This
serves to automatically take care of managing RFC connections based on the
config done above.  The two main class methods for use are function_module,
which allows you to declare what RFCs you want to use, and parameter which is
a helper method for declaring attributes of a SapUser (or any other Model
object).
<br>
In the interests of simplifying the application, by reducing the amount of
ABAP code to be written, and the number of RFC calls to be made, I have in a
way "cheated", with the arrangement of methods defined in SapUser.  Instead of
having a SapUser#find method, I rely on the use of SapUser#find_all and
SapUser#find_cache to reduce a series of SapUser searches down to one RFC call
only.  In reality this is probably not good practice, but it suits for this
example.
<br>
<p>
Read the code comments below for further details:
</p>
<pre>
require_gem "sap4rails"

class SapUser < SAP4Rails::Base

# You must define a list of RFCs to preload
  function_module :Z_BAPI_USER_GETLIST,
	                :BAPI_USER_LOCK,
	                :BAPI_USER_UNLOCK

# You must define a list of attribute accessors to preload
  parameter :last, :first, :userid, :locked

# do your attribute initialisation for each SapUser instance
	def initialize(last, first, userid, locked)
	  @last = last
	  @first = first
	  @userid = userid
	  @locked = locked
	  @changed = false
	end

# what is the lock state
	def locked?
	  return self.locked ? true : false
	end

# on #save - flip the lock state of the SapUser, calling the 
# appropriate RFC to do it
  def save()
    RAILS_DEFAULT_LOGGER.warn("[SapUser]#save what did we get: " + self.inspect)
   
    if self.locked?
      SapUser.BAPI_USER_LOCK.reset()
      SapUser.BAPI_USER_LOCK.username.value = self.userid
      SapUser.BAPI_USER_LOCK.call()
    else
      SapUser.BAPI_USER_UNLOCK.reset()
      SapUser.BAPI_USER_UNLOCK.username.value = self.userid
      SapUser.BAPI_USER_UNLOCK.call()
    end

    # just so something happens ...
    return true
  end

# one RFC call to get them all
  def self.find_all
    RAILS_DEFAULT_LOGGER.warn("[SapUser]#find_all ")
    SapUser.Z_BAPI_USER_GETLIST.reset()
    SapUser.Z_BAPI_USER_GETLIST.with_username.value = 'X'
    SapUser.Z_BAPI_USER_GETLIST.call()
    users = []
    SapUser.Z_BAPI_USER_GETLIST.userlistlock.rows().each {|row|
		  next if row['FIRSTNAME'].strip.length == 0
		  state = nil
		  if row['WRNG_LOGON'] == "L" || 
                     row['LOCAL_LOCK'] == "L" || 
                     row['GLOB_LOCK'] == "L"
		    state = true
	          else
		    state = false
		  end
		  users.push(SapUser.new(row['LASTNAME'], 
			                 row['FIRSTNAME'], 
				         row['USERNAME'], 
					 state))
		}
    return users
  end

# find a user base on the results of a SapUser#find_all
  def self.find_cache(user, cache)
    RAILS_DEFAULT_LOGGER.warn("[SapUser]#find_cache: #{user} ")
    cache.each{|row|
	  return row if user.strip == row.userid.strip
    }
  end

# get a list of all the locked users
  def self.find_locked
    RAILS_DEFAULT_LOGGER.warn("[SapUser]#find_locked ")
    locked = []
    find_all().each{|user|
	  locked.push(user) if user.locked
    }
    return locked
  end

# get a list of all the unlocked users
  def self.find_unlocked
    RAILS_DEFAULT_LOGGER.warn("[SapUser]#find_unlocked ")
    unlocked = []
    find_all().each{|user|
	  unlocked.push(user) unless user.locked
    }
    return unlocked
  end
end
</pre>

<p>

<h4>Controller - lock_controller.rb</h4>
There are only 3 basic actions to the only controller in this application.
The initial list action, build the starting page presenting the two lists of
users (locked and unlocked).  From there, as a result of the AJAX enabled
calls from the dragndrop feature, two further actions are called - set_locked
and set_unlocked.
</p>
<pre>

class LockController < ApplicationController

# gnerate the starting user lists, and hand off to the default list view
  def list
    RAILS_DEFAULT_LOGGER.warn("[LIST] Parameters: " + @params.inspect)
    @locked_users = SapUser.find_locked()
    @unlocked_users = SapUser.find_unlocked()
    RAILS_DEFAULT_LOGGER.warn("[LIST] of Locked: " + @locked_users.inspect)
    RAILS_DEFAULT_LOGGER.warn("[LIST] of UNLocked: " + @unlocked_users.inspect)
  end

# check through the list of locked users in the locked sortable_element box
# and set their locked state in necessary
# on completion - render the partial locked_users
  def set_locked
    RAILS_DEFAULT_LOGGER.warn("[SET_LOCKED] Parameters: " + @params.inspect)
    @locked_users = []
    cache = SapUser.find_all()
    if @params['locked_box']
      @params['locked_box'].each {|locked|
	  next if locked.length == 0
          user = SapUser.find_cache(locked, cache)
	  next if user.first.strip.length == 0
	  if user && ! user.locked?
  	    user.locked = !user.locked?
   	    user.save
          end
          @locked_users.push(user)
      }
    end
    render :partial => 'locked_users', :object => locked_users
  end

# exact opposite/the same as set_locked
  def set_unlocked
    RAILS_DEFAULT_LOGGER.warn("[SET_UNLOCKED] Parameters: " + @params.inspect)
    @unlocked_users = []
    cache = SapUser.find_all()
    if @params['unlocked_box']
      @params['unlocked_box'].each {|unlocked|
	  next unless unlocked.length > 0
	  user = SapUser.find_cache(unlocked, cache)
	  next if user.first.strip.length == 0
	  if user && user.locked?
	    user.locked = !user.locked?
  	    user.save
  	  end
	  @unlocked_users.push(user)
      }
    end
    render :partial => 'unlocked_users', :object => unlocked_users
  end

# called by the rendering action of the partial locked_users
  def locked_users
    RAILS_DEFAULT_LOGGER.warn("[LOCKED_USERS] of Locked: " + @locked_users.inspect)
    @locked_users
  end

# called by the rendering action of the partial unlocked_users
  def unlocked_users
    RAILS_DEFAULT_LOGGER.warn("[UNLOCKED_USERS] of UNLocked: " + @unlocked_users.inspect)
    @unlocked_users
  end

end
</pre>
<p>
<h4>Views </h4>
The the overall page template (layout) defines the shape of the page, and what
JavaScript libraries are pulled in for the effects (AJAX).  All pages inherit from this.
</p>
<p>
<b>layout/application.rhtml</b>
</p>
<pre>
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;
  &lt;head&gt;
    &lt;meta http-equiv="Content-type" content="text/html; charset=utf-8" /&gt;
    &lt;title&gt;User Administration:  &lt;%= controller.controller_name %&gt;&lt;/title&gt;
    &lt;meta http-equiv="imagetoolbar" content="no" /&gt;
    &lt;%= stylesheet_link_tag "administration.css" %&gt;
    &lt;%= javascript_include_tag "prototype", "effects", "dragdrop",
                       "controls" %&gt;
  &lt;/head&gt;

  &lt;body&gt;
  &lt;div id="container"&gt;
    &lt;div id="header"&gt;
    &lt;div id="info"&gt;
      &lt;%= link_to "home", :controller=&gt; "/lock", :action =&gt; 'list' %&gt;
    &lt;/div&gt;
      &lt;h1&gt;&lt;%= link_to "UserAdmin - #{controller.controller_name}", :controller =&gt; "/lock" %&gt;
        &lt;/h1&gt;
    &lt;/div&gt;
    &lt;div id="content"&gt;
      &lt;h2&gt;&lt;%= @page_heading %&gt;&lt;/h2&gt;
      &lt;%= @content_for_layout %&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;

</pre>

<p>
<b> lock/list.rhtml</b>
</p>
<p>
The two most important things in list are the two container div tags -
unlocked_box and locked_box.  These in turn, have a corresponding partial
(unlocked_users, and locked_users), that are responsible for generating the
dragable user items.
</p>
<pre>
&lt;% @heading = "User Admin - Lock/UnLock" %&gt;

  &lt;div id="user-admin"&gt;
    &lt;div id="unlocked" class="dropbox"&gt;
      &lt;h3&gt;Unlocked Users&lt;/h3&gt;
      &lt;div id="unlocked_box"&gt;
        &lt;%= render :partial =&gt; 'unlocked_users', :object =&gt; @unlocked_users %&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div id="cnt-locked" class="dropbox"&gt;
      &lt;h3&gt;Locked Users&lt;/h3&gt;
      &lt;div id="locked_box"&gt;
        &lt;%= render :partial =&gt; 'locked_users', :object =&gt; @locked_users %&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;br clear="all" /&gt;

  &lt;/div&gt;

</pre>

<p><b> lock/_unlocked_users.rhtml</b></p>
<p>the partial unlocked_users either displays a place holder element if there
are no users, or calls the render of unlocked_user for each user.
It also uses the AJAX function sortable_element which dictates what div
container holds the sortable drag and drop elements, and what actions to take
when an event is fired with them.  This is how we trigger the call to the
set_unlocked or set_locked action of the list  controller for updating the
individual "boxes" of users.
</p>
<pre>

&lt;% if unlocked_users.empty? %&gt;
  &lt;div class="target"&gt;  You have no Unlocked SAP Users.... &lt;/div&gt;
&lt;% else %&gt;
  &lt;%= render :partial =&gt; 'unlocked_user', :collection =&gt; unlocked_users %&gt;
&lt;% end %&gt;

&lt;%= sortable_element "unlocked_box",
  :update =&gt; "unlocked_box",
  :url =&gt; {:action=&gt;'set_unlocked'},
  :tag =&gt; 'div', :handle =&gt; 'handle', :containment =&gt; ['unlocked_box','locked_box'] %&gt;

&lt;%= sortable_element "locked_box",
  :update =&gt; "locked_box",
  :url=&gt; {:action=&gt;'set_locked'},
  :tag =&gt; 'div', :handle =&gt; 'handle', :containment =&gt; ['locked_box','unlocked_box'] %&gt;

</pre>


<p><b>lock/_unlocked_user.rhtml</b></p>
<p>
the partial unlocked_user renders a dragble_element for each SapUser.
</p>
<pre>

&lt;div id="unlockeduser_&lt;%= unlocked_user.userid %&gt;" class="dragitem"&gt;
  &lt;h4 class="handle"&gt;&lt;%= unlocked_user.userid %&gt;&lt;/h4&gt;
  &lt;p&gt;&lt;%= unlocked_user.last + ", " + unlocked_user.first %&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;%= draggable_element "unlockeduser_#{unlocked_user.userid}" %&gt;

</pre>


<p><b>lock/_locked_users.rhtml</b></p>
<p> the same as for the partial unlocked_users</p>
<pre>
&lt;% if locked_users.empty? %&gt;
  &lt;div class="target"&gt;  You have no Locked Users ...  &lt;/div&gt;
&lt;% else %&gt;
 &lt;%= render :partial =&gt; 'locked_user', :collection =&gt; locked_users %&gt;
&lt;% end %&gt;

&lt;%= sortable_element "unlocked_box",
  :update =&gt; "unlocked_box",
  :url =&gt; {:action=&gt;'set_unlocked'},
  :tag =&gt; 'div', :handle =&gt; 'handle', :containment =&gt; ['unlocked_box','locked_box'] %&gt;

&lt;%= sortable_element "locked_box",
  :update =&gt; "locked_box",
  :url=&gt; {:action=&gt;'set_locked'}, 
  :tag =&gt; 'div', :handle =&gt; 'handle', :containment =&gt; ['locked_box','unlocked_box'] %&gt;

</pre>


<p><b>lock/_locked_user.rhtml</b></p>
<p> the same as for the partial unlocked_user</p>
<pre>
&lt;div id="lockeduser_&lt;%= locked_user.userid %&gt;" class="dragitem"&gt;
  &lt;h4 class="handle"&gt;&lt;%= locked_user.userid %&gt;&lt;/h4&gt;
  &lt;p&gt;&lt;%= locked_user.last + ", " + locked_user.first %&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;%= draggable_element "lockeduser_#{locked_user.userid}" %&gt;

</pre>

<p>
In config/routes.rb
add:
<pre>
 map.connect '', :controller => "lock", :action => 'list'
 </pre>

and make sure that you delete public/index.html
</P>
<p>
This makes sure that any requests to the root of the server eg.
http://localhost:3000, are forwarded onto the list action of the lock
controller.
</p>

<h4>firing Up</h4>
<p>
Start the Rails WEBrick server by running the script:
<pre>ruby scripts/server</pre>
Open your broswer and point to http://localhost:3000.
</p>
<p>
When you connect to http://localhost:3000 (there will be an inital delay as
sap4rails caches the RFC calls), you should get a screen that looks like this <br> <a href="http://www.piersharding.com/download/ruby/rails/useradmin.png" target="_blank"><img src="http://www.piersharding.com/download/ruby/rails/useradmin.thumb.png" /></a>
</p>
<p>
A Flash movie of this in action can be seen <a href="http://www.piersharding.com/download/ruby/rails/useradmin.html">here</a>.
</p>

]]></description>
<link>http://www.piersharding.com/blog/archives/2006/09/saprfc_ruby_on.html</link>
<guid>http://www.piersharding.com/blog/archives/2006/09/saprfc_ruby_on.html</guid>
<category>rails</category>
<pubDate>Fri, 29 Sep 2006 11:08:40 +0000</pubDate>
</item>


</channel>
</rss>