[djabberd] mart, r550: Basic support for in-process "components...

Artur Bergman sky at crucially.net
Sat Jul 8 17:00:28 UTC 2006


The register_subdomain function is slated for deprecation because I  
want to kill subdomains to make the code easier, so you will have to  
declare the vhost and then tell the component to take it over  
completely.

I also don't like the indirection classes, why cannot Component.pm be  
a Plugin.pm?

Artur


On Jul 8, 2006, at 7:34 AM, commits at code.sixapart.com wrote:

> Basic support for in-process "components".
>
> Here I'm defining a component as an agent that handles JIDs for an  
> entire domain. This is differs from a bot, which handles only a  
> single node.
>
> A plugin is provided for making a component appear as a subdomain  
> of a vhost. However, the component subclasses don't really care  
> what calls them so you could theoretically make a Delivery plugin  
> that turns an entire VHost into a component, or whatever.
>
>
> A   trunk/lib/DJabberd/Component/
> A   trunk/lib/DJabberd/Component/Example.pm
> A   trunk/lib/DJabberd/Component.pm
> A   trunk/lib/DJabberd/Plugin/Component.pm
>
>
> Added: trunk/lib/DJabberd/Component/Example.pm
> ===================================================================
> --- trunk/lib/DJabberd/Component/Example.pm	2006-07-08 11:26:31 UTC  
> (rev 549)
> +++ trunk/lib/DJabberd/Component/Example.pm	2006-07-08 14:34:02 UTC  
> (rev 550)
> @@ -0,0 +1,107 @@
> +
> +
> +=head1 NAME
> +
> +DJabberd::Component::Example - An example DJabberd service component
> +
> +=head1 SYNOPSIS
> +
> +  <Plugin DJabberd::Plugin::Component>
> +     Subdomain example
> +     Class DJabberd::Component::Example
> +     Greeting Hello, world!
> +  </Plugin>
> +
> +This class implements a very simple component that responds to all  
> incoming messages
> +with a predefined greeting and which returns a vCard for itself  
> when one is requested.
> +You can change the greeting returned using the optional Greeting  
> configuration setting
> +as above.
> +
> +=cut
> +
> +package DJabberd::Component::Example;
> +use base DJabberd::Component;
> +use DJabberd::Util qw(exml);
> +use DJabberd::Log;
> +
> +our $logger = DJabberd::Log->get_logger();
> +
> +sub initialize {
> +    my ($self, $opts) = @_;
> +
> +    $self->{greeting} = $opts->{greeting} || "Hi! I'm an example  
> DJabberd component!";
> +}
> +
> +sub handle_stanza {
> +    my ($self, $vhost, $cb, $stanza) = @_;
> +
> +    $logger->info("Got stanza: ".$stanza->as_summary);
> +
> +    # We only want stanzas addressed to the domain, since there
> +    # aren't any nodes under this component.
> +    if ($stanza->to_jid->as_string ne $self->domain) {
> +        $logger->info("Message to ".$stanza->to_jid->as_string.",  
> not ".$self->domain.". Discarding.");
> +        $cb->decline;
> +        return;
> +    }
> +
> +    if ($stanza->isa('DJabberd::IQ')) {
> +
> +        if ($stanza->signature eq 'get-{vcard-temp}vCard') {
> +
> +            $logger->info("Got vCard request from ".$stanza- 
> >from_jid->as_string);
> +
> +            my $response = $self->_make_response($stanza);
> +            $response->attrs->{"{}type"} = "result";
> +            $response->set_raw("<vCard xmlns='vcard- 
> temp'><FN>Example DJabberd component</FN></vCard>");
> +            $logger->info("Responding with ".$response->as_xml);
> +            $response->deliver($vhost);
> +
> +            $cb->delivered;
> +            return;
> +
> +        }
> +        else {
> +            $logger->info("Got unrecognized IQ stanza from ". 
> $stanza->from_jid->as_string);
> +            $cb->decline;
> +            return;
> +        }
> +    }
> +    elsif ($stanza->isa('DJabberd::Message')) {
> +        my $from = $stanza->from_jid;
> +
> +        $logger->info("Got message from ".$from->as_string);
> +
> +        my $response = $self->_make_response($stanza);
> +
> +        $response->attrs->{"{}type"} = "normal";
> +        $response->set_raw("<body>".exml($self->{greeting})."</ 
> body>");
> +
> +        $logger->info("Responding with ".$response->as_xml);
> +
> +        $response->deliver($vhost);
> +
> +        $cb->delivered;
> +        return;
> +    }
> +
> +    $logger->info("I don't know what to do with $stanza");
> +    $cb->decline;
> +}
> +
> +
> +sub _make_response {
> +    my ($self, $stanza) = @_;
> +
> +    my $response = $stanza->clone;
> +    my $from = $stanza->from;
> +    my $to   = $stanza->to;
> +
> +    $response->set_to($from);
> +    $to ? $response->set_from($to) : delete($response->attrs->{"{} 
> from"});
> +
> +    return $response;
> +}
> +
> +
> +1;
>
> Added: trunk/lib/DJabberd/Component.pm
> ===================================================================
> --- trunk/lib/DJabberd/Component.pm	2006-07-08 11:26:31 UTC (rev 549)
> +++ trunk/lib/DJabberd/Component.pm	2006-07-08 14:34:02 UTC (rev 550)
> @@ -0,0 +1,65 @@
> +
> +=head1 NAME
> +
> +DJabberd::Component - Abstract class representing a component in  
> DJabberd
> +
> +=head1 SYNOPSIS
> +
> +    package MyPackage::DJabberd::MyComponent;
> +    use base DJabberd::Component;
> +
> +    sub initialize {
> +        my ($self, $opts) = @_;
> +
> +        # ... perform initialization
> +    }
> +
> +    sub handle_stanza {
> +        my ($self, $vhost, $cb, $stanza) = @_;
> +
> +        # ... handle the given stanza
> +    }
> +
> +This class provides a parent class for all DJabberd components.  
> Components
> +that inherit from this class can then be used directly by the  
> server via the
> +DJabberd::Plugin::Component plugin, or used directly by other  
> classes.
> +
> +See DJabberd::Component::Example for an example component  
> implementation.
> +
> +TODO: Write more docs
> +
> +=cut
> +
> +package DJabberd::Component;
> +
> +use strict;
> +use DJabberd::Log;
> +
> +our $logger = DJabberd::Log->get_logger();
> +
> +sub new {
> +    my ($class, $domain, $opts) = @_;
> +
> +    my $self = bless {
> +        _component_domain => $domain,
> +    }, $class;
> +
> +    $self->initialize($opts);
> +
> +    return $self;
> +}
> +
> +sub initialize { }
> +
> +sub domain {
> +    return $_[1] ? $_[0]->{_component_domain} = $_[1] : $_[0]-> 
> {_component_domain};
> +}
> +
> +sub handle_stanza {
> +    my ($self, $vhost, $cb, $stanza) = @_;
> +
> +    $logger->warn("handle_stanza not implemented for $self");
> +    $cb->decline;
> +}
> +
> +1;
>
> Added: trunk/lib/DJabberd/Plugin/Component.pm
> ===================================================================
> --- trunk/lib/DJabberd/Plugin/Component.pm	2006-07-08 11:26:31 UTC  
> (rev 549)
> +++ trunk/lib/DJabberd/Plugin/Component.pm	2006-07-08 14:34:02 UTC  
> (rev 550)
> @@ -0,0 +1,61 @@
> +
> +package DJabberd::Plugin::Component;
> +
> +use strict;
> +use base 'DJabberd::Plugin';
> +use warnings;
> +use Carp;
> +our $logger = DJabberd::Log->get_logger();
> +
> +
> +sub set_config_subdomain {
> +    my ($self, $subdomain) = @_;
> +    $self->{subdomain} = $subdomain;
> +}
> +
> +sub set_config_class {
> +    my ($self, $class) = @_;
> +    $logger->logdie("Invalid classname $class") if $class =~ /[^ 
> \w:]/;
> +    $self->{class} = $class;
> +}
> +
> +# Pass any unrecognised configuration parameters on to the  
> component itself
> +sub set_config__option {
> +    my ($self, $key, $val) = @_;
> +    $self->{component_opts} ||= {};
> +    $self->{component_opts}{$key} = $val;
> +}
> +
> +sub finalize {
> +    my ($self) = @_;
> +
> +    $logger->logdie("The Class directive is required") unless  
> $self->{class};
> +    $logger->logdie("The Subdomain directive is required") unless  
> $self->{subdomain};
> +
> +    my $loaded = eval "use $self->{class}; 1;";
> +    $logger->logdie("Unable to load component class $self-> 
> {class}: $@") unless $loaded;
> +}
> +
> +sub register {
> +    my ($self, $vhost) = @_;
> +
> +    $self->{domain} = $self->{subdomain} . "." . $vhost->server_name;
> +    $vhost->register_subdomain($self->{subdomain}, __PACKAGE__);
> +
> +    $self->{component} = $self->{class}->new($self->{domain},  
> $self->{component_opts});
> +    $logger->logdie("Failed to instantiate component $self-> 
> {class}") unless ref $self->{component};
> +
> +    $logger->info("Attaching component $self->{domain} ($self-> 
> {class})");
> +
> +    $self->{vhost} = $vhost;
> +    Scalar::Util::weaken($self->{vhost});
> +
> +    $vhost->register_hook('deliver',sub {
> +        if ($_[2]->to_jid && $_[2]->to_jid->domain eq $self-> 
> {domain}) {
> +            $logger->debug("Delivering ".$_[2]->element_name."  
> stanza to component ".$self->{domain});
> +            $self->{component}->handle_stanza(@_);
> +        }
> +    });
> +}
> +
> +1;
>
>



More information about the Djabberd mailing list