File Coverage

InstanceExporter.pm
Criterion Covered Total %
statement 6 66 9.0
branch 0 30 0.0
condition 0 12 0.0
subroutine 2 8 25.0
pod 0 5 0.0
total 8 121 6.6


line stmt bran cond sub pod time code
1             package SOAP::Lite::InstanceExporter;
2             #
3             # Name: SOAP::Lite::InstanceExporter
4             #
5             # Author: Sean Meisner
6             #
7             # Usage: use SOAP::Lite::InstanceExporter qw(bareword_objectname, bareword_objectname);
8             #
9             # Purpose: Allow SOAP objects to persist in the main package namespace between SOAP calls
10             #
11             # Detailed Description: This class is used to provide a SOAP interface wrapped around
12             # a reference to an object residing in a package namespace on the server. SOAP objects
13             # exported without this wrapper are initialized and destroyed on a per session basis.
14             # SOAP::Lite::InstanceExporter allows the server to preserve the state of an object across
15             # sessions.
16             #
17              
18             require 5.005_62;
19 1     1   1020 use strict;
  1         2  
  1         36  
20 1     1   6 use warnings;
  1         2  
  1         945  
21              
22             require Exporter;
23              
24             our @ISA = qw(Exporter);
25              
26             our %EXPORT_TAGS = ( 'all' => [ qw(
27              
28             ) ] );
29              
30             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
31              
32             our @EXPORT = qw(
33              
34             );
35              
36             our $VERSION = '0.02';
37              
38             # Set up an array in the package namespace to hold names of objects we
39             # will allow an instance of SOAP::Lite::InstanceExporter to access
40             our @allowedObjects = ();
41              
42              
43             # CLASS METHODS
44              
45             # Return the list of object names to which we will allow access
46             sub allowedObjects
47             {
48 0     0 0   return @allowedObjects;
49             }
50              
51             # Here we store the object names we will allow SOAP to access
52             sub import
53             {
54             # Test to be sure the array of allowed objects is empty.
55             # This hopes to ensure that import can not be called again
56             # on the client side to force access to objects we don't
57             # want SOAP clients to be able to access.
58 0 0   0     unless (scalar(@allowedObjects))
59             {
60 0           push (@allowedObjects, @_);
61             }
62             }
63              
64             # Return list of available objects
65             sub getAvailableObjects
66             {
67 0     0 0   return @allowedObjects;
68             }
69              
70              
71             # Constructor. We save a reference to an object residing in another package's namespace
72             # (defaults to main::), and attempt to set up dispatcher methods to access the object's
73             # methods from SOAP
74             sub new
75             {
76 0     0 0   my $self = shift(@_);
77 0   0       my $class = ref($self) || $self;
78 0           my $objectname = shift(@_);
79 0           my @requestedMethods;
80             my $requestedMethod;
81 0           my $allowedObject;
82 0           my $thisObjectAllowed = 0;
83              
84 0 0         unless (ref $objectname) # This case handles a string passed in representing
85             # the name of a non-lexically scoped variable
86             # somewhere in the address space of the server.
87             {
88              
89             # Check to see that we have permission from the SOAP server
90             # to access the object the remote SOAP client wants to access
91 0           foreach $allowedObject (@allowedObjects)
92             {
93             # exact match: its all good
94 0 0 0       if ($allowedObject eq $objectname)
    0 0        
    0          
95             {
96 0           $thisObjectAllowed = 1;
97 0           last;
98             }
99             # Match when the SOAP client has specified main:: or just :: on the requested
100             # object, but the server allowed an object without package qualification, assuming
101             # the package to default to main
102             elsif (($objectname =~ /.*::.*/) && ($allowedObject !~ /.*::.*/) )
103             {
104             # try sticking a main:: onto allowedObject to match
105 0 0         if ("main::$allowedObject" eq $objectname)
    0          
106             {
107 0           $thisObjectAllowed = 1;
108 0           last;
109             }
110             # try sticking a :: onto allowedObject to match
111             elsif ("::$allowedObject" eq $objectname)
112             {
113 0           $thisObjectAllowed = 1;
114 0           last;
115             }
116             }
117              
118             # Match when the SOAP server has specified main:: or just :: on the allowed
119             # object, but the client requested an object without package qualification, assuming
120             # the package to default to main
121             elsif (($allowedObject =~ /.*::.*/) && ($objectname !~ /.*::.*/) )
122             {
123             # try sticking a main:: onto $objectname to match
124 0 0         if ($allowedObject eq "main::$objectname")
    0          
125             {
126 0           $thisObjectAllowed = 1;
127 0           last;
128             }
129             # try sticking a :: onto $objectname to match
130             elsif ($allowedObject eq "::$objectname")
131             {
132 0           $thisObjectAllowed = 1;
133 0           last;
134             }
135             }
136             }
137              
138 0 0         unless ($thisObjectAllowed)
139             {
140 0           die "Attempting to define a SOAP interface on an object which is not allowed!\n";
141             }
142              
143             # Prepare a string containing the name of the object we want to wrap to be eval'ed.
144             # We don't know the name of the wrapped object until the InstanceExporter is
145             # instantiated, so we use eval.
146            
147             # First, check to see if there are ::'s existing in the objectname. If not, we will
148             # assume the object exists in the main namespace. We can't assume it exists in the
149             # calling package's namespace, as that would put it somewhere in Paul Kulchenko's
150             # SOAP libraries. This assumption could change in a future version.
151              
152 0 0         if ($objectname =~ /.*::.*/)
153             {
154             # if we found a qualifying ::, just stick a $ onto the beginning
155             # to make it a valid variable name when given to eval.
156 0           $objectname = "\$$objectname";
157             }
158             else
159             {
160             # We found no ::, so append $main:: to the beginning
161 0           $objectname = "\$main::$objectname";
162             }
163              
164             # Check to see that the object the remote client wants to access has been initialized
165 0 0 0       unless (defined eval $objectname && ref eval $objectname)
166             {
167 0           die "Attempting to define a SOAP interface on $objectname, which has not been initialized!\n";
168             }
169            
170             # Store a reference to the requested object
171 0           my $objRefstring = "\\"."$objectname";
172 0           $self = bless
173             {
174             #objRef => \ eval $objectname
175             objRef => eval $objRefstring
176             }, $class;
177             }
178             else # Special case of a reference being passed in rather than a string..
179             # useless to a SOAP client, but we can use it when we want to instantiate
180             # an InstanceExporter directly on the server side, to pass a usable
181             # reference to a lexically scoped variable to the client.
182             {
183 0           $self = bless
184             {
185             objRef => $objectname
186             }, $class;
187            
188             }
189            
190             # Set up methods to dispatch to the contained object
191 0           @requestedMethods = $self->getAvailableMethods();
192              
193 0           foreach $requestedMethod (@requestedMethods)
194             {
195 0           $self->generateDispatcher($requestedMethod);
196             }
197              
198 0           return $self;
199             }
200              
201             # INSTANCE METHODS
202              
203             # Generate a method to pass a specified call to the contained class.
204             sub generateDispatcher
205             {
206 0     0 0   my $self = shift(@_);
207 0           my $method = shift(@_);
208              
209             # Die if this method is not available in the contained class
210 0 0         unless ( ${$self->{objRef}}->can($method) )
  0            
211             {
212 0           die "Requested method $method not available!\n";
213             }
214              
215             # Check to see if this method has already been generated
216 0 0         unless ($self->can($method))
217             {
218             # Prepare a string to be eval'ed. All $s and @s we wish to appear
219             # in the eval'ed subroutine are escaped, while the method name undergoes
220             # variable expansion.
221            
222             #my $methodtemplate =
223             #"
224             #sub SOAP::Lite::InstanceExporter::${method}
225             #{
226             # my \$self = shift(\@_);
227             # return \${\$self->{objRef}}->${method}(\@_);
228             #}
229             #";
230              
231             # EXPERIMENTAL: Make InstanceExporters properly handle shared memory segments
232             # created with IPC::Shareable
233 0           my $methodtemplate =
234             "
235             sub SOAP::Lite::InstanceExporter::${method}
236             {
237             my \$self = shift(\@_);
238             my \$returnvalue;
239            
240             # Handle shared memory correctly
241             my \$sharedmemoryhandle = (tied \${\$self->{objRef}});
242             if ( defined \$sharedmemoryhandle )
243             {
244             # Check if we can call shlock, if so call it
245             if (\$sharedmemoryhandle->can(shlock))
246             {
247             \$sharedmemoryhandle->shlock()
248             }
249            
250             # Call the method
251             \$returnvalue = \${\$self->{objRef}}->${method}(\@_);
252            
253             # Check if we can call shunlock, if so call it
254             if (\$sharedmemoryhandle->can(shunlock))
255             {
256             \$sharedmemoryhandle->shunlock()
257             }
258            
259             return \$returnvalue;
260            
261             }
262            
263             # If we got to this line, we are not dealing with shared
264             # memory so just call the method and return
265             return \${\$self->{objRef}}->${method}(\@_);
266             }
267             ";
268              
269             # Declare our subroutine
270 0           eval $methodtemplate;
271             }
272             }
273              
274              
275             # Return list of available methods for this instance
276             sub getAvailableMethods
277             {
278 0     0 0   my $self = shift(@_);
279 0           my $packageName = ref(${$self->{objRef}});
  0            
280 0           my @availableMethods = ();
281              
282 0           my %stash;
283             my $varName;
284 0           my $globValue;
285 0           my $evalstring = "\%stash = \%${packageName}::";
286            
287 0           eval $evalstring; # %stash now contains the symbol table
288             # for the class of which ${$self->{objRef}}
289             # is an instance.
290              
291 0           while (($varName) = each %stash) # Iterate through each symbol name
292             # in the stash
293             {
294             # Check each to see if it refers
295             # to a subroutine
296 0 0         if (${$self->{objRef}}->can($varName))
  0            
297             {
298 0           push(@availableMethods,$varName);
299             }
300             }
301 0           @availableMethods;
302             }
303              
304             1;
305              
306             # End of the code
307             __END__