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

NAME

CatalystX::ConsumesJMS - role for components providing Catalyst actions consuming messages

VERSION

version 1.08

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 on the_actual_destination_name
actual_type_2 on another_destination_name
actual_type_3 on another_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.