Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things

POE method problem (building applications with POE)

by Orchid_NL (Novice)
on Dec 31, 2004 at 11:36 UTC ( #418516=perlquestion: print w/ replies, xml ) Need Help??
Orchid_NL has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to get the hang of POE and followed this tutorial on Building Applications with POE.

Problem is when I try the code used in the tutorial (copy & past, the download doesn't work) it only works partially.

The TCP-server starts up, accepts connections but after entering a test string ('foo=bar&quux=perl') and hitting enter it stops with this failure message:
Can't call method "put" on an undefined value at ./ line 110.

Problem is the method put is called with a defined value, Data Dumper gives this:

$VAR1 = { 'quux' => 'perl', 'foo' => 'bar' }; $VAR1 = 2;
Code snippet (line 110 is the $_HEAP part)

sub client_input { my ($input, $wheel_id) = @_[ARG0, ARG1]; use Data::Dumper; print Dumper $input; print Dumper $wheel_id; $_[HEAP]->{clients}->{ $wheel_id }->put( $input ); }
Complete code can be found here: Orchid's scratchpad
Help much appreciated (if you need more information let me know!).

Comment on POE method problem (building applications with POE)
Select or Download Code
Replies are listed 'Best First'.
Re: POE method problem (building applications with POE)
by RMGir (Prior) on Dec 31, 2004 at 14:04 UTC
    Interesting... I'd suggest adding a debugging print to factory_success, probably like so:
    sub factory_success { my( $handle, $wheel_id ) = @_[ARG0, ARG1]; $_[HEAP]->{clients}->{ $wheel_id } = POE::Wheel::ReadWrite->new( Handle => $handle, Driver => POE::Driver::SysRW->new(), Filter => POE::Filter::SimpleQueryString->new(), InputEvent => 'client_input', ); print "factory_success called, creating wheel $wheel_id for handle + $handle\n"; }
    and then add a similar print to client_input.

    I don't see any obvious typos in your code or in the article; the perldoc for POE::Wheel::ReadWrite says ARG0 and ARG1 are what you say they are.

    (Although the perldoc for the version I have has a whacky example:

    sub input_state { my ($heap, $input, $wheel_id) = @_[HEAP, ARG0, ARG1]; print "Echoing input from wheel $wheel_id: $input\n"; $heap->{wheel}->put($input); # Echo it back. }
    Note the use of wheel rather than wheel_id :) )

    Wish I could be more help, but the link to the example code in that article is dead, so I can't run a local copy to see if I get the same problems you do.

      Let me start with a "Happy New Year!" to all perlmonks.
      Your debugging print tip did the trick: it showed that $wheel_id (ARG1) is empty.
      POE::Wheel::SocketFactory uses ARG3 not ARG1 for the wheels id.

      Because of this I had to change the way the wheel id is passed from subroutine 'start' to 'factory_succes' to 'client_input'.
      I used the HEAP variable (is this the correct way?)

      Problem is now that it works with one telnet session. As soon a second telnet session is started the script breaks connection with telnet session one.
      As far I can see this is because POE::Wheel::SocketFactory reuses the wheel id from telnet session one for telnet session two.
      I don't know why it does this because according to the documentation it should start a new 'wheel'.
      Can somebody help me with this problem?

      In the second part of the article concurrent telnet sessions work by using POE::Component::Server:TCP.
      But I'm a bit stubborn and want to get the basics right before moving on...

      Here is the code I modified:

      #!/usr/bin/perl -w use warnings; use strict; use Carp qw(carp croak); use POE qw( Wheel::SocketFactory Driver::SysRW Wheel::ReadWrite); { package POE::Filter::SimpleQueryString; use Carp qw(carp croak); sub new { my $class = shift; my $self = bless {}, $class; return $self; } sub get { my $self = shift; my $buffer = shift; my @chunks; foreach my $record (@$buffer) { $record =~ s/\x0d\x0a$//; my @pairs = split(/&/, $record); my %chunk; foreach my $pair (@pairs) { my ($key, $value) = split(/=/, $pair, 2); if(defined $chunk{$key}) { if(ref $chunk{$key} eq 'ARRAY') { push @{ $chunk{$key} }, $value; } else { $chunk{$key} = [ $chunk{$key}, $value ], } } else { $chunk{$key} = $value; } } push @chunks, \%chunk; } return \@chunks; } sub put { my $self = shift; my $records = shift; print "$self\n$records\n"; my @raw; foreach my $record (@$records) { my @chunks; foreach my $key (sort keys %$record) { if(ref $record->{$key}) { if(ref $record->{$key} eq 'ARRAY') { foreach my $value ( @{ $record->{$key} } ) { push @chunks, $key."=".$value; } } else { carp __PACKAGE__." cannot handle data of type ".ref $record->{$key}; } } else { push @chunks, $key."=".$record->{$key}; } } push @raw, join('&',@chunks)."\x0d\x0a"; } return \@raw; } } sub start { $_[HEAP]->{factory} = POE::Wheel::SocketFactory->new( BindAddress + => '', BindPort + => '31337', SuccessEvent + => 'factory_success', FailureEvent + => 'fatal_error', SocketProtocol + => 'tcp', Reuse + => 'on', ); } sub factory_success { my ($handle, $wheel_id) = @_[ARG0, ARG3]; my $temp_rw_id = POE::Wheel::ReadWrite->new( Handle => $handle, Driver => POE::Dri +ver::SysRW->new(), Filter => POE::Fil +ter::SimpleQueryString->new(), InputEvent => 'client_ +input', ); $_[HEAP]->{clients}->{$wheel_id} = $temp_rw_id; + $_[HEAP]->{current_client}->{$temp_rw_id->ID} = $wheel_id; print "factory_success called, creating wheel $wheel_id for handle $ +handle | ( ".$temp_rw_id->ID." )\n"; } sub client_input { my ($input, $wheel_id) = @_[ARG0, ARG1]; my $factory_id = $_[HEAP]->{current_client}->{$wheel_id}; use Data::Dumper; print Dumper $input; print "wheel_id is: $wheel_id\n"; print "factory_id is: $factory_id\n"; $_[HEAP]->{clients}->{$factory_id}->put($input); } POE::Session->create( inline_states => { _start => \&start, factory_success => \&factory_ +success, client_input => \&client_i +nput, client_error => \&client_e +rror, fatal_error => sub { die +"A fatal error occurred" }, _stop => sub {}, }, ); POE::Kernel->run();

      PS I mailed the editors of and asked them to fix the download link in the article.

        I don't have POE installed on this PC, but I'm pretty sure the reason you're losing your connection when you get a new one is your factory_success method.

        When you create a connection, you store it as:

        $_[HEAP]->{clients}->{$wheel_id} = $temp_rw_id;
        But $wheel_id is the same from one connection to another, so when the next connection comes along, you overwrite the last reference to the previous Wheel:RW, and it gets destroyed.

        Try keeping a hash of active wheels, something like

        instead. When you're done with a wheel, just "delete" it from that hash.

        This way, you're keeping a live reference to all active wheels.

        Hope this helps!

Re: POE method problem (building applications with POE)
by PodMaster (Abbot) on Dec 31, 2004 at 12:53 UTC
    `perldoc diagnostics', `perldoc perldiag'
    Can't call method ``%s'' on an undefined value
    (F) You used the syntax of a method call,
    but the slot filled by the object reference
    or package name contains an undefined value.
    Something like this will reproduce the error: 
        $BADREF = undef;
        process $BADREF 1,2,3;

    MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
    I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
    ** The third rule of perl club is a statement of fact: pod is sexy.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://418516]
Approved by Arunbear
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (3)
As of 2016-05-29 04:04 GMT
Find Nodes?
    Voting Booth?