nwrfc 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +85 -0
- data/Rakefile +8 -0
- data/lib/dev_rfc.trc +133 -0
- data/lib/nwrfc.old.rb +338 -0
- data/lib/nwrfc.rb +409 -0
- data/lib/nwrfc/nwrfclib.rb +449 -0
- metadata +71 -0
data/README.rdoc
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
= NWRFC - Wrapper for SAP Netweaver RFC SDK using Ruby-FFI
|
2
|
+
|
3
|
+
This library provides a wrapper around the sapnwrfc shared library provided in the SAP Netweaver RFC SDK using Ruby-FFI.
|
4
|
+
|
5
|
+
The NW RFC SDK allows you to call remote-enabled function modules on an ABAP server (referred to as RFC,
|
6
|
+
which stands for "Remote Function Call").
|
7
|
+
|
8
|
+
To use this library, you must have the nwrfcsdk (libsapnwrfc.so / sapnwrfc.dll) library
|
9
|
+
and related libraries somewhere in your path.
|
10
|
+
|
11
|
+
I am developing the library using the 7.20 patch level 2 version of the NW RFC SDK. I have used 7.11, but found for instance
|
12
|
+
that it suffers from the bug described in note 1058327, where RfcGetStringLength() returns the incorrect length
|
13
|
+
of the string if it is longer than 255 characters (supposed to have been fixed in 7.10 patch 2).
|
14
|
+
|
15
|
+
== Issues
|
16
|
+
|
17
|
+
Ruby-FFI does not seem to be able to take string encoding into consideration, so for example, calling RfcGetVersion()
|
18
|
+
is problematic, because the returned string pointer has (like all NW RFC SDK functions) UTF-16LE encoding,
|
19
|
+
and FFI does not seem to be able to work with this. So far this is not too problematic, but let's see.
|
20
|
+
|
21
|
+
== Obtaining the Netweaver RFC shared library
|
22
|
+
The Netweaver RFC SDK libraries are available from SAP. You cannot, unfortunately, obtain these as a public user; you need to have access via a customer account to download them
|
23
|
+
from SAP Service Marketplace (http://service.sap.com)
|
24
|
+
|
25
|
+
Alternatively, you can download and install one of the Netweaver Trial Editions from SDN (requires signup): http://www.sdn.sap.com/irj/scn/downloads
|
26
|
+
|
27
|
+
After installation, the files are available in /usr/sap/<sysid>/exe
|
28
|
+
|
29
|
+
== Usage
|
30
|
+
|
31
|
+
Connecting to the SAP system:
|
32
|
+
|
33
|
+
require 'rubygems'
|
34
|
+
|
35
|
+
gem 'nwrfc'
|
36
|
+
include NWRFC
|
37
|
+
|
38
|
+
c = Connection.new {"user" => "martin", "passwd" => "secret",
|
39
|
+
"client" => "100", "ashost" => "ajax.domain.com", "sysnr" => "00"}
|
40
|
+
|
41
|
+
Calling a function:
|
42
|
+
|
43
|
+
f = c.get_function("STFC_STRUCTURE") #Obtain description of a function module
|
44
|
+
fc = f.get_function_call
|
45
|
+
f.invoke
|
46
|
+
|
47
|
+
Setting and getting parameters and fields:
|
48
|
+
|
49
|
+
import_struc = fc[:IMPORTSTRUC]
|
50
|
+
import_struc[:RFCCHAR1] = 'a'
|
51
|
+
|
52
|
+
== Installation
|
53
|
+
|
54
|
+
On Linux:
|
55
|
+
|
56
|
+
sudo gem install nwrfc
|
57
|
+
|
58
|
+
On Windows
|
59
|
+
|
60
|
+
gem install nwrfc
|
61
|
+
|
62
|
+
== Running the tests
|
63
|
+
The test are located in the tests/ directory. The file `login_params.yaml` contains parameters that you will need
|
64
|
+
to customize to log on to your local system that you are testing with. The YAML file contains parameters for multiple
|
65
|
+
systems, so if you want to switch to a different system, change the following line in `test_nwrfc.rb`:
|
66
|
+
|
67
|
+
$login_params = YAML.load_file(File.dirname(__FILE__) + "/login_params.yaml")["system2"]
|
68
|
+
|
69
|
+
so that ["system2"] at the end points to whatever label you gave it in the YAML file, then
|
70
|
+
|
71
|
+
rake test
|
72
|
+
|
73
|
+
== Release Notes
|
74
|
+
|
75
|
+
=== What's new in 0.0.0
|
76
|
+
|
77
|
+
* Able to call functions
|
78
|
+
* Most types work,
|
79
|
+
* Getting list of fields from a structure causes a SEGFAULT
|
80
|
+
|
81
|
+
== Contributing
|
82
|
+
|
83
|
+
Improvements to the source code are welcome. Email me at martin dot ceronio at infosize dot co dot za.
|
84
|
+
|
85
|
+
Also let me know, please on which platforms you have tested the gem.
|
data/Rakefile
ADDED
data/lib/dev_rfc.trc
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
|
2
|
+
**** Trace file opened at 2012-02-15, 13:50:29 SAST
|
3
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
4
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
5
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
6
|
+
|
7
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
8
|
+
|
9
|
+
**** Trace file opened at 2012-02-15, 13:51:02 SAST
|
10
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
11
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
12
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
13
|
+
|
14
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
15
|
+
|
16
|
+
**** Trace file opened at 2012-02-15, 14:07:53 SAST
|
17
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
18
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
19
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
20
|
+
|
21
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
22
|
+
|
23
|
+
**** Trace file opened at 2012-02-15, 14:08:23 SAST
|
24
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
25
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
26
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
27
|
+
|
28
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
29
|
+
|
30
|
+
**** Trace file opened at 2012-02-15, 14:09:49 SAST
|
31
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
32
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
33
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
34
|
+
|
35
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
36
|
+
|
37
|
+
**** Trace file opened at 2012-02-15, 14:40:33 SAST
|
38
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb1
|
39
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
40
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
41
|
+
|
42
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
43
|
+
|
44
|
+
**** Trace file opened at 2012-02-15, 14:42:34 SAST
|
45
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
46
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
47
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
48
|
+
|
49
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
50
|
+
|
51
|
+
**** Trace file opened at 2012-02-15, 14:50:05 SAST
|
52
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
53
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
54
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
55
|
+
|
56
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
57
|
+
|
58
|
+
**** Trace file opened at 2012-02-15, 15:19:16 SAST
|
59
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
60
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
61
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
62
|
+
|
63
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
64
|
+
|
65
|
+
**** Trace file opened at 2012-02-15, 15:19:42 SAST
|
66
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
67
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
68
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
69
|
+
|
70
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
71
|
+
|
72
|
+
**** Trace file opened at 2012-02-15, 15:20:37 SAST
|
73
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
74
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
75
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
76
|
+
|
77
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
78
|
+
|
79
|
+
**** Trace file opened at 2012-02-15, 15:21:59 SAST
|
80
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
81
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
82
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
83
|
+
|
84
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
85
|
+
|
86
|
+
**** Trace file opened at 2012-02-15, 15:23:05 SAST
|
87
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
88
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
89
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
90
|
+
|
91
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
92
|
+
|
93
|
+
**** Trace file opened at 2012-02-15, 15:25:42 SAST
|
94
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
95
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
96
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
97
|
+
|
98
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
99
|
+
|
100
|
+
**** Trace file opened at 2012-02-15, 15:26:25 SAST
|
101
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
102
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
103
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
104
|
+
|
105
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
106
|
+
|
107
|
+
**** Trace file opened at 2012-02-15, 22:23:25 SAST
|
108
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
109
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
110
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
111
|
+
|
112
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
113
|
+
|
114
|
+
**** Trace file opened at 2012-02-15, 22:23:59 SAST
|
115
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
116
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
117
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
118
|
+
|
119
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
120
|
+
|
121
|
+
**** Trace file opened at 2012-02-15, 22:27:38 SAST
|
122
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
123
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
124
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
125
|
+
|
126
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
127
|
+
|
128
|
+
**** Trace file opened at 2012-02-15, 22:28:08 SAST
|
129
|
+
RFC library: 720, Current working directory: /home/martin/Development/ruby/sapnwrfc/lib, Program: irb
|
130
|
+
Hardware: Intel x86 with Linux i686, Operating_system: Linux 3.0.0-14-generic-pae, Kernel_release: 720 patchlevel 2
|
131
|
+
Hostname: martin-laptop, IP address: 127.0.1.1, IP address_v6: 127.0.1.1
|
132
|
+
|
133
|
+
ERROR pfuuid_init returns error -> UUIDs cannot be generated
|
data/lib/nwrfc.old.rb
ADDED
@@ -0,0 +1,338 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/nwrfc/nwrfclib'
|
2
|
+
|
3
|
+
# This library provides a way to call the functions of the SAP Netweaver RFC
|
4
|
+
# SDK, i.e. opening a connection to an ABAP system, calling functions etc., as
|
5
|
+
# well as running an RFC service
|
6
|
+
#---
|
7
|
+
# *TODO*: Create an error class that wraps the SAP error struct, so it can
|
8
|
+
# be raised and the caller can get all the information from there
|
9
|
+
#+++
|
10
|
+
|
11
|
+
module NWRFC
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
self.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
def NWRFC.check_error(error_handle)
|
18
|
+
raise "Error code #{error_handle[:code]} group #{error_handle[:group]} message #{error_handle[:message].get_str}" \
|
19
|
+
if error_handle[:code] > 0
|
20
|
+
end
|
21
|
+
|
22
|
+
# Representation of a data container (function, structure or table)
|
23
|
+
module DataContainer
|
24
|
+
|
25
|
+
def initialize(handle)
|
26
|
+
@handle = handle
|
27
|
+
@error = NWRFCLib::RFCError.new
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return the member specified by string or symbol
|
31
|
+
def [](element)
|
32
|
+
element = element.upcase
|
33
|
+
metadata = member_metadata(element)
|
34
|
+
case metadata[:type]
|
35
|
+
when :RFCTYPE_CHAR
|
36
|
+
# TODO: Try use :string parameter in get_chars
|
37
|
+
size = metadata[:ucLength]
|
38
|
+
cb = FFI::MemoryPointer.new :char, size
|
39
|
+
NWRFCLib.get_chars(@handle, element.cU, cb, metadata[:nucLength], @error.to_ptr)
|
40
|
+
value = cb.read_string(size).uC
|
41
|
+
when :RFCTYPE_DATE
|
42
|
+
when :RFCTYPE_BCD
|
43
|
+
when :RFCTYPE_TIME
|
44
|
+
when :RFCTYPE_BYTE
|
45
|
+
when :RFCTYPE_TABLE
|
46
|
+
value = get_data_container(element, :table)
|
47
|
+
when :RFCTYPE_NUM
|
48
|
+
when :RFCTYPE_FLOAT
|
49
|
+
when :RFCTYPE_INT
|
50
|
+
when :RFCTYPE_INT2
|
51
|
+
when :RFCTYPE_INT1
|
52
|
+
when :RFCTYPE_NULL
|
53
|
+
when :RFCTYPE_STRUCTURE
|
54
|
+
value = get_data_container(element, :structure)
|
55
|
+
when :RFCTYPE_DECF16
|
56
|
+
when :RFCTYPE_DECF34
|
57
|
+
when :RFCTYPE_XMLDATA
|
58
|
+
when :RFCTYPE_STRING
|
59
|
+
when :RFCTYPE_XSTRING
|
60
|
+
else
|
61
|
+
raise "Illegal member type #{metadata[:type]}"
|
62
|
+
end
|
63
|
+
NWRFC.check_error(@error)
|
64
|
+
value
|
65
|
+
end
|
66
|
+
|
67
|
+
def []=(element, value)
|
68
|
+
metadata = member_metadata(element)
|
69
|
+
case member_metadata[element][:type]
|
70
|
+
when :RFCTYPE_CHAR
|
71
|
+
NWRFCLib.set_chars(@handle, element.cU, value.cU, value.length, @error.to_ptr)
|
72
|
+
when :RFCTYPE_DATE
|
73
|
+
when :RFCTYPE_BCD
|
74
|
+
when :RFCTYPE_TIME
|
75
|
+
when :RFCTYPE_BYTE
|
76
|
+
when :RFCTYPE_TABLE
|
77
|
+
when :RFCTYPE_NUM
|
78
|
+
when :RFCTYPE_FLOAT
|
79
|
+
when :RFCTYPE_INT
|
80
|
+
when :RFCTYPE_INT2
|
81
|
+
when :RFCTYPE_INT1
|
82
|
+
when :RFCTYPE_NULL
|
83
|
+
when :RFCTYPE_STRUCTURE
|
84
|
+
when :RFCTYPE_DECF16
|
85
|
+
when :RFCTYPE_DECF34
|
86
|
+
when :RFCTYPE_XMLDATA
|
87
|
+
when :RFCTYPE_STRING
|
88
|
+
when :RFCTYPE_XSTRING
|
89
|
+
else
|
90
|
+
raise "Illegal member type #{@members[:type]}"
|
91
|
+
end
|
92
|
+
NWRFC.check_error(@error)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Get the metadata of a member (function, structure or table)
|
96
|
+
def member_metadata(member)
|
97
|
+
if self.class == NWRFC::FunctionCall
|
98
|
+
self.function_description.member_metadata(member)
|
99
|
+
elsif self.class.instance_of?(Table) || self.class.instance_of?(Structure)
|
100
|
+
NWRFCLib.get_field_desc_by_name(@handle, member.uC, )
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
# Returns the subset of metadata values common to both a function parameter
|
107
|
+
# and a type field
|
108
|
+
def member_to_hash(member)
|
109
|
+
{
|
110
|
+
:type => NWRFCLib::RFCTYPE[member[:type]],
|
111
|
+
:nucLength => member[:nucLength],
|
112
|
+
:ucLength => member[:ucLength],
|
113
|
+
:decimals => member[:decimals],
|
114
|
+
:typeDescHandle => member[:typeDescHandle]
|
115
|
+
}
|
116
|
+
end
|
117
|
+
|
118
|
+
# Return either table or structure representing the element given
|
119
|
+
# from the current data container
|
120
|
+
def get_data_container(element, type)
|
121
|
+
datac = FFI::MemoryPointer.new NWRFCLib::RFCDataContainer
|
122
|
+
if type == :table
|
123
|
+
NWRFCLib.get_table(@handle, element.cU, datac, @error.to_ptr)
|
124
|
+
@components[element] = Table.new(datac)
|
125
|
+
end
|
126
|
+
if type == :structure
|
127
|
+
NWRFCLib.get_structure(@handle, element.cU, datac, @error.to_ptr)
|
128
|
+
@components[element] = Structure.new(datac)
|
129
|
+
end
|
130
|
+
NWRFC.check_error(@error)
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
# Represents a connection to a SAP system that can be used to invoke
|
136
|
+
# remote-enabled functions
|
137
|
+
class Connection
|
138
|
+
|
139
|
+
attr_reader :handle, :error
|
140
|
+
|
141
|
+
# Opens a connection to the SAP system with the given connection parameters
|
142
|
+
# (described in the NW RFC SDK document), passed in the form of a Hash, e.g.
|
143
|
+
# Connection.new { 'ashost' :=> 'ajax.domain.com', ... }
|
144
|
+
def initialize(conn_params)
|
145
|
+
conn_params.untaint #For params loaded from file, e.g.
|
146
|
+
raise "Connection parameters must be a Hash" unless conn_params.instance_of? Hash
|
147
|
+
#NWRFCLib.init
|
148
|
+
@cparams = NWRFCLib.make_conn_params(conn_params)
|
149
|
+
raise "Could not create valid pointer from parameters" unless @cparams.instance_of? FFI::MemoryPointer
|
150
|
+
#@errp = FFI::MemoryPointer.new(NWRFCLib::RFCError)
|
151
|
+
@error = NWRFCLib::RFCError.new #@errp
|
152
|
+
@handle = NWRFCLib.open_connection(@cparams, conn_params.length, @error.to_ptr)
|
153
|
+
NWRFC.check_error(@error)
|
154
|
+
self
|
155
|
+
end
|
156
|
+
|
157
|
+
# Call the NW RFC SDK's RfcCloseConnection() function with the current
|
158
|
+
# connection; this (should - *TODO* - check) invalidate the connection handle
|
159
|
+
# and cause an error on any subsequent use of this connection
|
160
|
+
def disconnect
|
161
|
+
NWRFCLib.close_connection(@handle, @error.to_ptr)
|
162
|
+
NWRFC.check_error(@error)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Return details about the current connection and the system
|
166
|
+
def connection_info
|
167
|
+
return @get_connection_attributes if @get_connection_attributes
|
168
|
+
conn_info = NWRFCLib::RFCConnection.new
|
169
|
+
NWRFCLib.get_connection_attributes(@handle, conn_info.to_ptr, @error.to_ptr)
|
170
|
+
check_error
|
171
|
+
@get_connection_attributes = conn_info.members.inject({}) {|hash, member|
|
172
|
+
hash[member] = conn_info[member].get_str #get_str, own definition in nwrfclib.rb, FFI::StructLayout::CharArray#get_str
|
173
|
+
hash
|
174
|
+
}
|
175
|
+
end
|
176
|
+
|
177
|
+
def get_function(func_name)
|
178
|
+
Function.new(self, func_name)
|
179
|
+
end
|
180
|
+
|
181
|
+
# Check the status of the error structure; raise an exception if it
|
182
|
+
# contains an error
|
183
|
+
# *TODO* - Raise a more meaningful error
|
184
|
+
def check_error
|
185
|
+
raise "Error code #{@error[:code]} group #{@error[:group]}" if @error[:code] > 0
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
# Represents a remote-enabled function module for RFC, can be instantiated either by the caller
|
191
|
+
# or by calling Connection#get_function. This only represents the description of the function;
|
192
|
+
# to call a function, an instance of a function call must be obtained with #get_function_call
|
193
|
+
#---
|
194
|
+
# FIXME: We are using a shared error object in the connection, so what happens in a
|
195
|
+
# multi-threaded situation? Functions should each have their own error handle
|
196
|
+
#+++
|
197
|
+
class Function
|
198
|
+
attr_reader :func_desc, :connection, :function_name, :members
|
199
|
+
|
200
|
+
# Get a function module instance; can also be obtained by calling Connection#get_function
|
201
|
+
def initialize(connection, func_name)
|
202
|
+
@connection = connection
|
203
|
+
@func_desc = NWRFCLib.get_function_desc(@connection.handle, func_name.cU, @connection.error.to_ptr)
|
204
|
+
NWRFC.check_error(@connection.error)
|
205
|
+
@function_name = func_name
|
206
|
+
@error = NWRFCLib::RFCError.new
|
207
|
+
@members = self_describe # Load the metadata of the function description
|
208
|
+
end
|
209
|
+
|
210
|
+
# Create and return a callable instance of this function module
|
211
|
+
def get_function_call
|
212
|
+
FunctionCall.new(self)
|
213
|
+
end
|
214
|
+
|
215
|
+
def parameter_count
|
216
|
+
pcount = FFI::MemoryPointer.new(:uint)
|
217
|
+
NWRFCLib.get_parameter_count(@func_desc, pcount, @error.to_ptr)
|
218
|
+
NWRFC.check_error(@error)
|
219
|
+
pcount.read_uint
|
220
|
+
end
|
221
|
+
|
222
|
+
# Returns the definitions of parameters associated with this function module (lazy loading)
|
223
|
+
def parameters
|
224
|
+
@members
|
225
|
+
end
|
226
|
+
|
227
|
+
def [](param)
|
228
|
+
member_metadata(param)
|
229
|
+
end
|
230
|
+
|
231
|
+
# Look up parameter by method name given, returns parameter instance
|
232
|
+
# if it refers to a valid parameter
|
233
|
+
def method_missing(method_sym, *args, &block)
|
234
|
+
param_name = method_sym.to_s.upcase
|
235
|
+
fpar = NWRFCLib::RFCFuncParam.new
|
236
|
+
rc = NWRFCLib.get_parameter_desc_by_name(@func_desc, param_name.cU, fpar.to_ptr, @error.to_ptr)
|
237
|
+
NWRFC.check_error(@error) if rc > 0
|
238
|
+
parameter_to_hash(fpar)
|
239
|
+
end
|
240
|
+
|
241
|
+
def member_metadata(param)
|
242
|
+
param_name = param.to_s.upcase
|
243
|
+
fpar = NWRFCLib::RFCFuncParam.new
|
244
|
+
rc = NWRFCLib.get_parameter_desc_by_name(@func_desc, param_name.cU, fpar.to_ptr, @error.to_ptr)
|
245
|
+
NWRFC.check_error(@error) if rc > 0
|
246
|
+
parameter_to_hash(fpar)
|
247
|
+
end
|
248
|
+
|
249
|
+
private
|
250
|
+
|
251
|
+
def parameter_to_hash(param)
|
252
|
+
{
|
253
|
+
:type => NWRFCLib::RFCTYPE[param[:type]],
|
254
|
+
:direction => NWRFCLib::RFC_DIRECTION[param[:direction]],
|
255
|
+
:nucLength => param[:nucLength],
|
256
|
+
:ucLength => param[:ucLength],
|
257
|
+
:decimals => param[:decimals],
|
258
|
+
:typeDescHandle => param[:typeDescHandle],
|
259
|
+
:defaultValue => param[:defaultValue].get_str,
|
260
|
+
:parameterText => param[:parameterText].get_str,
|
261
|
+
:optional => param[:optional]
|
262
|
+
}
|
263
|
+
end
|
264
|
+
|
265
|
+
# Load the metadata of the function description (only called during init)
|
266
|
+
def self_describe
|
267
|
+
parameter_count.times.inject({}) do |params, index|
|
268
|
+
param = NWRFCLib::RFCFuncParam.new
|
269
|
+
NWRFCLib.get_parameter_desc_by_index(@func_desc, index, param.to_ptr, @error.to_ptr)
|
270
|
+
params[param[:name].get_str] = {
|
271
|
+
:type => NWRFCLib::RFCTYPE[param[:type]],
|
272
|
+
:direction => NWRFCLib::RFC_DIRECTION[param[:direction]],
|
273
|
+
:nucLength => param[:nucLength],
|
274
|
+
:ucLength => param[:ucLength],
|
275
|
+
:decimals => param[:decimals],
|
276
|
+
:typeDescHandle => param[:typeDescHandle],
|
277
|
+
:defaultValue => param[:defaultValue].get_str,
|
278
|
+
:parameterText => param[:parameterText].get_str,
|
279
|
+
:optional => param[:optional]
|
280
|
+
}
|
281
|
+
params
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
end
|
286
|
+
|
287
|
+
# Represents a callable instance of a function module, using a function module description
|
288
|
+
# obtained previously. Can be obtained by passing either a function description or
|
289
|
+
# calling Function#get_function_call
|
290
|
+
class FunctionCall
|
291
|
+
include DataContainer
|
292
|
+
attr_reader :function_description, :handle, :connection
|
293
|
+
|
294
|
+
def initialize(function_description)
|
295
|
+
@error = NWRFCLib::RFCError.new
|
296
|
+
@function_description = function_description
|
297
|
+
@connection = function_description.connection
|
298
|
+
@handle = NWRFCLib.create_function(@function_description.func_desc, @error.to_ptr)
|
299
|
+
@members = function_description
|
300
|
+
NWRFC.check_error(@error)
|
301
|
+
end
|
302
|
+
|
303
|
+
# Execute the function call on the SAP server
|
304
|
+
def invoke
|
305
|
+
rc = NWRFCLib.invoke(@connection.handle, @handle, @connection.error.to_ptr)
|
306
|
+
NWRFC.check_error(@error) if rc > 0
|
307
|
+
end
|
308
|
+
|
309
|
+
def method_missing(method_sym, *args, &block)
|
310
|
+
param_name = method_sym.to_s.upcase
|
311
|
+
# fpar = NWRFCLib::RFCFuncParam.new
|
312
|
+
# rc = NWRFCLib.get_parameter_desc_by_name(@function_description, param_name.cU, fpar.to_ptr, @error.to_ptr)
|
313
|
+
# NWRFC.check_error(@error) if rc > 0
|
314
|
+
if param_name[-1..-1] == "="
|
315
|
+
self[:param_name] = *args[0]
|
316
|
+
else
|
317
|
+
self[:param_name]
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
end
|
322
|
+
|
323
|
+
# Represents a structure
|
324
|
+
class Structure
|
325
|
+
include DataContainer
|
326
|
+
end
|
327
|
+
|
328
|
+
# Represents a table
|
329
|
+
class Table
|
330
|
+
include DataContainer
|
331
|
+
end
|
332
|
+
|
333
|
+
# Wrapper around the RFC Server functionality
|
334
|
+
class Server
|
335
|
+
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|