| 1 | ########################################################################### |
|---|
| 2 | # simple plugin demonstrating how to create an add-on service for Perlbal |
|---|
| 3 | # using the plugin infrastructure |
|---|
| 4 | ########################################################################### |
|---|
| 5 | |
|---|
| 6 | package Perlbal::Plugin::EchoService; |
|---|
| 7 | |
|---|
| 8 | use strict; |
|---|
| 9 | use warnings; |
|---|
| 10 | |
|---|
| 11 | # on load we need to define the service and any parameters we want |
|---|
| 12 | sub load { |
|---|
| 13 | |
|---|
| 14 | # define the echo service, which instantiates this type of object |
|---|
| 15 | Perlbal::Service::add_role( |
|---|
| 16 | echo => \&Perlbal::Plugin::EchoService::Client::new, |
|---|
| 17 | ); |
|---|
| 18 | |
|---|
| 19 | # add up custom configuration options that people are allowed to set on the echo_service |
|---|
| 20 | Perlbal::Service::add_tunable( |
|---|
| 21 | # allow the following: |
|---|
| 22 | # SET myservice.echo_delay = 5 |
|---|
| 23 | # defines how long to wait between getting text and echoing it back |
|---|
| 24 | echo_delay => { |
|---|
| 25 | des => "Time in seconds to pause before sending text back using the echo service.", |
|---|
| 26 | default => 0, # no delay |
|---|
| 27 | check_role => "echo", |
|---|
| 28 | check_type => "int", |
|---|
| 29 | } |
|---|
| 30 | ); |
|---|
| 31 | |
|---|
| 32 | return 1; |
|---|
| 33 | } |
|---|
| 34 | |
|---|
| 35 | # remove the various things we've hooked into, this is required as a way of |
|---|
| 36 | # being good to the system... |
|---|
| 37 | sub unload { |
|---|
| 38 | Perlbal::Service::remove_tunable('echo_delay'); |
|---|
| 39 | Perlbal::Service::remove_role('echo'); |
|---|
| 40 | return 1; |
|---|
| 41 | } |
|---|
| 42 | |
|---|
| 43 | |
|---|
| 44 | ########################################################################### |
|---|
| 45 | # this is the implementation of the client that gets instantiated by the |
|---|
| 46 | # service. (which is really all a service does - instantiate the right |
|---|
| 47 | # type of client, and store some information) |
|---|
| 48 | ########################################################################### |
|---|
| 49 | |
|---|
| 50 | package Perlbal::Plugin::EchoService::Client; |
|---|
| 51 | use strict; |
|---|
| 52 | use warnings; |
|---|
| 53 | |
|---|
| 54 | use base "Perlbal::Socket"; |
|---|
| 55 | use fields ('service', # the service we're from |
|---|
| 56 | 'buf'); # the buffer of what we're reading |
|---|
| 57 | |
|---|
| 58 | # create a new object of this class |
|---|
| 59 | sub new { |
|---|
| 60 | my $class = "Perlbal::Plugin::EchoService::Client"; |
|---|
| 61 | my ($service, $sock) = @_; |
|---|
| 62 | my $self = fields::new($class); |
|---|
| 63 | $self->SUPER::new($sock); |
|---|
| 64 | $self->{service} = $service; |
|---|
| 65 | $self->{buf} = ""; # what we've read so far, not forming a complete line |
|---|
| 66 | |
|---|
| 67 | $self->watch_read(1); |
|---|
| 68 | return $self; |
|---|
| 69 | } |
|---|
| 70 | |
|---|
| 71 | # called when we are readable - i.e. there is data available |
|---|
| 72 | sub event_read { |
|---|
| 73 | my Perlbal::Plugin::EchoService::Client $self = shift; |
|---|
| 74 | |
|---|
| 75 | # try to read in 1k of data, remember to close if you get undef, as that means |
|---|
| 76 | # something went wrong, or the socket was closed |
|---|
| 77 | my $bref = $self->read(1024); |
|---|
| 78 | return $self->close() unless defined $bref; |
|---|
| 79 | $self->{buf} .= $$bref; |
|---|
| 80 | |
|---|
| 81 | # now, parse out any lines that we have gotten. this just removes data line by |
|---|
| 82 | # line so we can handle it. |
|---|
| 83 | while ($self->{buf} =~ s/^(.+?)\r?\n//) { |
|---|
| 84 | my $line = $1; |
|---|
| 85 | |
|---|
| 86 | # package up a sub to do what we want. this is in a coderef because we either |
|---|
| 87 | # need to call it now or schedule it for later. saves some duplication. |
|---|
| 88 | my $do_echo = sub { $self->write("$line\r\n"); }; |
|---|
| 89 | |
|---|
| 90 | # if they want a delay, we have to schedule this for later |
|---|
| 91 | if (my $delay = $self->{service}->{extra_config}->{echo_delay}) { |
|---|
| 92 | # schedule |
|---|
| 93 | Danga::Socket->AddTimer($delay, $do_echo); |
|---|
| 94 | |
|---|
| 95 | } else { |
|---|
| 96 | # immediately, so run it |
|---|
| 97 | $do_echo->(); |
|---|
| 98 | |
|---|
| 99 | } |
|---|
| 100 | } |
|---|
| 101 | } |
|---|
| 102 | |
|---|
| 103 | # called when we are writeable - that is, we are allowed to write data now. try to |
|---|
| 104 | # flush any existing data and then if we have nothing in the write buffer left, |
|---|
| 105 | # go ahead and stop notifying us about writeability. |
|---|
| 106 | sub event_write { |
|---|
| 107 | my Perlbal::Plugin::EchoService::Client $self = shift; |
|---|
| 108 | $self->watch_write(0) if $self->write(undef); |
|---|
| 109 | } |
|---|
| 110 | |
|---|
| 111 | # if we run into some socket error, just close |
|---|
| 112 | sub event_err { |
|---|
| 113 | my Perlbal::Plugin::EchoService::Client $self = shift; |
|---|
| 114 | $self->close; |
|---|
| 115 | } |
|---|
| 116 | |
|---|
| 117 | # same thing if we get a hup |
|---|
| 118 | sub event_hup { |
|---|
| 119 | my Perlbal::Plugin::EchoService::Client $self = shift; |
|---|
| 120 | $self->close; |
|---|
| 121 | } |
|---|
| 122 | |
|---|
| 123 | 1; |
|---|