The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

POE::Event::Message - A generic messaging protocol

VERSION

This document describes version 0.05, released December, 2005.

SYNOPSIS

 use POE;
 use POE::Event::Message;

 $MsgClass = "POE::Event::Message";


 # Creating Messages

 $body = <whatever>;              #  body can be almost anything 
 $mesg = $MsgClass->new( undef, $body );

 $mesg = $MsgClass->package( $body );   # alternative to 'new()' 


 # Creating Responses to Messages
 # Note that every message has a unique identifier. For
 # replies, the original 'message identifier' is retained
 # as an 'in response to' identifier.

 $resp = $MsgClass->new( $mesg        );
 $resp = $MsgClass->new( $mesg, $body );

 $resp = $MsgClass->new( $mesg->header()        );
 $resp = $MsgClass->new( $mesg->header(), $body );


 # Accessing Message Components

 $mesg->body( $body );            # replace message body
 $body = $mesg->body();           # get message body
 $head = $mesg->header();         # get message header

 print $mesg->dump();             # useful when debugging
 print $mesg->header()->dump();   # useful when debugging


 # Accessing Header Components

 $mesgID = $mesg->id();           # unique message identifier
 $respID = $mesg->r2id();         # 'In Response To' identifier

 $bool   = $mesg->hasRouting();
 $bool   = $mesg->hasRouteTo();
 $bool   = $mesg->hasRouteBack();

 $routing= $mesg->nextRouteType();      # post, call, remote or ''
 $bool   = $mesg->nextRouteIsRemote();  # 1 if remote    or 0 if not
 $bool   = $mesg->nextRouteIsLocal();   # 1 if post|call or 0 
 $bool   = $mesg->nextRouteIsPost();    # 1 if post      or 0 
 $bool   = $mesg->nextRouteIsCall();    # 1 if call      or 0 


 # Set default 'mode' used with 'route / routeto / routeback'

 $mesg->setMode( "call" );        # original default: "post"

 $mode = $mesg->mode();           # one of 'post' or 'call'


 # Routing Messages Within the POE environment
 # note that '$session' defaults to current session context
 # (and the first parameter ('mode') defaults to 'post'
 # unless the 'setMode()' method was used to change this)

 $mesg->addRouteTo( "post", $session, $to_event, @stateArgs );

 $mesg->addRouteBack( "post", "", 'my_handler', @stateArgs );

 ($resp) = $mesg->route( @routeArgs );


 # Routing Messages Outside the POE environment
 # 'sync' expects some sort of response message 
 # 'asynch' expects only 'delivery status' response

 $mesg->addRemoteRouteTo( $host, $port "sync" );

 $mesg->addRemoteRouteTo( $host, $port "async" );

 ($response) = $mesg->route( @routeArgs );


 # Processing Messages Within the POE environment
 # (brief example of 'object_state' event method)

 sub handle_send_response
 {   my($self, $stateArgs, $routeArgs) = @_[ OBJECT, ARG0, ARG1 ];

     my $message  = $routeArgs->[0];
     my $response = $MsgClass->new( $message, "" );

     return $response->route();
 }


 # Directly Sending and Receiving Messages Outside the POE environment
 # where '$fh' is a file handle, socket or whatever

 $MsgClass->write( $fh, $mesg );
 $mesg->write( $fh );

 $response = $MsgClass->read( $fh );
 $response = $mesg->read( $fh );

DESCRIPTION

This class is a starting point for creating a generic application messaging protocol. The intent is for this to be used as a foundation when building network and/or event-driven applications such as Web services.

Features of POE::Event::Message include the following.

  • Messages of this class have flexible routing capabilities that work both inside and outside POE-based applications.

  • A 'local' message routing mechanism is designed to be plug-compatible with POE's existing 'postback' mechanism. This is referred to as a 'routeback' mechanism in this class.

  • A 'remote' message routing mechanism can be used by message-based client scripts, or perhaps as a simple alternative to the 'IKC' (Inter Kernel Communication) mechanism.

  • Messages are delivered based on the type of the destination. A 'local' routing will trigger a POE 'post' or 'call' event, while a 'remote' routing invokes a socket connection to a particular host and port.

  • In addition, this class has the ability to introduce multiple 'forward' and/or 'reverse' routing to multiple event and/or remote host destinations. This allows for temporary interruption of normal message flow, without any of the original participants (events or whatever) knowing or caring.

  • Messages can also be sent through a file handle, such as from a non-POE child to a POE-based parent process.

  • Messages can contain almost anything including binary data and complex Perl data structures.

Messages are created using the 'new()' method or the envelope()' or package()' methods. A message body can be add during creation or afterward using the body()' method.

Responses to messages can be created using the same message creation methods but with the originating message as an added argument. This way, the unique message identifier on the originating message is added to the reply as an 'in reply to' identifier. This allows correlating originating messages with their replies, if necessary.

Message routing is flexible and can be either 'local' or 'remote'. The 'addRouteTo()' and 'addRouteBack()' methods are used to add 'local' routing to POE-based event services. This type of routing triggers POE's internal 'post' or 'call' mechanism for asynchronous or synchronous message delivery.

The 'addRemoteRouteTo()' and 'addRemoteRouteBack()' methods are used to add 'remote' routing of messages to services on remote hosts. This type of routing triggers 'read()' and 'write()' methods to send and receive messages across network sockets. Remote delivery can be either synchronous (expecting an immediate response message) or asynchronous (where response is only a 'delivery status' of either successful or unsuccessful).

Multiple forward and/or reverse routing destinations can be added to a given message. Additional local and/or remote routing destinations can be added at any time during processing. This is useful, for example, to interrupt the original message flow, as none of the original participants need be aware of any interruption(s).

When adding a routing destination, a list of 'StateArgs' can be included. These will be available to the receiving event method when the message is delivered (just like POE's postback/callback StateArgs list).

The 'route()' method is used to actually route messages to their destinations. When routing messages, a list of 'RouteArgs' can be included. These will be available to the receiving event method when the message is delivered (just like POE's postback/callback AdditionalArgs list). Note that the routed message object will be delivered as the first element of the 'RouteArgs' list reference.

This class does not explicitly use POE. This allows 'light weight' scripts that run outside the full POE environment to use this messaging protocol. When POE is not used, only the 'remote' mechanism is available. This is because the 'post' and 'call' routing semantics rely on the POE kernel.

Note also that 'local' routing destinations using 'post' and 'call' can be created outside of POE. If, for example, messages are sent via a 'remote routing' to a POE-based process, that process can then 'route()' messages using 'post' and/or 'call' to the specified event service destination(s).

When running outside of the POE environment either explicitly add a 'remote' routing -or- use the routeIsRemote() method (as shown in the Synopsis section, above) to determine if the route() method can be called on a given message object.

Constructor

new ( [ Message ] [, Body ] )
new ( [ Header ] [, Body ] )

This method instantiates a new message object. Optionally it can be used to create a response to an existing message. A message body can also be specified during creation and/or added later.

Message | Header

The optional Message or Header argument, used when creating a response to an originating message, is expected to be either the originating message or the header object from the originating message.

Every message has a guaranteed unique message identifier. The response mechanism is used to include the original message identifier as an 'in response to' identifier. This 'r2id' can then be used, when necessary, to correlate original messages with any responses received.

Body

The optional Body argument, when included, can be simple text. The Body can also be any arbitrary Perl data structure. This class provides a filtering mechanism that allows messages containing Perl data structures to be passed across file handles, including sockets and parent/child stdio file descriptors.

WARN: Use caution when using messages containing Perl 'CODE references.' While, as of version 2.05, the underlying 'Storable' module does now support passing CODE refs, this has not been tested here.

Methods

envelope ( Body )
package ( Body )

This method is included to simplify creating a new message object. The Body argument is included as the body of the message.

Body

The required Body argument can be either simple text or a Perl data structure as explained in the new method, above.

call ( [ Session ], Event [, Args ] )
post ( [ Session ], Event [, Args ] )

These methods provide direct message routing within a POE application and will invoke the corresponding method on the POE Kernel for immediate, synchronous event dispatch (call) or queued for asynchronous event dispatch (post).

Session

This optional argument is the name of an active POE 'session'. When omitted, the current POE 'session context' is used.

Event

This argument specifies the 'Event' that will be generated.

Args

The Args parameter is an optional list that can be included with either of the POE 'post' or 'call' event generators.

addRouteTo ( Mode, [ Session ], Event [, StateArgs ] )
addRouteBack ( Mode, [ Session ], Event [, StateArgs ] )

    These methods add local capabilities to messages of this class. These add routing entries to the message header that can then be invoked via the route methods, explained below.

    Mode

    The Mode must be a string equal to one of 'post' or 'call'. Later, when one of the route methods is used, will then invoke either the corresponding method on the POE Kernel for immediate, synchronous event dispatch (call) or queued for asynchronous event dispatch (post).

    Session

    This optional argument is the name of an active POE 'session'. When omitted, the current POE 'session context' is used.

    Event

    This argument is the name of an 'event' in an active POE 'Session'.

    StateArgs

    The StateArgs parameter is an optional list that can be included with either of the POE 'post' or 'call' event generators.

addRemoteRouteTo ( Host, Port, Mode, [ Session ], Event [, StateArgs ] )
addRemoteRouteBack ( Host, Port, Mode, [ Session ], Event [, StateArgs ] )

    These methods add remote capabilities to messages of this class. These add routing entries to the message header that can then be invoked via the route methods, explained below.

    Host

    The remote host to which the message will be routed.

    Port

    The network port on the remote host.

    Mode

    The Mode must be a string equal to one of 'sync' or 'asynch'. Later, when one of the route methods is used, will then either expect an immediate response message ('sync') or simply return a status indicating success or failure of 'message delivery' only, and not wait for any other response. This is useful when some other process, perhaps on another host, will handle the response, if any.

    The remaining arguments are identical as the corresponding addRouteTo() and addRouteBack() methods described above.

route ( [ RouteArgs ] )
routeto ( [ RouteArgs ] )
routeback ( [ RouteArgs ] )

These methods add auto-routing capabilities to messages of this class. These extract routing entries from the message header that are then used to generate POE events using immediate dispatch (when the routing entry was created with a Mode of 'call') or queued for delayed dispatch (when the routing entry was created with a Mode of 'post').

The route method will first extract routeTo entries and, when empty, will extract routeBack entries.

The routeto method will extract only those entries created using the addRouteTo method, shown above, while the routeback method will extract only those entries created using the addRouteBack method, also shown above.

If/when the routing list(s) are empty, these methods will silently do nothing. If this should cause an error, use the hasRouting, hasRouteTo or hasRouteBack methods to determine when an error should be generated.

NOTE: The 'local' routing mechanism is designed to be plug-compatible with POE's existing 'postback' mechanism. These methods add the ability to introduce multiple forward and/or reverse event destinations and can be used to temporarially interrupt normal message routing (and none of the handlers that expect to process the message will even be aware of the interruption).

NOTE: The 'remote' routing mechanism can be used as a simple alternative to the 'IKC' (Inter Kernel Communication) mechanism.

RouteArgs

The RouteArgs parameter is an optional list that can be added when any of the 'route()' methods are invoked. These arguments are then received by whichever event handler happens to receive the routed message. Note that the message itself is included as the first argument in the RouteArgs list. See the Synopsis section, above, and the Examples section, below, for usage syntax.

send ( FH [, Message ] )
write ( FH [, Message ] )

These methods provide the ability to send the message object through an open file handle. The 'FH' parameter should be an open file handle and the 'Message' parameter, when used, is expected to be an object of this class. When the 'Message' is omitted, the current object is sent through the file handle.

WARN: Use caution when using these methods with messages containing Perl 'CODE references.' While the underlying 'Storable' module does now support passing CODE refs, this has not been tested here.

FH

An open file handle.

Message

A message object of this class. Optional when these methods are called as object methods on an existing object, and required when these methods are called as class methods.

When omitted, Message defaults to the current message object.

read ( FH )
recv ( FH )

These methods provide the ability to receive a message object through an open file handle.

WARN: Use caution when using these methods with messages containing Perl 'CODE references.' While the underlying 'Storable' module does now support passing CODE refs, this has not been tested here.

FH

An open file handle.

id ()

This method is used to determine the unique message identifier of the current message object.

inResponseToId ()
r2id ()

For 'response messages' these methods are used to determine the message identifier of the original message.

Note: This is why, when creating a response message, it is important to include an originating message as the first argument to the 'new()' method. This way the original message ID can be collected and included as the 'r2id'.

hasRouting ()
hasRouteTo ()
hasRouteBack ()

Miscellaneous methods used to determine 'header' state. Each of these returns the next 'routing' header entry, if any, or an 'undef' value. The 'routing' entry returned is retained in the message header.

nextRouteType ()
nextRouteIsRemote ()
nextRouteIsLocal ()
nextRouteIsPost ()
nextRouteIsCall ()

Miscellaneous methods used to determine 'header' state. Each of these, with the exception of 'nextRouteType()', returns a boolean value where '0' indicates the test is false and '1' indicates the test is true.

The 'nextRouteType()' method returns a string value that is one of 'remote', 'call', 'post' or '' (an empty string).

status

This method returns the current error status of a message object. When the status code is non-zero, the error message will contain text of the corresponding error.

dump

This method is included for convenience when developing or debugging applications that use this class. This does not produce a 'pretty' output, but is formatted to show the contents of the message object and the message header object, when one exists.

EXAMPLES

The following is an incomplete example of creating a network interface that accepts message objects from a client script and returns message responses.

For a complete working example of this see the 'TCP' and 'Cmd' server classes in the 'POE-Component-GCS' distribution available on CPAN.

 package POE::Component::GCS::Server::TCP;

 $ServerAlias = "MsgServer";
 $ServerPort  = 12345;

 sub spawn
 {   my($self) = @_;

     POE::Component::Server::TCP->new(
         Port     => $ServerPort,    # Bind Port
         Alias    => $ServerAlias,   # necessary for 'routeback' response

         ClientInput   => \&handle_msg_input,
         ClientFilter  => "POE::Filter::Reference",

         # Most of the events we need are pre-defined by POE.
         # Add one here to so we can create a "routeback"
         # event target to simplify the response mechanism.

         InlineStates => {
              client_msg_output => \&handle_msg_output,
         },
     );
     return;
 } 

 sub handle_msg_input
 {   my($kernel, $heap, $session, $input) = @_[KERNEL, HEAP, SESSION, ARG0];

     # The $input variable here is expected to be a "$MsgClass" object.
     # Create a msg routeback wormhole using "post" for asynch delivery.

     $input->addRouteBack( post => undef, "client_msg_output", undef );

     # Then "dispatch" the message to some command processor

     $CmdClass->dispatch( $input );        # (non-POE method)
     return;
 }

 sub handle_msg_output                # 'client_msg_output' event handler
 {   my($kernel, $heap, $session, $state_args, $result_args) =
     @_[ KERNEL,  HEAP,  SESSION,  ARG0,        ARG1       ];

     # This handler is the "routeback" target that returns data
     # to client ($reply is expected to be a "$MsgClass" object).
     # Here we simply return it to the waiting Msg-based client.

     my $reply = $result_args->[0];

     # The "client" attribute was added by POE to the heap to
     # facilitate sending the correct response to the correct
     # client. All we need to do is "put" the "$reply".

     $heap->{client}->put( $reply );

     my $sessionId = $session->ID;
     $Log->write(4, "$MsgAlias: client reply sent (cid=$sessionId)");
     return;
 }

Then, to continue this example for a moment more, if the '$CmdClass' containing the 'dispatch()' method were to determine that the incoming message was a valid message object but an illegal commnad, the entire response mechanism is as simple as this next method. In this case, the '$reply' sent ('put') just above contains the following error.

 package POE::Component::GCS::Server::Cmd;

 sub _invalid_request
 {   my($class, $message, $error_text) = @_;

     $error_text ||= "invalid command";

     # We must reply with something or TCP clients will hang. 
     # Simply closing the socket results in a "no response 
     # from server" error on the client side.

     my $command  = $message->body();
     my $response = $MsgClass->new( $message, $command );
     $response->setErr( -1, $error_text );
    
     $response->route();       # return error to waiting TCP client
     return;
 }

WARNINGS

When routing message objects through file handles or sockets use caution with messages containing Perl 'CODE references.' While the underlying 'Storable' module does now support passing CODE refs, this has not been tested here.

Also note that this class does not explicitly use POE. This allows 'light weight' scripts that run outside the full POE environment to use this messaging protocol. When POE is not used, the 'route()' mechanism is only available when the next routing is to a remote host. This is because the 'post' and 'call' routing semantics rely on the POE kernel. Without POE, only the 'read' and 'write' mechanism is available to send messages across file handles or sockets.

However, the 'addRouteTo()' and 'addRouteBack()' methods are available outside of POE, and can be used to add 'post' and 'call' routing. For example, if messages are sent via a 'remote route' to a POE-based process, that process can then 'route()' messages which contain pre-defined 'post' and/or 'call' destinations.

When running outside of the POE environment either explicitly add a 'remote' routing -or- use the routeIsRemote() method (as shown in the Synopsis section, above) to determine if the route() method can be called on a given message object.

DEPENDENCIES

This class depends upon the following classes:

 POE::Event::Message::Header
 POE::Event::Message::UniqueID
 POE::Driver::SysRW
 POE::Filter::Reference

 POE::Kernel (optional, but required for 'post' and 'call' message routing)

SEE ALSO

See POE::Event::Message::Header and POE::Event::Message::UniqueID.

See the POE-Component-GCS distribution available on CPAN for a complete working model of a Generic Client/Server that uses message-based events.

AUTHOR

Chris Cobb [no dot spam at ccobb dot net]

COPYRIGHT

Copyright (c) 2005-2010 by Chris Cobb, All rights reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

2 POD Errors

The following errors were encountered while parsing the POD:

Around line 918:

You can't have =items (as at line 924) unless the first thing after the =over is an =item

Around line 954:

You can't have =items (as at line 960) unless the first thing after the =over is an =item