/[cvs]/joko/Scripts/psh/lib/RPC/XML/Procedure.pm
ViewVC logotype

Annotation of /joko/Scripts/psh/lib/RPC/XML/Procedure.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1.1.1 - (hide annotations) (vendor branch)
Fri Jun 14 21:22:13 2002 UTC (22 years, 3 months ago) by cvsjoko
Branch: nfo, MAIN
CVS Tags: r001, HEAD
Changes since 1.1: +0 -0 lines
first import

1 cvsjoko 1.1 ###############################################################################
2     #
3     # This file copyright (c) 2001 by Randy J. Ray, all rights reserved
4     #
5     # Copying and distribution are permitted under the terms of the Artistic
6     # License as distributed with Perl versions 5.005 and later. See
7     # http://language.perl.com/misc/Artistic.html
8     #
9     ###############################################################################
10     #
11     # $Id: Procedure.pm,v 1.5 2002/05/22 09:45:59 rjray Exp $
12     #
13     # Description: This class abstracts out all the procedure-related
14     # operations from the RPC::XML::Server class
15     #
16     # Functions: new
17     # name \
18     # code \
19     # signature \ These are the accessor functions for the
20     # help / data in the object, though it's visible.
21     # version /
22     # hidden /
23     # clone
24     # is_valid
25     # add_signature
26     # delete_signature
27     # make_sig_table
28     # match_signature
29     # reload
30     # load_XPL_file
31     #
32     # Libraries: XML::Parser (used only on demand in load_XPL_file)
33     # File::Spec
34     #
35     # Global Consts: $VERSION
36     #
37     # Environment: None.
38     #
39     ###############################################################################
40    
41     package RPC::XML::Procedure;
42    
43     use 5.005;
44     use strict;
45     use vars qw($VERSION);
46     use subs qw(new is_valid name code signature help version hidden
47     add_signature delete_signature make_sig_table match_signature
48     reload load_XPL_file);
49    
50     use AutoLoader 'AUTOLOAD';
51     require File::Spec;
52    
53     $VERSION = do { my @r=(q$Revision: 1.5 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r };
54    
55     1;
56    
57     ###############################################################################
58     #
59     # Sub Name: new
60     #
61     # Description: Create a new object of this class, storing the info on
62     # regular keys (no obfuscation used here).
63     #
64     # Arguments: NAME IN/OUT TYPE DESCRIPTION
65     # $class in scalar Class to bless into
66     # @argz in variable Disposition is variable; see
67     # below
68     #
69     # Returns: Success: object ref
70     # Failure: error string
71     #
72     ###############################################################################
73     sub new
74     {
75     my $class = shift;
76     my @argz = @_;
77    
78     my $data; # This will be a hashref that eventually gets blessed
79    
80     $class = ref($class) || $class;
81    
82     #
83     # There are three things that @argz could be:
84     #
85     if (ref $argz[0])
86     {
87     # 1. A hashref containing all the relevant keys
88     $data = {};
89     %$data = %{$argz[0]};
90     }
91     elsif (@argz == 1)
92     {
93     # 2. Exactly one non-ref element, a file to load
94    
95     # And here is where I cheat in a way that makes even me uncomfortable.
96     #
97     # Loading code from an XPL file, it can actually be of a type other
98     # than how this constructor was called. So what we are going to do is
99     # this: If $class is undef, that can only mean that we were called
100     # with the intent of letting the XPL file dictate the resulting object.
101     # If $class is set, then we'll call load_XPL_file normally, as a
102     # method, to allow for subclasses to tweak things.
103     if (defined $class)
104     {
105     $data = $class->load_XPL_file($argz[0]);
106     return $data unless ref $data; # load_XPL_path signalled an error
107     }
108     else
109     {
110     # Spoofing the "class" argument to load_XPL_file makes me feel
111     # even dirtier...
112     $data = load_XPL_file(\$class, $argz[0]);
113     return $data unless ref $data; # load_XPL_path signalled an error
114     $class = "RPC::XML::$class";
115     }
116     }
117     else
118     {
119     # 3. If there is more than one arg, it's a sort-of-hash. That is, the
120     # key 'signature' is allowed to repeat.
121     my ($key, $val);
122     $data = {};
123     $data->{signature} = [];
124     while (@argz)
125     {
126     ($key, $val) = splice(@argz, 0, 2);
127     if ($key eq 'signature')
128     {
129     # Since there may be more than one signature, we allow it to
130     # repeat. Of course, that's also why we can't just take @argz
131     # directly as a hash. *shrug*
132     push(@{$data->{signature}},
133     [ ref($val) ? @$val : split(/ /, $val) ]);
134     }
135     elsif (exists $data->{$key})
136     {
137     return "${class}::new: Key '$key' may not be repeated";
138     }
139     else
140     {
141     $data->{$key} = $val;
142     }
143     }
144     }
145    
146     return "${class}::new: Missing required data"
147     unless (exists $data->{signature} and
148     (ref($data->{signature}) eq 'ARRAY') and
149     scalar(@{$data->{signature}}) and
150     $data->{name} and $data->{code});
151     bless $data, $class;
152     # This needs to happen post-bless in case of error (for error messages)
153     $data->make_sig_table;
154     }
155    
156     ###############################################################################
157     #
158     # Sub Name: make_sig_table
159     #
160     # Description: Create a hash table of the signatures that maps to the
161     # corresponding return type for that particular invocation.
162     # Makes looking up call patterns much easier.
163     #
164     # Arguments: NAME IN/OUT TYPE DESCRIPTION
165     # $self in ref Object of this class
166     #
167     # Returns: Success: $self
168     # Failure: error message
169     #
170     ###############################################################################
171     sub make_sig_table
172     {
173     my $self = shift;
174    
175     my ($sig, $return, $rest);
176    
177     delete $self->{sig_table};
178     for $sig (@{$self->{signature}})
179     {
180     ($return, $rest) = split(/ /, $sig, 2); $rest = '' unless $rest;
181     # If the key $rest already exists, then this is a collision
182     return ref($self) . '::make_sig_table: Cannot have two different ' .
183     "return values for one set of params ($return vs. " .
184     "$self->{sig_table}->{$rest})"
185     if $self->{sig_table}->{$rest};
186     $self->{sig_table}->{$rest} = $return;
187     }
188    
189     $self;
190     }
191    
192     #
193     # These are basic accessor/setting functions for the various attributes
194     #
195     sub name { $_[0]->{name}; } # "name" cannot be changed at this level
196     sub help { $_[1] and $_[0]->{help} = $_[1]; $_[0]->{help}; }
197     sub version { $_[1] and $_[0]->{version} = $_[1]; $_[0]->{version}; }
198     sub hidden { $_[1] and $_[0]->{hidden} = $_[1]; $_[0]->{hidden}; }
199     sub code
200     {
201     ref $_[1] eq 'CODE' and $_[0]->{code} = $_[1];
202     $_[0]->{code};
203     }
204     sub signature
205     {
206     if ($_[1] and ref $_[1] eq 'ARRAY')
207     {
208     my $old = $_[0]->{signature};
209     $_[0]->{signature} = $_[1];
210     unless (ref($_[0]->make_sig_table))
211     {
212     # If it failed to re-init the table, restore the old list (and old
213     # table). We don't have to check this return, since it had worked
214     $_[0]->{signature} = $old;
215     $_[0]->make_sig_table;
216     }
217     }
218     # Return a copy of the array, not the original
219     [ @{$_[0]->{signature}} ];
220     }
221    
222     package RPC::XML::Method;
223    
224     use strict;
225    
226     @RPC::XML::Method::ISA = qw(RPC::XML::Procedure);
227    
228     package RPC::XML::Procedure;
229    
230     =head1 NAME
231    
232     RPC::XML::Procedure - Object encapsulation of server-side RPC procedures
233    
234     =head1 SYNOPSIS
235    
236     require RPC::XML::Procedure;
237    
238     ...
239     $method_1 = RPC::XML::Procedure->new({ name => 'system.identity',
240     code => sub { ... },
241     signature => [ 'string' ] });
242     $method_2 = RPC::XML::Procedure->new('/path/to/status.xpl');
243    
244     =head1 IMPORTANT NOTE
245    
246     This package is comprised of the code that was formerly B<RPC::XML::Method>.
247     The package was renamed when the decision was made to support procedures and
248     methods as functionally different entities. It is not necessary to include
249     both this module and B<RPC::XML::Method> -- this module provides the latter as
250     an empty subclass. In time, B<RPC::XML::Method> will be removed from the
251     distribution entirely.
252    
253     =head1 DESCRIPTION
254    
255     The B<RPC::XML::Procedure> package is designed primarily for behind-the-scenes
256     use by the B<RPC::XML::Server> class and any subclasses of it. It is
257     documented here in case a project chooses to sub-class it for their purposes
258     (which would require setting the C<method_class> attribute when creating
259     server objects, see L<RPC::XML::Server>).
260    
261     This package grew out of the increasing need to abstract the operations that
262     related to the methods a given server instance was providing. Previously,
263     methods were passed around simply as hash references. It was a small step then
264     to move them into a package and allow for operations directly on the objects
265     themselves. In the spirit of the original hashes, all the key data is kept in
266     clear, intuitive hash keys (rather than obfuscated as the other classes
267     do). Thus it is important to be clear on the interface here before
268     sub-classing this package.
269    
270     =head1 USAGE
271    
272     The following methods are provided by this class:
273    
274     =over 4
275    
276     =item new(FILE|HASHREF|LIST)
277    
278     Creates a new object of the class, and returns a reference to it. The
279     arguments to the constructor are variable in nature, depending on the type:
280    
281     =over 8
282    
283     =item FILE
284    
285     If there is exactly on argument that is not a reference, it is assumed to be a
286     filename from which the method is to be loaded. This is presumed to be in the
287     B<XPL> format descibed below (see L</"XPL File Structure">). If the file
288     cannot be opened, or if once opened cannot be parsed, an error is raised.
289    
290     =item HASHREF
291    
292     If there is exactly one argument that is a reference, it is assumed to be a
293     hash with the relevant information on the same keys as the object itself
294     uses. This is primarily to support backwards-compatibility to code written
295     when methods were implemented simply as hash references.
296    
297     =item LIST
298    
299     If there is more than one argument in the list, then the list is assumed to be
300     a sort of "ersatz" hash construct, in that one of the keys (C<signature>) is
301     allowed to occur multiple times. Otherwise, each of the following is allowed,
302     but may only occur once:
303    
304     =over 12
305    
306     =item name
307    
308     The name of the method, as it will be presented to clients
309    
310     =item code
311    
312     A reference to a subroutine, or an anonymous subroutine, that will receive
313     calls for the method
314    
315     =item signature
316    
317     (May appear more than once) Provides one calling-signature for the method, as
318     either a space-separated string of types or a list-reference
319    
320     =item help
321    
322     The help-text for a method, which is generally used as a part of the
323     introspection interface for a server
324    
325     =item version
326    
327     The version number/string for the method
328    
329     =item hidden
330    
331     A boolean (true or false) value indicating whether the method should be hidden
332     from introspection and similar listings
333    
334     =back
335    
336     Note that all of these correspond to the values that can be changed via the
337     accessor methods detailed later.
338    
339     =back
340    
341     If any error occurs during object creation, an error message is returned in
342     lieu of the object reference.
343    
344     =item clone
345    
346     Create a copy of the calling object, and return the new reference. All
347     elements are copied over cleanly, except for the code reference stored on the
348     C<code> hash key. The clone will point to the same code reference as the
349     original. Elements such as C<signature> are copied, so that changes to the
350     clone will not impact the original.
351    
352     =item name
353    
354     Returns the name by which the server is advertising the method. Unlike the
355     next few accessors, this cannot be changed on an object. In order to
356     streamline the managment of methods within the server classes, this must
357     persist. However, the other elements may be used in the creation of a new
358     object, which may then be added to the server, if the name absolutely must
359     change.
360    
361     =item code([NEW])
362    
363     Returns or sets the code-reference that will receive calls as marshalled by
364     the server. The existing value is lost, so if it must be preserved, then it
365     should be retrieved prior to the new value being set.
366    
367     =item signature([NEW])
368    
369     Return a list reference containing the signatures, or set it. Each element of
370     the list is a string of space-separated types (the first of which is the
371     return type the method produces in that calling context). If this is being
372     used to set the signature, then an array reference must be passed that
373     contains one or more strings of this nature. Nested list references are not
374     allowed at this level. If the new signatures would cause a conflict (a case in
375     which the same set of input types are specified for different output types),
376     the old set is silently restored.
377    
378     =item help([NEW])
379    
380     Returns or sets the help-text for the method. As with B<code>, the previous
381     value is lost.
382    
383     =item hidden([NEW])
384    
385     Returns or sets the hidden status of the method. Setting it loses the previous
386     value.
387    
388     =item version([NEW])
389    
390     Returns or sets the version string for the method (overwriting as with the
391     other accessors).
392    
393     =item is_valid
394    
395     Returns a true/false value as to whether the object currently has enough
396     content to be a valid method for a server to publish. This entails having at
397     the very least a name, one or more signatures, and a code-reference to route
398     the calls to. A server created from the classes in this software suite will
399     not accept a method that is not valid.
400    
401     =item add_signature(LIST)
402    
403     Add one or more signatures (which may be a list reference or a string) to the
404     internal tables for this method. Duplicate signatures are ignored. If the new
405     signature would cause a conflict (a case in which the same set of input types
406     are specified for different output types), the old set is restored and an
407     error message is returned.
408    
409     =item delete_signature(LIST)
410    
411     Deletes the signature or signatures (list reference or string) from the
412     internal tables. Quietly ignores any signature that does not exist. If the new
413     signature would cause a conflict (a case in which the same set of input types
414     are specified for different output types), the old set is restored and an
415     error message is returned.
416    
417     =item match_signature(SIGNATURE)
418    
419     Check that the passed-in signature is known to the method, and if so returns
420     the type that the method should be returning as a result of the call. Returns
421     a zero (0) otherwise. This differs from other signature operations in that the
422     passed-in signature (which may be a list-reference or a string) B<I<does not
423     include the return type>>. This method is provided so that servers may check a
424     list of arguments against type when marshalling an incoming call. For example,
425     a signature of C<'int int'> would be tested for by calling
426     C<$M-E<gt>match_signature('int')> and expecting the return value to be C<int>.
427    
428     =item call(SERVER, PARAMLIST)
429    
430     Execute the code that this object encapsulates, using the list of parameters
431     passed in PARAMLIST. The SERVER argument should be an object derived from the
432     B<RPC::XML::Server> class. For some types of procedure objects, this becomes
433     the first argument of the parameter list to simulate a method call as if it
434     were on the server object itself. The return value should be a data object
435     (possible a B<RPC::XML::fault>), but may not always be pre-encoded. This
436     method is generally used in the C<dispatch> and C<call> methods of the server
437     class, where the return value is subsequently wrapped within a
438     B<RPC::XML::response> object.
439    
440     =item reload
441    
442     Instruct the object to reload itself from the file it originally was loaded
443     from, assuming that it was loaded from a file to begin with. Returns an error
444     if the method was not originally loaded from a file, or if an error occurs
445     during the reloading operation.
446    
447     =back
448    
449     =head2 Additional Hash Data
450    
451     In addition to the attributes managed by the accessors documented earlier, the
452     following hash keys are also available for use. These are also not strongly
453     protected, and the same care should be taken before altering any of them:
454    
455     =over 4
456    
457     =item C<file>
458    
459     When the method was loaded from a file, this key contains the path to the file
460     used.
461    
462     =item C<mtime>
463    
464     When the method was loaded from a file, this key contains the
465     modification-time of the file, as a UNIX-style C<time> value. This is used to
466     check for changes to the file the code was originally read from.
467    
468     =item C<called>
469    
470     When the method is being used by one of the server classes provided in this
471     software suite, this key is incremented each time the server object dispatches
472     a request to the method. This can later be checked to provide some indication
473     of how frequently the method is being invoked.
474    
475     =back
476    
477     =head2 XPL File Structure
478    
479     This section focuses on the way in which methods are expressed in these files,
480     referred to here as "XPL files" due to the C<*.xpl> filename extension
481     (which stands for "XML Procedure Layout"). This mini-dialect, based on XML,
482     is meant to provide a simple means of specifying method definitions separate
483     from the code that comprises the application itself. Thus, methods may
484     theoretically be added, removed, debugged or even changed entirely without
485     requiring that the server application itself be rebuilt (or, possibly, without
486     it even being restarted).
487    
488     =head3 The XPL file structure
489    
490     The B<XPL Procedure Layout> dialect is a very simple application of XML to the
491     problem of expressing the method in such a way that it could be useful to
492     other packages than this one, or useful in other contexts than this one.
493    
494     The lightweight DTD for the layout can be summarized as:
495    
496     <!ELEMENT proceduredef (name, version?, hidden?, signature+,
497     help?, code)>
498     <!ELEMENT methoddef (name, version?, hidden?, signature+,
499     help?, code)>
500     <!ELEMENT name (#PCDATA)>
501     <!ELEMENT version (#PCDATA)>
502     <!ELEMENT hidden EMPTY>
503     <!ELEMENT signature (#PCDATA)>
504     <!ELEMENT help (#PCDATA)>
505     <!ELEMENT code (#PCDATA)>
506     <!ATTLIST code language (#PCDATA)>
507    
508     The containing tag is always one of C<E<lt>methoddefE<gt>> or
509     C<E<lt>proceduredefE<gt>>. The tags that specify name, signatures and the code
510     itself must always be present. Some optional information may also be
511     supplied. The "help" text, or what an introspection API would expect to use to
512     document the method, is also marked as optional. Having some degree of
513     documentation for all the methods a server provides is a good rule of thumb,
514     however.
515    
516     The default methods that this package provides are turned into XPL files by
517     the B<make_method> tool (see L<make_method>). The final forms of these may
518     serve as direct examples of what the file should look like.
519    
520     =head3 Information used only for book-keeping
521    
522     Some of the information in the XPL file is only for book-keeping: the version
523     stamp of a method is never involved in the invocation. The server also keeps
524     track of the last-modified time of the file the method is read from, as well
525     as the full directory path to that file. The C<E<lt>hidden /E<gt>> tag is used
526     to identify those methods that should not be exposed to the outside world
527     through any sort of introspection/documentation API. They are still available
528     and callable, but the client must possess the interface information in order
529     to do so.
530    
531     =head3 The information crucial to the method
532    
533     The name, signatures and code must be present for obvious reasons. The
534     C<E<lt>nameE<gt>> tag tells the server what external name this procedure is
535     known by. The C<E<lt>signatureE<gt>> tag, which may appear more than once,
536     provides the definition of the interface to the function in terms of what
537     types and quantity of arguments it will accept, and for a given set of
538     arguments what the type of the returned value is. Lastly is the
539     C<E<lt>codeE<gt>> tag, without which there is no procedure to remotely call.
540    
541     =head3 Why the <code> tag allows multiple languages
542    
543     Note that the C<E<lt>codeE<gt>> tag is the only one with an attribute, in this
544     case "language". This is designed to allow for one XPL file to provide a given
545     method in multiple languages. Why, one might ask, would there be a need for
546     this?
547    
548     It is the hope behind this package that collections of RPC suites may one day
549     be made available as separate entities from this specific software package.
550     Given this hope, it is not unreasonable to suggest that such a suite of code
551     might be implemented in more than one language (each of Perl, Python, Ruby and
552     Tcl, for example). Languages which all support the means by which to take new
553     code and add it to a running process on demand (usually through an "C<eval>"
554     keyword or something similar). If the file F<A.xpl> is provided with
555     implementations in all four of the above languages, the name, help text,
556     signature and even hidden status would likely be identical. So, why not share
557     the non-language-specific elements in the spirit of re-use?
558    
559     =head3 The "make_method" utility
560    
561     The utility script C<make_method> is provided as a part of this software
562     suite. It allows for the automatic creation of XPL files from either
563     command-line information or from template files. It has a wide variety of
564     features and options, and is out of the scope of this particular manual
565     page. The package F<Makefile.PL> features an example of engineering the
566     automatic generation of XPL files and their delivery as a part of the normal
567     Perl module build process. Using this tool is highly recommended over managing
568     XPL files directly. For the full details, see L<make_method>.
569    
570     =head1 DIAGNOSTICS
571    
572     Unless otherwise noted in the individual documentation sections, all methods
573     return the object reference on success, or a (non-reference) text string
574     containing the error message upon failure.
575    
576     =head1 CAVEATS
577    
578     Moving the method management to a separate class adds a good deal of overhead
579     to the general system. The trade-off in reduced complexity and added
580     maintainability should offset this.
581    
582     =head1 LICENSE
583    
584     This module is licensed under the terms of the Artistic License that covers
585     Perl. See <http://language.perl.com/misc/Artistic.html> for the
586     license.
587    
588     =head1 SEE ALSO
589    
590     L<RPC::XML::Server>, L<make_method>
591    
592     =head1 AUTHOR
593    
594     Randy J. Ray <rjray@blackperl.com>
595    
596     =cut
597    
598     __END__
599    
600     ###############################################################################
601     #
602     # Sub Name: clone
603     #
604     # Description: Create a near-exact copy of the invoking object, save that
605     # the listref in the "signature" key is a copy, not a ref
606     # to the same list.
607     #
608     # Arguments: NAME IN/OUT TYPE DESCRIPTION
609     # $self in ref Object of this class
610     #
611     # Returns: Success: $new_self
612     # Failure: error message
613     #
614     ###############################################################################
615     sub clone
616     {
617     my $self = shift;
618    
619     my $new_self = {};
620     for (keys %$self)
621     {
622     next if $_ eq 'signature';
623     $new_self->{$_} = $self->{$_};
624     }
625     $new_self->{signature} = [];
626     @{$new_self->{signature}} = @{$self->{signature}};
627    
628     bless $new_self, ref($self);
629     }
630    
631     ###############################################################################
632     #
633     # Sub Name: is_valid
634     #
635     # Description: Boolean test to tell if the calling object has sufficient
636     # data to be used as a server method for RPC::XML::Server or
637     # Apache::RPC::Server.
638     #
639     # Arguments: NAME IN/OUT TYPE DESCRIPTION
640     # $self in ref Object to test
641     #
642     # Returns: Success: 1, valid/complete
643     # Failure: 0, invalid/incomplete
644     #
645     ###############################################################################
646     sub is_valid
647     {
648     my $self = shift;
649    
650     return ((ref($self->{code}) eq 'CODE') and $self->{name} and
651     (ref($self->{signature}) && scalar(@{$self->{signature}})));
652     }
653    
654     ###############################################################################
655     #
656     # Sub Name: add_signature
657     # delete_signature
658     #
659     # Description: This pair of functions may be used to add and remove
660     # signatures from a method-object.
661     #
662     # Arguments: NAME IN/OUT TYPE DESCRIPTION
663     # $self in ref Object of this class
664     # @args in list One or more signatures
665     #
666     # Returns: Success: $self
667     # Failure: error string
668     #
669     ###############################################################################
670     sub add_signature
671     {
672     my $self = shift;
673     my @args = @_;
674    
675     my (%sigs, $one_sig, $tmp, $old);
676    
677     # Preserve the original in case adding the new one causes a problem
678     $old = $self->{signature};
679     %sigs = map { $_ => 1 } @{$self->{signature}};
680     for $one_sig (@args)
681     {
682     $tmp = (ref $one_sig) ? join(' ', @$one_sig) : $one_sig;
683     $sigs{$tmp} = 1;
684     }
685     $self->{signature} = [ keys %sigs ];
686     unless (ref($tmp = $self->make_sig_table))
687     {
688     # Because this failed, we have to restore the old table and return
689     # an error
690     $self->{signature} = $old;
691     $self->make_sig_table;
692     return ref($self) . '::add_signature: Error re-hashing table: ' .
693     $tmp;
694     }
695    
696     $self;
697     }
698    
699     sub delete_signature
700     {
701     my $self = shift;
702     my @args = @_;
703    
704     my (%sigs, $one_sig, $tmp, $old);
705    
706     # Preserve the original in case adding the new one causes a problem
707     $old = $self->{signature};
708     %sigs = map { $_ => 1 } @{$self->{signature}};
709     for $one_sig (@args)
710     {
711     $tmp = (ref $one_sig) ? join(' ', @$one_sig) : $one_sig;
712     delete $sigs{$tmp};
713     }
714     $self->{signature} = [ keys %sigs ];
715     unless (ref($tmp = $self->make_sig_table))
716     {
717     # Because this failed, we have to restore the old table and return
718     # an error
719     $self->{signature} = $old;
720     $self->make_sig_table;
721     return ref($self) . '::delete_signature: Error re-hashing table: ' .
722     $tmp;
723     }
724    
725     $self;
726     }
727    
728     ###############################################################################
729     #
730     # Sub Name: match_signature
731     #
732     # Description: Determine if the passed-in signature string matches any
733     # of this method's known signatures.
734     #
735     # Arguments: NAME IN/OUT TYPE DESCRIPTION
736     # $self in ref Object of this class
737     # $sig in scalar Signature to check for
738     #
739     # Returns: Success: return type as a string
740     # Failure: 0
741     #
742     ###############################################################################
743     sub match_signature
744     {
745     my $self = shift;
746     my $sig = shift;
747    
748     $sig = join(' ', @$sig) if ref $sig;
749    
750     return $self->{sig_table}->{$sig} || 0;
751     }
752    
753     ###############################################################################
754     #
755     # Sub Name: reload
756     #
757     # Description: Reload the method's code and ancillary data from the file
758     #
759     # Arguments: NAME IN/OUT TYPE DESCRIPTION
760     # $self in ref Object of this class
761     #
762     # Returns: Success: $self
763     # Failure: error message
764     #
765     ###############################################################################
766     sub reload
767     {
768     my $self = shift;
769    
770     return ref($self) . '::reload: No file associated with method ' .
771     $self->{name} unless $self->{file};
772     my $tmp = $self->load_XPL_file($self->{file});
773    
774     # Re-calculate the signature table, in case that changed as well
775     return (ref $tmp) ? $self->make_sig_table : $tmp;
776     }
777    
778     ###############################################################################
779     #
780     # Sub Name: load_XPL_file
781     #
782     # Description: Load a XML-encoded method description (generally denoted
783     # by a *.xpl suffix) and return the relevant information.
784     #
785     # Note that this does not fill in $self if $self is a hash
786     # or object reference. This routine is not a substitute for
787     # calling new() (which is why it isn't part of the public
788     # API).
789     #
790     # Arguments: NAME IN/OUT TYPE DESCRIPTION
791     # $self in ref Object of this class
792     # $file in scalar File to load
793     #
794     # Returns: Success: hashref of values
795     # Failure: error string
796     #
797     ###############################################################################
798     sub load_XPL_file
799     {
800     my $self = shift;
801     my $file = shift;
802    
803     require XML::Parser;
804    
805     my ($me, $pkg, $data, $signature, $code, $codetext, $accum, $P, %attr);
806     local *F;
807    
808     if (ref($self) eq 'SCALAR')
809     {
810     $me = __PACKAGE__ . '::load_XPL_file';
811     }
812     else
813     {
814     $me = (ref $self) || $self || __PACKAGE__;
815     $me .= '::load_XPL_file';
816     }
817     $data = {};
818     # So these don't end up undef, since they're optional elements
819     $data->{hidden} = 0; $data->{version} = ''; $data->{help} = '';
820     $data->{called} = 0;
821     open(F, "< $file");
822     return "$me: Error opening $file for reading: $!" if ($?);
823     $P = XML::Parser
824     ->new(Handlers => {Char => sub { $accum .= $_[1] },
825     Start => sub { %attr = splice(@_, 2) },
826     End =>
827     sub {
828     my $elem = $_[1];
829    
830     $accum =~ s/^[\s\n]+//;
831     $accum =~ s/[\s\n]+$//;
832     if ($elem eq 'signature')
833     {
834     $data->{signature} ||= [];
835     push(@{$data->{signature}}, $accum);
836     }
837     elsif ($elem eq 'code')
838     {
839     $data->{$elem} = $accum
840     unless ($attr{language} and
841     $attr{language} ne 'perl');
842     }
843     elsif (substr($elem, -3) eq 'def')
844     {
845     # Don't blindly store the container tag...
846     # We may need it to tell the caller what
847     # our type is
848     $$self = ucfirst substr($elem, 0, -3)
849     if (ref($self) eq 'SCALAR');
850     }
851     else
852     {
853     $data->{$elem} = $accum;
854     }
855    
856     %attr = ();
857     $accum = '';
858     }});
859     return "$me: Error creating XML::Parser object" unless $P;
860     # Trap any errors
861     eval { $P->parse(*F) };
862     return "$me: Error parsing $file: $@" if $@;
863    
864     # Try to normalize $codetext before passing it to eval
865     my $class = __PACKAGE__; # token won't expand in the s/// below
866     ($codetext = $data->{code}) =~
867     s/sub[\s\n]+([\w:]+)?[\s\n]*\{/sub \{ package $class; /;
868     $code = eval $codetext;
869     return "$me: Error creating anonymous sub: $@" if $@;
870    
871     $data->{code} = $code;
872     # Add the file's mtime for when we check for stat-based reloading
873     $data->{mtime} = (stat $file)[9];
874     $data->{file} = $file;
875    
876     $data;
877     }
878    
879     ###############################################################################
880     #
881     # Sub Name: call
882     #
883     # Description: Encapsulates the invocation of the code block that the
884     # object is abstracting. Manages parameters, signature
885     # checking, etc.
886     #
887     # Arguments: NAME IN/OUT TYPE DESCRIPTION
888     # $self in ref Object of this class
889     # $srv in ref An object derived from the
890     # RPC::XML::Server class
891     # @dafa in list The params for the call itself
892     #
893     # Globals: None.
894     #
895     # Environment: None.
896     #
897     # Returns: Success: value
898     # Failure: dies with RPC::XML::Fault object as message
899     #
900     ###############################################################################
901     sub call
902     {
903     my ($self, $srv, @data) = @_;
904    
905     my (@paramtypes, @params, $signature, $resptype, $response, $name);
906    
907     $name = $self->name;
908     # Create the param list.
909     # The type for the response will be derived from the matching signature
910     @paramtypes = map { $_->type } @data;
911     @params = map { $_->value } @data;
912     $signature = join(' ', @paramtypes);
913     $resptype = $self->match_signature($signature);
914     # Since there must be at least one signature with a return value (even
915     # if the param list is empty), this tells us if the signature matches:
916     return RPC::XML::fault->new(301,
917     "method $name nas no matching " .
918     'signature for the argument list')
919     unless ($resptype);
920    
921     # Set these in case the server object is part of the param list
922     local $srv->{signature} = [ $resptype, @paramtypes ];
923     local $srv->{method_name} = $name;
924     # For RPC::XML::Method (and derivatives), pass the server object
925     if ($self->isa('RPC::XML::Method'))
926     {
927     unshift(@params, $srv);
928     }
929    
930     # Now take a deep breath and call the method with the arguments
931     eval { $response = $self->{code}->(@params); };
932     # Report a Perl-level error/failure if it occurs
933     return RPC::XML::fault->new(302, "Method $name returned error: $@") if $@;
934    
935     $self->{called}++;
936     # Create a suitable return value
937     if ((! ref($response)) && UNIVERSAL::can("RPC::XML::$resptype", 'new'))
938     {
939     my $class = "RPC::XML::$resptype";
940     $response = $class->new($response);
941     }
942    
943     $response;
944     }

MailToCvsAdmin">MailToCvsAdmin
ViewVC Help
Powered by ViewVC 1.1.26 RSS 2.0 feed