File Coverage

blib/lib/POE/Component/Generic.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package POE::Component::Generic;
2             # $Id: Generic.pm 1047 2012-11-30 19:53:13Z fil $
3              
4 9     9   305369 use strict;
  9         23  
  9         380  
5              
6 9     9   14194 use POE 0.31;
  0            
  0            
7             use POE::Wheel::Run;
8             use POE::Filter::Line;
9             use POE::Filter::Reference;
10             use POE::Component::Generic::Child;
11             use POE::Component::Generic::Object;
12             use Carp qw(carp croak);
13             use Devel::Symdump;
14             use vars qw($AUTOLOAD $VERSION);
15             use Config;
16             use Scalar::Util qw( reftype blessed );
17              
18             $VERSION = '0.1403';
19              
20              
21             ##########################################################################
22             sub spawn
23             {
24             my( $package, @args ) = @_;
25             croak "$package needs an even number of parameters" if @args & 1;
26              
27             my $self = $package->new( @args );
28              
29             if( $^O eq 'MSWin32' and $self->{alt_fork} and $POE::VERSION < 1.3 ) {
30             carp "Sorry, alt_fork does not work on MSWin32.";
31             delete $self->{alt_fork};
32             }
33              
34             my $options = $self->{'options'};
35              
36             $self->{session_id} = POE::Session->create(
37             object_states => [
38             $self => {
39             map { $_ => '__request1' }
40             keys %{$self->{package_map}{ $self->{package} }}
41             },
42             $self => [ qw(_start _stop shutdown kill _child __request2
43             __wheel_close __wheel_err
44             __wheel_out __wheel_stderr
45             )
46             ],
47             ],
48             ( ( defined ( $options ) and ref ( $options ) eq 'HASH' ) ?
49             ( options => $options ) : () ),
50             )->ID();
51              
52             $self->{debug}
53             and warn "$self->{name}: session $self->{session_id} created for $self->{package}";
54            
55             return $self;
56             }
57              
58             ##################################################
59             sub new
60             {
61             my $package = shift;
62             @_ = ( package => $_[0] ) if @_ == 1;
63             croak "$package needs an even number of parameters" if @_ & 1;
64             my %params;
65             {
66             my %p = @_;
67             while( my( $k, $v ) = each %p ) {
68             $params{ lc $k } = $v;
69             }
70             }
71              
72             unless( $params{package} ) {
73             croak "Please specify a package";
74             }
75              
76             # map of commands to packages
77             $params{package_map} = {};
78             # param storage
79             $params{store} = {};
80             # request IDs
81             $params{RID} = "REQ000000";
82              
83             my $self = bless(\%params, $package);
84              
85             $self->{child_PID} = 0;
86              
87             if( $self->{error} ) {
88             my $rt = reftype $self->{error};
89             unless( $rt ) {
90             $self->{error_session} = $poe_kernel->get_active_session;
91             }
92             elsif( 'HASH' eq $rt ) {
93             @{ $self }{ qw( error_session error ) } =
94             @{ $self->{error} }{ qw( session event ) };
95             }
96             }
97              
98             #######
99             if( $self->{on_exit} ) {
100             my $r = ref $self->{on_exit};
101             unless( $r ) {
102             $self->{on_exit} = [ $poe_kernel->get_active_session, $self->{on_exit} ];
103             }
104             elsif( 'ARRAY' eq $r or blessed $self->{on_exit} ) {
105             # POEx::URI is OK.
106             }
107             else {
108             croak "on_exit must be a array reference, not $r";
109             }
110             }
111              
112              
113             #######
114             POE::Component::Generic::Child::package_load( $self->{package} );
115             $self->__package_register( $self->{package}, {
116             methods => delete($self->{methods}),
117             callbacks => delete($self->{callbacks}),
118             postbacks => delete($self->{postbacks}),
119             factories => delete($self->{factories})
120             } );
121              
122             if( $self->{packages} ) {
123             my $pdefs = delete $self->{packages};
124             while( my( $class, $pdef ) = each %{ $pdefs } ) {
125             $self->__package_register( $class, $pdef );
126             }
127             }
128              
129             return $self;
130             }
131              
132             ##################################################
133             sub __package_register
134             {
135             my( $self, $class, $pdef ) = @_;
136              
137             unless( ref $pdef ) {
138             $self->__package_map( $class );
139             }
140             elsif( 'ARRAY' eq ref $pdef ) {
141             $self->__package_map( $class, $pdef );
142             }
143             else {
144             $self->__package_map( $class, $pdef->{methods} );
145             $self->__callback_map( $class, $pdef->{callbacks} );
146             $self->__postback_map( $class, $pdef->{postbacks} );
147             $self->__factory_map( $class, $pdef->{factories} );
148             }
149             }
150              
151             ##################################################
152             # Build a map of all methods => package
153             sub __package_map
154             {
155             my( $self, $class, $methods ) = @_;
156             my @methods = $self->__package_methods( $class );
157            
158             my %OK;
159             if( $methods ) {
160             @OK{ @$methods } = (1) x @$methods;
161             }
162              
163              
164             my $map = {};
165              
166             foreach my $p ( @methods ) {
167             my ($pk,$sub) = $self->__method_map( $p );
168             next unless $sub;
169             next if $map->{ $sub };
170             next if $methods and not $OK{ $sub };
171            
172             my $o = $p;
173             if (defined &$o) {
174             $map->{ $sub } = $pk;
175             }
176             }
177              
178             $self->{package_map}{ $class } = $map;
179             }
180              
181             ##################################################
182             sub __method_map
183             {
184             my( $P, $method ) = @_;
185             ($method =~ m/^(.+)\:\:([^\:]+)/);
186             my $pk = $1;
187             my $sub = $2;
188              
189             return unless $P->__is_method_name( $pk, $sub );
190             return ( $pk, $sub );
191             }
192              
193             sub __is_method_name
194             {
195             my( $P, $pk, $sub ) = @_;
196             return unless $sub =~ /[^A-Z]$/; # I18N detection of CONSTANTS
197             return if $sub =~ m/^_/ or # private and/or protected method
198             $sub =~ m/(carp|croak|confess|cluck)$/; # very common subs
199             return 1;
200             }
201              
202             ##################################################
203             # Get a list of all methods from the package
204             sub __package_methods
205             {
206             my( $P, $class ) = @_;
207             my @obj = Devel::Symdump->functions( $class );
208              
209             my $isa = Symbol::qualify_to_ref( "ISA", $class );
210             foreach my $subpack ( @{ *$isa } ) {
211             next if $subpack eq 'Exporter';
212             push @obj, $P->__package_methods( $subpack );
213             }
214             # we can't distinguish methods from functions :-/
215             return @obj
216             }
217              
218             ##########################################################################
219             # POE related object methods
220              
221             sub _start
222             {
223             my ($kernel,$self) = @_[KERNEL,OBJECT];
224              
225             $self->{session_id} = $_[SESSION]->ID;
226              
227             if ( $self->{alias} ) {
228             $self->{name} = $self->{alias};
229             $kernel->alias_set( $self->{alias} );
230             $self->{debug} and warn "$self->{name}: alias is $self->{alias}";
231             }
232             else {
233             $self->{name} = "poe-generic";
234             $kernel->refcount_increment( $self->session_id() => __PACKAGE__ );
235             }
236              
237             $self->{referenced} = 1;
238            
239             my $child_p = $self->{child_package} || 'POE::Component::Generic::Child';
240             my %prog = ( Program => $self->__subref( $child_p, $self->{name} ) );
241              
242             if ($self->{alt_fork}) {
243            
244             my $perl = $^X;
245             $perl = $self->{alt_fork} if -x $self->{alt_fork};
246             if( $ENV{HARNESS_PERL_SWITCHES} ) {
247             $perl .= " $ENV{HARNESS_PERL_SWITCHES}";
248             }
249             my $os_quote = ($^O eq 'MSWin32') ? q(") : q('); #"
250              
251             %prog = (Program => "$perl -M".ref( $self )
252             ." -I".join( ' -I', map quotemeta, @INC )
253             ." -e $os_quote".__PACKAGE__."::process_requests(qq(\Q$child_p\E),qq(\Q$self->{name}\E), 1)$os_quote");
254             $self->{debug} and
255             warn "$self->{name}: Launching $prog{Program}";
256             }
257            
258             $self->{wheel} = POE::Wheel::Run->new(
259             %prog,
260             CloseOnCall => 0,
261             StdinFilter => POE::Filter::Reference->new(),
262             StdoutFilter => POE::Filter::Reference->new(),
263             StderrFilter => POE::Filter::Line->new(),
264             StdoutEvent => '__wheel_out',
265             StderrEvent => '__wheel_stderr',
266             ErrorEvent => '__wheel_err',
267             CloseEvent => '__wheel_close',
268             );
269              
270             #########
271             my $pid = $self->{wheel}->PID;
272             my $state = ref( $self )."--child--".$pid;
273             # NB we don't ever remove this state, but a- it won't be enough to
274             # keep the session alive, and b- only one state is created ever anyway
275             $poe_kernel->state( $state, $self, '_child' );
276             $poe_kernel->sig_child( $pid, $state );
277             $self->{child_PID} = $pid;
278             #########
279             # Tell the other side to create an object
280             $self->{object_options} ||= [];
281             unless( ref $self->{object_options} ) {
282             $self->{object_options} = [ $self->{object_options} ]
283             }
284              
285             my $new = { req => 'setup',
286             debug => $self->{debug},
287             args => $self->{object_options},
288             package => $self->{package},
289             name => $self->{name},
290             verbose => $self->{verbose},
291             };
292             $new->{size} = $self->{size} if $self->{size};
293            
294             $self->{debug} and warn "$self->{name}: Ask to create object";
295             $self->{wheel}->put( $new );
296              
297             undef;
298             }
299              
300             # Build the smallest closure possible
301             sub __subref
302             {
303             my( $child_p, $name ) = @_[1,2];
304             return sub { process_requests( $child_p, $name ) };
305             }
306              
307             sub _stop
308             {
309             my( $self ) = @_[OBJECT, ARG0];
310             $self->{debug} and
311             warn "$self->{name}: _stop";
312             }
313              
314             ######################################################
315             # Clean up everything
316             sub _done
317             {
318             my( $self ) = @_;
319              
320             $self->{debug} and warn "$self->{name}: _done";
321             $self->{child_PID} = 0;
322              
323             # remove the wheel
324             if ($self->{wheel}) {
325             $self->{debug} and warn "$self->{name}: drop wheel";
326             $self->{wheel}->shutdown_stdin;
327             delete $self->{wheel};
328             delete $self->{close};
329             delete $self->{CHLD};
330             }
331              
332             # remove alias or decrease ref count
333             my @aliases;
334             if( $self->{referenced} ) {
335             if ( $self->{alias} ) {
336             foreach my $alias ( $poe_kernel->alias_list() ) {
337             $self->{debug} and warn "$self->{name}: remove alias $alias";
338             $poe_kernel->alias_remove( $alias );
339             push @aliases, $alias;
340             }
341             } else {
342             $poe_kernel->refcount_decrement($self->session_id() => __PACKAGE__);
343             }
344             $self->{referenced} = 0;
345             }
346              
347             # Also need to clean up any pending
348             $self->__session_clear;
349              
350             # Tell the user code about this
351             if( $self->{on_exit} ) {
352             $poe_kernel->post( @{$self->{on_exit}}, { objects=>\@aliases } );
353             }
354             }
355              
356             sub __is_done
357             {
358             my( $self ) = @_;
359             return 0==$self->{child_PID};
360             }
361              
362             sub _close_on
363             {
364             my( $self, $what ) = @_;
365              
366             $self->{$what}++;
367             if( $self->{close} and $self->{CHLD} ) {
368             $self->_done;
369             }
370             }
371              
372             ######################################################
373             # POE request to the parent object
374             sub __request1
375             {
376             my ( $self,$state, $sender ) = @_[OBJECT, STATE, SENDER];
377             $self->__request( $sender->ID, $state, @_[ARG0..$#_] );
378             }
379              
380             # POE request to a sub-object
381             sub __request2
382             {
383             my ( $self, $sender ) = @_[OBJECT, SENDER];
384              
385             $self->__request( $sender->ID, @_[ARG0..$#_] );
386             }
387              
388              
389             ######################################################
390             # Send request to child process
391             sub __request
392             {
393             my ( $self, $sender, $method, $hash, @args ) = @_;
394            
395             warn "$self->{name}: $$: processing request $method\n" if ($self->{debug});
396            
397             # Get the arguments
398             if (ref( $hash ) eq 'HASH') {
399             # shallow copy because we are going to modify this hash
400             $hash = { %{ $hash } };
401             }
402             else {
403             die "Data hash is not a hashref!";
404             }
405            
406             unless ($self->{wheel}) {
407             warn "$self->{name}: No wheel";
408             return;
409             }
410            
411             # If we have an {event}, it means the user wants *something* back
412             if( $hash->{event} and not defined $hash->{wantarray} ) {
413             $hash->{wantarray} = 0;
414             }
415             my $params = {
416             method => $method,
417             event => $hash->{event},
418             wantarray => $hash->{wantarray},
419             session => ($hash->{session}||$sender),
420             args => \@args,
421             package => ($hash->{package}||$self->{package})
422             };
423              
424             $params->{obj} = $hash->{obj} if $hash->{obj};
425             if( ref $params->{obj} ) {
426             $params->{obj} = $params->{obj}->ID;
427             }
428             my $class = $params->{package} || $self->{package};
429             my $RID = $params->{RID} = $self->{RID}++;
430              
431             if( $self->{factory_map}{ $method } ) {
432             $self->__factory_marshall( $params );
433             }
434              
435             # param storage
436             if ( keys %$hash ) {
437             # id to match in param storage
438             $self->{store}->{$RID} = $hash;
439             $hash->{session} = $params->{session};
440             $hash->{package} = $params->{package};
441             }
442              
443             # if we have an event to report to...make sure it stays around
444             if ( $hash->{event} ) {
445             $self->__session_inc( $hash );
446             }
447              
448             if( $self->{callback_map}{$class}{ $method } ) {
449             $self->__callback_marshall( $params );
450             }
451             if( $self->{postback_map}{$class}{ $method } ) {
452             $self->__postback_marshall( $params, $sender );
453             }
454              
455             $self->{debug} and warn "$self->{name}: request put";
456             $self->{wheel}->put( $params );
457            
458             return;
459             }
460              
461             sub __session_inc
462             {
463             my( $self, $hash ) = @_;
464             my $session = $self->__session_id( $hash );
465            
466             $poe_kernel->refcount_increment( $session => $self->{name} );
467             $self->{pending}{$session}++;
468             }
469              
470             sub __session_dec
471             {
472             my( $self, $hash ) = @_;
473             my $session = $self->__session_id( $hash );
474            
475             if( $self->{pending}{$session} ) {
476             $poe_kernel->refcount_decrement( $session => $self->{name} );
477             delete $self->{pending}{$session} unless $self->{pending}{$session}--;
478             }
479             }
480              
481             sub __session_clear
482             {
483             my( $self ) = @_;
484             foreach my $session ( keys %{ $self->{pending}||{} } ) {
485             while( $self->{pending}{$session} ) {
486             $self->__session_dec( { session=>$session } );
487             }
488             }
489             }
490              
491             sub __session_id
492             {
493             my( $self, $hash ) = @_;
494             my $session = $poe_kernel->alias_resolve( $hash->{session} );
495             # TODO : Above will explode if $hash->{session} isn't an extant
496             # session. This is OK, but the error message will point here, not
497             # to the user's code.
498              
499             return unless $session;
500             return $session->ID;
501             }
502              
503             ##################################################
504             # Prepare the callback definitions
505             sub __callback_map
506             {
507             my( $self, $class, $c ) = @_;
508             return unless $c;
509            
510             $c = [$c] unless ref $c;
511             my %callbacks;
512             @callbacks{ @$c } = map {method=>$_}, @$c;
513             $self->{callback_map}{ $class } = \%callbacks;
514             return;
515             }
516              
517              
518              
519             ##################################################
520             # Marshall any callback definitions
521             sub __callback_marshall
522             {
523             my( $self, $params ) = @_;
524            
525             my $cmap = $self->{callback_map}{ $params->{package} }{ $params->{method} };
526             return unless $cmap;
527              
528             my $args = $params->{args};
529             my @callbacks;
530             for( my $pos=0; $pos <= $#$args; $pos++ ) {
531             next unless 'CODE' eq (reftype( $args->[$pos] ) ||'');
532            
533             my $CBid = "---CALLBACK-$params->{RID}-$pos---";
534              
535             ## the callbacks will be GCed when the method returns, in ->response
536             $self->{callback_defs}{ $params->{RID} }{ $pos } = {
537             coderef => $args->[$pos]
538             };
539              
540             push @callbacks, { CBid=>$CBid, pos=>$pos };
541             $args->[$pos] = $CBid;
542             }
543             return unless @callbacks;
544             $params->{callbacks} = \@callbacks;
545             return;
546             }
547              
548             ##################################################
549             # Convert a hash-argument into a callback coderef
550             sub __callback_argument
551             {
552             my( $self, $event, $args ) = @_;
553             my $session = $poe_kernel->get_active_session;
554             if( $args->{"${event}Event"} ) { # ex: StdoutEvent => 'state'
555             return $session->postback( $args->{"{event}Event"} );
556             }
557             elsif( $args->{"${event}Sub"} ) { # ex: StdoutSub => sub { }
558             return $args->{"${event}Sub"};
559             }
560             return undef(); # undef() => not present
561             }
562              
563              
564              
565              
566             ##################################################
567             # Prepare the postback definitions
568             sub __postback_map
569             {
570             my( $self, $class, $c ) = @_;
571             return unless $c;
572              
573             $c = {$c => {pos=>0}} unless ref $c;
574             $c = { map { $_ => 0 } @$c } if 'ARRAY' eq ref $c;
575              
576             my %postbacks;
577             while( my( $method, $pdef ) = each %$c ) {
578             $postbacks{ $method } = { method=>$method, pos=>[] };
579             unless( ref $pdef ) {
580             $postbacks{ $method }{pos} = [$pdef||0];
581             }
582             elsif( 'ARRAY' eq ref $pdef ) {
583             $postbacks{ $method }{pos} = [ map { $_||0 } @$pdef ];
584             }
585             else {
586             carp "postback position must be an arrayref or scalar";
587             }
588             }
589            
590             $self->{postback_map}{ $class } = \%postbacks;
591             return;
592             }
593              
594              
595              
596             ##################################################
597             # Marshall any postback definitions
598             sub __postback_marshall
599             {
600             my( $self, $params, $sender ) = @_;
601            
602             my $pmap = $self->{postback_map}{ $params->{package} }{ $params->{method} };
603             return unless $pmap;
604              
605             my $args = $params->{args};
606             my @postbacks;
607             foreach my $pos ( @{ $pmap->{pos} } ) {
608            
609             my $PBid = "---POSTBACK-$params->{package}-$pmap->{method}-$pos-$params->{RID}---";
610              
611             push @postbacks, $self->__postback_def( $args->[$pos], $sender, $params->{RID} );
612             $postbacks[-1]->{pos} = $pos;
613             $postbacks[-1]->{PBid} = $PBid;
614              
615             $args->[$pos] = $PBid;
616             }
617             return unless @postbacks;
618             $params->{postbacks} = \@postbacks;
619             return;
620             }
621              
622             ##################################################
623             sub __postback_def
624             {
625             my( $self, $arg, $sender, $RID ) = @_;
626              
627             unless( ref $arg ) { # simply an event name
628             return { event=>$arg, session=>$sender };
629             }
630             elsif( 'HASH' eq ref $arg ) { # { event=>'...' }
631             $arg->{session} ||= $sender;
632             return $arg;
633             }
634             die "$arg isn't not a valid postback";
635             }
636              
637             ##################################################
638             # Convert a hash-argument into a postback hashref
639             sub __postback_argument
640             {
641             my( $self, $event, $args ) = @_;
642              
643             my $session = $poe_kernel->get_active_session;
644             if( $args->{"${event}Event"} ) {
645             # ex: StdoutEvent => 'state'
646             # or StdoutEvent => { event=>'state', session=>'sessionID'}
647             return $args->{"${event}Event"};
648             }
649             elsif( $args->{"${event}Sub"} ) { # ex: StdoutSub => sub { }
650             croak "${event}Code not supported yet";
651            
652             # Problem : how do we know when to remove the state?
653             my $state_name = "SOMETHING";
654             $session->state( $state_name => $args->{"{event}Sub"} );
655             return $state_name;
656             }
657             return undef(); # undef() => not present
658             }
659              
660              
661              
662              
663              
664              
665             ##################################################
666             # Prepare the factory-method definitions
667             sub __factory_map
668             {
669             my( $self, $class, $c ) = @_;
670             return unless $c;
671             $c = {$c => {method=>$c}} unless ref $c;
672             $c = { map { $_ => {method=>$_} } @$c } if 'ARRAY' eq ref $c;
673              
674             my %factories;
675             @factories{ keys %$c } = map { ref $c->{$_} ? $c->{$_} : {method=>$_} }
676             keys %$c;
677             $self->{factory_map} = \%factories;
678             return;
679             }
680              
681             ##################################################
682             # Prepare a request for a factory method
683             sub __factory_marshall
684             {
685             my( $self, $params ) = @_;
686              
687             # tell the remote side it's a special request
688             $params->{factory} = $params->{method};
689             return;
690             }
691              
692             ##################################################
693             #
694             sub __factory_response
695             {
696             my( $self, $input ) = @_;
697              
698             my $obj_def = $input->{result}->[0];
699             $input->{result} = [ POE::Component::Generic::Object->new(
700             $obj_def,
701             $self->session_id,
702             $self->{package_map}{ $obj_def->{package}||'' } )
703             ];
704              
705             return;
706             }
707              
708              
709              
710              
711             ######################################################
712             # Child process sent us a response
713             sub __wheel_out
714             {
715             my ($self,$input) = @_[ OBJECT,ARG0 ];
716              
717             $self->{debug} and
718             warn "$self->{name}: __wheel_out";
719              
720             $input->{result} ||= [];
721              
722             if( $input->{response} ) {
723             $self->OOB_response( $input );
724             return;
725             }
726              
727             $self->response( $input );
728             undef;
729             }
730              
731             sub __wheel_stderr {
732             my ($kernel,$self,$input) = @_[KERNEL,OBJECT,ARG0];
733              
734             warn "$self->{name}:ERR: $input\n"
735             if $self->{debug} or $self->{verbose};
736              
737             if( $self->{error} ) {
738             $poe_kernel->post( $self->{error_session}, $self->{error},
739             { stderr=>$input }
740             );
741             }
742             }
743              
744             sub __wheel_err {
745             my ($self, $operation, $errnum, $errstr, $wheel_id) = @_[OBJECT, ARG0..ARG3];
746            
747             warn "$self->{name}: Wheel $wheel_id generated $operation error $errnum: $errstr\n"
748             if $self->{debug} or
749             ( $self->{verbose} and $errnum != 0 );
750             if( $errnum!=0 and $self->{error} ) {
751             $poe_kernel->post( $self->{error_session}, $self->{error},
752             { operation => $operation,
753             errnum => $errnum,
754             errstr => $errstr }
755             );
756             }
757             }
758              
759             sub __wheel_close {
760             my $self = $_[OBJECT];
761            
762             warn "$self->{name}: Wheel closed\n" if ($self->{debug});
763            
764             # We should see a CHLD soon
765             $self->_close_on( 'close' );
766             }
767              
768             sub _child
769             {
770             my( $self, $name, $PID, $ret ) = @_[ OBJECT, ARG0..ARG2 ];
771             unless( $PID == ($self->{child_PID}||0) ) {
772             $self->{debug} and warn "$self->{name}: Got CHLD for $PID, not $self->{child_PID}\n";
773             return;
774             }
775             $self->{debug} and warn "$self->{name}: Child $PID exited with $ret";
776             $poe_kernel->sig_handled;
777             $self->_close_on( 'CHLD' );
778             return;
779             }
780              
781              
782             ##########################################################################
783             #
784             # Child sent us a response to a {req} request
785             sub OOB_response
786             {
787             my( $self, $input ) = @_;
788              
789             my $res = $input->{result};
790              
791             if( $input->{response} eq 'new' ) {
792             # $self->{child_PID} = $input->{PID};
793             $self->{debug} and warn "$self->{name}: Child PID=$input->{PID}";
794             }
795             elsif( $input->{response} eq 'callback' ) {
796             my $RID = $input->{RID};
797             my $pos = $input->{pos};
798             my $CB = $self->{callback_defs}{ $RID }{ $pos };
799            
800             unless( $CB ) {
801             warn "$self->{name}: Callback to undefined $RID\[$input->{pos}]";
802             return;
803             }
804             eval { $CB->{coderef}->( @$res ) };
805             warn "$self->{name}: Error in callback: $@" if $@;
806             }
807             elsif( $input->{response} eq 'postback' ) {
808             my $PBid = $input->{PBid};
809            
810             unless( $input->{session} and $input->{event} ) {
811             warn "$self->{name}: Bad postback $PBid. Missing {session} or {event}";
812             return;
813             }
814             $poe_kernel->post( $input->{session} => $input->{event}, @$res );
815             }
816             else {
817             warn "$self->{name}: Unknown OOB child response $input->{response}";
818             }
819             }
820              
821              
822              
823             ############################################################################
824             # Child sent us a regular response
825             sub response
826             {
827             my( $self, $input ) = @_;
828              
829             if (defined $input->{RID}) {
830             my $RID = delete $input->{RID};
831             # splice in stored data, because we might not trust other side
832             @{ $input }{ keys %{$self->{store}->{$RID}} }
833             = values %{$self->{store}->{$RID}};
834             delete $self->{store}->{$RID};
835             delete $self->{callback_defs}->{$RID};
836             }
837              
838             if( $input->{factory} ) {
839             $self->__factory_response( $input );
840             }
841              
842             my $session = delete $input->{session};
843             my $event = delete $input->{event};
844              
845             if ($event) {
846             $self->{debug} and warn "$self->{name}: ($$) Reply to $session/$event";
847             $poe_kernel->post( $session => $event, $input, @{$input->{result}} );
848             $self->__session_dec( {session=>$session} );
849             }
850             }
851              
852              
853              
854              
855             ############################################################################
856             # Dual event and object methods
857              
858             sub kill {
859             unless (UNIVERSAL::isa($_[KERNEL],'POE::Kernel')) {
860             my $self = shift;
861             if ($poe_kernel and $self->session_id) {
862             $poe_kernel->call($self->session_id() => 'kill' => @_);
863             }
864             return;
865             }
866            
867             my ($kernel,$self,$sig) = @_[KERNEL,OBJECT,ARG0];
868             $self->{debug} and warn "$self->{name}: $self->{wheel}->kill( $sig )";
869             return unless $self->{wheel};
870             $self->{wheel}->kill( $sig );
871             }
872              
873             sub shutdown {
874             unless (UNIVERSAL::isa($_[KERNEL],'POE::Kernel')) {
875             my $self = shift;
876             if ($poe_kernel and $self->session_id) {
877             $poe_kernel->call($self->session_id() => 'shutdown' => @_);
878             }
879             return;
880             }
881            
882             my ($kernel,$self) = @_[KERNEL,OBJECT];
883              
884              
885             $self->{debug} and warn "$self->{name}: shutdown";
886             # if we still have a wheel, tell it to close
887             if ($self->{wheel}) {
888             $self->{wheel}->shutdown_stdin;
889             # this provokes CHLD, which will call ->_done
890             }
891             else {
892             # no wheel; clean up now
893             $self->_done;
894             }
895             undef;
896             }
897              
898              
899             # Object methods
900              
901             sub session_id {
902             shift->{session_id};
903             }
904              
905             sub yield {
906             my $self = shift;
907             $poe_kernel->post($self->session_id() => @_);
908             }
909              
910             sub call {
911             my $self = shift;
912             $poe_kernel->call($self->session_id() => @_);
913             }
914              
915             sub DESTROY {
916             $_[0]->{debug} and
917             warn "$_[0]->{name}: DESTROY";
918             if (UNIVERSAL::isa($_[0],__PACKAGE__)) {
919             $_[0]->shutdown();
920             }
921             }
922              
923             sub AUTOLOAD
924             {
925             my $self = shift;
926              
927             my $method = $AUTOLOAD;
928             $method =~ s/.*:://;
929              
930             my $hash;
931              
932             my $bad = '';
933             unless( UNIVERSAL::isa( $self, __PACKAGE__ ) ) {
934             $bad = 'object';
935             }
936             elsif( not blessed $self ) {
937             $bad = 'package';
938             }
939             else {
940             $hash = shift;
941             unless( ref($hash) eq 'HASH' ) {
942             croak "First argument to $method must be a hashref";
943             }
944              
945             unless( $self->{package_map}{ $self->{package} }{ $method } ) {
946             $bad = 'object';
947             }
948             }
949              
950             if( $bad ) {
951             croak qq( Can't locate $bad method "$method" via package ")
952             .ref( $self ). qq("); #"
953             }
954              
955             $hash->{wantarray} = wantarray() unless defined $hash->{wantarray};
956              
957             warn "$self->{name}: autoload method $method" if ($self->{debug});
958            
959             # use ->call() so that they happen in order
960             $poe_kernel->call( $self->session_id() => $method => $hash => @_ );
961             }
962              
963              
964             ##########################################################################
965             # Main Wheel::Run process sub
966              
967             sub process_requests {
968             my( $class, $name, $alt_fork ) = @_;
969             $alt_fork ||= 0;
970              
971             my $ID = $name;
972             $ID =~ s/\W/-/g;
973              
974             my $runner = $class->new(
975             name => __PACKAGE__,
976             ID => $ID,
977             size => 4096,
978             debug => 0,
979             proc => $0,
980             alt_fork => $alt_fork
981             );
982             $runner->loop;
983             }
984              
985              
986             1;
987              
988             __END__