Why not adopt me?
NAME
CatalystX::ConsumesJMS - role for components providing Catalyst actions consuming messages
VERSION
version 1.05
SYNOPSIS
package MyApp::Base::MyConsumer;
use Moose;
extends 'Catalyst::Component';
with 'CatalystX::ConsumesJMS';
sub _kind_name {'MyConsumer'}
sub _wrap_code {
my ($self,$c,$destination_name,$msg_type,$route) = @_;
my $code = $route->{code};
my $extra_config = $route->{extra_config};
return sub {
my ($controller,$ctx) = @_;
my $message = $ctx->req->data;
$self->$code($message);
}
}
Then:
package MyApp::MyConsumer::One;
use Moose;
extends 'MyApp::Base::MyConsumer';
sub routes {
return {
my_input_destination => {
my_message_type => {
code => \&my_consume_method,
extra_config => $whatever,
},
...
},
...
}
}
sub my_consume_method {
my ($self,$message) = @_;
# do something
}
Also, remember to tell Catalyst to load your MyConsumer
components:
<setup_components>
search_extra [ ::MyConsumer ]
</setup_components>
DESCRIPTION
This role is to be used to define base classes for your Catalyst-based JMS / STOMP consumer applications. It's not to be consumed directly by application components. See CatalystX::RouteMaster for implementation details and a rationale. Most of the rest of this document is copied from there.
Routing
Subclasses of your component base specify which messages they are interested in, by writing a routes
sub, see the synopsis for an example.
They can specify as many destinations and message types as they want / need, and they can re-use the code
values as many times as needed.
The main limitation is that you can't have two components using the exact same destination / type pair (even if they derive from different base classes!). If you do, the results are undefined.
It is possible to alter the destination name via configuration, like:
<MyConsumer::One>
<routes_map>
my_input_destination the_actual_destination_name
</routes_map>
</MyConsumer::One>
You can also do this:
<MyConsumer::One>
<routes_map>
my_input_destination the_actual_destination_name
my_input_destination another_destination_name
</routes_map>
</MyConsumer::One>
to get the consumer to consume from two different destinations without altering the code.
You can even alter the message type via the configuration:
<MyConsumer::One>
<routes_map>
<my_input_destination the_actual_destination_name>
my_message_type actual_type_1
</my_input_destination>
<my_input_destination another_destination_name>
my_message_type actual_type_2
my_message_type actual_type_3
</my_input_destination>
</routes_map>
</Stuff::One>
That would install 3 identical actions for the following destination / message type pairs:
actual_type_1
onthe_actual_destination_name
actual_type_2
onanother_destination_name
actual_type_3
onanother_destination_name
The "code"
The hashref specified by each destination / type pair will be passed to the "_wrap_code" function (that the consuming class has to provide), and the coderef returned will be installed as the action to invoke when a message of that type is received from that destination.
Required methods
_kind_name
As in the synopsis, this should return a string that, in the names of the classes deriving from the consuming class, separates the "application name" from the "component name".
These names are mostly used to access the configuration.
_wrap_code
This method is called with:
the Catalyst application as passed to
register_actions
the destination name
the message type
the value from the
routes
corresponding to the destination name and message type slot (see "Routing" above)
You can do whatever you need in this method, but the synopsis gives a generally useful idea. You can find more examples of use at https://github.com/dakkar/CatalystX-StompSampleApps
The coderef returned will be invoked as a Catalyst action for each received message, which means it will get:
the controller instance (you should rarely need this)
the Catalyst application context
You can get the de-serialized message by calling $c->req->data
. The JMS headers will most probably be in $c->req->env
(or $c->engine->env
for older Catalyst), all keys namespaced by prefixing them with jms.
. So to get all JMS headers you could do:
my $psgi_env = $c->req->can('env')
? $c->req->env
: $c->engine->env;
my %headers = map { s/^jms\.//r, $psgi_env->{$_} }
grep { /^jms\./ } keys $psgi_env;
You can set the message to serialise in the response by setting $c->stash->{message}
, and the headers by calling $c->res->header
(yes, incoming and outgoing data are handled asymmetrically. Sorry.)
_controller_base_classes
List (not arrayref!) of class names that the controllers generated by _generate_controller_package
should inherit from. Defaults to 'Catalyst::Controller::JMS'
.
_controller_roles
List (not arrayref!) of role names that should be applied to the controllers created by _generate_controller_package
. Defaults to the empty list.
_action_extra_params
my %extra_params = $self->_action_extra_params(
$c,$destination,
$message_type,$route->{$message_type},
);
You can override this method to provide additional arguments for the create_action
call inside _generate_register_action_modifier
. For example you could return:
attributes => { MySpecialAttr => [ 'foo' ] }
to set that attribute for all generated actions. Defaults to:
attributes => { 'MessageTarget' => [$message_type] }
to get Catalyst::Controller::JMS to create an action to which that message type gets dispatched.
AUTHOR
Gianni Ceccarelli <gianni.ceccarelli@net-a-porter.com>
COPYRIGHT AND LICENSE
This software is copyright (c) 2012 by Net-a-porter.com.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.