NAME
POE::Session::Multiplex - POE session with object multiplexing
SYNOPSIS
use POE;
use POE::Session::Multiplex;
My::Component->spawn( @args );
$poe_kernel->run;
package My::Component;
sub spawn {
my( $package, @args ) = @_;
POE::Session::Multiplex->create(
package_states => [
$package => [ qw( _start ) ]
'My::Component::Object' =>
[qw( fetch decode back )]
]
args => [ @args ]
);
}
##### To add an object to the session:
my $obj = My::Component::Object->new();
$_[SESSION]->object( $name, $obj );
# or
$_[SESSION]->object_register( name => $name, object => $obj );
##### To remove an object
$_[SESSION]->object_unregister( $name );
##### Address an event to the current object:
my $state = ev"state";
##### To build a session/event tuple addressed to the current object
my $rsvp = rsvp "state";
# this tuple would be useful for getting a response from another session
$poe_kernel->post( $session=>'fetch', $params, rsvp"fetch_back" );
# and in the 'fetch' handler, you could reply with:
my $rsvp = $_[ARG1];
$poe_kernel->post( @$rsvp, $reply );
##### Posting to a specific object from inside the session
$poe_kernel->yield( evo( $name, $state ), @args );
##### Posting to a specific object from outside the session
$poe_kernel->post( evos( $session, $name, $state ), @args );
$poe_kernel->post( $session, evo( $name, $state ), @args );
DESCRIPTION
POE::Session::Multiplex allows you to have multiple objects handling events from a single POE::Session.
A standard POE design is to have one POE::Session per object and to address each object using session IDs or aliases. POE::Session::Multiplex takes the oposite approach; there is only one session and each object is addressed by manipulating the event name.
The advantage is that you save the overhead of multiple sessions. While session creation is very fast, the POE kernel garbage collection must continually verify that each session should still be alive. For a system with many sessions this could represent a non-trivial task.
Overview
Each object has a name associated with it. Events are addressed to the object by including the object's name in the event name. When invoked POE::Session::Multiplex then seperates the object name from the event name and calls an event handler on the object.
Objects are made available for multiplexing with "object_register". They are removed with "object_unregister".
POE::Session::Multiplex provides handy routines to do the event name manipulation. See "HELPER FUNCTIONS".
Event handlers for a class (aka package) must be defined before hand. This is done with either "package_register" or POE::Session's package_states.
POE::Session::Multiplex keeps a reference to all objects. This means that DESTROY will not be called until you unregister the object. It also means that you don't have to keep track of your objects. See "object_get" if you want to retrieve an object.
Objects passed to the session via object_states
are currently not multiplexed, though their events are available to objects of the same class. This could change in the future.
Event methods
POE::Session::Multiplex makes sure that a given event handler method has been set up for a given object. That is, if you define an event for a certain class, that event is not available for objects of other classes, unless they are descendents of the first class.
For example, a session is created with the following.
POE::Session::Multiplex->create(
# ...
package_states => [
Class1 => [ qw( load save ) ],
Class2 => [ qw( marshall demarshall ) ],
],
# ...
);
Objects of Class1
are only accessible via the load
and save
events and objects of Class2
are only accessible via marshall
and demarshall
. Unless Class2
is a sub-class of Class1
in which case all 4 events are available.
POE::Session::Multiplex does the same thing with object_states
. UNIVERSAL::isa is used to verify that 2 objects are of the same class.
_start and _stop vs _psm_begin and _psm_end
The _start
event is invoked directly by POE. This means that no object will be associated with the event and that the helper functions will not work. However, when an object is registered, its "_psm_begin" handler is called and when it is unregistered, its "_psm_end" handler is called. The _start handler then becomes a place to register an alias, create and register one or more objects. Furthur initialisation can happen in "_psm_begin".
sub _start {
my( $package, $session, @args ) = @_[OBJECT,SESSION,ARG0..$#_];
$poe_kernel->alias_set( 'multiplex' );
$session->object( main => $package->new( @args ) );
}
sub _psm_begin {
my( $self, @args ) = @_[OBJECT,ARG0..$#_];
$poe_kernel->sig( CHLD => ev"sig_CHLD" );
$poe_kernel->sig( INT => ev"sig_INT" );
# ....
}
Examples
When creating a socket factory, we use "ev" to create an event name addressed to the current object:
package Example;
use strict;
use warnings;
use POE;
use POE::Session::Multiplex;
use POE::Wheel::SocketFactory;
sub spawn {
my( $package, $params ) = @_;
POE::Session::Multiplex->create(
args => [ $params ],
package_states => [
$package => [ qw( _start _psm_begin connected error ) ]
] );
}
sub _start {
my( $package, $session, $params ) = @_[OBJECT,SESSION,ARG0];
# We can't call call open_connection(), because ev() won't
# have a current object.
# So we create an object
my $obj = $package->new( $params );
# And register it.
$session->object( listener => $obj );
# This will cause _psm_begin to be invoked
}
sub new {
my( $package, $params ) = @_;
return bless { params=>$params }, $package;
}
# we now have a 'current' object, so open_connection() may call ev() without
# worries
sub _psm_begin {
my( $self ) = @_;
$self->open_connection( $self->{params} );
}
sub open_connection {
my( $self, $params ) = @_[OBJECT, ARG0];
$self->{wheel} = POE::Wheel::SocketFactory->new(
%$params,
SuccessEvent => ev "connected",
FailureEvent => ev "error"
);
}
When sending a request to another session, we use "rsvp" to create an event that is addressed tot he current object and session:
$poe_kernel->post( $session, 'sum',
[ 1, 2, 3, 4 ], rsvp "reply"
);
$session
's sum event handler would then be:
sub sum_handler {
my( $array, $reply ) = @_[ ARG0, ARG1 ];
my $tot = 0;
foreach ( @$array ) { $tot += $_ }
$_[KERNEL]->post( @$reply, $tot );
}
This could also have been implemented as:
$poe_kernel->post( $session, 'sum',
[ 1, 2, 3, 4 ], ev "reply"
);
sub sum_handler {
my( $array, $reply ) = @_[ ARG0, ARG1 ];
# ...
$_[KERNEL]->post( $_[SENDER], $reply, $tot );
}
Limits
It is impossible to multiplex events that are sent from the POE kernel. Specifically, _start
, _stop
, _child
and _parent
can not be multiplexed. Use "_being" and "_psm_end" or _start
and _stop
. For _child
and _parent
, use a call to the right object:
sub _child {
my( $self, $session, @args ) = @_[OBJECT,SESSION,ARG0..$#_];
my $call = evo $self->{name}, "poe_child";
$poe_kernel->call( $session, $call, @args );
}
sub poe_child {
my( $self, $reason, $child, $retval ) = @_[OBJECT,ARG0,ARG1,ARG2];
# Do the work ...
}
Object Names
POE::Session::Multiplex requires each object to have a name. If you do not supply one when registering an object, the method __name
is called to fetch the name. This is a crude form of meta-object protocol. If your object does not implement the __name
method, a name is generated from the stringised object reference.
Note
This documentation tries to consistently use the proper term 'event' to refer to POE's confusingly named 'state'.
Event Names
Currently POE::Session::Multiplex uses event names of the form NAME->EVENT
to address EVENT
to the object named NAME
. BUT YOU MUST NOT DEPEND ON THIS BEHAVIOUR. It could very well change to EVENT@NAME
or anything else in the future. Please use the event helper functions provided.
EVENTS
POE::Session::Multiplex provides 2 object management events: _psm_begin
and _psm_end
. They are invoked synchronously whenever an object is registered or unregistered.
_psm_begin
_psm_begin
is invoked when an object is registered. This is roughly equivalent to POE's _start
. Helper functions like "ev" will have a default object to work with.
_psm_end
_psm_end
is invoked when an object is registered. This is roughly equivalent to POE's _stop
. However, there is no guarantee that _psm_end
will be called; if a session is destroyed before an object is unregistering _psm_end
won't be called. If _psm_end
is necessary, you must explicitly unregister the object:
sub _stop {
my $session = $_[SESSION];
foreach my $name ( $session->object_list ) {
$session->object_unregister( $name );
}
}
METHODS
create
POE::Session::Multiplex->create( @lots_of_stuff );
Creates a new multiplexed POE::Session. No new parameters are defined by POE::Session::Multiplex. Parameters of interest to this module are package_states
and object_states
; they define event -> object method mappings that are also used by POE::Session::Multiplex. Objects referenced in ojbect_states
are currently not multiplexed.
object_register
$_[SESSION]->object_register( $object );
$_[SESSION]->object_register( name => $name,
object => $object,
events => $events
);
Register a new $object
named $name
with the session. Optionally creating POE states in $events
.
- object
-
The object to be registered with the session. Required.
- name
-
The name of the object being registered. If omitted, "object_register" will attempt to get an object name via a
__name
method call. If this method isn't available, a stringised object reference is used.If an object with the same name has already registered, that object is unregistered.
- events
-
Optional hashref or arrayref of POE events to create. If it is a hashref, keys are POE event names, values are the names of event handler methods.
events => { load => 'load_handler', save => 'save_handler }
If you create POE events with an object, they are available to other objects of the same class. However, they will be removed when this object is unregistered. If you do not want this, use "package_register".
If defined, the "_psm_begin" event handler is invoked when an object is registered.
object_get
my $obj = $_[SESSION]->object_get( $name );
Returns the object named $name
.
object_list
my @list = $_[SESSION]->object_list;
Returns a list of names of all the currently registered objects.
object_unregister
$_[SESSION]->object_unregister( $name );
$_[SESSION]->object_unregister( $self );
Unregisters an object. This makes the object unavailable for events. Any POE events created when the object was registered are removed.
If defined, the "_psm_end" event handler is invoked.
object
# Register an object
$_[SESSION]->object( $name => $self[, $events] );
$_[SESSION]->object( $self, $events );
# Unregister an object
$_[SESSION]->object( $name );
$_[SESSION]->object( $self );
Syntactic sugar for "object_register" or "object_unregister".
package_register
$_[SESSION]->package_register( $package, $events );
Creates the POE events defined in $events
as package methods of $package
. This also makes the events available to all objects of class $package
.
It is not currently possible to unregister a package.
HELPER FUNCTIONS
POE::Session::Multiplex exports a few helper functions for manipulating the event names.
ev
$event = ev "handler";
$poe_kernel->yield( ev"handler" );
Returns an event name that is addressed to a handler of the current object. Obviously may only be called from within a multiplexed event.
evo
$state = evo( $name, "handler" );
$poe_kernel->yield( ev"handler" );
Returns an event name addressed to a handler of the $nameed object. Used when you want to address a specific object.
evs
$poe_kernel->post( evs "handler", @args );
Returns session/event tuple addressed to handler of the current object. Obviously may only be called from within a multiplexed event.
evos
$poe_kernel->post( evos( $session, $name, "handler" ), @args );
Returns session/event tuple addressed to a handler of the $nameed object in $session. Currently syntatic sugar for:
$poe_kernel->post( $session, evo( $name, "handler" ), @args );
rsvp
my $rsvp = rsvp "handler";
Returns an opaque object that may be used to post an event addressed to a handler of the current object. Obviously may only be called from within a multiplexed event.
rsvp
is used by objects to create postbacks. You may pass the rsvp to other objects or sessions. They reply with:
$poe_kernel->post( @$rsvp, @answer );
FYI, rsvp is from the French Repondez, s'il vous plais. That is, Answer, please in English.
POE::Session::PlainCall
It is unfortunately impossible to have clean multiple inheritance of POE::Session. However, POE::Session::Multiplex is compatible with POE::Session::PlainCall. It does this by checking its inheritance and implementing a few of POE::Session::PlainCall's methods.
If you wish to use both, create a session class as follows:
package My::Session;
use base qw( POE::Session::Multiplex POE::Session::PlainCall );
Then use that class to create your sessions:
My::Session->create(
package_states => [],
args => \@args
);
SEE ALSO
POE and POE::Session for details of POE.
Reflex for the final solution.
AUTHOR
Philip Gwyn, <gwyn-at-cpan.org>
COPYRIGHT AND LICENSE
Copyright (C) 2009,2010 by Philip Gwyn
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.8 or, at your option, any later version of Perl 5 you may have available.