File Coverage

blib/lib/SAP/BC.pm
Criterion Covered Total %
statement 18 89 20.2
branch 0 18 0.0
condition 0 3 0.0
subroutine 6 14 42.8
pod 6 6 100.0
total 30 130 23.0


line stmt bran cond sub pod time code
1             #
2             # Utils for SAP Business Connector (BC)
3             #
4              
5              
6             package SAP::BC;
7              
8             =pod
9              
10             =head1 NAME
11              
12             SAP::BC - Interface to SAP's Business Connector
13              
14             =head1 SYNOPSIS
15              
16             use SAP::BC;
17              
18             my $bc = SAP::BC->new( server => 'http://karma:5555',
19             user => 'dj',
20             password => 'secret' );
21              
22             my $service_ref = $bc->services(); # list (SAP) services available
23              
24             =head1 DESCRIPTION
25              
26             I is an OO interface that exposes functions within
27             SAP's Business Connector (BC) as methods. It was primarily written
28             as a class for discovering services and their respective RFC
29             components for another module project SAP::BC::Proxy::SOAP which
30             is a SOAP (to RFCXML) proxy for calls to SAP via the BC.
31              
32             =head1 METHODS
33              
34             =over 4
35              
36             =cut
37              
38 1     1   915 use strict;
  1         2  
  1         38  
39              
40 1     1   1015 use LWP::Simple;
  1         101708  
  1         11  
41 1     1   458 use LWP::UserAgent;
  1         7  
  1         26  
42 1     1   5 use HTTP::Request;
  1         1  
  1         23  
43 1     1   1023 use HTTP::Cookies;
  1         8736  
  1         37  
44              
45 1     1   8 use vars qw($VERSION %BC $COOKIES);
  1         2  
  1         1338  
46              
47             $VERSION = '0.03';
48              
49             # Not sure about the usefulness of this hash, yet, but I'll
50             # keep it for now. It keeps the BC implemention details visible.
51              
52             %BC = (
53             'listServers' => '/invoke/sap.admin.server/list',
54             'listServiceMaps' => '/invoke/sap.admin.map/list',
55             'getProperties' => '/WmRoot/server-environment.dsp',
56             'disconnect' => '/invoke/wm.server/disconnect',
57             );
58              
59             $COOKIES = new HTTP::Cookies(hide_cookie2 => 1);
60              
61             # The constructor.
62             # Represents a Business Connector instance.
63              
64             =pod
65              
66             =item B
67              
68             Use this to create a BC instance. You can pass either a single
69             argument, which is the URL of the BC you want to manipulate, or
70             a list of values, like this:
71              
72             my $bc = SAP::BC->new('http://karma:5555'); not allowed after BC 4.x
73              
74             or
75              
76             my $bc = SAP::BC->new( 'server' => 'http://karma',
77             'user' => 'username',
78             'password' => 'secret' ); <= manditory after BC 4.x
79              
80             where the user and password parameters are the ones for the
81             SAP BC itself.
82              
83             =cut
84              
85             sub new {
86 0     0 1   my $class = shift;
87              
88 0           my $self;
89              
90 0 0         if ($#_ == 0) { # if there's only one thing passed
91 0           my $arg = shift;
92 0 0         if (ref($arg) eq 'HASH') { # and it's a hash-ref
93 0           $self = $arg; # great - take that for $self
94             }
95             else { # otherwise..
96 0           $self = { 'server' => $arg }; # assume it's the BC's URL
97             }
98             }
99             else { # otherwise, if there's more stuff passed,
100 0           $self = { @_ }; # so make a hash out of it
101             }
102              
103             # $self->{'server'} =~ s/\/?$//; # remove possible trailing slash
104             # die "Cannot connect to $self->{'server'}: $!\n" unless get $self->{'server'};
105              
106 0           bless $self, $class;
107 0           return $self;
108             }
109              
110             =pod
111              
112             =item B
113              
114             Use this method to get or set the user and password values for
115             authentication with the BC.
116              
117             =cut
118              
119             sub authentication {
120 0     0 1   my $self = shift;
121              
122             # Set if passed
123 0 0         if ($#_ > 0) {
124 0           $self->{'user'} = shift;
125 0           $self->{'password'} = shift;
126             }
127              
128             # Make sure there's no undefs
129             # $self->{'user'} ||= '';
130             # $self->{'password'} ||= '';
131              
132             # Return both values
133 0           return ($self->{'user'},$self->{'password'});
134              
135             }
136              
137             =pod
138              
139             =item B
140              
141             Use this method to get a list of SAP systems known to the BC.
142             The data will be cached after the first call.
143              
144             =cut
145              
146             sub SAP_systems {
147             # Return a list of SAP systems
148 0     0 1   my $self = shift;
149              
150 0 0         unless (exists ($self->{'SAP_systems'})) {
151              
152             # Prime
153 0           $self->{'SAP_systems'} = [];
154              
155             # Call service on BC
156             # my $res = get "$self->{'server'}$BC{'listServers'}"
157             # or die "Cannot retrieve server list: $!\n";
158             # Call service on BC
159 0           $self->_prime_ua();
160 0           my $req = HTTP::Request->new('GET', "$self->{'server'}$BC{'listServers'}");
161 0           $req->authorization_basic($self->authentication);
162 0           $req = $self->{'ua'}->prepare_request($req);
163 0 0         my $res = $self->{'ua'}->request($req)->content()
164             or die "Cannot retrieve server list: $!\n";
165 0           $res =~ s/\n//g;
166             # print STDERR "The Server List: ".$res."\n";
167              
168             # Parse results for server names
169             #foreach (grep(/$BC{'listServiceMaps'}/,split("\n",$res))) {
170 0           foreach (grep(/\>serverName/,split(/<\/TR>/,$res))) {
171             # print STDERR "LINE: $_ \n";
172             #my ($sapsys) = $_ =~ m/$BC{'listServiceMaps'}\?serverName=(\w{3})/;
173 0           my ($sapsys) = $_ =~ m/(\w+)<\/TD>.*?$/;
174 0           push(@{$self->{'SAP_systems'}},$sapsys);
  0            
175             }
176              
177             }
178              
179 0           return $self->{'SAP_systems'};
180              
181             }
182              
183             =pod
184              
185             =item B
186              
187             To discover a list of services associated with the SAP systems
188             known to the BC, use this method. You can pass a list of
189             SAP systems for which you want to discover the services, or
190              
191             if you don't pass anything, services for all the SAP systems
192             known to the BC will be returned. If the SAP systems haven't
193             previously been discovered using the I method,
194             this will happen automatically.
195              
196             A reference to a hash will be returned, with the keys being
197             the service names, and the argument being a hashref with the
198             details, like this:
199              
200             {
201             'SOAP:getStateName' =>
202             {
203             'sapsys' => 'LNX',
204             'rfcname' => 'Z_SOAP_GET_STATE_NAME',
205             },
206             'SOAP:getStateStruct' =>
207             {
208             'sapsys' => 'LNX',
209             'rfcname' => 'Z_SOAP_GET_STATE_STRUCT',
210             },
211             ...
212             }
213              
214             =cut
215              
216             sub services {
217             # Return a list of BC (-> SAP) services)
218             # Can receive an optional list of SAP systems
219             # to use to restrict the search
220 0     0 1   my $self = shift;
221 0   0       my $sys_list = shift || $self->SAP_systems();
222              
223 0 0         unless (exists($self->{'services'})) {
224 0           $self->_prime_ua();
225              
226             # Prime
227 0           $self->{'services'} = {};
228              
229             # Invoke the map list service for each of the SAP systems
230 0           foreach my $sys (@{$sys_list}) {
  0            
231 0           my $req = HTTP::Request->new('GET', "$self->{'server'}$BC{'listServiceMaps'}?serverName=$sys");
232 0           $req->authorization_basic($self->authentication);
233 0 0         my $res = $self->{'ua'}->request($req)->content()
234             or die "Cannot retrieve Service Map for $sys: $!\n";
235 0           $res =~ s/\n//g;
236             #print STDERR "SERVICE LIST: $res \n";
237             #my $res = get "$self->{'server'}$BC{'listServiceMaps'}?serverName=$sys"
238             # or die "Cannot retrieve Service Map for $sys: $!\n";
239              
240             #foreach my $serviceMap (grep(/editServiceMap.*svcname/,split("\n",$res))) {
241 0           while ( $res =~ m/serverName<\/b><\/td>([\w_]+).*?outboundMaps<\/b><\/td>/gi) {
242 0           my ( $srvname, $outbm ) = ( $1, $' );
243 0           while ( $outbm =~ m/functionName<\/b><\/td>(\w+).*?folder<\/b><\/td>([\w.]+).*?service<\/b><\/td>(\w+)/gi) {
244 0           my ( $rfcname, $srvpath, $service ) = ( $1, $2, $3 );
245             # print STDERR "LINE: $srvname $rfcname $srvpath $service $package \n";
246             # my ($sapsys, $rfcname, $service) =
247             # $serviceMap =~ m/^.*?serverName\=(.*?)\&.*?rfcname\=(.*?)\&.*?svcname\=(.*?)\&.*$/;
248 0           $self->{'services'}->{$srvpath.':'.$service} = {
249             'sapsys' => $srvname,
250             'rfcname' => $rfcname,
251             };
252            
253             # $self->{'services'}->{$service} = {
254             # 'sapsys' => $sapsys,
255             # 'rfcname' => $rfcname,
256             # };
257              
258             }
259             }
260              
261             }
262              
263             }
264              
265 0           return $self->{'services'};
266            
267             }
268              
269             =pod
270              
271             =item B
272              
273             Disconnects from the BC and frees the session.
274              
275             =cut
276              
277             sub disconnect {
278 0     0 1   my $self = shift;
279 0           my $ua = LWP::UserAgent->new(timeout => 5);
280 0           $ua->agent("sap::bc/$VERSION");
281 0           $ua->cookie_jar($COOKIES);
282 0           my $req = HTTP::Request->new('GET', "$self->{'server'}$BC{'disconnect'}");
283 0           $req->authorization_basic($self->authentication);
284 0           my $res = $ua->request($req);
285 0           return 1;
286             }
287              
288              
289             =pod
290              
291             =item B<_clear_caches()>
292              
293             This is an internal method that removes the cached information
294             (such as that determined by I and I - so that
295             the information can be refreshed by another call, if e.g. services
296             have been added to the BC.
297              
298             =cut
299              
300             sub _clear_caches {
301 0     0     my $self = shift;
302 0           delete $self->{'SAP_systems'};
303 0           delete $self->{'services'};
304              
305 0           return 1;
306             }
307              
308             =pod
309              
310             =item B
311              
312             An experimental method that returns a hashref of properties
313             pertaining to the BC instance connected to.
314              
315             It relies on parsing some HTML, which is flakey at best.
316              
317             =cut
318              
319             sub properties {
320 0     0 1   my $self = shift;
321              
322 0 0         unless (exists ($self->{'properties'})) {
323              
324             # Prime
325 0           $self->{'properties'} = {};
326              
327             # Call service on BC
328 0           $self->_prime_ua();
329 0           my $req = HTTP::Request->new('GET', "$self->{'server'}$BC{'getProperties'}");
330 0           $req->authorization_basic($self->authentication);
331              
332 0           $self->{'scratch'} = $self->{'ua'}->request($req);
333              
334             }
335              
336 0           return $self->{'scratch'};
337              
338             }
339              
340             =pod
341              
342             =item B<_prime_ua()>
343              
344             An internal method to prime a UserAgent.
345              
346             =cut
347              
348             sub _prime_ua {
349 0     0     my $self = shift;
350              
351             # Don't do anything if it's already primed
352 0 0         return if exists $self->{'ua'};
353              
354 0           $self->{'ua'} = LWP::UserAgent->new();
355 0           $self->{'ua'}->agent("sap::bc/$VERSION");
356 0           $self->{'ua'}->cookie_jar($COOKIES);
357             }
358              
359              
360             =pod
361              
362             =back
363              
364             =cut
365              
366             1;